support FRIENDS in http mode

This commit is contained in:
Jan-Piet Mens
2016-02-23 12:05:46 +01:00
parent 115302feb6
commit cb7cdbaa30
4 changed files with 153 additions and 3 deletions
+43
View File
@@ -857,6 +857,38 @@ The content of the request is used by the Recorder as though it had arrived as a
If the Recorder is compiled without specifying `WITH_MQTT` at build time, support for MQTT is disabled completely.
### Friends in HTTP mode
When a device posts a location request in HTTP mode, the endpoint may return a JSON array of OwnTracks objects of which `_type`s `cmd`, `location` and `card` may be supported by the device. This allows the device to see, say, friends. The Recorder has built-in support for this with the named "friends" lmdb database.
Assuming the following content of the friends database
```
$ ocat -S JP --dump=friends
jane-phone ["john/android"]
```
when user `jane` and device `phone` POST a new location via HTTP, the Recorder will present the following payload to the device:
```json
[
{
"_type": "card",
"tid": "JA",
"face": "/9j/4AAQSkZJR...",
"name": "John Doe"
},
{
"_type": "location",
"tid": "JA",
"lat": 48.95833,
"lon": 2.39523,
"tst": 1456212791
}
]
```
### Authentication
In HTTP mode, the Recorder provides no form of authentication; anybody who "stumbles" over the correct endpoint will be able to post location data to your Recorder! You do not want this to happen.
@@ -902,6 +934,17 @@ echo "jjolie-iphone s3cr1t" | ocat --load=keys
Beware: these secret keys are stored in plain text so the database must be protected!
#### `friends`
For http mode, the `friends` named LMDB database contains lists of "friends" on a per user-device key. The key's value must be a valid JSON array of strings, each in the form `"user:device"` or `"user/device"` which indicate which locations a particular user may see. For example, when a user called `jane` on device `phone` publishes in http mode and she should be permitted to see where `john` / `android` is, we add the following key/value to the friends named database:
```bash
ocat --load=friends <<EOF
jane-phone [ "john/android" ]
```
The user/device separator in the array's strings may be a slash (`/`), a dash (`-`), or a colon (`:`).
## Encryption (*experimental!*)
If compiled with `WITH_ENCRYPT` support (this is the default in our packages), the recorder will handle messages from OwnTracks [devices which support payload encryption](http://owntracks.org/booklet/features/encrypt/). Each user / device requires a secret key which is configured on the device and which must be configured on the Recorder host in order for the Recorder to be able to decrypt the payloads.
+103 -3
View File
@@ -373,6 +373,103 @@ static int send_status(struct mg_connection *conn, int status, char *text)
return (MG_TRUE);
}
/*
* Create an array of OwnTracks objects of locations and cards of
* friends of user `u` and device `d`. Each of the objects in this
* array *must* contain a TID as the apps will use that to construct
* a ficticious topic name (owntracks/_http/<tid>) internally.
* If this user/device combo has no friends, return an empty array.
*/
JsonNode *populate_friends(struct mg_connection *conn, char *u, char *d)
{
struct udata *ud = (struct udata *)conn->server_param;
JsonNode *results = json_mkarray(), *lastuserlist;
JsonNode *friends, *obj, *jud, *newob, *jtid;
int np;
char *pairs[3];
static UT_string *userdevice = NULL;
utstring_renew(userdevice);
utstring_printf(userdevice, "%s-%s", u, d);
friends = gcache_json_get(ud->httpfriends, UB(userdevice));
if (ud->debug) {
char *js = NULL;
if (friends)
js = json_stringify(friends, NULL);
debug(ud, "Friends of %s: %s", UB(userdevice), js ? js : "<nil>");
if (js)
free(js);
}
/* assume the following are friends of jane/3s */
// json_append_element(friends, json_mkstring("foo/bar"));
// json_append_element(friends, json_mkstring("e:1"));
// json_append_element(friends, json_mkstring("jog/fok"));
// json_append_element(friends, json_mkstring("iss-iss"));
// json_append_element(friends, json_mkstring("db/station"));
/*
* Run through the array of friends of this user. Get LAST object,
* which contains CARD and LOCATION data. Create an array of
* separate location and card objects to return in HTTP mode.
*/
json_foreach(jud, friends) {
if ((np = splitter(jud->string_, "/:-", pairs)) != 2) {
continue;
}
if ((lastuserlist = last_users(pairs[0], pairs[1], NULL)) == NULL) {
splitterfree(pairs);
continue;
}
splitterfree(pairs);
if ((obj = json_find_element(lastuserlist, 0)) == NULL) {
json_delete(lastuserlist);
continue;
}
// printf("OBJ --->%s<---\n", json_stringify(obj, " "));
/* TID is mandatory; if we don't have that, skip */
if ((jtid = json_find_member(obj, "tid")) == NULL) {
json_delete(lastuserlist);
continue;
}
/* CARD */
if (json_find_member(obj, "face") && json_find_member(obj, "name")) {
newob = json_mkobject();
json_append_member(newob, "_type", json_mkstring("card"));
json_copy_element_to_object(newob, "tid", jtid);
json_copy_element_to_object(newob, "face", json_find_member(obj, "face"));
json_copy_element_to_object(newob, "name", json_find_member(obj, "name"));
json_append_element(results, newob);
}
/* LOCATION */
newob = json_mkobject();
json_append_member(newob, "_type", json_mkstring("location"));
json_copy_element_to_object(newob, "tid", jtid);
json_copy_element_to_object(newob, "lat", json_find_member(obj, "lat"));
json_copy_element_to_object(newob, "lon", json_find_member(obj, "lon"));
json_copy_element_to_object(newob, "tst", json_find_member(obj, "tst"));
json_append_element(results, newob);
json_delete(lastuserlist);
}
json_delete(friends);
return (results);
}
/*
* Invoked from an HTTP POST to /pub?u=username&d=devicename
* We need u and d in order to contruct a topic name. Obtain
@@ -385,6 +482,7 @@ static int dopublish(struct mg_connection *conn, const char *uri)
struct udata *ud = (struct udata *)conn->server_param;
char *payload, *u, *d;
static UT_string *topic = NULL;
JsonNode *jarray;
if ((u = field(conn, "u")) == NULL) {
u = strdup("owntracks");
@@ -396,8 +494,6 @@ static int dopublish(struct mg_connection *conn, const char *uri)
utstring_renew(topic);
utstring_printf(topic, "owntracks/%s/%s", u, d);
free(u);
free(d);
/* We need a nul-terminated payload in handle_message() */
payload = calloc(sizeof(char), conn->content_len + 1);
@@ -409,7 +505,11 @@ static int dopublish(struct mg_connection *conn, const char *uri)
free(payload);
return json_response(conn, NULL);
jarray = populate_friends(conn, u, d);
free(u);
free(d);
return json_response(conn, jarray);
}
/*
+6
View File
@@ -1490,6 +1490,11 @@ int main(int argc, char **argv)
}
gcache_close(gt);
#endif /* !ENCRYPT */
if ((gt = gcache_open(path, "friends", FALSE)) == NULL) {
fprintf(stderr, "Cannot lmdb-open `friends'\n");
exit(2);
}
gcache_close(gt);
exit(0);
}
@@ -1547,6 +1552,7 @@ int main(int argc, char **argv)
# ifdef WITH_ENCRYPT
ud->keydb = gcache_open(err, "keys", TRUE);
# endif
ud->httpfriends = gcache_open(err, "friends", TRUE);
#if WITH_LUA
/*
+1
View File
@@ -38,6 +38,7 @@ struct udata {
char *label; /* Server label */
char *geokey; /* Google reverse-geo API key */
int debug; /* enable for debugging */
struct gcache *httpfriends; /* lmdb named database 'friends' */
};
#endif