From d5216eba96a45b413435219037f2988b32c9a1ea Mon Sep 17 00:00:00 2001 From: Nick Walker Date: Sat, 30 Mar 2024 20:33:45 -0700 Subject: [PATCH] Make putrec hook also decide whether `last` and websocket receive a message Current hook just governs whether the record is written, but this means that the `last` file and the live websocket would still be updated with data that the user decided to filter. Now we use the hook to skip those steps as well. Update HOOKS.md to note the functionality change --- doc/HOOKS.md | 2 +- recorder.c | 107 +++++++++++++++++++++++++++++++-------------------- 2 files changed, 66 insertions(+), 43 deletions(-) diff --git a/doc/HOOKS.md b/doc/HOOKS.md index c6e2440..d8a59d5 100644 --- a/doc/HOOKS.md +++ b/doc/HOOKS.md @@ -80,7 +80,7 @@ It is 14:10:01 in the year 2015 owntracks/jane/phone lat=48.858339 Avenue Anatol An optional function you provide is called `otr_putrec(u, d, s)`. If it exists, it is called with the current user in `u`, the device in `d` and the payload (which for OwnTracks apps is JSON but for, eg Greenwich devices might not be) in the string `s`. If your function returns a -non-zero value, the Recorder will *not* write the REC file for this publish. +non-zero value, the Recorder will *not* write the REC file for this publish, will *not* update the `last` record, and will *not* forward the publish over the websocket (if used). ## `otr_httpobject` diff --git a/recorder.c b/recorder.c index 5097afc..0e88be4 100644 --- a/recorder.c +++ b/recorder.c @@ -268,53 +268,46 @@ JsonNode *csv_to_json(char *payload) #define RECFORMAT "%s\t%-18s\t%s\n" /* - * Store payload in REC file unless our Lua putrec() function says - * we shouldn't for this particular user/device combo. Use the epoch + * Store payload in REC file. Use the epoch * time to construct path name and "key" */ static void putrec(struct udata *ud, time_t epoch, UT_string *reltopic, UT_string *username, UT_string *device, char *string) { FILE *fp; - int rc = 0; if (ud->norec) return; -#ifdef WITH_LUA - rc = hooks_norec(ud, UB(username), UB(device), string); -#endif + if ((fp = pathn("a", "rec", username, device, "rec", epoch)) == NULL) { + olog(LOG_ERR, "Cannot write REC for %s/%s: %m", + UB(username), UB(device)); + return; + } - if (rc == 0) { - if ((fp = pathn("a", "rec", username, device, "rec", epoch)) == NULL) { - olog(LOG_ERR, "Cannot write REC for %s/%s: %m", - UB(username), UB(device)); - return; - } + /* + * `string' might contain JSON, and it might be such that is + * contains newlines, etc. We have to sanitize if so else the + * .rec file will become unparseable. + */ + if (strchr(string, '\n') != 0 || strchr(string, '\t') != 0) { + JsonNode *j; + char *js = NULL; - /* - * `string' might contain JSON, and it might be such that is - * contains newlines, etc. We have to sanitize if so else the - * .rec file will become unparseable. - */ - if (strchr(string, '\n') != 0 || strchr(string, '\t') != 0) { - JsonNode *j; - char *js = NULL; + if ((j = json_decode(string)) != NULL) { + js = json_stringify(j, NULL); + fprintf(stderr, "JPJPJP: [%s]\n", js); + fprintf(fp, RECFORMAT, isotime(epoch), + UB(reltopic), js); + free(js); + json_delete(j); + } + } else { + fprintf(fp, RECFORMAT, isotime(epoch), + UB(reltopic), string); + } + fclose(fp); - if ((j = json_decode(string)) != NULL) { - js = json_stringify(j, NULL); - fprintf(stderr, "JPJPJP: [%s]\n", js); - fprintf(fp, RECFORMAT, isotime(epoch), - UB(reltopic), js); - free(js); - json_delete(j); - } - } else { - fprintf(fp, RECFORMAT, isotime(epoch), - UB(reltopic), string); - } - fclose(fp); - } } /* @@ -790,7 +783,7 @@ void handle_message(void *userdata, char *topic, char *payload, size_t payloadle bool cached, fresh; static UT_string *basetopic = NULL, *username = NULL, *device = NULL, *addr = NULL, *cc = NULL, *ghash = NULL, *ts = NULL; static UT_string *reltopic = NULL, *filename = NULL; - char *jsonstring, *_typestr = NULL; + char *jsonstring, *_typestr, *dumpedpayload = NULL; time_t now, epoch; int pingping = FALSE, skipslash = 0, geoprec = geohash_prec(); int r_ok = TRUE; /* True if recording enabled for a publish */ @@ -917,9 +910,15 @@ void handle_message(void *userdata, char *topic, char *payload, size_t payloadle if ((json = json_decode(payload)) == NULL) { if ((json = csv_to_json(payload)) == NULL) { + dumpedpayload = bindump(payload, payloadlen); /* It's not JSON or it's not a location CSV; store it using * now as time -- we have no other */ - putrec(ud, now, reltopic, username, device, bindump(payload, payloadlen)); +#ifdef WITH_LUA + r_ok = hooks_norec(ud, UB(username), UB(device), dumpedpayload) == 0; +#endif + if (r_ok) { + putrec(ud, now, reltopic, username, device, dumpedpayload); + } return; } } @@ -957,6 +956,13 @@ void handle_message(void *userdata, char *topic, char *payload, size_t payloadle do_info(ud, username, device, json); goto cleanup; case T_BEACON: + dumpedpayload = bindump(payload, payloadlen); +#ifdef WITH_LUA + r_ok = hooks_norec(ud, UB(username), UB(device), dumpedpayload) == 0; +#endif + if (!r_ok) { + goto cleanup; + } #ifdef WITH_HTTP if (ud->mgserver && !pingping) { json_append_member(json, "topic", json_mkstring(topic)); @@ -965,10 +971,8 @@ void handle_message(void *userdata, char *topic, char *payload, size_t payloadle http_ws_push_json(ud->mgserver, json); } #endif - if (r_ok) { - putrec(ud, now, reltopic, username, device, bindump(payload, payloadlen)); - } - goto cleanup; + putrec(ud, now, reltopic, username, device, dumpedpayload); + goto cleanup; case T_LWT: /* * LWT gets a pseudo-reltopic called 'lwt'; reason: if we keep the @@ -985,6 +989,9 @@ void handle_message(void *userdata, char *topic, char *payload, size_t payloadle case T_CMD: case T_STEPS: +#ifdef WITH_LUA + r_ok = hooks_norec(ud, UB(username), UB(device), payload) == 0; +#endif if (r_ok) { putrec(ud, now, reltopic, username, device, payload); } @@ -1034,8 +1041,12 @@ void handle_message(void *userdata, char *topic, char *payload, size_t payloadle break; #endif /* WITH_TOURS */ default: + dumpedpayload = bindump(payload, payloadlen); +#ifdef WITH_LUA + r_ok = hooks_norec(ud, UB(username), UB(device), dumpedpayload) == 0; +#endif if (r_ok) { - putrec(ud, now, reltopic, username, device, bindump(payload, payloadlen)); + putrec(ud, now, reltopic, username, device, dumpedpayload); } goto cleanup; } @@ -1248,11 +1259,23 @@ void handle_message(void *userdata, char *topic, char *payload, size_t payloadle double d_epoch = number(json, "tst"); epoch = (isnan(d_epoch)) ? now : d_epoch; - putrec(ud, epoch, reltopic, username, device, jsonstring); + +#ifdef WITH_LUA + r_ok = hooks_norec(ud, UB(username), UB(device), jsonstring) == 0; +#endif + if (r_ok) { + putrec(ud, epoch, reltopic, username, device, jsonstring); + } free(jsonstring); } } + if (!r_ok) { + // No further processing of this data if hook determined + // not to record it. + goto cleanup; + } + /* * Append a few bits to the location type to add to LAST and * for Lua / Websockets.