diff --git a/src/Filter.c b/src/Filter.c index e269ef7..d08fe87 100644 --- a/src/Filter.c +++ b/src/Filter.c @@ -32,6 +32,8 @@ #include +#include + /* Verifies whenever an item passes through a set of blacklisted and * whitelisted groups(e.g "not_rooms"/"rooms"), and makes the calling * function return true/false if it is explicitely in a filtered list, @@ -120,6 +122,52 @@ IsRoomFiltered(Filter *filter, char *roomID) return false; } +static HashMap * +IncludeFields(Array * fields, HashMap * event) +{ + HashMap *cpy; + size_t i, len; + if (!event) + { + return NULL; + } + + if (!fields) + { + return JsonDuplicate(event); + } + + cpy = HashMapCreate(); + + /* NOTE: We intentionally add some fields due to some requirements + * around certain schemas. The specification does find such behavior + * compliant: + * > "[...] A server may include more fields than were requested." */ +#define CopyField(field) \ + HashMapSet(cpy, field, JsonValueDuplicate(HashMapGet(event, field))) + + CopyField("event_id"); + CopyField("origin_server_ts"); + CopyField("sender"); + CopyField("type"); + len = ArraySize(fields); + for (i = 0; i < len; i++) + { + char *field = ArrayGet(fields, i); + JsonValue *val = JsonValueDuplicate(MatrixGetJSON(event, field)); + + JsonValueFree(MatrixSetJSON(cpy, field, val)); + } + + /* Only copy it if not already present. */ + if (!HashMapGet(cpy, "content")) + { + CopyField("content"); + } + +#undef CopyField + return cpy; +} /* TODO: MORE FILTERS! */ HashMap * FilterApply(Filter * filter, HashMap * event) @@ -144,26 +192,31 @@ FilterApply(Filter * filter, HashMap * event) { return NULL; } - copy = JsonDuplicate(event); + copy = IncludeFields(filter->event_fields, event); return copy; } Filter * -FilterDecode(Db *db, char *user, char *filterStr) +FilterDecode(Db *db, char *user, char *filterStr, char **errp) { Filter *ret; DbRef *filterRef; HashMap *filterObj; if (!db || !user || !filterStr) { + if (errp) + { + *errp = + "Internal error: Field required for FilterDecode is missing."; + } return NULL; } if (*filterStr != '{') { - char *err; /* TODO: use error status somewhere... */ + char *err; filterRef = DbLock(db, 3, "filters", user, filterStr); filterObj = DbJson(filterRef); @@ -172,17 +225,24 @@ FilterDecode(Db *db, char *user, char *filterStr) if (!FilterFromJson(filterObj, ret, &err)) { + if (errp) + { + *errp = err; + } DbUnlock(db, filterRef); Free(ret); return NULL; } DbUnlock(db, filterRef); - (void) err; return ret; } /* TODO: Decode JSON here... */ + if (errp) + { + *errp = "Unimplemented feature: decoding from a raw JSON object"; + } return NULL; } diff --git a/src/Matrix.c b/src/Matrix.c index a2fac3f..e66efdd 100644 --- a/src/Matrix.c +++ b/src/Matrix.c @@ -428,3 +428,84 @@ MatrixGetJSON(HashMap *json, char *string) } return retVal; } + +JsonValue * +MatrixSetJSON(HashMap *json, char *string, JsonValue *new) +{ + JsonValue *retVal; + HashMap *currentObj; + char *field, *fieldTemp; + size_t i, length; + + if (!json || !string || !new) + { + return NULL; + } + + currentObj = json; + retVal = NULL; + length = strlen(string); + field = NULL; + +#define Append(ch) do \ + { \ + char chb[2] = { ch, 0 }; \ + fieldTemp = field; \ + field = StrConcat(2, field, chb); \ + Free(fieldTemp); \ + } while (0) + + /* We include the 0-terminator as a valid separator. */ + for (i = 0; i < length + 1; i++) + { + char currentChar = string[i]; + char escape; + if (currentChar == '\\' && (escape = string[i+1])) + { + if (escape != '.' && escape != '\\') + { + Append('\\'); + } + Append(escape); + i++; + continue; + } + else if (currentChar == '.' || currentChar == '\0') + { + if (!field || !currentObj) + { + Free(field); + return NULL; + } + + if (currentChar == '.') + { + retVal = HashMapGet(currentObj, field); + if (!retVal) + { + retVal = JsonValueObject(HashMapCreate()); + HashMapSet(currentObj, field, retVal); + } + currentObj = JsonValueAsObject(retVal); + + Free(field); + field = NULL; + continue; + } + + retVal = HashMapSet(currentObj, field, new); + Free(field); + field = NULL; + continue; + } + Append(currentChar); + } +#undef Append + + if (field) + { + /* This is weird. */ + Free(field); + } + return retVal; +} diff --git a/src/Routes/RouteSync.c b/src/Routes/RouteSync.c index 0660363..043ae30 100644 --- a/src/Routes/RouteSync.c +++ b/src/Routes/RouteSync.c @@ -121,9 +121,8 @@ ROUTE_IMPL(RouteSync, path, argp) if (filter) { char *userName = UserGetName(user); - if (!(filterData = FilterDecode(db, userName, filter))) + if (!(filterData = FilterDecode(db, userName, filter, &err))) { - err = "Couldn't decode filter given in"; HttpResponseStatus(args->context, HTTP_BAD_REQUEST); response = MatrixErrorCreate(M_BAD_JSON, err); } diff --git a/src/include/Filter.h b/src/include/Filter.h index f60daa5..a5f952f 100644 --- a/src/include/Filter.h +++ b/src/include/Filter.h @@ -55,9 +55,10 @@ extern bool IsRoomFiltered(Filter *, char *); /** * Decodes a filter from a JSON stream to decode or an ID that was - * registered using the filter API. + * registered using the filter API, and may return an optional error + * string, if not set to NULL. */ -extern Filter * FilterDecode(Db *, char *, char *); +extern Filter * FilterDecode(Db *, char *, char *, char **); /** * Frees all memory created by diff --git a/src/include/Matrix.h b/src/include/Matrix.h index a8b7011..0a7532d 100644 --- a/src/include/Matrix.h +++ b/src/include/Matrix.h @@ -163,4 +163,11 @@ extern HashMap * MatrixClientWellKnown(char *, char *); */ extern JsonValue * MatrixGetJSON(HashMap *, char *); +/** + * Sets a value given a dot-separated path (see + * .Fn MatrixGetJSON + * for more information). + */ +extern JsonValue * MatrixSetJSON(HashMap *, char *, JsonValue *); + #endif