forked from Telodendria/Telodendria
[MOD/FIX] More descriptive errors, "fix" join
I'll need to be able to "propagate" a user ref as much as possible/be able to lock a user for read-only operations(as DB intents now allow)
This commit is contained in:
parent
aa6e375167
commit
620cf91ba0
11 changed files with 324 additions and 98 deletions
|
@ -46,7 +46,7 @@ HtmlBegin(Stream * stream, char *title)
|
||||||
"<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">"
|
"<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">"
|
||||||
"<title>%s | Telodendria</title>"
|
"<title>%s | Telodendria</title>"
|
||||||
"<link rel=\"stylesheet\" href=\"/_matrix/static/telodendria.css\">"
|
"<link rel=\"stylesheet\" href=\"/_matrix/static/telodendria.css\">"
|
||||||
"<script src=\"/_matrix/static/telodendria.js\"></script>"
|
"<script src=\"/_matrix/static/telodendria.js\"></script>"
|
||||||
"</head>"
|
"</head>"
|
||||||
"<body>"
|
"<body>"
|
||||||
"<pre class=\"logo\">"
|
"<pre class=\"logo\">"
|
||||||
|
|
|
@ -297,7 +297,8 @@ start:
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tConfig.log.timestampFormat || !StrEquals(tConfig.log.timestampFormat, "default"))
|
if (!tConfig.log.timestampFormat ||
|
||||||
|
!StrEquals(tConfig.log.timestampFormat, "default"))
|
||||||
{
|
{
|
||||||
LogConfigTimeStampFormatSet(LogConfigGlobal(), tConfig.log.timestampFormat);
|
LogConfigTimeStampFormatSet(LogConfigGlobal(), tConfig.log.timestampFormat);
|
||||||
}
|
}
|
||||||
|
|
291
src/Room.c
291
src/Room.c
|
@ -126,7 +126,7 @@ RoomPopulate(Room *room, User *user, RoomCreateRequest *req, ServerPart s)
|
||||||
JsonValueFree(HashMapSet(content, key, JsonValueDuplicate(val)));
|
JsonValueFree(HashMapSet(content, key, JsonValueDuplicate(val)));
|
||||||
}
|
}
|
||||||
event = RoomEventCreate(sender_str, "m.room.create", "", content);
|
event = RoomEventCreate(sender_str, "m.room.create", "", content);
|
||||||
JsonFree(RoomEventSend(room, event));
|
JsonFree(RoomEventSend(room, event, NULL));
|
||||||
JsonFree(event);
|
JsonFree(event);
|
||||||
UserAddJoin(user, room->id);
|
UserAddJoin(user, room->id);
|
||||||
|
|
||||||
|
@ -134,7 +134,7 @@ RoomPopulate(Room *room, User *user, RoomCreateRequest *req, ServerPart s)
|
||||||
content = HashMapCreate();
|
content = HashMapCreate();
|
||||||
JsonSet(content, JsonValueString("join"), 1, "membership");
|
JsonSet(content, JsonValueString("join"), 1, "membership");
|
||||||
event = RoomEventCreate(sender_str, "m.room.member", sender_str, content);
|
event = RoomEventCreate(sender_str, "m.room.member", sender_str, content);
|
||||||
JsonFree(RoomEventSend(room, event));
|
JsonFree(RoomEventSend(room, event, NULL));
|
||||||
JsonFree(event);
|
JsonFree(event);
|
||||||
|
|
||||||
/* m.room.power_levels */
|
/* m.room.power_levels */
|
||||||
|
@ -155,7 +155,7 @@ RoomPopulate(Room *room, User *user, RoomCreateRequest *req, ServerPart s)
|
||||||
HashMapSet(content, key, JsonValueDuplicate(val));
|
HashMapSet(content, key, JsonValueDuplicate(val));
|
||||||
}
|
}
|
||||||
event = RoomEventCreate(sender_str, "m.room.power_levels", "", content);
|
event = RoomEventCreate(sender_str, "m.room.power_levels", "", content);
|
||||||
JsonFree(RoomEventSend(room, event));
|
JsonFree(RoomEventSend(room, event, NULL));
|
||||||
JsonFree(event);
|
JsonFree(event);
|
||||||
|
|
||||||
/* Presets */
|
/* Presets */
|
||||||
|
@ -203,7 +203,7 @@ RoomPopulate(Room *room, User *user, RoomCreateRequest *req, ServerPart s)
|
||||||
event = RoomEventCreate( \
|
event = RoomEventCreate( \
|
||||||
sender_str, \
|
sender_str, \
|
||||||
"m.room." #p, "", content); \
|
"m.room." #p, "", content); \
|
||||||
JsonFree(RoomEventSend(room, event)); \
|
JsonFree(RoomEventSend(room, event, NULL)); \
|
||||||
JsonFree(event); \
|
JsonFree(event); \
|
||||||
} \
|
} \
|
||||||
} \
|
} \
|
||||||
|
@ -227,7 +227,7 @@ RoomPopulate(Room *room, User *user, RoomCreateRequest *req, ServerPart s)
|
||||||
}
|
}
|
||||||
HashMapSet(rseObject, "sender", JsonValueString(sender_str));
|
HashMapSet(rseObject, "sender", JsonValueString(sender_str));
|
||||||
|
|
||||||
JsonFree(RoomEventSend(room, rseObject));
|
JsonFree(RoomEventSend(room, rseObject, NULL));
|
||||||
JsonFree(rseObject);
|
JsonFree(rseObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,7 +237,7 @@ RoomPopulate(Room *room, User *user, RoomCreateRequest *req, ServerPart s)
|
||||||
content = HashMapCreate();
|
content = HashMapCreate();
|
||||||
JsonSet(content, JsonValueString(req->name), 1, "name");
|
JsonSet(content, JsonValueString(req->name), 1, "name");
|
||||||
event = RoomEventCreate(sender_str, "m.room.name", "", content);
|
event = RoomEventCreate(sender_str, "m.room.name", "", content);
|
||||||
JsonFree(RoomEventSend(room, event));
|
JsonFree(RoomEventSend(room, event, NULL));
|
||||||
JsonFree(event);
|
JsonFree(event);
|
||||||
}
|
}
|
||||||
if (req->topic)
|
if (req->topic)
|
||||||
|
@ -245,7 +245,7 @@ RoomPopulate(Room *room, User *user, RoomCreateRequest *req, ServerPart s)
|
||||||
content = HashMapCreate();
|
content = HashMapCreate();
|
||||||
JsonSet(content, JsonValueString(req->topic), 1, "topic");
|
JsonSet(content, JsonValueString(req->topic), 1, "topic");
|
||||||
event = RoomEventCreate(sender_str, "m.room.topic", "", content);
|
event = RoomEventCreate(sender_str, "m.room.topic", "", content);
|
||||||
JsonFree(RoomEventSend(room, event));
|
JsonFree(RoomEventSend(room, event, NULL));
|
||||||
JsonFree(event);
|
JsonFree(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,7 +269,7 @@ RoomPopulate(Room *room, User *user, RoomCreateRequest *req, ServerPart s)
|
||||||
event = RoomEventCreate(
|
event = RoomEventCreate(
|
||||||
sender_str,
|
sender_str,
|
||||||
"m.room.canonical_alias", "", content);
|
"m.room.canonical_alias", "", content);
|
||||||
JsonFree(RoomEventSend(room, event));
|
JsonFree(RoomEventSend(room, event, NULL));
|
||||||
JsonFree(event);
|
JsonFree(event);
|
||||||
|
|
||||||
Free(fullStr);
|
Free(fullStr);
|
||||||
|
@ -299,7 +299,7 @@ RoomPopulate(Room *room, User *user, RoomCreateRequest *req, ServerPart s)
|
||||||
}
|
}
|
||||||
|
|
||||||
event = RoomEventCreate(sender_str, "m.room.power_levels", "", pl_content);
|
event = RoomEventCreate(sender_str, "m.room.power_levels", "", pl_content);
|
||||||
JsonFree(RoomEventSend(room, event));
|
JsonFree(RoomEventSend(room, event, NULL));
|
||||||
JsonFree(event);
|
JsonFree(event);
|
||||||
|
|
||||||
JsonValueFree(JsonSet(
|
JsonValueFree(JsonSet(
|
||||||
|
@ -688,7 +688,7 @@ PopulateEventV1(Room * room, HashMap * event, PduV1 * pdu, ServerPart serv)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
static bool
|
static bool
|
||||||
AuthoriseCreateV1(PduV1 pdu)
|
AuthoriseCreateV1(PduV1 pdu, char **errp)
|
||||||
{
|
{
|
||||||
bool ret = true;
|
bool ret = true;
|
||||||
CommonID sender = { 0 }, room_id = { 0 };
|
CommonID sender = { 0 }, room_id = { 0 };
|
||||||
|
@ -696,12 +696,20 @@ AuthoriseCreateV1(PduV1 pdu)
|
||||||
|
|
||||||
if (ArraySize(pdu.auth_events) > 0)
|
if (ArraySize(pdu.auth_events) > 0)
|
||||||
{
|
{
|
||||||
|
if (errp)
|
||||||
|
{
|
||||||
|
*errp = "Room creation event has auth events";
|
||||||
|
}
|
||||||
ret = false;
|
ret = false;
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
if (!ParseCommonID(pdu.room_id, &room_id) ||
|
if (!ParseCommonID(pdu.room_id, &room_id) ||
|
||||||
!ParseCommonID(pdu.sender, &sender))
|
!ParseCommonID(pdu.sender, &sender))
|
||||||
{
|
{
|
||||||
|
if (errp)
|
||||||
|
{
|
||||||
|
*errp = "Couldn't parse the sender/room ID";
|
||||||
|
}
|
||||||
ret = false;
|
ret = false;
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
@ -709,12 +717,20 @@ AuthoriseCreateV1(PduV1 pdu)
|
||||||
id_serv = ParserRecomposeServerPart(room_id.server);
|
id_serv = ParserRecomposeServerPart(room_id.server);
|
||||||
if (!StrEquals(sender_serv, id_serv))
|
if (!StrEquals(sender_serv, id_serv))
|
||||||
{
|
{
|
||||||
|
if (errp)
|
||||||
|
{
|
||||||
|
*errp = "Room is not properly namespaced";
|
||||||
|
}
|
||||||
ret = false;
|
ret = false;
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
/* TODO: Check room_version as in step 1.3 */
|
/* TODO: Check room_version as in step 1.3 */
|
||||||
if (!HashMapGet(pdu.content, "creator"))
|
if (!HashMapGet(pdu.content, "creator"))
|
||||||
{
|
{
|
||||||
|
if (errp)
|
||||||
|
{
|
||||||
|
*errp = "Room creation event has auth events";
|
||||||
|
}
|
||||||
ret = false;
|
ret = false;
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
@ -756,7 +772,10 @@ ValidAuthEventV1(PduV1 *auth_pdu, PduV1 *pdu)
|
||||||
JsonValue *third_pid =
|
JsonValue *third_pid =
|
||||||
JsonGet(pdu->content, 1, "third_party_invite");
|
JsonGet(pdu->content, 1, "third_party_invite");
|
||||||
|
|
||||||
if (IsState(auth_pdu, "m.room.member", auth_pdu->sender))
|
/* The PDU's state_key is the target. So we check if if the
|
||||||
|
* auth PDU would count as the target's membership. Was very fun to
|
||||||
|
* find that out when I wanted to kick my 'yukari' alt. */
|
||||||
|
if (IsState(auth_pdu, "m.room.member", pdu->state_key))
|
||||||
{
|
{
|
||||||
/* TODO: Check if it's the latest in terms of [pdu] */
|
/* TODO: Check if it's the latest in terms of [pdu] */
|
||||||
return true;
|
return true;
|
||||||
|
@ -788,6 +807,8 @@ ValidAuthEventV1(PduV1 *auth_pdu, PduV1 *pdu)
|
||||||
static bool
|
static bool
|
||||||
VerifyPDUV1(PduV1 *auth_pdu)
|
VerifyPDUV1(PduV1 *auth_pdu)
|
||||||
{
|
{
|
||||||
|
/* TODO: The PDU could come from an unknown source, which may lack
|
||||||
|
* the tools to verify softfailing(or a source that we may not trust)*/
|
||||||
/* TODO:
|
/* TODO:
|
||||||
* https://spec.matrix.org/v1.7/server-server-api/
|
* https://spec.matrix.org/v1.7/server-server-api/
|
||||||
* #checks-performed-on-receipt-of-a-pdu */
|
* #checks-performed-on-receipt-of-a-pdu */
|
||||||
|
@ -804,7 +825,7 @@ VerifyPDUV1(PduV1 *auth_pdu)
|
||||||
* soft-failed */
|
* soft-failed */
|
||||||
}
|
}
|
||||||
static bool
|
static bool
|
||||||
ConsiderAuthEventsV1(Room * room, PduV1 pdu)
|
ConsiderAuthEventsV1(Room * room, PduV1 pdu, char **errp)
|
||||||
{
|
{
|
||||||
char *ignored;
|
char *ignored;
|
||||||
size_t i;
|
size_t i;
|
||||||
|
@ -823,6 +844,10 @@ ConsiderAuthEventsV1(Room * room, PduV1 pdu)
|
||||||
{
|
{
|
||||||
JsonFree(event);
|
JsonFree(event);
|
||||||
HashMapFree(state_keytype);
|
HashMapFree(state_keytype);
|
||||||
|
if (errp)
|
||||||
|
{
|
||||||
|
*errp = "Couldn't parse an auth event";
|
||||||
|
}
|
||||||
return false; /* Yeah... we aren't doing that. */
|
return false; /* Yeah... we aren't doing that. */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -834,6 +859,11 @@ ConsiderAuthEventsV1(Room * room, PduV1 pdu)
|
||||||
if (HashMapGet(state_keytype, key_type_id))
|
if (HashMapGet(state_keytype, key_type_id))
|
||||||
{
|
{
|
||||||
/* Duplicate found! We actually ignore it's actual value. */
|
/* Duplicate found! We actually ignore it's actual value. */
|
||||||
|
if (errp)
|
||||||
|
{
|
||||||
|
*errp = "Duplicate auth event was found";
|
||||||
|
}
|
||||||
|
|
||||||
JsonFree(event);
|
JsonFree(event);
|
||||||
PduV1Free(&auth_pdu);
|
PduV1Free(&auth_pdu);
|
||||||
|
|
||||||
|
@ -851,6 +881,10 @@ ConsiderAuthEventsV1(Room * room, PduV1 pdu)
|
||||||
* described in the server specification, reject. */
|
* described in the server specification, reject. */
|
||||||
if (!ValidAuthEventV1(&auth_pdu, &pdu))
|
if (!ValidAuthEventV1(&auth_pdu, &pdu))
|
||||||
{
|
{
|
||||||
|
if (errp)
|
||||||
|
{
|
||||||
|
*errp = "Invalid authevent given.";
|
||||||
|
}
|
||||||
JsonFree(event);
|
JsonFree(event);
|
||||||
PduV1Free(&auth_pdu);
|
PduV1Free(&auth_pdu);
|
||||||
HashMapFree(state_keytype);
|
HashMapFree(state_keytype);
|
||||||
|
@ -861,6 +895,10 @@ ConsiderAuthEventsV1(Room * room, PduV1 pdu)
|
||||||
* TODO */
|
* TODO */
|
||||||
if (!VerifyPDUV1(&auth_pdu))
|
if (!VerifyPDUV1(&auth_pdu))
|
||||||
{
|
{
|
||||||
|
if (errp)
|
||||||
|
{
|
||||||
|
*errp = "Event depends on rejected PDUs";
|
||||||
|
}
|
||||||
PduV1Free(&auth_pdu);
|
PduV1Free(&auth_pdu);
|
||||||
JsonFree(event);
|
JsonFree(event);
|
||||||
HashMapFree(state_keytype);
|
HashMapFree(state_keytype);
|
||||||
|
@ -878,6 +916,10 @@ ConsiderAuthEventsV1(Room * room, PduV1 pdu)
|
||||||
PduV1Free(&auth_pdu);
|
PduV1Free(&auth_pdu);
|
||||||
}
|
}
|
||||||
HashMapFree(state_keytype);
|
HashMapFree(state_keytype);
|
||||||
|
if (!room_create && errp)
|
||||||
|
{
|
||||||
|
*errp = "Room creation event was not in the PDU's auth events";
|
||||||
|
}
|
||||||
return room_create; /* Step 2.4 is actually done here. */
|
return room_create; /* Step 2.4 is actually done here. */
|
||||||
}
|
}
|
||||||
static bool
|
static bool
|
||||||
|
@ -915,16 +957,24 @@ end:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
static bool
|
static bool
|
||||||
AuthoriseAliasV1(PduV1 pdu)
|
AuthoriseAliasV1(PduV1 pdu, char **errp)
|
||||||
{
|
{
|
||||||
/* Step 4.1: If event has no state_key, reject. */
|
/* Step 4.1: If event has no state_key, reject. */
|
||||||
if (!pdu.state_key || StrEquals(pdu.state_key, ""))
|
if (!pdu.state_key || StrEquals(pdu.state_key, ""))
|
||||||
{
|
{
|
||||||
|
if (errp)
|
||||||
|
{
|
||||||
|
*errp = "Step 4.1 fail: no state key in the alias";
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
/* Step 4.2: If sender's domain doesn't matches state_key, reject. */
|
/* Step 4.2: If sender's domain doesn't matches state_key, reject. */
|
||||||
if (!VerifyServers(pdu.state_key, pdu.sender))
|
if (!VerifyServers(pdu.state_key, pdu.sender))
|
||||||
{
|
{
|
||||||
|
if (errp)
|
||||||
|
{
|
||||||
|
*errp = "Step 4.2 fail: alias domain doesnt match statekey";
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -932,7 +982,7 @@ AuthoriseAliasV1(PduV1 pdu)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
static bool
|
static bool
|
||||||
AuthorizeInviteMembershipV1(Room * room, PduV1 pdu, State *state)
|
AuthorizeInviteMembershipV1(Room * room, PduV1 pdu, State *state, char **errp)
|
||||||
{
|
{
|
||||||
int64_t invite_level;
|
int64_t invite_level;
|
||||||
int64_t pdu_level;
|
int64_t pdu_level;
|
||||||
|
@ -947,12 +997,20 @@ AuthorizeInviteMembershipV1(Room * room, PduV1 pdu, State *state)
|
||||||
/* Step 5.3.1.1: If target user is banned, reject. */
|
/* Step 5.3.1.1: If target user is banned, reject. */
|
||||||
if (RoomUserHasMembership(room, state, pdu.state_key, "ban"))
|
if (RoomUserHasMembership(room, state, pdu.state_key, "ban"))
|
||||||
{
|
{
|
||||||
|
if (errp)
|
||||||
|
{
|
||||||
|
*errp = "Step 5.3.1.1 fail: target is banned";
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
/* Step 5.3.1.2: If content.third_party_invite does not have a signed
|
/* Step 5.3.1.2: If content.third_party_invite does not have a signed
|
||||||
* property, reject. */
|
* property, reject. */
|
||||||
if (!(signed_val = JsonGet(third_pi_obj, 1, "signed")))
|
if (!(signed_val = JsonGet(third_pi_obj, 1, "signed")))
|
||||||
{
|
{
|
||||||
|
if (errp)
|
||||||
|
{
|
||||||
|
*errp = "Step 5.3.1.2 fail: unsigned 3PII";
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
signed_obj = JsonValueAsObject(signed_val);
|
signed_obj = JsonValueAsObject(signed_val);
|
||||||
|
@ -961,16 +1019,28 @@ AuthorizeInviteMembershipV1(Room * room, PduV1 pdu, State *state)
|
||||||
* reject. */
|
* reject. */
|
||||||
if (!(mxid = JsonGet(signed_obj, 1, "mxid")))
|
if (!(mxid = JsonGet(signed_obj, 1, "mxid")))
|
||||||
{
|
{
|
||||||
|
if (errp)
|
||||||
|
{
|
||||||
|
*errp = "Step 5.3.1.3 fail: no MXID in 3PII";
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!(token = JsonGet(signed_obj, 1, "token")))
|
if (!(token = JsonGet(signed_obj, 1, "token")))
|
||||||
{
|
{
|
||||||
|
if (errp)
|
||||||
|
{
|
||||||
|
*errp = "Step 5.3.1.3 fail: no token in 3PII";
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Step 5.3.1.4: If mxid does not match state_key, reject. */
|
/* Step 5.3.1.4: If mxid does not match state_key, reject. */
|
||||||
if (!StrEquals(JsonValueAsString(mxid), pdu.state_key))
|
if (!StrEquals(JsonValueAsString(mxid), pdu.state_key))
|
||||||
{
|
{
|
||||||
|
if (errp)
|
||||||
|
{
|
||||||
|
*errp = "Step 5.3.1.4 fail: 3PII's MXID != state_key";
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -980,6 +1050,10 @@ AuthorizeInviteMembershipV1(Room * room, PduV1 pdu, State *state)
|
||||||
state,
|
state,
|
||||||
"m.room.third_party_invite", JsonValueAsString(token))))
|
"m.room.third_party_invite", JsonValueAsString(token))))
|
||||||
{
|
{
|
||||||
|
if (errp)
|
||||||
|
{
|
||||||
|
*errp = "Step 5.3.1.5 fail: no proper 3PII event";
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
third_pi_event = RoomEventFetch(room, third_pi_id);
|
third_pi_event = RoomEventFetch(room, third_pi_id);
|
||||||
|
@ -989,6 +1063,10 @@ AuthorizeInviteMembershipV1(Room * room, PduV1 pdu, State *state)
|
||||||
thirdpi_event_sender = JsonValueAsString(JsonGet(third_pi_event, 1, "sender"));
|
thirdpi_event_sender = JsonValueAsString(JsonGet(third_pi_event, 1, "sender"));
|
||||||
if (!StrEquals(thirdpi_event_sender, pdu.sender))
|
if (!StrEquals(thirdpi_event_sender, pdu.sender))
|
||||||
{
|
{
|
||||||
|
if (errp)
|
||||||
|
{
|
||||||
|
*errp = "Step 5.3.1.6 fail: sender does not match 3PII";
|
||||||
|
}
|
||||||
JsonFree(third_pi_event);
|
JsonFree(third_pi_event);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1003,6 +1081,10 @@ AuthorizeInviteMembershipV1(Room * room, PduV1 pdu, State *state)
|
||||||
* - A list of public keys in the public_keys property. */
|
* - A list of public keys in the public_keys property. */
|
||||||
|
|
||||||
/* Step 5.3.1.8: Otherwise, reject. */
|
/* Step 5.3.1.8: Otherwise, reject. */
|
||||||
|
if (errp)
|
||||||
|
{
|
||||||
|
*errp = "Step 5.3.1.8 fail: signature check do not match";
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1010,12 +1092,20 @@ AuthorizeInviteMembershipV1(Room * room, PduV1 pdu, State *state)
|
||||||
* reject. */
|
* reject. */
|
||||||
if (!RoomUserHasMembership(room, state, pdu.sender, "join"))
|
if (!RoomUserHasMembership(room, state, pdu.sender, "join"))
|
||||||
{
|
{
|
||||||
|
if (errp)
|
||||||
|
{
|
||||||
|
*errp = "Step 5.3.2 fail: sender is not 'join'ed";
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
/* Step 5.3.3: If target user’s current membership state is join or ban, reject. */
|
/* Step 5.3.3: If target user's current membership state is join or ban, reject. */
|
||||||
if (RoomUserHasMembership(room, state, pdu.state_key, "join") ||
|
if (RoomUserHasMembership(room, state, pdu.state_key, "join") ||
|
||||||
RoomUserHasMembership(room, state, pdu.state_key, "ban"))
|
RoomUserHasMembership(room, state, pdu.state_key, "ban"))
|
||||||
{
|
{
|
||||||
|
if (errp)
|
||||||
|
{
|
||||||
|
*errp = "Step 5.3.3 fail: target is 'join'|'ban'd";
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1028,10 +1118,14 @@ AuthorizeInviteMembershipV1(Room * room, PduV1 pdu, State *state)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
/* Step 5.3.5: Otherwise, reject. */
|
/* Step 5.3.5: Otherwise, reject. */
|
||||||
|
if (errp)
|
||||||
|
{
|
||||||
|
*errp = "Step 5.3.5 fail: sender has no permissions to do so";
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
static bool
|
static bool
|
||||||
AuthorizeLeaveMembershipV1(Room * room, PduV1 pdu, State *state)
|
AuthorizeLeaveMembershipV1(Room * room, PduV1 pdu, State *state, char **errp)
|
||||||
{
|
{
|
||||||
int64_t ban_level = RoomMinPL(room, state, NULL, "ban");
|
int64_t ban_level = RoomMinPL(room, state, NULL, "ban");
|
||||||
int64_t kick_level = RoomMinPL(room, state, NULL, "kick");
|
int64_t kick_level = RoomMinPL(room, state, NULL, "kick");
|
||||||
|
@ -1042,15 +1136,26 @@ AuthorizeLeaveMembershipV1(Room * room, PduV1 pdu, State *state)
|
||||||
* that user's current membership state is invite or join. */
|
* that user's current membership state is invite or join. */
|
||||||
if (StrEquals(pdu.sender, pdu.state_key))
|
if (StrEquals(pdu.sender, pdu.state_key))
|
||||||
{
|
{
|
||||||
return
|
bool flag =
|
||||||
RoomUserHasMembership(room, state, pdu.sender, "invite") ||
|
RoomUserHasMembership(room, state, pdu.sender, "invite") ||
|
||||||
RoomUserHasMembership(room, state, pdu.sender, "join");
|
RoomUserHasMembership(room, state, pdu.sender, "join");
|
||||||
|
if (!flag && errp)
|
||||||
|
{
|
||||||
|
*errp = "Step 5.4.1 fail: user tries to leave but is "
|
||||||
|
"~'invite' AND ~'join'.";
|
||||||
|
}
|
||||||
|
return flag;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Step 5.4.2: If the sender's current membership state is not join,
|
/* Step 5.4.2: If the sender's current membership state is not join,
|
||||||
* reject. */
|
* reject. */
|
||||||
if (!RoomUserHasMembership(room, state, pdu.sender, "join"))
|
if (!RoomUserHasMembership(room, state, pdu.sender, "join"))
|
||||||
{
|
{
|
||||||
|
if (errp)
|
||||||
|
{
|
||||||
|
*errp = "Step 5.4.2 fail: sender tries to kick but is "
|
||||||
|
"~'invite'.";
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1059,6 +1164,11 @@ AuthorizeLeaveMembershipV1(Room * room, PduV1 pdu, State *state)
|
||||||
if (RoomUserHasMembership(room, state, pdu.state_key, "ban") &&
|
if (RoomUserHasMembership(room, state, pdu.state_key, "ban") &&
|
||||||
sender_level < ban_level)
|
sender_level < ban_level)
|
||||||
{
|
{
|
||||||
|
if (errp)
|
||||||
|
{
|
||||||
|
*errp = "Step 5.4.3 fail: sender tries to unban but has no "
|
||||||
|
"permissions to do so.";
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1071,16 +1181,26 @@ AuthorizeLeaveMembershipV1(Room * room, PduV1 pdu, State *state)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Step 5.4.5: Otherwise, reject. */
|
/* Step 5.4.5: Otherwise, reject. */
|
||||||
|
if (errp)
|
||||||
|
{
|
||||||
|
*errp = "Step 5.4.5 fail: sender tried to kick but has no "
|
||||||
|
"permissions to do so.";
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
static bool
|
static bool
|
||||||
AuthorizeBanMembershipV1(Room * room, PduV1 pdu, State *state)
|
AuthorizeBanMembershipV1(Room * room, PduV1 pdu, State *state, char **errp)
|
||||||
{
|
{
|
||||||
int64_t ban_pl, pdu_pl, target_pl;
|
int64_t ban_pl, pdu_pl, target_pl;
|
||||||
|
|
||||||
/* Step 5.5.1: If the sender's current membership state is not join, reject. */
|
/* Step 5.5.1: If the sender's current membership state is not join, reject. */
|
||||||
if (!RoomUserHasMembership(room, state, pdu.sender, "join"))
|
if (!RoomUserHasMembership(room, state, pdu.sender, "join"))
|
||||||
{
|
{
|
||||||
|
if (errp)
|
||||||
|
{
|
||||||
|
*errp = "Step 5.5.1 fail: sender tries to ban but is not "
|
||||||
|
"'join'ed";
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1096,10 +1216,15 @@ AuthorizeBanMembershipV1(Room * room, PduV1 pdu, State *state)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Step 5.5.3: Otherwise, reject. */
|
/* Step 5.5.3: Otherwise, reject. */
|
||||||
|
if (errp)
|
||||||
|
{
|
||||||
|
*errp = "Step 5.5.3 fail: sender tries to ban has no permissions to "
|
||||||
|
"do so";
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
static bool
|
static bool
|
||||||
AuthorizeJoinMembershipV1(Room * room, PduV1 pdu, State *state)
|
AuthorizeJoinMembershipV1(Room * room, PduV1 pdu, State *state, char **errp)
|
||||||
{
|
{
|
||||||
/* Step 5.2.1: If the only previous event is an m.room.create and the
|
/* Step 5.2.1: If the only previous event is an m.room.create and the
|
||||||
* state_key is the creator, allow. */
|
* state_key is the creator, allow. */
|
||||||
|
@ -1132,11 +1257,20 @@ AuthorizeJoinMembershipV1(Room * room, PduV1 pdu, State *state)
|
||||||
/* Step 5.2.2: If the sender does not match state_key, reject. */
|
/* Step 5.2.2: If the sender does not match state_key, reject. */
|
||||||
if (!StrEquals(pdu.sender, pdu.state_key))
|
if (!StrEquals(pdu.sender, pdu.state_key))
|
||||||
{
|
{
|
||||||
|
if (errp)
|
||||||
|
{
|
||||||
|
*errp = "Step 5.2.2 fail: sender does not match statekey "
|
||||||
|
"on 'join'";
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
/* Step 5.2.3: If the sender is banned, reject. */
|
/* Step 5.2.3: If the sender is banned, reject. */
|
||||||
if (RoomUserHasMembership(room, state, pdu.sender, "ban"))
|
if (RoomUserHasMembership(room, state, pdu.sender, "ban"))
|
||||||
{
|
{
|
||||||
|
if (errp)
|
||||||
|
{
|
||||||
|
*errp = "Step 5.2.2 fail: sender is banned on 'join'";
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1153,11 +1287,16 @@ AuthorizeJoinMembershipV1(Room * room, PduV1 pdu, State *state)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Step 5.2.6: Otherwise, reject. */
|
/* Step 5.2.6: Otherwise, reject. */
|
||||||
|
if (errp)
|
||||||
|
{
|
||||||
|
*errp = "Step 5.2.6 fail: join_rule does not allow 'join'";
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
static bool
|
static bool
|
||||||
AuthoriseMemberV1(Room * room, PduV1 pdu, State *state)
|
AuthoriseMemberV1(Room * room, PduV1 pdu, State *state, char **errp)
|
||||||
{
|
{
|
||||||
JsonValue *membership;
|
JsonValue *membership;
|
||||||
char *membership_str;
|
char *membership_str;
|
||||||
|
@ -1167,18 +1306,26 @@ AuthoriseMemberV1(Room * room, PduV1 pdu, State *state)
|
||||||
StrEquals(pdu.state_key, "") ||
|
StrEquals(pdu.state_key, "") ||
|
||||||
!(membership = JsonGet(pdu.content, 1, "membership")))
|
!(membership = JsonGet(pdu.content, 1, "membership")))
|
||||||
{
|
{
|
||||||
|
if (errp)
|
||||||
|
{
|
||||||
|
*errp = "Step 5.1 fail: broken membership's statekey/membership";
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (JsonValueType(membership) != JSON_STRING)
|
if (JsonValueType(membership) != JSON_STRING)
|
||||||
{
|
{
|
||||||
/* Also check for the type */
|
/* Also check for the type */
|
||||||
|
if (errp)
|
||||||
|
{
|
||||||
|
*errp = "Step 5.1 fail: broken membership's membership";
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
membership_str = JsonValueAsString(membership);
|
membership_str = JsonValueAsString(membership);
|
||||||
#define JumpIfMembership(mem, func) do { \
|
#define JumpIfMembership(mem, func) do { \
|
||||||
if (StrEquals(membership_str, mem)) \
|
if (StrEquals(membership_str, mem)) \
|
||||||
{ \
|
{ \
|
||||||
return func(room, pdu, state); \
|
return func(room, pdu, state, errp); \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
@ -1375,7 +1522,7 @@ AuthorisePowerLevelsV1(Room * room, PduV1 pdu, State *state)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool
|
bool
|
||||||
RoomAuthoriseEventV1(Room * room, PduV1 pdu, State *state)
|
RoomAuthoriseEventV1(Room * room, PduV1 pdu, State *state, char **errp)
|
||||||
{
|
{
|
||||||
HashMap *create_event;
|
HashMap *create_event;
|
||||||
char *create_event_id;
|
char *create_event_id;
|
||||||
|
@ -1385,11 +1532,11 @@ RoomAuthoriseEventV1(Room * room, PduV1 pdu, State *state)
|
||||||
/* Step 1: If m.room.create */
|
/* Step 1: If m.room.create */
|
||||||
if (StrEquals(pdu.type, "m.room.create"))
|
if (StrEquals(pdu.type, "m.room.create"))
|
||||||
{
|
{
|
||||||
return AuthoriseCreateV1(pdu);
|
return AuthoriseCreateV1(pdu, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Step 2: Considering the event's auth_events. */
|
/* Step 2: Considering the event's auth_events. */
|
||||||
if (!ConsiderAuthEventsV1(room, pdu))
|
if (!ConsiderAuthEventsV1(room, pdu, errp))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1403,6 +1550,11 @@ RoomAuthoriseEventV1(Room * room, PduV1 pdu, State *state)
|
||||||
if (!state || !create_event_id)
|
if (!state || !create_event_id)
|
||||||
{
|
{
|
||||||
/* At this point, [create_event_id] has to exist */
|
/* At this point, [create_event_id] has to exist */
|
||||||
|
if (errp)
|
||||||
|
{
|
||||||
|
*errp = "No creation event in the state. Needless to say, "
|
||||||
|
"your room is done for.";
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
create_event = RoomEventFetch(room, create_event_id);
|
create_event = RoomEventFetch(room, create_event_id);
|
||||||
|
@ -1416,6 +1568,10 @@ RoomAuthoriseEventV1(Room * room, PduV1 pdu, State *state)
|
||||||
char *p_sender = pdu.sender;
|
char *p_sender = pdu.sender;
|
||||||
if (!VerifyServers(c_sender, p_sender))
|
if (!VerifyServers(c_sender, p_sender))
|
||||||
{
|
{
|
||||||
|
if (errp)
|
||||||
|
{
|
||||||
|
*errp = "Trying to access a room with m.federate off.";
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1425,13 +1581,13 @@ RoomAuthoriseEventV1(Room * room, PduV1 pdu, State *state)
|
||||||
/* Step 4: If type is m.room.aliases */
|
/* Step 4: If type is m.room.aliases */
|
||||||
if (StrEquals(pdu.type, "m.room.aliases"))
|
if (StrEquals(pdu.type, "m.room.aliases"))
|
||||||
{
|
{
|
||||||
return AuthoriseAliasV1(pdu);
|
return AuthoriseAliasV1(pdu, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Step 5: If type is m.room.member */
|
/* Step 5: If type is m.room.member */
|
||||||
if (StrEquals(pdu.type, "m.room.member"))
|
if (StrEquals(pdu.type, "m.room.member"))
|
||||||
{
|
{
|
||||||
return AuthoriseMemberV1(room, pdu, state);
|
return AuthoriseMemberV1(room, pdu, state, errp);
|
||||||
}
|
}
|
||||||
/* Step 6: If the sender's current membership state is not join, reject. */
|
/* Step 6: If the sender's current membership state is not join, reject. */
|
||||||
if (!RoomUserHasMembership(room, state, pdu.sender, "join"))
|
if (!RoomUserHasMembership(room, state, pdu.sender, "join"))
|
||||||
|
@ -1571,19 +1727,27 @@ EventFitsV1(PduV1 pdu)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
static PduV1Status
|
static PduV1Status
|
||||||
RoomGetEventStatusV1(Room *room, PduV1 *pdu, State *prev, bool client)
|
RoomGetEventStatusV1(Room *room, PduV1 *pdu, State *prev, bool client, char **errp)
|
||||||
{
|
{
|
||||||
if (!room || !pdu || !prev)
|
if (!room || !pdu || !prev)
|
||||||
{
|
{
|
||||||
|
if (errp)
|
||||||
|
{
|
||||||
|
*errp = "Illegal arguments given to RoomGetEventStatusV1";
|
||||||
|
}
|
||||||
return PDUV1_STATUS_DROPPED;
|
return PDUV1_STATUS_DROPPED;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!EventFitsV1(*pdu))
|
if (!EventFitsV1(*pdu))
|
||||||
{
|
{
|
||||||
/* Reject this event as it is too large. */
|
/* Reject this event as it is too large. */
|
||||||
|
if (errp)
|
||||||
|
{
|
||||||
|
*errp = "PDU is too large to fit";
|
||||||
|
}
|
||||||
return PDUV1_STATUS_DROPPED;
|
return PDUV1_STATUS_DROPPED;
|
||||||
}
|
}
|
||||||
if (!RoomAuthoriseEventV1(room, *pdu, prev))
|
if (!RoomAuthoriseEventV1(room, *pdu, prev, errp))
|
||||||
{
|
{
|
||||||
/* Reject this event as the current state does not allow it.
|
/* Reject this event as the current state does not allow it.
|
||||||
* TODO: Make the auth check function return a string showing the
|
* TODO: Make the auth check function return a string showing the
|
||||||
|
@ -1595,7 +1759,7 @@ RoomGetEventStatusV1(Room *room, PduV1 *pdu, State *prev, bool client)
|
||||||
{
|
{
|
||||||
State *current = StateCurrent(room);
|
State *current = StateCurrent(room);
|
||||||
|
|
||||||
if (!RoomAuthoriseEventV1(room, *pdu, current))
|
if (!RoomAuthoriseEventV1(room, *pdu, current, NULL))
|
||||||
{
|
{
|
||||||
StateFree(current);
|
StateFree(current);
|
||||||
return PDUV1_STATUS_SOFTFAIL;
|
return PDUV1_STATUS_SOFTFAIL;
|
||||||
|
@ -1606,7 +1770,7 @@ RoomGetEventStatusV1(Room *room, PduV1 *pdu, State *prev, bool client)
|
||||||
return PDUV1_STATUS_ACCEPTED;
|
return PDUV1_STATUS_ACCEPTED;
|
||||||
}
|
}
|
||||||
static HashMap *
|
static HashMap *
|
||||||
RoomEventSendV1(Room * room, HashMap * event)
|
RoomEventSendV1(Room * room, HashMap * event, char **errp)
|
||||||
{
|
{
|
||||||
PduV1 pdu = { 0 };
|
PduV1 pdu = { 0 };
|
||||||
HashMap *pdu_object = NULL;
|
HashMap *pdu_object = NULL;
|
||||||
|
@ -1702,8 +1866,7 @@ RoomEventSendV1(Room * room, HashMap * event)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* TODO: For PDU events, we should verify their hashes. */
|
/* TODO: For PDU events, we should verify their hashes. */
|
||||||
status = RoomGetEventStatusV1(room, &pdu, state, client_event);
|
status = RoomGetEventStatusV1(room, &pdu, state, client_event, errp);
|
||||||
Log(LOG_DEBUG, "status='%s'", PduV1StatusToStr(status));
|
|
||||||
if (status == PDUV1_STATUS_DROPPED)
|
if (status == PDUV1_STATUS_DROPPED)
|
||||||
{
|
{
|
||||||
goto finish;
|
goto finish;
|
||||||
|
@ -1745,7 +1908,7 @@ RoomEventSendV3(Room * room, HashMap * event)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
HashMap *
|
HashMap *
|
||||||
RoomEventSend(Room * room, HashMap * event)
|
RoomEventSend(Room * room, HashMap * event, char **errp)
|
||||||
{
|
{
|
||||||
if (!room || !event)
|
if (!room || !event)
|
||||||
{
|
{
|
||||||
|
@ -1754,7 +1917,7 @@ RoomEventSend(Room * room, HashMap * event)
|
||||||
if (room->version < 3)
|
if (room->version < 3)
|
||||||
{
|
{
|
||||||
/* Manage with PDUv1 */
|
/* Manage with PDUv1 */
|
||||||
return RoomEventSendV1(room, event);
|
return RoomEventSendV1(room, event, errp);
|
||||||
}
|
}
|
||||||
/* Manage with PDUv3 otherwise */
|
/* Manage with PDUv3 otherwise */
|
||||||
return RoomEventSendV3(room, event);
|
return RoomEventSendV3(room, event);
|
||||||
|
@ -2198,7 +2361,7 @@ RoomSendInvite(User *sender, bool direct, char *user, Room *room)
|
||||||
JsonSet(content, JsonValueBoolean(direct), 1, "is_direct");
|
JsonSet(content, JsonValueBoolean(direct), 1, "is_direct");
|
||||||
JsonSet(content, JsonValueString("invite"), 1, "membership");
|
JsonSet(content, JsonValueString("invite"), 1, "membership");
|
||||||
event = RoomEventCreate(senderStr, "m.room.member", user, content);
|
event = RoomEventCreate(senderStr, "m.room.member", user, content);
|
||||||
JsonFree(RoomEventSend(room, event));
|
JsonFree(RoomEventSend(room, event, NULL));
|
||||||
JsonFree(event);
|
JsonFree(event);
|
||||||
|
|
||||||
/* TODO: Send to "local" invite list if user is local. */
|
/* TODO: Send to "local" invite list if user is local. */
|
||||||
|
@ -2303,7 +2466,55 @@ end:
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
RoomJoin(Room *room, User *user)
|
RoomLeave(Room *room, User *user, char **errp)
|
||||||
|
{
|
||||||
|
CommonID *userId = NULL;
|
||||||
|
char *userString = NULL;
|
||||||
|
char *server = NULL;
|
||||||
|
HashMap *content = NULL;
|
||||||
|
HashMap *event = NULL;
|
||||||
|
HashMap *pdu = NULL;
|
||||||
|
bool ret = false;
|
||||||
|
|
||||||
|
if (!room || !user)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
server = ConfigGetServerName(room->db);
|
||||||
|
if (!server)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
userId = UserIdParse(UserGetName(user), server);
|
||||||
|
userId->sigil = '@';
|
||||||
|
userString = ParserRecomposeCommonID(*userId);
|
||||||
|
Free(server);
|
||||||
|
server = NULL;
|
||||||
|
|
||||||
|
content = HashMapCreate();
|
||||||
|
JsonSet(content, JsonValueString("leave"), 1, "membership");
|
||||||
|
event = RoomEventCreate(userString, "m.room.member", userString, content);
|
||||||
|
pdu = RoomEventSend(room, event, errp);
|
||||||
|
|
||||||
|
/* TODO: One ought to be extremely careful with managing users in those
|
||||||
|
* scenarios, as the DB flushes do not sync. */
|
||||||
|
ret = !!pdu;
|
||||||
|
if (ret)
|
||||||
|
{
|
||||||
|
UserRemoveJoin(user, room->id);
|
||||||
|
}
|
||||||
|
UserIdFree(userId);
|
||||||
|
JsonFree(event);
|
||||||
|
JsonFree(pdu);
|
||||||
|
if (userString)
|
||||||
|
{
|
||||||
|
Free(userString);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
bool
|
||||||
|
RoomJoin(Room *room, User *user, char **errp)
|
||||||
{
|
{
|
||||||
CommonID *userId = NULL;
|
CommonID *userId = NULL;
|
||||||
char *userString = NULL;
|
char *userString = NULL;
|
||||||
|
@ -2338,9 +2549,15 @@ RoomJoin(Room *room, User *user)
|
||||||
content = HashMapCreate();
|
content = HashMapCreate();
|
||||||
JsonSet(content, JsonValueString("join"), 1, "membership");
|
JsonSet(content, JsonValueString("join"), 1, "membership");
|
||||||
event = RoomEventCreate(userString, "m.room.member", userString, content);
|
event = RoomEventCreate(userString, "m.room.member", userString, content);
|
||||||
pdu = RoomEventSend(room, event);
|
pdu = RoomEventSend(room, event, errp);
|
||||||
|
|
||||||
|
/* TODO: One ought to be extremely careful with managing users in those
|
||||||
|
* scenarios, as the DB flushes do not sync. */
|
||||||
ret = !!pdu;
|
ret = !!pdu;
|
||||||
|
if (ret)
|
||||||
|
{
|
||||||
|
UserAddJoin(user, room->id);
|
||||||
|
}
|
||||||
end:
|
end:
|
||||||
UserIdFree(userId);
|
UserIdFree(userId);
|
||||||
JsonFree(event);
|
JsonFree(event);
|
||||||
|
|
|
@ -82,7 +82,7 @@ RouterBuild(void)
|
||||||
R("/_matrix/client/v3/rooms/(.*)/state/(.*)", RouteSendState);
|
R("/_matrix/client/v3/rooms/(.*)/state/(.*)", RouteSendState);
|
||||||
R("/_matrix/client/v3/rooms/(.*)/event/(.*)", RouteFetchEvent);
|
R("/_matrix/client/v3/rooms/(.*)/event/(.*)", RouteFetchEvent);
|
||||||
R("/_matrix/client/v3/rooms/(.*)/(join|leave)", RouteJoinRoom);
|
R("/_matrix/client/v3/rooms/(.*)/(join|leave)", RouteJoinRoom);
|
||||||
R("/_matrix/client/v3/rooms/(.*)/(kick|ban)", RouteKickRoom);
|
R("/_matrix/client/v3/rooms/(.*)/(kick|ban|unban)", RouteKickRoom);
|
||||||
R("/_matrix/client/v3/rooms/(.*)/messages", RouteMessages);
|
R("/_matrix/client/v3/rooms/(.*)/messages", RouteMessages);
|
||||||
|
|
||||||
R("/_matrix/client/v3/join/(.*)", RouteJoinRoomAlias);
|
R("/_matrix/client/v3/join/(.*)", RouteJoinRoomAlias);
|
||||||
|
|
|
@ -130,11 +130,10 @@ ROUTE_IMPL(RouteJoinRoom, path, argp)
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
/* TODO: Custom reason parameter. */
|
/* TODO: Custom reason parameter. */
|
||||||
if (!RoomJoin(room, user))
|
if (!RoomJoin(room, user, &err))
|
||||||
{
|
{
|
||||||
err = "User could not be the room due to unknown reasons.";
|
HttpResponseStatus(args->context, HTTP_UNAUTHORIZED);
|
||||||
HttpResponseStatus(args->context, HTTP_INTERNAL_SERVER_ERROR);
|
response = MatrixErrorCreate(M_BAD_STATE, err);
|
||||||
response = MatrixErrorCreate(M_UNKNOWN, err);
|
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,9 +142,6 @@ ROUTE_IMPL(RouteJoinRoom, path, argp)
|
||||||
}
|
}
|
||||||
else if (StrEquals(action, "leave"))
|
else if (StrEquals(action, "leave"))
|
||||||
{
|
{
|
||||||
HashMap *membership, *content;
|
|
||||||
HashMap *pduResponse;
|
|
||||||
|
|
||||||
if (!RoomContainsUser(room, sender))
|
if (!RoomContainsUser(room, sender))
|
||||||
{
|
{
|
||||||
err = "User is not already in the room.";
|
err = "User is not already in the room.";
|
||||||
|
@ -155,25 +151,12 @@ ROUTE_IMPL(RouteJoinRoom, path, argp)
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
content = HashMapCreate();
|
if (!RoomLeave(room, user, &err))
|
||||||
membership = RoomEventCreate(
|
|
||||||
sender, "m.room.member", sender,
|
|
||||||
content
|
|
||||||
);
|
|
||||||
|
|
||||||
HashMapSet(content, "membership", JsonValueString("leave"));
|
|
||||||
pduResponse = RoomEventSend(room, membership);
|
|
||||||
if (!pduResponse)
|
|
||||||
{
|
{
|
||||||
err = "Couldn't accept leave event";
|
HttpResponseStatus(args->context, HTTP_UNAUTHORIZED);
|
||||||
HttpResponseStatus(args->context, HTTP_INTERNAL_SERVER_ERROR);
|
response = MatrixErrorCreate(M_BAD_STATE, err);
|
||||||
response = MatrixErrorCreate(M_UNKNOWN, err);
|
|
||||||
|
|
||||||
JsonFree(membership);
|
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
JsonFree(pduResponse);
|
|
||||||
JsonFree(membership);
|
|
||||||
|
|
||||||
response = HashMapCreate();
|
response = HashMapCreate();
|
||||||
}
|
}
|
||||||
|
@ -213,8 +196,7 @@ ROUTE_IMPL(RouteKickRoom, path, argp)
|
||||||
char *action = ArrayGet(path, 1);
|
char *action = ArrayGet(path, 1);
|
||||||
char *kicked = NULL, *reason = NULL;
|
char *kicked = NULL, *reason = NULL;
|
||||||
char *sender = NULL, *serverName = NULL;
|
char *sender = NULL, *serverName = NULL;
|
||||||
char *membershipState = StrEquals(action, "kick") ?
|
char *membershipState;
|
||||||
"leave" : "ban";
|
|
||||||
|
|
||||||
Room *room = NULL;
|
Room *room = NULL;
|
||||||
|
|
||||||
|
@ -226,6 +208,26 @@ ROUTE_IMPL(RouteKickRoom, path, argp)
|
||||||
HttpResponseStatus(args->context, HTTP_INTERNAL_SERVER_ERROR);
|
HttpResponseStatus(args->context, HTTP_INTERNAL_SERVER_ERROR);
|
||||||
return MatrixErrorCreate(M_UNKNOWN, NULL);
|
return MatrixErrorCreate(M_UNKNOWN, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (StrEquals(action, "kick"))
|
||||||
|
{
|
||||||
|
membershipState = "leave";
|
||||||
|
}
|
||||||
|
else if (StrEquals(action, "ban"))
|
||||||
|
{
|
||||||
|
membershipState = "ban";
|
||||||
|
}
|
||||||
|
else if (StrEquals(action, "unban"))
|
||||||
|
{
|
||||||
|
membershipState = "leave";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Should be impossible */
|
||||||
|
HttpResponseStatus(args->context, HTTP_INTERNAL_SERVER_ERROR);
|
||||||
|
return MatrixErrorCreate(M_UNKNOWN, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
if (HttpRequestMethodGet(args->context) != HTTP_POST)
|
if (HttpRequestMethodGet(args->context) != HTTP_POST)
|
||||||
{
|
{
|
||||||
err = "Unknown request method.";
|
err = "Unknown request method.";
|
||||||
|
@ -284,7 +286,7 @@ ROUTE_IMPL(RouteKickRoom, path, argp)
|
||||||
response = MatrixErrorCreate(M_FORBIDDEN, err);
|
response = MatrixErrorCreate(M_FORBIDDEN, err);
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
if (!RoomContainsUser(room, kicked))
|
if (RoomContainsUser(room, kicked) == StrEquals(action, "unban"))
|
||||||
{
|
{
|
||||||
err = "Victim is not present in the room";
|
err = "Victim is not present in the room";
|
||||||
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
|
HttpResponseStatus(args->context, HTTP_BAD_REQUEST);
|
||||||
|
@ -305,12 +307,11 @@ ROUTE_IMPL(RouteKickRoom, path, argp)
|
||||||
HashMapSet(content, "reason", JsonValueString(reason));
|
HashMapSet(content, "reason", JsonValueString(reason));
|
||||||
}
|
}
|
||||||
|
|
||||||
pduResponse = RoomEventSend(room, membership);
|
pduResponse = RoomEventSend(room, membership, &err);
|
||||||
if (!pduResponse)
|
if (!pduResponse)
|
||||||
{
|
{
|
||||||
err = "Couldn't accept event";
|
HttpResponseStatus(args->context, HTTP_UNAUTHORIZED);
|
||||||
HttpResponseStatus(args->context, HTTP_INTERNAL_SERVER_ERROR);
|
response = MatrixErrorCreate(M_BAD_STATE, err);
|
||||||
response = MatrixErrorCreate(M_UNKNOWN, err);
|
|
||||||
|
|
||||||
JsonFree(membership);
|
JsonFree(membership);
|
||||||
goto finish;
|
goto finish;
|
||||||
|
|
|
@ -143,11 +143,10 @@ ROUTE_IMPL(RouteJoinRoomAlias, path, argp)
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
/* TODO: Custom reason parameter. */
|
/* TODO: Custom reason parameter. */
|
||||||
if (!RoomJoin(room, user))
|
if (!RoomJoin(room, user, &err))
|
||||||
{
|
{
|
||||||
err = "User could not be the room due to unknown reasons.";
|
HttpResponseStatus(args->context, HTTP_UNAUTHORIZED);
|
||||||
HttpResponseStatus(args->context, HTTP_INTERNAL_SERVER_ERROR);
|
response = MatrixErrorCreate(M_FORBIDDEN, err);
|
||||||
response = MatrixErrorCreate(M_UNKNOWN, err);
|
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,7 @@ ROUTE_IMPL(RouteSendEvent, path, argp)
|
||||||
|
|
||||||
Room *room = NULL;
|
Room *room = NULL;
|
||||||
|
|
||||||
char *err;
|
char *err = NULL;
|
||||||
|
|
||||||
if (!roomId || !eventType || !transId)
|
if (!roomId || !eventType || !transId)
|
||||||
{
|
{
|
||||||
|
@ -123,12 +123,11 @@ ROUTE_IMPL(RouteSendEvent, path, argp)
|
||||||
}
|
}
|
||||||
|
|
||||||
event = RoomEventCreate(sender, eventType, NULL, JsonDuplicate(request));
|
event = RoomEventCreate(sender, eventType, NULL, JsonDuplicate(request));
|
||||||
filled = RoomEventSend(room, event);
|
filled = RoomEventSend(room, event, &err);
|
||||||
JsonFree(event);
|
JsonFree(event);
|
||||||
|
|
||||||
if (!filled)
|
if (!filled)
|
||||||
{
|
{
|
||||||
err = "User is not allowed to send event.";
|
|
||||||
HttpResponseStatus(args->context, HTTP_UNAUTHORIZED);
|
HttpResponseStatus(args->context, HTTP_UNAUTHORIZED);
|
||||||
response = MatrixErrorCreate(M_FORBIDDEN, err);
|
response = MatrixErrorCreate(M_FORBIDDEN, err);
|
||||||
goto finish;
|
goto finish;
|
||||||
|
@ -238,12 +237,11 @@ ROUTE_IMPL(RouteSendState, path, argp)
|
||||||
eventType, stateKey ? stateKey : "",
|
eventType, stateKey ? stateKey : "",
|
||||||
JsonDuplicate(request)
|
JsonDuplicate(request)
|
||||||
);
|
);
|
||||||
filled = RoomEventSend(room, event);
|
filled = RoomEventSend(room, event, &err);
|
||||||
JsonFree(event);
|
JsonFree(event);
|
||||||
|
|
||||||
if (!filled)
|
if (!filled)
|
||||||
{
|
{
|
||||||
err = "User is not allowed to send state.";
|
|
||||||
HttpResponseStatus(args->context, HTTP_UNAUTHORIZED);
|
HttpResponseStatus(args->context, HTTP_UNAUTHORIZED);
|
||||||
response = MatrixErrorCreate(M_FORBIDDEN, err);
|
response = MatrixErrorCreate(M_FORBIDDEN, err);
|
||||||
goto finish;
|
goto finish;
|
||||||
|
|
|
@ -63,7 +63,7 @@ SendMembership(Db *db, User *user)
|
||||||
HashMapSet(content, "displayname", JsonValueString(displayname));
|
HashMapSet(content, "displayname", JsonValueString(displayname));
|
||||||
HashMapSet(content, "avatar_url", JsonValueString(avatar_url));
|
HashMapSet(content, "avatar_url", JsonValueString(avatar_url));
|
||||||
|
|
||||||
JsonFree(RoomEventSend(room, membership));
|
JsonFree(RoomEventSend(room, membership, NULL));
|
||||||
JsonFree(membership);
|
JsonFree(membership);
|
||||||
|
|
||||||
RoomUnlock(room);
|
RoomUnlock(room);
|
||||||
|
|
|
@ -209,7 +209,7 @@ FixoutConflictV1(char *t, Room *room, State *R, HashMap *conflicts)
|
||||||
char *msg;
|
char *msg;
|
||||||
|
|
||||||
PduV1FromJson(event, &pdu, &msg);
|
PduV1FromJson(event, &pdu, &msg);
|
||||||
if (RoomAuthoriseEventV1(room, pdu, R))
|
if (RoomAuthoriseEventV1(room, pdu, R, NULL))
|
||||||
{
|
{
|
||||||
StateSet(R, pdu.type, pdu.state_key, pdu.event_id);
|
StateSet(R, pdu.type, pdu.state_key, pdu.event_id);
|
||||||
}
|
}
|
||||||
|
@ -289,7 +289,7 @@ StateResolveV1(Room * room, Array * states)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (RoomAuthoriseEventV1(room, pdu, R))
|
if (RoomAuthoriseEventV1(room, pdu, R, NULL))
|
||||||
{
|
{
|
||||||
StateSet(R, pdu.type, pdu.state_key, pdu.event_id);
|
StateSet(R, pdu.type, pdu.state_key, pdu.event_id);
|
||||||
PduV1Free(&pdu);
|
PduV1Free(&pdu);
|
||||||
|
|
30
src/User.c
30
src/User.c
|
@ -130,8 +130,14 @@ UserLock(Db * db, char *name)
|
||||||
user->name = StrDuplicate(name);
|
user->name = StrDuplicate(name);
|
||||||
user->deviceId = NULL;
|
user->deviceId = NULL;
|
||||||
|
|
||||||
user->inviteRef = DbLock(db, 3, "users", user->name, "invites");
|
if (!(user->inviteRef = DbLock(db, 3, "users", user->name, "invites")))
|
||||||
user->joinRef = DbLock(db, 3, "users", user->name, "joins");
|
{
|
||||||
|
user->inviteRef = DbCreate(db, 3, "users", user->name, "invites");
|
||||||
|
}
|
||||||
|
if (!(user->joinRef = DbLock(db, 3, "users", user->name, "joins")))
|
||||||
|
{
|
||||||
|
user->joinRef = DbCreate(db, 3, "users", user->name, "joins");
|
||||||
|
}
|
||||||
|
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
@ -184,7 +190,7 @@ UserAuthenticate(Db * db, char *accessToken)
|
||||||
bool
|
bool
|
||||||
UserUnlock(User * user)
|
UserUnlock(User * user)
|
||||||
{
|
{
|
||||||
bool ret;
|
bool refOk, joinOk, inviteOk;
|
||||||
Db *db;
|
Db *db;
|
||||||
DbRef *ref;
|
DbRef *ref;
|
||||||
|
|
||||||
|
@ -198,14 +204,15 @@ UserUnlock(User * user)
|
||||||
Free(user->name);
|
Free(user->name);
|
||||||
Free(user->deviceId);
|
Free(user->deviceId);
|
||||||
|
|
||||||
ret = DbUnlock(db, ref) &&
|
refOk = DbUnlock(db, ref);
|
||||||
DbUnlock(db, user->joinRef) &&
|
joinOk = DbUnlock(db, user->joinRef);
|
||||||
DbUnlock(db, user->inviteRef);
|
inviteOk = DbUnlock(db, user->inviteRef);
|
||||||
|
|
||||||
user->db = NULL;
|
user->db = NULL;
|
||||||
user->ref = NULL;
|
user->ref = NULL;
|
||||||
Free(user);
|
Free(user);
|
||||||
|
|
||||||
return ret;
|
return refOk && joinOk && inviteOk;
|
||||||
}
|
}
|
||||||
|
|
||||||
User *
|
User *
|
||||||
|
@ -1101,10 +1108,7 @@ UserAddJoin(User *user, char *roomId)
|
||||||
|
|
||||||
data = DbJson(user->joinRef);
|
data = DbJson(user->joinRef);
|
||||||
|
|
||||||
if (data && !HashMapGet(data, roomId))
|
JsonValueFree(HashMapSet(data, roomId, JsonValueObject(HashMapCreate())));
|
||||||
{
|
|
||||||
JsonValueFree(HashMapSet(data, roomId, JsonValueNull()));
|
|
||||||
}
|
|
||||||
UserNotifyUser(user);
|
UserNotifyUser(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1112,13 +1116,13 @@ void
|
||||||
UserRemoveJoin(User *user, char *roomId)
|
UserRemoveJoin(User *user, char *roomId)
|
||||||
{
|
{
|
||||||
HashMap *data;
|
HashMap *data;
|
||||||
if (!user || !roomId)
|
if (!user || !roomId || !user->joinRef)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
data = DbJson(user->joinRef);
|
data = DbJson(user->joinRef);
|
||||||
|
|
||||||
JsonValueFree(HashMapDelete(data, roomId));
|
JsonValueFree(HashMapDelete(data, roomId));
|
||||||
UserNotifyUser(user);
|
UserNotifyUser(user);
|
||||||
}
|
}
|
||||||
|
|
|
@ -144,7 +144,7 @@ extern int RoomPrevEventsSet(Room *, Array *);
|
||||||
* the room version, which includes setting the
|
* the room version, which includes setting the
|
||||||
* prev_events and auth_events fields correctly.
|
* prev_events and auth_events fields correctly.
|
||||||
*/
|
*/
|
||||||
extern HashMap * RoomEventSend(Room *, HashMap *);
|
extern HashMap * RoomEventSend(Room *, HashMap *, char **);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends an invite to a user in a room, and tries
|
* Sends an invite to a user in a room, and tries
|
||||||
|
@ -170,7 +170,7 @@ extern HashMap * RoomEventClientify(HashMap *);
|
||||||
* Verifies whenever an event(as a PDUv1) is
|
* Verifies whenever an event(as a PDUv1) is
|
||||||
* authorised by a room.
|
* authorised by a room.
|
||||||
*/
|
*/
|
||||||
extern bool RoomAuthoriseEventV1(Room *, PduV1, State *);
|
extern bool RoomAuthoriseEventV1(Room *, PduV1, State *, char **);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the room's creator as a ServerPart. This value should
|
* Gets the room's creator as a ServerPart. This value should
|
||||||
|
@ -229,7 +229,13 @@ extern bool RoomCanJoin(Room *, char *);
|
||||||
* Makes a local user join a room, and returns true if
|
* Makes a local user join a room, and returns true if
|
||||||
* the room was joined.
|
* the room was joined.
|
||||||
*/
|
*/
|
||||||
extern bool RoomJoin(Room *, User *);
|
extern bool RoomJoin(Room *, User *, char **);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes a local user leave a room, and returns true if
|
||||||
|
* the room was left.
|
||||||
|
*/
|
||||||
|
extern bool RoomLeave(Room *, User *, char **);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds or overwrites a room alias.
|
* Adds or overwrites a room alias.
|
||||||
|
|
Loading…
Reference in a new issue