From c2956568d6166a10cf4a1a7f7b2515ec8822eeed Mon Sep 17 00:00:00 2001 From: Jeremy Lakeman Date: Wed, 5 Oct 2016 10:47:51 +1030 Subject: [PATCH] Send a meshmb message via restful api --- conf_schema.h | 1 + dataformats.c | 7 ++- headerfiles.mk | 1 + httpd.h | 2 +- meshmb.c | 12 ++-- meshmb.h | 7 +++ meshmb_restful.c | 142 ++++++++++++++++++++++++++++++++++++++++++++ meshms_restful.c | 4 +- serval_types.h | 4 +- servald_features.c | 1 + sourcefiles.mk | 1 + tests/meshmbrestful | 66 ++++++++++++++++++++ 12 files changed, 234 insertions(+), 14 deletions(-) create mode 100644 meshmb.h create mode 100644 meshmb_restful.c create mode 100755 tests/meshmbrestful diff --git a/conf_schema.h b/conf_schema.h index d20feae0..e41e104a 100644 --- a/conf_schema.h +++ b/conf_schema.h @@ -268,6 +268,7 @@ ATOM(bool_t, rhizome_direct, 0, boolean,, "") ATOM(bool_t, server, 0, boolean,, "") ATOM(bool_t, subscriber, 0, boolean,, "") ATOM(bool_t, meshms, 0, boolean,, "") +ATOM(bool_t, meshmb, 0, boolean,, "") ATOM(bool_t, vomp, 0, boolean,, "") ATOM(bool_t, profiling, 0, boolean,, "") ATOM(bool_t, linkstate, 0, boolean,, "") diff --git a/dataformats.c b/dataformats.c index f4af7a08..ab31eb6b 100644 --- a/dataformats.c +++ b/dataformats.c @@ -122,10 +122,11 @@ int str_to_identity_t(identity_t *idp, const char *hex) return parse_hex_t(idp, hex); } -int strn_to_identity_t(identity_t *idp, const char *hex, size_t hexlen) +int strn_to_identity_t(identity_t *idp, const char *hex, const char **endp) { - const char *endp; - return parse_hexn_t(idp, hex, hexlen, &endp); + if (strn_fromhex(idp->binary, sizeof *idp, hex, endp)==sizeof *idp) + return 0; + return -1; } diff --git a/headerfiles.mk b/headerfiles.mk index 737b1b35..c726d83f 100644 --- a/headerfiles.mk +++ b/headerfiles.mk @@ -13,6 +13,7 @@ HDRS= fifo.h \ httpd.h \ instance.h \ meshms.h \ + meshmb.h \ message_ply.h \ nibble_tree.h \ serval_types.h \ diff --git a/httpd.h b/httpd.h index 6de95f5b..a2260a51 100644 --- a/httpd.h +++ b/httpd.h @@ -176,7 +176,7 @@ typedef struct httpd_request /* For responses that list MeshMS messages in a single conversation. */ struct { - struct newsince_position { + struct meshms_position { enum meshms_which_ply which_ply; uint64_t offset; uint64_t their_ack; diff --git a/meshmb.c b/meshmb.c index f293f7ca..22bb3380 100644 --- a/meshmb.c +++ b/meshmb.c @@ -1,3 +1,4 @@ +#include "serval.h" #include "serval_types.h" #include "dataformats.h" #include "cli.h" @@ -8,7 +9,7 @@ #include "commandline.h" #include "overlay_buffer.h" -static int meshmb_send(keyring_identity *id, const char *message, size_t message_len, +int meshmb_send(keyring_identity *id, const char *message, size_t message_len, unsigned nassignments, const struct rhizome_manifest_field_assignment *assignments){ struct message_ply ply; @@ -43,8 +44,8 @@ static int app_meshmb_send(const struct cli_parsed *parsed, struct cli_context * unsigned nfields = (parsed->varargi == -1) ? 0 : parsed->argc - (unsigned)parsed->varargi; struct rhizome_manifest_field_assignment fields[nfields]; - if (nfields) { - assert(parsed->varargi >= 0); + + if (nfields){ if (rhizome_parse_field_assignments(fields, nfields, parsed->args + parsed->varargi)==-1) return -1; } @@ -55,13 +56,12 @@ static int app_meshmb_send(const struct cli_parsed *parsed, struct cli_context * if (create_serval_instance_dir() == -1) return -1; + if (rhizome_opendb() == -1) + return -1; if (!(keyring = keyring_open_instance_cli(parsed))) return -1; int ret = -1; - if (rhizome_opendb() == -1) - goto end; - keyring_identity *id = keyring_find_identity(keyring, &identity); if (!id){ WHY("Invalid identity"); diff --git a/meshmb.h b/meshmb.h new file mode 100644 index 00000000..50391d8c --- /dev/null +++ b/meshmb.h @@ -0,0 +1,7 @@ +#ifndef __SERVAL_DNA__MESHMB_H +#define __SERVAL_DNA__MESHMB_H + +int meshmb_send(keyring_identity *id, const char *message, size_t message_len, + unsigned nassignments, const struct rhizome_manifest_field_assignment *assignments); + +#endif \ No newline at end of file diff --git a/meshmb_restful.c b/meshmb_restful.c new file mode 100644 index 00000000..ef1f2d89 --- /dev/null +++ b/meshmb_restful.c @@ -0,0 +1,142 @@ +#include "serval.h" +#include "conf.h" +#include "httpd.h" +#include "str.h" +#include "numeric_str.h" +#include "base64.h" +#include "strbuf_helpers.h" +#include "meshmb.h" + +DEFINE_FEATURE(http_rest_meshmb); + +static char *PART_MESSAGE = "message"; +static int send_part_start(struct http_request *hr) +{ + httpd_request *r = (httpd_request *) hr; + assert(r->u.sendmsg.current_part == NULL); + return 0; +} + +static int send_part_end(struct http_request *hr) +{ + httpd_request *r = (httpd_request *) hr; + if (r->u.sendmsg.current_part == PART_MESSAGE) { + if (r->u.sendmsg.message.length == 0) + return http_response_form_part(r, 400, "Invalid (empty)", PART_MESSAGE, NULL, 0); + r->u.sendmsg.received_message = 1; + DEBUGF(httpd, "received %s = %s", PART_MESSAGE, alloca_toprint(-1, r->u.sendmsg.message.buffer, r->u.sendmsg.message.length)); + } else + FATALF("current_part = %s", alloca_str_toprint(r->u.sendmsg.current_part)); + r->u.sendmsg.current_part = NULL; + return 0; +} + +static int send_part_header(struct http_request *hr, const struct mime_part_headers *h) +{ + httpd_request *r = (httpd_request *) hr; + if (!h->content_disposition.type[0]) + return http_response_content_disposition(r, 415, "Missing", h->content_disposition.type); + if (strcmp(h->content_disposition.type, "form-data") != 0) + return http_response_content_disposition(r, 415, "Unsupported", h->content_disposition.type); + if (strcmp(h->content_disposition.name, PART_MESSAGE) == 0) { + if (r->u.sendmsg.received_message) + return http_response_form_part(r, 400, "Duplicate", PART_MESSAGE, NULL, 0); + r->u.sendmsg.current_part = PART_MESSAGE; + form_buf_malloc_init(&r->u.sendmsg.message, MESSAGE_PLY_MAX_LEN); + } + else + return http_response_form_part(r, 415, "Unsupported", h->content_disposition.name, NULL, 0); + if (!h->content_type.type[0] || !h->content_type.subtype[0]) + return http_response_content_type(r, 400, "Missing", &h->content_type); + if (strcmp(h->content_type.type, "text") != 0 || strcmp(h->content_type.subtype, "plain") != 0) + return http_response_content_type(r, 415, "Unsupported", &h->content_type); + if (!h->content_type.charset[0]) + return http_response_content_type(r, 400, "Missing charset", &h->content_type); + if (strcmp(h->content_type.charset, "utf-8") != 0) + return http_response_content_type(r, 415, "Unsupported charset", &h->content_type); + return 0; +} + +static int send_part_body(struct http_request *hr, char *buf, size_t len) +{ + httpd_request *r = (httpd_request *) hr; + if (r->u.sendmsg.current_part == PART_MESSAGE) { + form_buf_malloc_accumulate(r, PART_MESSAGE, &r->u.sendmsg.message, buf, len); + } else + FATALF("current_part = %s", alloca_str_toprint(r->u.sendmsg.current_part)); + return 0; +} + +static int send_content_end(struct http_request *hr) +{ + httpd_request *r = (httpd_request *) hr; + if (!r->u.sendmsg.received_message) + return http_response_form_part(r, 400, "Missing", PART_MESSAGE, NULL, 0); + assert(r->u.sendmsg.message.length > 0); + assert(r->u.sendmsg.message.length <= MESSAGE_PLY_MAX_LEN); + + keyring_identity *id = keyring_find_identity(keyring, &r->bid); + if (!id){ + http_request_simple_response(&r->http, 500, "TODO, detailed errors"); + return 500; + } + if (meshmb_send(id, r->u.sendmsg.message.buffer, r->u.sendmsg.message.length, 0, NULL)==-1){ + http_request_simple_response(&r->http, 500, "TODO, detailed errors"); + return 500; + } + http_request_simple_response(&r->http, 201, "TODO, detailed response"); + return 201; +} + +static void send_finalise(httpd_request *r) +{ + form_buf_malloc_release(&r->u.sendmsg.message); +} + +static int restful_meshmb_send(httpd_request *r, const char *remainder) +{ + if (*remainder) + return 404; + assert(r->finalise_union == NULL); + r->finalise_union = send_finalise; + // Parse the request body as multipart/form-data. + assert(r->u.sendmsg.current_part == NULL); + assert(!r->u.sendmsg.received_message); + r->http.form_data.handle_mime_part_start = send_part_start; + r->http.form_data.handle_mime_part_end = send_part_end; + r->http.form_data.handle_mime_part_header = send_part_header; + r->http.form_data.handle_mime_body = send_part_body; + // Send the message once the body has arrived. + r->http.handle_content_end = send_content_end; + return 1; +} + +DECLARE_HANDLER("/restful/meshmb/", restful_meshmb_); +static int restful_meshmb_(httpd_request *r, const char *remainder) +{ + r->http.response.header.content_type = CONTENT_TYPE_JSON; + if (!is_rhizome_http_enabled()) + return 404; + int ret = authorize_restful(&r->http); + if (ret) + return ret; + const char *verb = HTTP_VERB_GET; + HTTP_HANDLER *handler = NULL; + const char *end; + + if (strn_to_identity_t(&r->bid, remainder, &end) != -1) { + remainder = end; + + if (strcmp(remainder, "/sendmessage") == 0) { + handler = restful_meshmb_send; + verb = HTTP_VERB_POST; + remainder = ""; + } + } + + if (handler == NULL) + return 404; + if (r->http.verb != verb) + return 405; + return handler(r, remainder); +} diff --git a/meshms_restful.c b/meshms_restful.c index d5c4f693..e2663b11 100644 --- a/meshms_restful.c +++ b/meshms_restful.c @@ -51,7 +51,7 @@ static void finalise_union_meshms_sendmessage(httpd_request *r) #define MESHMS_TOKEN_STRLEN (BASE64_ENCODED_LEN(MAX_TOKEN_LEN)) #define alloca_meshms_token(pos) meshms_token_to_str(alloca(MESHMS_TOKEN_STRLEN + 1), (pos)) -static char *meshms_token_to_str(char *buf, struct newsince_position *pos) +static char *meshms_token_to_str(char *buf, struct meshms_position *pos) { uint8_t tmp[MAX_TOKEN_LEN]; int ofs = 0; @@ -65,7 +65,7 @@ static char *meshms_token_to_str(char *buf, struct newsince_position *pos) return buf; } -static int strn_to_meshms_token(const char *str, struct newsince_position *pos, const char **afterp) +static int strn_to_meshms_token(const char *str, struct meshms_position *pos, const char **afterp) { uint8_t token[MAX_TOKEN_LEN]; size_t token_len = base64url_decode(token, sizeof token, str, 0, afterp, 0, NULL); diff --git a/serval_types.h b/serval_types.h index ffbf5f24..6ed3b217 100644 --- a/serval_types.h +++ b/serval_types.h @@ -92,8 +92,8 @@ int sid_get_special_type(const sid_t *sid); #define alloca_tohex_identity_t(identity) alloca_tohex((identity)->binary, IDENTITY_SIZE) int cmp_identity_t(const identity_t *a, const identity_t *b); -int str_to_identity_t(identity_t *sid, const char *hex); -int strn_to_identity_t(identity_t *sid, const char *hex, size_t hexlen); +int str_to_identity_t(identity_t *id, const char *hex); +int strn_to_identity_t(identity_t *idp, const char *hex, const char **endp); /* MDP port number */ diff --git a/servald_features.c b/servald_features.c index 6abbf084..8ca63e7f 100644 --- a/servald_features.c +++ b/servald_features.c @@ -57,6 +57,7 @@ void servald_features() USE_FEATURE(http_rest_keyring); USE_FEATURE(http_rest_rhizome); USE_FEATURE(http_rest_meshms); + USE_FEATURE(http_rest_meshmb); } #include diff --git a/sourcefiles.mk b/sourcefiles.mk index 0c6616bd..9b659ae0 100644 --- a/sourcefiles.mk +++ b/sourcefiles.mk @@ -67,6 +67,7 @@ SERVAL_DAEMON_SOURCES = \ message_ply.c \ meshms_cli.c \ meshms_restful.c \ + meshmb_restful.c \ msp_client.c \ msp_proxy.c \ monitor.c \ diff --git a/tests/meshmbrestful b/tests/meshmbrestful new file mode 100755 index 00000000..8c26304a --- /dev/null +++ b/tests/meshmbrestful @@ -0,0 +1,66 @@ +#!/bin/bash + +source "${0%/*}/../testframework.sh" +source "${0%/*}/../testdefs.sh" +source "${0%/*}/../testdefs_json.sh" + +setup_instance() { + set_instance $1 + executeOk_servald config \ + set api.restful.users.harry.password potter \ + set api.restful.users.ron.password weasley \ + set api.restful.users.hermione.password grainger \ + set debug.http_server on \ + set debug.httpd on \ + set debug.meshmb on \ + set debug.verbose on \ + set log.console.level debug + set_extra_config + if [ -z "$IDENTITY_COUNT" ]; then + create_single_identity + else + create_identities $IDENTITY_COUNT + fi +} + +set_extra_config() { + : +} + +setup() { + setup_curl 7 + setup_json + setup_servald + export SERVALD_RHIZOME_DB_RETRY_LIMIT_MS=60000 + setup_instance +A + start_servald_instances +A + wait_until servald_restful_http_server_started +A + get_servald_restful_http_server_port PORTA +A +} + +finally() { + stop_all_servald_servers +} + +teardown() { + kill_all_servald_processes + assert_no_servald_processes + report_all_servald_servers +} + +doc_MeshmbSend="Send a broadcast message" +test_MeshmbSend() { + executeOk curl \ + -H "Expect:" \ + --silent --fail --show-error \ + --output sendmessage.json \ + --basic --user harry:potter \ + --form "message=Hello World;type=text/plain;charset=utf-8" \ + "http://$addr_localhost:$PORTA/restful/meshmb/$IDA1/sendmessage" + executeOk_servald rhizome export bundle $IDA1 broadcast.manifest broadcast + tfw_cat -h broadcast.manifest + tfw_cat -h broadcast +} + +runTests "$@" +