mirror of
https://github.com/servalproject/serval-dna.git
synced 2025-04-07 11:08:36 +00:00
Implement HTTP POST /restful/meshms/<SID>/<SID>/sendmessage
Rename struct meshms_ply fields from "buffer" to "record" for consistency with comment terminology
This commit is contained in:
parent
fb2709d10c
commit
fd86a3d17f
77
httpd.c
77
httpd.c
@ -350,6 +350,83 @@ int authorize(struct http_request *r)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int accumulate_text(httpd_request *r, const char *partname, char *textbuf, size_t textsiz, size_t *textlenp, const char *buf, size_t len)
|
||||
{
|
||||
if (len) {
|
||||
size_t newlen = *textlenp + len;
|
||||
if (newlen > textsiz) {
|
||||
if (config.debug.httpd)
|
||||
DEBUGF("Form part \"%s\" too long, %zu bytes overflows maximum %zu by %zu",
|
||||
partname, newlen, textsiz, (size_t)(newlen - textsiz)
|
||||
);
|
||||
strbuf msg = strbuf_alloca(100);
|
||||
strbuf_sprintf(msg, "Overflow in \"%s\" form part", partname);
|
||||
http_request_simple_response(&r->http, 403, strbuf_str(msg));
|
||||
return 0;
|
||||
}
|
||||
memcpy(textbuf + *textlenp, buf, len);
|
||||
*textlenp = newlen;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int form_buf_malloc_init(struct form_buf_malloc *f, size_t size_limit)
|
||||
{
|
||||
assert(f->buffer == NULL);
|
||||
assert(f->buffer_alloc_size == 0);
|
||||
assert(f->length == 0);
|
||||
f->size_limit = size_limit;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int form_buf_malloc_accumulate(httpd_request *r, const char *partname, struct form_buf_malloc *f, const char *buf, size_t len)
|
||||
{
|
||||
if (len == 0)
|
||||
return 0;
|
||||
size_t newlen = f->length + len;
|
||||
if (newlen > f->size_limit) {
|
||||
if (config.debug.httpd)
|
||||
DEBUGF("form part \"%s\" overflow, %zu bytes exceeds limit %zu by %zu",
|
||||
partname, newlen, f->size_limit, (size_t)(newlen - f->size_limit)
|
||||
);
|
||||
strbuf msg = strbuf_alloca(100);
|
||||
strbuf_sprintf(msg, "Overflow in \"%s\" form part", partname);
|
||||
http_request_simple_response(&r->http, 403, strbuf_str(msg));
|
||||
return 403;
|
||||
}
|
||||
if (newlen > f->buffer_alloc_size) {
|
||||
if ((f->buffer = erealloc(f->buffer, newlen)) == NULL) {
|
||||
http_request_simple_response(&r->http, 500, NULL);
|
||||
return 500;
|
||||
}
|
||||
f->buffer_alloc_size = newlen;
|
||||
}
|
||||
memcpy(f->buffer + f->length, buf, len);
|
||||
f->length = newlen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void form_buf_malloc_release(struct form_buf_malloc *f)
|
||||
{
|
||||
if (f->buffer) {
|
||||
free(f->buffer);
|
||||
f->buffer = NULL;
|
||||
}
|
||||
f->buffer_alloc_size = 0;
|
||||
f->length = 0;
|
||||
f->size_limit = 0;
|
||||
}
|
||||
|
||||
int http_response_form_part(httpd_request *r, const char *what, const char *partname, const char *text, size_t textlen)
|
||||
{
|
||||
if (config.debug.httpd)
|
||||
DEBUGF("%s \"%s\" form part %s", what, partname, text ? alloca_toprint(-1, text, textlen) : "");
|
||||
strbuf msg = strbuf_alloca(100);
|
||||
strbuf_sprintf(msg, "%s \"%s\" form part", what, partname);
|
||||
http_request_simple_response(&r->http, 403, strbuf_str(msg));
|
||||
return 403;
|
||||
}
|
||||
|
||||
static int root_page(httpd_request *r, const char *remainder)
|
||||
{
|
||||
if (*remainder)
|
||||
|
32
httpd.h
32
httpd.h
@ -34,6 +34,19 @@ extern unsigned int httpd_request_count;
|
||||
|
||||
enum list_phase { LIST_HEADER = 0, LIST_ROWS, LIST_END, LIST_DONE };
|
||||
|
||||
struct form_buf_malloc {
|
||||
char *buffer;
|
||||
size_t size_limit; // == 0 means no limit
|
||||
size_t buffer_alloc_size;
|
||||
size_t length;
|
||||
};
|
||||
|
||||
struct httpd_request;
|
||||
|
||||
int form_buf_malloc_init(struct form_buf_malloc *, size_t size_limit);
|
||||
int form_buf_malloc_accumulate(struct httpd_request *, const char *partname, struct form_buf_malloc *, const char *, size_t);
|
||||
void form_buf_malloc_release(struct form_buf_malloc *);
|
||||
|
||||
typedef struct httpd_request
|
||||
{
|
||||
struct http_request http; // MUST BE FIRST ELEMENT
|
||||
@ -108,9 +121,7 @@ typedef struct httpd_request
|
||||
size_t force_new_text_len;
|
||||
bool_t force_new;
|
||||
// For storing the manifest text (malloc/realloc) as we receive it
|
||||
char *manifest_text;
|
||||
size_t manifest_text_size;
|
||||
size_t manifest_len;
|
||||
struct form_buf_malloc manifest;
|
||||
// For receiving the payload
|
||||
enum rhizome_payload_status payload_status;
|
||||
uint64_t payload_size;
|
||||
@ -159,6 +170,18 @@ typedef struct httpd_request
|
||||
}
|
||||
msglist;
|
||||
|
||||
/* For responses that send a MeshMS message.
|
||||
*/
|
||||
struct {
|
||||
// Which part is currently being received
|
||||
const char *current_part;
|
||||
// Which parts have already been received
|
||||
bool_t received_message;
|
||||
// The text of the message to send
|
||||
struct form_buf_malloc message;
|
||||
}
|
||||
sendmsg;
|
||||
|
||||
} u;
|
||||
|
||||
} httpd_request;
|
||||
@ -169,6 +192,9 @@ typedef int HTTP_HANDLER(httpd_request *r, const char *remainder);
|
||||
|
||||
int is_http_header_complete(const char *buf, size_t len, size_t read_since_last_call);
|
||||
int authorize(struct http_request *r);
|
||||
int http_response_form_part(httpd_request *r, const char *what, const char *partname, const char *text, size_t textlen);
|
||||
int accumulate_text(httpd_request *r, const char *partname, char *textbuf, size_t textsiz, size_t *textlenp, const char *buf, size_t len);
|
||||
|
||||
int rhizome_response_content_init_filehash(httpd_request *r, const rhizome_filehash_t *hash);
|
||||
int rhizome_response_content_init_payload(httpd_request *r, rhizome_manifest *);
|
||||
HTTP_CONTENT_GENERATOR rhizome_payload_content;
|
||||
|
96
meshms.c
96
meshms.c
@ -29,7 +29,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#include "dataformats.h"
|
||||
|
||||
#define MESHMS_BLOCK_TYPE_ACK 0x01
|
||||
#define MESHMS_BLOCK_TYPE_MESSAGE 0x02
|
||||
#define MESHMS_BLOCK_TYPE_MESSAGE 0x02 // NUL-terminated UTF8 string
|
||||
#define MESHMS_BLOCK_TYPE_BID_REFERENCE 0x03
|
||||
|
||||
void meshms_free_conversations(struct meshms_conversations *conv)
|
||||
@ -206,10 +206,11 @@ static int create_ply(const sid_t *my_sid, struct meshms_conversations *conv, rh
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int append_footer(unsigned char *buffer, char type, int payload_len)
|
||||
static int append_footer(unsigned char *buffer, char type, size_t message_len)
|
||||
{
|
||||
payload_len = (payload_len << 4) | (type&0xF);
|
||||
write_uint16(buffer, payload_len);
|
||||
assert(message_len <= MESHMS_MESSAGE_MAX_LEN);
|
||||
message_len = (message_len << 4) | (type&0xF);
|
||||
write_uint16(buffer, message_len);
|
||||
return 2;
|
||||
}
|
||||
|
||||
@ -231,11 +232,11 @@ static int ply_read_open(struct meshms_ply_read *ply, const rhizome_bid_t *bid,
|
||||
|
||||
static void ply_read_close(struct meshms_ply_read *ply)
|
||||
{
|
||||
if (ply->buffer){
|
||||
free(ply->buffer);
|
||||
ply->buffer=NULL;
|
||||
if (ply->record){
|
||||
free(ply->record);
|
||||
ply->record=NULL;
|
||||
}
|
||||
ply->buffer_size=0;
|
||||
ply->record_size=0;
|
||||
ply->buff.len=0;
|
||||
rhizome_read_close(&ply->read);
|
||||
}
|
||||
@ -248,7 +249,7 @@ static int ply_read_prev(struct meshms_ply_read *ply)
|
||||
unsigned char footer[2];
|
||||
if (ply->read.offset <= sizeof footer) {
|
||||
if (config.debug.meshms)
|
||||
DEBUGF("EOF");
|
||||
DEBUG("EOF");
|
||||
return 1;
|
||||
}
|
||||
ply->read.offset -= sizeof footer;
|
||||
@ -271,14 +272,14 @@ static int ply_read_prev(struct meshms_ply_read *ply)
|
||||
}
|
||||
ply->read.offset -= ply->record_length + sizeof(footer);
|
||||
uint64_t record_start = ply->read.offset;
|
||||
if (ply->buffer_size < ply->record_length){
|
||||
ply->buffer_size = ply->record_length;
|
||||
unsigned char *b=realloc(ply->buffer, ply->buffer_size);
|
||||
if (ply->record_size < ply->record_length){
|
||||
ply->record_size = ply->record_length;
|
||||
unsigned char *b = erealloc(ply->record, ply->record_size);
|
||||
if (!b)
|
||||
return WHY("realloc() failed");
|
||||
ply->buffer = b;
|
||||
return -1;
|
||||
ply->record = b;
|
||||
}
|
||||
read = rhizome_read_buffered(&ply->read, &ply->buff, ply->buffer, ply->record_length);
|
||||
read = rhizome_read_buffered(&ply->read, &ply->buff, ply->record, ply->record_length);
|
||||
if (read == -1)
|
||||
return WHYF("rhizome_read_buffered() failed");
|
||||
if ((size_t) read != ply->record_length)
|
||||
@ -416,7 +417,7 @@ static int update_conversation(const sid_t *my_sid, struct meshms_conversations
|
||||
goto end;
|
||||
|
||||
if (ret==0){
|
||||
if (unpack_uint(ply.buffer, ply.record_length, &previous_ack) == -1)
|
||||
if (unpack_uint(ply.record, ply.record_length, &previous_ack) == -1)
|
||||
previous_ack=0;
|
||||
}
|
||||
if (config.debug.meshms)
|
||||
@ -740,7 +741,7 @@ int meshms_message_iterator_open(struct meshms_message_iterator *iter, const sid
|
||||
// Find their latest ACK so we know which of my messages have been delivered.
|
||||
int r = ply_find_prev(&iter->_their_reader, MESHMS_BLOCK_TYPE_ACK);
|
||||
if (r == 0) {
|
||||
if (unpack_uint(iter->_their_reader.buffer, iter->_their_reader.record_length, &iter->latest_ack_my_offset) == -1)
|
||||
if (unpack_uint(iter->_their_reader.record, iter->_their_reader.record_length, &iter->latest_ack_my_offset) == -1)
|
||||
iter->latest_ack_my_offset = 0;
|
||||
else
|
||||
iter->latest_ack_offset = iter->_their_reader.record_end_offset;
|
||||
@ -804,15 +805,23 @@ int meshms_message_iterator_prev(struct meshms_message_iterator *iter)
|
||||
iter->type = ACK_RECEIVED;
|
||||
iter->offset = iter->_their_reader.record_end_offset;
|
||||
iter->text = NULL;
|
||||
if (unpack_uint(iter->_their_reader.buffer, iter->_their_reader.record_length, &iter->ack_offset) == -1)
|
||||
iter->text_length = 0;
|
||||
if (unpack_uint(iter->_their_reader.record, iter->_their_reader.record_length, &iter->ack_offset) == -1)
|
||||
iter->ack_offset = 0;
|
||||
return 0;
|
||||
case MESHMS_BLOCK_TYPE_MESSAGE:
|
||||
iter->type = MESSAGE_RECEIVED;
|
||||
iter->offset = iter->_their_reader.record_end_offset;
|
||||
iter->text = (const char *)iter->_their_reader.buffer;
|
||||
iter->read = iter->_their_reader.record_end_offset <= iter->_conv->read_offset;
|
||||
return 0;
|
||||
iter->text = (const char *)iter->_their_reader.record;
|
||||
iter->text_length = iter->_their_reader.record_length;
|
||||
if ( iter->_their_reader.record_length != 0
|
||||
&& iter->_their_reader.record[iter->_their_reader.record_length - 1] == '\0'
|
||||
) {
|
||||
iter->read = iter->_their_reader.record_end_offset <= iter->_conv->read_offset;
|
||||
return 0;
|
||||
}
|
||||
WARN("Malformed MeshMS2 ply journal, missing NUL terminator");
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@ -827,11 +836,11 @@ int meshms_message_iterator_prev(struct meshms_message_iterator *iter)
|
||||
case MESHMS_BLOCK_TYPE_ACK:
|
||||
// Read the received messages up to the ack'ed offset
|
||||
if (iter->_conv->found_their_ply) {
|
||||
int ofs = unpack_uint(iter->_my_reader.buffer, iter->_my_reader.record_length, (uint64_t*)&iter->_their_reader.read.offset);
|
||||
int ofs = unpack_uint(iter->_my_reader.record, iter->_my_reader.record_length, (uint64_t*)&iter->_their_reader.read.offset);
|
||||
if (ofs == -1)
|
||||
return WHYF("Malformed ACK");
|
||||
uint64_t end_range;
|
||||
int x = unpack_uint(iter->_my_reader.buffer + ofs, iter->_my_reader.record_length - ofs, &end_range);
|
||||
int x = unpack_uint(iter->_my_reader.record + ofs, iter->_my_reader.record_length - ofs, &end_range);
|
||||
if (x == -1)
|
||||
iter->_end_range = 0;
|
||||
else
|
||||
@ -846,7 +855,8 @@ int meshms_message_iterator_prev(struct meshms_message_iterator *iter)
|
||||
case MESHMS_BLOCK_TYPE_MESSAGE:
|
||||
iter->type = MESSAGE_SENT;
|
||||
iter->offset = iter->_my_reader.record_end_offset;
|
||||
iter->text = (const char *)iter->_my_reader.buffer;
|
||||
iter->text = (const char *)iter->_my_reader.record;
|
||||
iter->text_length = iter->_my_reader.record_length;
|
||||
iter->delivered = iter->latest_ack_my_offset && iter->_my_reader.record_end_offset <= iter->latest_ack_my_offset;
|
||||
return 0;
|
||||
}
|
||||
@ -855,6 +865,27 @@ int meshms_message_iterator_prev(struct meshms_message_iterator *iter)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int meshms_send_message(const sid_t *sender, const sid_t *recipient, const char *message, size_t message_len)
|
||||
{
|
||||
assert(message_len != 0);
|
||||
if (message_len > MESHMS_MESSAGE_MAX_LEN)
|
||||
return WHY("message too long");
|
||||
struct meshms_conversations *conv = find_or_create_conv(sender, recipient);
|
||||
if (!conv)
|
||||
return -1;
|
||||
// construct a message payload
|
||||
// TODO, new format here.
|
||||
unsigned char buffer[message_len + 4];
|
||||
strncpy((char*)buffer, message, message_len);
|
||||
// ensure message is NUL terminated
|
||||
if (message[message_len - 1] != '\0')
|
||||
buffer[message_len++] = '\0';
|
||||
message_len += append_footer(buffer + message_len, MESHMS_BLOCK_TYPE_MESSAGE, message_len);
|
||||
int ret = append_meshms_buffer(sender, conv, buffer, message_len);
|
||||
meshms_free_conversations(conv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// output the list of existing conversations for a given local identity
|
||||
int app_meshms_conversations(const struct cli_parsed *parsed, struct cli_context *context)
|
||||
{
|
||||
@ -934,21 +965,8 @@ int app_meshms_send_message(const struct cli_parsed *parsed, struct cli_context
|
||||
return WHY("invalid sender SID");
|
||||
if (str_to_sid_t(&their_sid, their_sidhex) == -1)
|
||||
return WHY("invalid recipient SID");
|
||||
struct meshms_conversations *conv = find_or_create_conv(&my_sid, &their_sid);
|
||||
if (!conv) {
|
||||
keyring_free(keyring);
|
||||
return -1;
|
||||
}
|
||||
// construct a message payload
|
||||
int message_len = strlen(message)+1;
|
||||
|
||||
// TODO, new format here.
|
||||
unsigned char buffer[message_len+3];
|
||||
strcpy((char*)buffer, message); // message
|
||||
message_len+=append_footer(buffer+message_len, MESHMS_BLOCK_TYPE_MESSAGE, message_len);
|
||||
int ret = append_meshms_buffer(&my_sid, conv, buffer, message_len);
|
||||
|
||||
meshms_free_conversations(conv);
|
||||
// include terminating NUL
|
||||
int ret = meshms_send_message(&my_sid, &their_sid, message, strlen(message) + 1);
|
||||
keyring_free(keyring);
|
||||
return ret;
|
||||
}
|
||||
|
16
meshms.h
16
meshms.h
@ -23,6 +23,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#include "serval.h"
|
||||
#include "rhizome.h"
|
||||
|
||||
#define MESHMS_MESSAGE_MAX_LEN 4095
|
||||
|
||||
// the manifest details for one half of a conversation
|
||||
struct meshms_ply {
|
||||
rhizome_bid_t bundle_id;
|
||||
@ -65,10 +67,10 @@ struct meshms_ply_read {
|
||||
// details of the current record
|
||||
uint64_t record_end_offset;
|
||||
uint16_t record_length;
|
||||
size_t buffer_size;
|
||||
size_t record_size;
|
||||
char type;
|
||||
// raw record data
|
||||
unsigned char *buffer;
|
||||
unsigned char *record;
|
||||
};
|
||||
|
||||
/* Fetch the list of all MeshMS conversations into a binary tree whose nodes
|
||||
@ -124,7 +126,8 @@ struct meshms_message_iterator {
|
||||
// (mine). For MESSAGE_RECEIVED and ACK_RECEIVED, it is the byte position
|
||||
// within the remote ply (theirs).
|
||||
uint64_t offset;
|
||||
const char *text; // NUL terminated text of message
|
||||
const char *text; // text of UTF8 message (NUL terminated)
|
||||
size_t text_length; // excluding terminating NUL
|
||||
union {
|
||||
bool_t delivered; // for MESSAGE_SENT
|
||||
bool_t read; // for MESSAGE_RECEIVED
|
||||
@ -145,4 +148,11 @@ int meshms_message_iterator_is_open(const struct meshms_message_iterator *);
|
||||
void meshms_message_iterator_close(struct meshms_message_iterator *);
|
||||
int meshms_message_iterator_prev(struct meshms_message_iterator *);
|
||||
|
||||
/* Append a message ('message_len' bytes of UTF8 at 'message') to the sender's
|
||||
* ply in the conversation between 'sender' and 'recipient'. If no
|
||||
* conversation (ply bundle) exists, then create it. Returns 0 on success, -1
|
||||
* on error (already logged).
|
||||
*/
|
||||
int meshms_send_message(const sid_t *sender, const sid_t *recipient, const char *message, size_t message_len);
|
||||
|
||||
#endif // __SERVAL_DNA__MESHMS_H
|
||||
|
@ -33,6 +33,11 @@ static void finalise_union_meshms_messagelist(httpd_request *r)
|
||||
meshms_message_iterator_close(&r->u.msglist.iter);
|
||||
}
|
||||
|
||||
static void finalise_union_meshms_sendmessage(httpd_request *r)
|
||||
{
|
||||
form_buf_malloc_release(&r->u.sendmsg.message);
|
||||
}
|
||||
|
||||
#define MESHMS_TOKEN_STRLEN (BASE64_ENCODED_LEN(sizeof(rhizome_bid_t) + sizeof(uint64_t)))
|
||||
#define alloca_meshms_token(bid, offset) meshms_token_to_str(alloca(MESHMS_TOKEN_STRLEN + 1), (bid), (offset))
|
||||
|
||||
@ -62,12 +67,14 @@ static int strn_to_meshms_token(const char *str, rhizome_bid_t *bidp, uint64_t *
|
||||
static HTTP_HANDLER restful_meshms_conversationlist_json;
|
||||
static HTTP_HANDLER restful_meshms_messagelist_json;
|
||||
static HTTP_HANDLER restful_meshms_newsince_messagelist_json;
|
||||
static HTTP_HANDLER restful_meshms_sendmessage;
|
||||
|
||||
int restful_meshms_(httpd_request *r, const char *remainder)
|
||||
{
|
||||
r->http.response.header.content_type = "application/json";
|
||||
if (!is_rhizome_http_enabled())
|
||||
return 403;
|
||||
const char *verb = HTTP_VERB_GET;
|
||||
HTTP_HANDLER *handler = NULL;
|
||||
const char *end;
|
||||
if (strn_to_sid_t(&r->sid1, remainder, &end) != -1) {
|
||||
@ -88,11 +95,16 @@ int restful_meshms_(httpd_request *r, const char *remainder)
|
||||
handler = restful_meshms_newsince_messagelist_json;
|
||||
remainder = "";
|
||||
}
|
||||
else if (strcmp(remainder, "/sendmessage") == 0) {
|
||||
handler = restful_meshms_sendmessage;
|
||||
verb = HTTP_VERB_POST;
|
||||
remainder = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (handler == NULL)
|
||||
return 404;
|
||||
if (r->http.verb != HTTP_VERB_GET)
|
||||
if (r->http.verb != verb)
|
||||
return 405;
|
||||
int ret = authorize(&r->http);
|
||||
if (ret)
|
||||
@ -407,3 +419,88 @@ static int restful_meshms_messagelist_json_content_chunk(struct http_request *hr
|
||||
return 0;
|
||||
}
|
||||
|
||||
static HTTP_REQUEST_PARSER restful_meshms_sendmessage_end;
|
||||
static int send_mime_part_start(struct http_request *);
|
||||
static int send_mime_part_end(struct http_request *);
|
||||
static int send_mime_part_header(struct http_request *, const struct mime_part_headers *);
|
||||
static int send_mime_part_body(struct http_request *, char *, size_t);
|
||||
|
||||
static int restful_meshms_sendmessage(httpd_request *r, const char *remainder)
|
||||
{
|
||||
if (*remainder)
|
||||
return 404;
|
||||
assert(r->finalise_union == NULL);
|
||||
r->finalise_union = finalise_union_meshms_sendmessage;
|
||||
// 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_mime_part_start;
|
||||
r->http.form_data.handle_mime_part_end = send_mime_part_end;
|
||||
r->http.form_data.handle_mime_part_header = send_mime_part_header;
|
||||
r->http.form_data.handle_mime_body = send_mime_part_body;
|
||||
// Send the message once the body has arrived.
|
||||
r->http.handle_content_end = restful_meshms_sendmessage_end;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char PART_MESSAGE[] = "message";
|
||||
|
||||
static int send_mime_part_start(struct http_request *hr)
|
||||
{
|
||||
httpd_request *r = (httpd_request *) hr;
|
||||
assert(r->u.sendmsg.current_part == NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int send_mime_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, "Invalid (empty)", PART_MESSAGE, NULL, 0);
|
||||
r->u.sendmsg.received_message = 1;
|
||||
if (config.debug.httpd)
|
||||
DEBUGF("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_mime_part_header(struct http_request *hr, const struct mime_part_headers *h)
|
||||
{
|
||||
httpd_request *r = (httpd_request *) hr;
|
||||
if (strcmp(h->content_disposition.name, PART_MESSAGE) == 0) {
|
||||
if (r->u.sendmsg.received_message)
|
||||
return http_response_form_part(r, "Duplicate", PART_MESSAGE, NULL, 0);
|
||||
r->u.sendmsg.current_part = PART_MESSAGE;
|
||||
form_buf_malloc_init(&r->u.sendmsg.message, MESHMS_MESSAGE_MAX_LEN);
|
||||
}
|
||||
else
|
||||
return http_response_form_part(r, "Unsupported", h->content_disposition.name, NULL, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int send_mime_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 restful_meshms_sendmessage_end(struct http_request *hr)
|
||||
{
|
||||
httpd_request *r = (httpd_request *) hr;
|
||||
if (!r->u.sendmsg.received_message)
|
||||
return http_response_form_part(r, "Missing", PART_MESSAGE, NULL, 0);
|
||||
assert(r->u.sendmsg.message.length > 0);
|
||||
assert(r->u.sendmsg.message.length <= MESHMS_MESSAGE_MAX_LEN);
|
||||
int ret = meshms_send_message(&r->sid1, &r->sid2, r->u.sendmsg.message.buffer, r->u.sendmsg.message.length);
|
||||
if (ret == -1)
|
||||
return 500;
|
||||
http_request_simple_response(&r->http, 201, "Message sent");
|
||||
return 201;
|
||||
}
|
||||
|
@ -31,10 +31,7 @@ static void finalise_union_read_state(httpd_request *r)
|
||||
|
||||
static void finalise_union_rhizome_insert(httpd_request *r)
|
||||
{
|
||||
if (r->u.insert.manifest_text) {
|
||||
free(r->u.insert.manifest_text);
|
||||
r->u.insert.manifest_text = NULL;
|
||||
}
|
||||
form_buf_malloc_release(&r->u.insert.manifest);
|
||||
if (r->u.insert.write.blob_fd != -1)
|
||||
rhizome_fail_write(&r->u.insert.write);
|
||||
}
|
||||
@ -284,26 +281,16 @@ static int insert_mime_part_start(struct http_request *hr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int http_response_form_part(httpd_request *r, const char *what, const char *partname, const char *text, size_t textlen)
|
||||
{
|
||||
if (config.debug.rhizome)
|
||||
DEBUGF("%s \"%s\" form part %s", what, partname, text ? alloca_toprint(-1, text, textlen) : "");
|
||||
strbuf msg = strbuf_alloca(100);
|
||||
strbuf_sprintf(msg, "%s \"%s\" form part", what, partname);
|
||||
http_request_simple_response(&r->http, 403, strbuf_str(msg));
|
||||
return 403;
|
||||
}
|
||||
|
||||
static int insert_make_manifest(httpd_request *r)
|
||||
{
|
||||
if (!r->u.insert.received_manifest)
|
||||
return http_response_form_part(r, "Missing", PART_MANIFEST, NULL, 0);
|
||||
if ((r->manifest = rhizome_new_manifest())) {
|
||||
if (r->u.insert.manifest_len == 0)
|
||||
if (r->u.insert.manifest.length == 0)
|
||||
return 0;
|
||||
assert(r->u.insert.manifest_len <= sizeof r->manifest->manifestdata);
|
||||
memcpy(r->manifest->manifestdata, r->u.insert.manifest_text, r->u.insert.manifest_len);
|
||||
r->manifest->manifest_all_bytes = r->u.insert.manifest_len;
|
||||
assert(r->u.insert.manifest.length <= sizeof r->manifest->manifestdata);
|
||||
memcpy(r->manifest->manifestdata, r->u.insert.manifest.buffer, r->u.insert.manifest.length);
|
||||
r->manifest->manifest_all_bytes = r->u.insert.manifest.length;
|
||||
int n = rhizome_manifest_parse(r->manifest);
|
||||
switch (n) {
|
||||
case -1:
|
||||
@ -330,19 +317,19 @@ static int insert_mime_part_header(struct http_request *hr, const struct mime_pa
|
||||
if (r->u.insert.received_author)
|
||||
return http_response_form_part(r, "Duplicate", PART_AUTHOR, NULL, 0);
|
||||
r->u.insert.current_part = PART_AUTHOR;
|
||||
assert(r->u.insert.author_hex_len == 0);
|
||||
}
|
||||
else if (strcmp(h->content_disposition.name, PART_SECRET) == 0) {
|
||||
if (r->u.insert.received_secret)
|
||||
return http_response_form_part(r, "Duplicate", PART_SECRET, NULL, 0);
|
||||
r->u.insert.current_part = PART_SECRET;
|
||||
assert(r->u.insert.secret_hex_len == 0);
|
||||
}
|
||||
else if (strcmp(h->content_disposition.name, PART_MANIFEST) == 0) {
|
||||
// Reject a request if it has a repeated manifest part.
|
||||
if (r->u.insert.received_manifest)
|
||||
return http_response_form_part(r, "Duplicate", PART_MANIFEST, NULL, 0);
|
||||
assert(r->u.insert.manifest_text == NULL);
|
||||
assert(r->u.insert.manifest_text_size == 0);
|
||||
assert(r->u.insert.manifest_len == 0);
|
||||
form_buf_malloc_init(&r->u.insert.manifest, MAX_MANIFEST_BYTES);
|
||||
if ( strcmp(h->content_type.type, "rhizome-manifest") != 0
|
||||
|| strcmp(h->content_type.subtype, "text") != 0
|
||||
)
|
||||
@ -384,26 +371,6 @@ static int insert_mime_part_header(struct http_request *hr, const struct mime_pa
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int accumulate_text(httpd_request *r, const char *partname, char *textbuf, size_t textsiz, size_t *textlenp, const char *buf, size_t len)
|
||||
{
|
||||
if (len) {
|
||||
size_t newlen = *textlenp + len;
|
||||
if (newlen > textsiz) {
|
||||
if (config.debug.rhizome)
|
||||
DEBUGF("Form part \"%s\" too long, %zu bytes overflows maximum %zu by %zu",
|
||||
partname, newlen, textsiz, (size_t)(newlen - textsiz)
|
||||
);
|
||||
strbuf msg = strbuf_alloca(100);
|
||||
strbuf_sprintf(msg, "Overflow in \"%s\" form part", partname);
|
||||
http_request_simple_response(&r->http, 403, strbuf_str(msg));
|
||||
return 0;
|
||||
}
|
||||
memcpy(textbuf + *textlenp, buf, len);
|
||||
*textlenp = newlen;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int insert_mime_part_body(struct http_request *hr, char *buf, size_t len)
|
||||
{
|
||||
httpd_request *r = (httpd_request *) hr;
|
||||
@ -422,24 +389,7 @@ static int insert_mime_part_body(struct http_request *hr, char *buf, size_t len)
|
||||
buf, len);
|
||||
}
|
||||
else if (r->u.insert.current_part == PART_MANIFEST) {
|
||||
if (len == 0)
|
||||
return 0;
|
||||
size_t newlen = r->u.insert.manifest_len + len;
|
||||
if (newlen > MAX_MANIFEST_BYTES) {
|
||||
if (config.debug.rhizome)
|
||||
DEBUGF("manifest too large, %zu bytes overflows maximum %zu by %zu",
|
||||
newlen, (size_t)MAX_MANIFEST_BYTES, (size_t)(newlen - MAX_MANIFEST_BYTES)
|
||||
);
|
||||
http_request_simple_response(&r->http, 403, "Manifest size overflow");
|
||||
return 403;
|
||||
}
|
||||
if (newlen > r->u.insert.manifest_text_size) {
|
||||
if ((r->u.insert.manifest_text = erealloc(r->u.insert.manifest_text, newlen)) == NULL)
|
||||
return 500;
|
||||
r->u.insert.manifest_text_size = newlen;
|
||||
}
|
||||
memcpy(r->u.insert.manifest_text + r->u.insert.manifest_len, buf, len);
|
||||
r->u.insert.manifest_len = newlen;
|
||||
form_buf_malloc_accumulate(r, PART_MANIFEST, &r->u.insert.manifest, buf, len);
|
||||
}
|
||||
else if (r->u.insert.current_part == PART_PAYLOAD) {
|
||||
r->u.insert.payload_size += len;
|
||||
|
@ -471,36 +471,49 @@ strbuf strbuf_json_boolean(strbuf sb, int boolean)
|
||||
return sb;
|
||||
}
|
||||
|
||||
static void _json_char(strbuf sb, char c)
|
||||
{
|
||||
if (c == '"' || c == '\\') {
|
||||
strbuf_putc(sb, '\\');
|
||||
strbuf_putc(sb, c);
|
||||
}
|
||||
else if (c == '\b')
|
||||
strbuf_puts(sb, "\\b");
|
||||
else if (c == '\f')
|
||||
strbuf_puts(sb, "\\f");
|
||||
else if (c == '\n')
|
||||
strbuf_puts(sb, "\\n");
|
||||
else if (c == '\r')
|
||||
strbuf_puts(sb, "\\r");
|
||||
else if (c == '\t')
|
||||
strbuf_puts(sb, "\\t");
|
||||
else if (iscntrl(c))
|
||||
strbuf_sprintf(sb, "\\u%04X", (unsigned char) c);
|
||||
else
|
||||
strbuf_putc(sb, c);
|
||||
}
|
||||
|
||||
strbuf strbuf_json_string(strbuf sb, const char *str)
|
||||
{
|
||||
if (str) {
|
||||
strbuf_putc(sb, '"');
|
||||
for (; *str; ++str) {
|
||||
if (*str == '"' || *str == '\\') {
|
||||
strbuf_putc(sb, '\\');
|
||||
strbuf_putc(sb, *str);
|
||||
}
|
||||
else if (*str == '\b')
|
||||
strbuf_puts(sb, "\\b");
|
||||
else if (*str == '\f')
|
||||
strbuf_puts(sb, "\\f");
|
||||
else if (*str == '\n')
|
||||
strbuf_puts(sb, "\\n");
|
||||
else if (*str == '\r')
|
||||
strbuf_puts(sb, "\\r");
|
||||
else if (*str == '\t')
|
||||
strbuf_puts(sb, "\\t");
|
||||
else if (iscntrl(*str))
|
||||
strbuf_sprintf(sb, "\\u%04X", (unsigned char) *str);
|
||||
else
|
||||
strbuf_putc(sb, *str);
|
||||
}
|
||||
for (; *str; ++str)
|
||||
_json_char(sb, *str);
|
||||
strbuf_putc(sb, '"');
|
||||
} else
|
||||
strbuf_json_null(sb);
|
||||
return sb;
|
||||
}
|
||||
|
||||
strbuf strbuf_json_string_len(strbuf sb, const char *str, size_t strlen)
|
||||
{
|
||||
strbuf_putc(sb, '"');
|
||||
for (; strlen; --strlen, ++str)
|
||||
_json_char(sb, *str);
|
||||
strbuf_putc(sb, '"');
|
||||
return sb;
|
||||
}
|
||||
|
||||
strbuf strbuf_json_hex(strbuf sb, const unsigned char *buf, size_t len)
|
||||
{
|
||||
if (buf) {
|
||||
|
@ -167,7 +167,8 @@ strbuf strbuf_append_quoted_string(strbuf sb, const char *str);
|
||||
*/
|
||||
strbuf strbuf_json_null(strbuf sb);
|
||||
strbuf strbuf_json_boolean(strbuf sb, int boolean);
|
||||
strbuf strbuf_json_string(strbuf sb, const char *str);
|
||||
strbuf strbuf_json_string(strbuf sb, const char *str); // str can be NULL
|
||||
strbuf strbuf_json_string_len(strbuf sb, const char *str, size_t strlen); // str cannot be NULL
|
||||
strbuf strbuf_json_hex(strbuf sb, const unsigned char *buf, size_t len);
|
||||
|
||||
/* Append a representation of a struct http_range[] array.
|
||||
|
@ -1196,8 +1196,28 @@ teardown_MeshmsListMessagesNewSinceArrival() {
|
||||
}
|
||||
|
||||
doc_MeshmsSend="HTTP RESTful send MeshMS message"
|
||||
Xtest_MeshmsSend() {
|
||||
:
|
||||
setup_MeshmsSend() {
|
||||
IDENTITY_COUNT=2
|
||||
setup
|
||||
}
|
||||
test_MeshmsSend() {
|
||||
executeOk curl \
|
||||
--silent --fail --show-error \
|
||||
--output sendmessage.json \
|
||||
--basic --user harry:potter \
|
||||
--form "message=Hello World" \
|
||||
"http://$addr_localhost:$PORTA/restful/meshms/$SIDA1/$SIDA2/sendmessage"
|
||||
executeOk_servald meshms list messages $SIDA1 $SIDA2
|
||||
assertStdoutGrep --matches=1 ':>:Hello World'
|
||||
executeOk curl \
|
||||
--silent --fail --show-error \
|
||||
--output sendmessage.json \
|
||||
--basic --user ron:weasley \
|
||||
--form "message=Hello back!" \
|
||||
"http://$addr_localhost:$PORTA/restful/meshms/$SIDA2/$SIDA1/sendmessage"
|
||||
executeOk_servald meshms list messages $SIDA1 $SIDA2
|
||||
assertStdoutGrep --matches=1 ':>:Hello World$'
|
||||
assertStdoutGrep --matches=1 ':<:Hello back!$'
|
||||
}
|
||||
|
||||
runTests "$@"
|
||||
|
Loading…
x
Reference in New Issue
Block a user