2015-09-01 08:19:52 -07:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2015 Jan-Piet Mens <jpmens@gmail.com> and OwnTracks
|
|
|
|
*
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
* of this software and associated documentation files (the "Software"), to
|
|
|
|
* deal in the Software without restriction, including without limitation the
|
|
|
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
|
|
* sell copies of the Software, and to permit persons to whom the Software is
|
|
|
|
* furnished to do so, subject to the following conditions:
|
|
|
|
*
|
|
|
|
* The above copyright notice and this permission notice shall be included in
|
|
|
|
* all copies or substantial portions of the Software.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
|
|
* JAN-PIET MENS OR OWNTRACKS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
|
|
* IN THE SOFTWARE.
|
|
|
|
*/
|
|
|
|
|
2015-08-29 06:03:56 -07:00
|
|
|
#include <errno.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include "json.h"
|
|
|
|
#include "config.h"
|
|
|
|
#include "util.h"
|
|
|
|
#include "storage.h"
|
2015-09-01 23:21:47 -07:00
|
|
|
#include "udata.h"
|
2015-08-29 06:03:56 -07:00
|
|
|
#ifdef HAVE_HTTP
|
|
|
|
# include "http.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef HAVE_HTTP
|
|
|
|
extern struct mg_server *mgserver;
|
|
|
|
|
2015-09-03 06:34:10 -07:00
|
|
|
/*
|
|
|
|
* Send a message into the HTTP server; this will be dispatched
|
|
|
|
* to listening WS clients.
|
|
|
|
*/
|
2015-08-29 06:03:56 -07:00
|
|
|
|
2015-09-03 06:34:10 -07:00
|
|
|
void http_ws_push(struct mg_server *server, char *text)
|
2015-08-29 06:03:56 -07:00
|
|
|
{
|
|
|
|
struct mg_connection *c;
|
|
|
|
char buf[4096];
|
2015-09-03 06:34:10 -07:00
|
|
|
int len = snprintf(buf, sizeof(buf), "%s", text);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Iterate over connections and push message to the WS connections.
|
|
|
|
*/
|
2015-08-29 06:03:56 -07:00
|
|
|
|
|
|
|
for (c = mg_next(server, NULL); c != NULL; c = mg_next(server, c)) {
|
|
|
|
if (c->is_websocket) {
|
|
|
|
mg_websocket_write(c, 1, buf, len);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int send_reply(struct mg_connection *conn)
|
|
|
|
{
|
|
|
|
if (conn->is_websocket) {
|
|
|
|
// This handler is called for each incoming websocket frame, one or more
|
|
|
|
// times for connection lifetime.
|
|
|
|
// Echo websocket data back to the client.
|
|
|
|
mg_websocket_write(conn, 1, conn->content, conn->content_len);
|
|
|
|
return conn->content_len == 4 && !memcmp(conn->content, "exit", 4) ? MG_FALSE : MG_TRUE;
|
|
|
|
} else {
|
|
|
|
mg_send_file(conn, "jp.html", NULL);
|
|
|
|
|
|
|
|
return MG_MORE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-09-03 08:26:13 -07:00
|
|
|
static int json_response(struct mg_connection *conn, JsonNode *json)
|
|
|
|
{
|
2015-08-29 06:03:56 -07:00
|
|
|
char *js;
|
|
|
|
|
2015-09-03 08:26:13 -07:00
|
|
|
mg_send_header(conn, "Content-Type", "application/json");
|
|
|
|
mg_send_header(conn, "Access-Control-Allow-Origin", "*");
|
2015-08-29 06:03:56 -07:00
|
|
|
|
2015-09-03 08:26:13 -07:00
|
|
|
if ((js = json_stringify(json, JSON_INDENT)) != NULL) {
|
|
|
|
mg_printf_data(conn, js);
|
|
|
|
free(js);
|
2015-08-29 06:03:56 -07:00
|
|
|
}
|
2015-09-03 08:26:13 -07:00
|
|
|
json_delete(json);
|
|
|
|
return (MG_TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We are being called with the portion behind /api/0/ as in
|
|
|
|
* /users/ or /list
|
|
|
|
*/
|
2015-08-29 06:03:56 -07:00
|
|
|
|
2015-09-03 08:26:13 -07:00
|
|
|
#define MAXPARTS 40
|
2015-08-29 06:03:56 -07:00
|
|
|
|
2015-09-03 08:26:13 -07:00
|
|
|
#define CLEANUP do {\
|
|
|
|
if (u) free(u);\
|
|
|
|
if (d) free(d);\
|
|
|
|
if (time_from) free(time_from);\
|
|
|
|
if (time_to) free(time_to);\
|
|
|
|
} while(0)
|
2015-08-29 06:03:56 -07:00
|
|
|
|
2015-09-03 08:26:13 -07:00
|
|
|
static int dispatch(struct mg_connection *conn, const char *uri)
|
|
|
|
{
|
|
|
|
output_type otype = JSON;
|
|
|
|
int nparts, ret, limit = 0;
|
|
|
|
char *uparts[MAXPARTS], buf[BUFSIZ], *u = NULL, *d = NULL;
|
|
|
|
char *time_from = NULL, *time_to = NULL;
|
|
|
|
time_t s_lo, s_hi;
|
|
|
|
JsonNode *json, *obj, *locs;
|
2015-08-29 06:03:56 -07:00
|
|
|
|
2015-09-03 08:26:13 -07:00
|
|
|
fprintf(stderr, "DISPATCH: %s\n", uri);
|
2015-08-29 06:03:56 -07:00
|
|
|
|
2015-09-03 08:26:13 -07:00
|
|
|
if ((nparts = splitter((char *)uri, "/", uparts)) == -1) {
|
|
|
|
mg_send_status(conn, 405);
|
|
|
|
mg_printf_data(conn, "no way\n");
|
|
|
|
return (MG_TRUE);
|
|
|
|
}
|
2015-08-29 06:03:56 -07:00
|
|
|
|
2015-09-03 08:26:13 -07:00
|
|
|
for (ret = 0; ret < nparts; ret++) {
|
|
|
|
fprintf(stderr, "%d = %s\n", ret, uparts[ret]);
|
|
|
|
}
|
2015-08-29 06:03:56 -07:00
|
|
|
|
2015-09-03 08:26:13 -07:00
|
|
|
if ((ret = mg_get_var(conn, "user", buf, sizeof(buf))) > 0) {
|
|
|
|
u = strdup(buf);
|
|
|
|
}
|
|
|
|
if ((ret = mg_get_var(conn, "device", buf, sizeof(buf))) > 0) {
|
|
|
|
d = strdup(buf);
|
|
|
|
}
|
|
|
|
if ((ret = mg_get_var(conn, "limit", buf, sizeof(buf))) > 0) {
|
|
|
|
limit = atoi(buf);
|
|
|
|
}
|
|
|
|
if ((ret = mg_get_var(conn, "from", buf, sizeof(buf))) > 0) {
|
|
|
|
time_from = strdup(buf);
|
|
|
|
}
|
|
|
|
if ((ret = mg_get_var(conn, "to", buf, sizeof(buf))) > 0) {
|
|
|
|
time_to = strdup(buf);
|
|
|
|
}
|
2015-08-29 06:03:56 -07:00
|
|
|
|
2015-09-03 08:26:13 -07:00
|
|
|
if ((ret = mg_get_var(conn, "format", buf, sizeof(buf))) > 0) {
|
|
|
|
if (!strcmp(buf, "geojson"))
|
|
|
|
otype = GEOJSON;
|
|
|
|
else if (!strcmp(buf, "json"))
|
|
|
|
otype = JSON;
|
|
|
|
else if (!strcmp(buf, "linestring"))
|
|
|
|
otype = LINESTRING;
|
|
|
|
else {
|
|
|
|
mg_send_status(conn, 400);
|
|
|
|
mg_printf_data(conn, "unrecognized format\n");
|
|
|
|
CLEANUP;
|
|
|
|
return (MG_TRUE);
|
2015-08-29 06:03:56 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-03 08:26:13 -07:00
|
|
|
if (make_times(time_from, &s_lo, time_to, &s_hi) != 1) {
|
|
|
|
mg_send_status(conn, 405);
|
|
|
|
mg_printf_data(conn, "wrong times\n");
|
|
|
|
return (MG_TRUE);
|
|
|
|
}
|
2015-08-29 06:03:56 -07:00
|
|
|
|
2015-09-03 08:26:13 -07:00
|
|
|
fprintf(stderr, "user=[%s], device=[%s]\n", (u) ? u : "<nil>", (d) ? d : "<NIL>");
|
2015-08-29 06:03:56 -07:00
|
|
|
|
2015-09-03 08:26:13 -07:00
|
|
|
/* /list [<username>[<device>]] */
|
2015-08-29 06:03:56 -07:00
|
|
|
|
2015-09-03 08:26:13 -07:00
|
|
|
if (nparts == 1 && !strcmp(uparts[0], "list")) {
|
|
|
|
if ((json = lister(u, d, 0, s_hi, FALSE)) != NULL) {
|
|
|
|
CLEANUP;
|
|
|
|
return (json_response(conn, json));
|
|
|
|
}
|
|
|
|
}
|
2015-08-29 06:03:56 -07:00
|
|
|
|
2015-09-03 08:26:13 -07:00
|
|
|
/* /locations [<username>[<device>]] */
|
|
|
|
|
|
|
|
if (nparts == 1 && !strcmp(uparts[0], "locations")) {
|
|
|
|
/*
|
|
|
|
* Obtain a list of .rec files from lister(), possibly limited by s_lo/s_hi times,
|
|
|
|
* process each and build the JSON `obj' with an array of locations.
|
|
|
|
*/
|
|
|
|
|
|
|
|
obj = json_mkobject();
|
|
|
|
locs = json_mkarray();
|
|
|
|
|
|
|
|
if ((json = lister(u, d, s_lo, s_hi, (limit > 0) ? TRUE : FALSE)) != NULL) {
|
|
|
|
JsonNode *arr;
|
|
|
|
|
|
|
|
CLEANUP;
|
|
|
|
|
|
|
|
if ((arr = json_find_member(json, "results")) != NULL) { // get array
|
|
|
|
JsonNode *f;
|
|
|
|
json_foreach(f, arr) {
|
|
|
|
locations(f->string_, obj, locs, s_lo, s_hi, otype, limit, NULL);
|
|
|
|
// printf("%s\n", f->string_);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
json_delete(json);
|
|
|
|
}
|
|
|
|
json_append_member(obj, "locations", locs);
|
|
|
|
|
|
|
|
if (otype == JSON) {
|
|
|
|
return (json_response(conn, obj));
|
|
|
|
} else if (otype == LINESTRING) {
|
|
|
|
JsonNode *geolinestring = geo_linestring(locs);
|
|
|
|
|
|
|
|
if (geolinestring != NULL) {
|
|
|
|
json_delete(obj);
|
|
|
|
return (json_response(conn, geolinestring));
|
2015-08-29 06:03:56 -07:00
|
|
|
}
|
|
|
|
|
2015-09-03 08:26:13 -07:00
|
|
|
} else if (otype == GEOJSON) {
|
|
|
|
JsonNode *geojson = geo_json(locs);
|
2015-08-29 06:03:56 -07:00
|
|
|
|
2015-09-03 08:26:13 -07:00
|
|
|
json_delete(obj);
|
|
|
|
if (geojson != NULL) {
|
|
|
|
return (json_response(conn, geojson));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-08-29 06:03:56 -07:00
|
|
|
|
2015-09-03 08:26:13 -07:00
|
|
|
// mg_printf_data(conn, "user=[%s], device=[%s]\n", (u) ? u : "<nil>", (d) ? d : "<NIL>");
|
|
|
|
mg_printf_data(conn, "no comprendo");
|
2015-08-29 06:03:56 -07:00
|
|
|
|
2015-09-03 06:34:10 -07:00
|
|
|
return (MG_TRUE);
|
|
|
|
}
|
2015-08-29 06:03:56 -07:00
|
|
|
|
2015-09-03 06:34:10 -07:00
|
|
|
int ev_handler(struct mg_connection *conn, enum mg_event ev)
|
|
|
|
{
|
|
|
|
switch (ev) {
|
|
|
|
case MG_AUTH:
|
|
|
|
return (MG_TRUE);
|
2015-08-29 06:03:56 -07:00
|
|
|
|
2015-09-03 06:34:10 -07:00
|
|
|
case MG_REQUEST:
|
2015-08-29 06:03:56 -07:00
|
|
|
|
2015-09-03 06:34:10 -07:00
|
|
|
/* Websockets URI ?*/
|
|
|
|
if (strcmp(conn->uri, "/ws") == 0) {
|
|
|
|
return send_reply(conn);
|
2015-08-29 06:03:56 -07:00
|
|
|
}
|
|
|
|
|
2015-09-03 06:34:10 -07:00
|
|
|
fprintf(stderr, "*** Request: %s [%s]\n", conn->request_method, conn->uri);
|
2015-08-29 06:03:56 -07:00
|
|
|
|
2015-09-03 06:34:10 -07:00
|
|
|
if (strncmp(conn->uri, API_PREFIX, strlen(API_PREFIX)) == 0) {
|
|
|
|
return dispatch(conn, conn->uri + strlen(API_PREFIX) - 1);
|
2015-08-29 06:03:56 -07:00
|
|
|
}
|
|
|
|
|
2015-09-03 06:34:10 -07:00
|
|
|
/*
|
|
|
|
* We can't handle this request ourselves. Return
|
|
|
|
* to Mongoose and have it try document root.
|
|
|
|
*/
|
|
|
|
|
|
|
|
return (MG_FALSE);
|
|
|
|
|
2015-08-29 06:03:56 -07:00
|
|
|
default:
|
2015-09-03 06:34:10 -07:00
|
|
|
return (MG_FALSE);
|
2015-08-29 06:03:56 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* HAVE_HTTP */
|
|
|
|
|