Refactor Rhizome result handling

- Introduce the new 'struct rhizome_bundle_result' that contains a
  rhizome_bundle_status enum value and an optional, nul-terminated
  string that provides an explanation of the cause of the status; add
  functions to construct, query, and free the struct

- Replace 'enum rhizome_add_result' with 'struct rhizome_bundle_result',
  removing an unnecessary level of enum interpretation

- Make rhizome_fill_manifest() return 'struct rhizome_bundle_result' and
  add logic to check that the supplied author SID is correct (previous
  behaviour was: if an incorrect author SID was supplied but the correct
  author could be found in the keyring, then the incorrect one was
  silently ignored)

- Simplify the response code in rhizome_restful.cc to take advantage of
  the new 'struct rhizome_bundle_result'; in particular, the mapping
  from 'enum rhizome_bundle_status' codes to HTTP status codes is now
  expressed in a single switch statement

- Fix some minor failures in test scripts revealed by the changes
This commit is contained in:
Andrew Bettison 2015-12-01 00:23:15 +10:30
parent 078bf5eb6b
commit 98ed0406c5
12 changed files with 514 additions and 304 deletions

View File

@ -222,6 +222,7 @@ static void httpd_server_finalise_http_request(struct http_request *hr)
if (current_httpd_requests == NULL) {
assert(current_httpd_request_count == 0);
}
rhizome_bundle_result_free(&r->bundle_result);
if (r->manifest) {
rhizome_manifest_free(r->manifest);
r->manifest = NULL;
@ -273,7 +274,7 @@ void httpd_server_poll(struct sched_ent *alarm)
current_httpd_requests = request;
++current_httpd_request_count;
request->payload_status = INVALID_RHIZOME_PAYLOAD_STATUS; // will cause FATAL unless set
request->bundle_status = INVALID_RHIZOME_BUNDLE_STATUS; // will cause FATAL unless set
request->bundle_result = INVALID_RHIZOME_BUNDLE_RESULT; // will cause FATAL unless set
if (peerip)
request->http.client_sockaddr_in = *peerip;
request->http.uuid = http_request_uuid_counter;

View File

@ -61,7 +61,7 @@ typedef struct httpd_request
*/
rhizome_manifest *manifest;
enum rhizome_payload_status payload_status;
enum rhizome_bundle_status bundle_status;
struct rhizome_bundle_result bundle_result;
/* For requests/responses that contain one or two SIDs.
*/

View File

@ -69,8 +69,28 @@ static enum meshms_status get_my_conversation_bundle(const sid_t *my_sidp, rhizo
if (m->haveSecret == NEW_BUNDLE_ID) {
rhizome_manifest_set_service(m, RHIZOME_SERVICE_FILE);
rhizome_manifest_set_name(m, "");
if (rhizome_fill_manifest(m, NULL, my_sidp) != NULL)
return WHY("Invalid conversation manifest");
struct rhizome_bundle_result result = rhizome_fill_manifest(m, NULL, my_sidp);
switch (result.status) {
case RHIZOME_BUNDLE_STATUS_NEW:
case RHIZOME_BUNDLE_STATUS_SAME:
case RHIZOME_BUNDLE_STATUS_DUPLICATE:
break;
case RHIZOME_BUNDLE_STATUS_ERROR:
case RHIZOME_BUNDLE_STATUS_INVALID:
case RHIZOME_BUNDLE_STATUS_INCONSISTENT:
WHYF("Error creating conversation manifest: %s", alloca_rhizome_bundle_result(result));
return MESHMS_STATUS_ERROR;
case RHIZOME_BUNDLE_STATUS_BUSY:
// TODO
case RHIZOME_BUNDLE_STATUS_OLD:
case RHIZOME_BUNDLE_STATUS_FAKE:
case RHIZOME_BUNDLE_STATUS_NO_ROOM:
WARNF("Cannot create conversation manifest: %s", alloca_rhizome_bundle_result(result));
return MESHMS_STATUS_PROTOCOL_FAULT;
case RHIZOME_BUNDLE_STATUS_READONLY:
INFOF("Cannot create conversation manifest: %s", alloca_rhizome_bundle_result(result));
return MESHMS_STATUS_SID_LOCKED;
}
// The 'meshms' automated test depends on this message; do not alter.
DEBUGF(meshms, "MESHMS CONVERSATION BUNDLE bid=%s secret=%s",
alloca_tohex_rhizome_bid_t(m->cryptoSignPublic),
@ -188,7 +208,7 @@ static enum meshms_status find_or_create_conv(const sid_t *my_sid, const sid_t *
return status;
}
static int create_ply(const sid_t *my_sid, struct meshms_conversations *conv, rhizome_manifest *m)
static enum meshms_status create_ply(const sid_t *my_sid, struct meshms_conversations *conv, rhizome_manifest *m)
{
DEBUGF(meshms, "Creating ply for my_sid=%s them=%s",
alloca_tohex_sid_t(conv->them),
@ -199,8 +219,27 @@ static int create_ply(const sid_t *my_sid, struct meshms_conversations *conv, rh
rhizome_manifest_set_recipient(m, &conv->them);
rhizome_manifest_set_filesize(m, 0);
rhizome_manifest_set_tail(m, 0);
if (rhizome_fill_manifest(m, NULL, my_sid) != NULL)
return -1;
struct rhizome_bundle_result result = rhizome_fill_manifest(m, NULL, my_sid);
switch (result.status) {
case RHIZOME_BUNDLE_STATUS_NEW:
case RHIZOME_BUNDLE_STATUS_SAME:
case RHIZOME_BUNDLE_STATUS_DUPLICATE:
break;
case RHIZOME_BUNDLE_STATUS_ERROR:
case RHIZOME_BUNDLE_STATUS_INVALID:
case RHIZOME_BUNDLE_STATUS_INCONSISTENT:
WHYF("Error creating ply manifest: %s", alloca_rhizome_bundle_result(result));
return MESHMS_STATUS_ERROR;
case RHIZOME_BUNDLE_STATUS_OLD:
case RHIZOME_BUNDLE_STATUS_FAKE:
case RHIZOME_BUNDLE_STATUS_NO_ROOM:
case RHIZOME_BUNDLE_STATUS_BUSY:
WARNF("Cannot create ply manifest: %s", alloca_rhizome_bundle_result(result));
return MESHMS_STATUS_PROTOCOL_FAULT;
case RHIZOME_BUNDLE_STATUS_READONLY:
INFOF("Cannot create ply manifest: %s", alloca_rhizome_bundle_result(result));
return MESHMS_STATUS_SID_LOCKED;
}
assert(m->haveSecret);
assert(m->payloadEncryption == PAYLOAD_ENCRYPTED);
conv->my_ply.bundle_id = m->cryptoSignPublic;
@ -350,15 +389,25 @@ static enum meshms_status append_meshms_buffer(const sid_t *my_sid, struct meshm
status = MESHMS_STATUS_PROTOCOL_FAULT;
goto end;
}
} else if (create_ply(my_sid, conv, m) == -1) {
goto end;
} else {
status = create_ply(my_sid, conv, m);
switch (status) {
case MESHMS_STATUS_OK:
break;
case MESHMS_STATUS_ERROR:
case MESHMS_STATUS_UPDATED:
case MESHMS_STATUS_SID_LOCKED:
case MESHMS_STATUS_PROTOCOL_FAULT:
goto end;
}
}
assert(m->haveSecret);
assert(m->authorship == AUTHOR_AUTHENTIC);
enum rhizome_payload_status pstatus = rhizome_append_journal_buffer(m, 0, buffer, len);
if (pstatus != RHIZOME_PAYLOAD_STATUS_NEW)
if (pstatus != RHIZOME_PAYLOAD_STATUS_NEW) {
status = MESHMS_STATUS_ERROR;
goto end;
}
enum rhizome_bundle_status bstatus = rhizome_manifest_finalise(m, &mout, 1);
DEBUGF(meshms, "bstatus=%d", bstatus);
switch (bstatus) {

295
rhizome.c
View File

@ -1,6 +1,6 @@
/*
Serval DNA - Rhizome entry points
Copyright (C) 2012-2013 Serval Project Inc.
Copyright (C) 2012-2015 Serval Project Inc.
Copyright (C) 2011-2012 Paul Gardner-Stephen
This program is free software; you can redistribute it and/or
@ -52,9 +52,13 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "serval.h"
#include "conf.h"
#include "str.h"
#include "strbuf_helpers.h"
#include "mem.h"
#include "rhizome.h"
#include "httpd.h"
#include "dataformats.h"
#include "log.h"
#include "debug.h"
int is_rhizome_enabled()
{
@ -144,7 +148,7 @@ int rhizome_fetch_delay_ms()
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
enum rhizome_add_file_result rhizome_manifest_add_file(int appending,
struct rhizome_bundle_result rhizome_manifest_add_file(int appending,
rhizome_manifest *m,
rhizome_manifest **mout,
const rhizome_bid_t *bid,
@ -152,12 +156,9 @@ enum rhizome_add_file_result rhizome_manifest_add_file(int appending,
const sid_t *author,
const char *file_path,
unsigned nassignments,
const struct rhizome_manifest_field_assignment *assignments,
strbuf reason
)
const struct rhizome_manifest_field_assignment *assignments)
{
const char *cause = NULL;
enum rhizome_add_file_result result = RHIZOME_ADD_FILE_ERROR;
struct rhizome_bundle_result result = INVALID_RHIZOME_BUNDLE_RESULT; // must be set before returning
rhizome_manifest *existing_manifest = NULL;
rhizome_manifest *new_manifest = NULL;
assert(m != NULL);
@ -167,25 +168,27 @@ enum rhizome_add_file_result rhizome_manifest_add_file(int appending,
// If appending to a journal, caller must not supply 'version', 'filesize' or 'filehash' fields,
// because these will be calculated by the journal append logic.
if (appending) {
if (m->version)
DEBUG(rhizome, cause = "Cannot set 'version' field in journal append");
else if (m->filesize != RHIZOME_SIZE_UNSET)
DEBUG(rhizome, cause = "Cannot set 'filesize' field in journal append");
else if (m->has_filehash)
DEBUG(rhizome, cause = "Cannot set 'filehash' field in journal append");
if (cause) {
result = RHIZOME_ADD_FILE_INVALID_FOR_JOURNAL;
if (m->version) {
result = rhizome_bundle_result_static(RHIZOME_BUNDLE_STATUS_INVALID, "Cannot set 'version' field in journal append");
goto error;
}
else if (m->filesize != RHIZOME_SIZE_UNSET) {
result = rhizome_bundle_result_static(RHIZOME_BUNDLE_STATUS_INVALID, "Cannot set 'filesize' field in journal append");
goto error;
}
else if (m->has_filehash) {
result = rhizome_bundle_result_static(RHIZOME_BUNDLE_STATUS_INVALID, "Cannot set 'filehash' field in journal append");
goto error;
}
}
if (bid) {
DEBUGF(rhizome, "Reading manifest from database: id=%s", alloca_tohex_rhizome_bid_t(*bid));
if ((existing_manifest = rhizome_new_manifest()) == NULL) {
WHY(cause = "Manifest struct could not be allocated");
result = rhizome_bundle_result_static(RHIZOME_BUNDLE_STATUS_ERROR, "Manifest struct could not be allocated");
goto error;
}
enum rhizome_bundle_status status = rhizome_retrieve_manifest(bid, existing_manifest);
switch (status) {
result.status = rhizome_retrieve_manifest(bid, existing_manifest);
switch (result.status) {
case RHIZOME_BUNDLE_STATUS_NEW:
// No manifest with that bundle ID exists in the store, so we are building a bundle from
// scratch.
@ -204,18 +207,20 @@ enum rhizome_add_file_result rhizome_manifest_add_file(int appending,
rhizome_manifest_del_filehash(existing_manifest);
}
if (rhizome_manifest_overwrite(existing_manifest, m) == -1) {
WHY(cause = "Existing manifest could not be overwritten");
result = rhizome_bundle_result_static(RHIZOME_BUNDLE_STATUS_ERROR,
"Existing manifest could not be overwritten");
goto error;
}
new_manifest = existing_manifest;
existing_manifest = NULL;
break;
case RHIZOME_BUNDLE_STATUS_BUSY:
WARN(cause = "Existing manifest not retrieved due to Rhizome store locking");
result = RHIZOME_ADD_FILE_BUSY;
result = rhizome_bundle_result_static(RHIZOME_BUNDLE_STATUS_BUSY,
"Existing manifest not retrieved due to Rhizome store locking");
goto error;
case RHIZOME_BUNDLE_STATUS_ERROR:
WHY(cause = "Error retrieving existing manifest from Rhizome store");
result = rhizome_bundle_result_static(RHIZOME_BUNDLE_STATUS_BUSY,
"Error retrieving existing manifest from Rhizome store");
goto error;
case RHIZOME_BUNDLE_STATUS_DUPLICATE:
case RHIZOME_BUNDLE_STATUS_OLD:
@ -224,7 +229,7 @@ enum rhizome_add_file_result rhizome_manifest_add_file(int appending,
case RHIZOME_BUNDLE_STATUS_INCONSISTENT:
case RHIZOME_BUNDLE_STATUS_NO_ROOM:
case RHIZOME_BUNDLE_STATUS_READONLY:
FATALF("rhizome_retrieve_manifest() returned %s", rhizome_bundle_status_message(status));
FATALF("rhizome_retrieve_manifest() returned %s", rhizome_bundle_status_message(result.status));
}
}
// If no existing bundle has been identified, we are building a bundle from scratch.
@ -250,41 +255,37 @@ enum rhizome_add_file_result rhizome_manifest_add_file(int appending,
enum rhizome_manifest_parse_status status = rhizome_manifest_parse_field(new_manifest, asg->label, asg->labellen, asg->value, asg->valuelen);
int status_ok = 0;
switch (status) {
case RHIZOME_MANIFEST_ERROR:
WHYF("Fatal error updating manifest field");
if (reason)
strbuf_sprintf(reason, "Fatal error updating manifest field: %s=%s", label, alloca_toprint(-1, asg->value, asg->valuelen));
goto error;
case RHIZOME_MANIFEST_OK:
status_ok = 1;
break;
case RHIZOME_MANIFEST_SYNTAX_ERROR:
DEBUGF(rhizome, "Manifest syntax error: %s=%s", label, alloca_toprint(-1, asg->value, asg->valuelen));
if (reason)
strbuf_sprintf(reason, "Manifest syntax error: %s=%s", label, alloca_toprint(-1, asg->value, asg->valuelen));
result = RHIZOME_ADD_FILE_INVALID;
goto error;
case RHIZOME_MANIFEST_DUPLICATE_FIELD:
// We already deleted the field, so if this happens, its a nasty bug
FATALF("Duplicate field should not occur: %s=%s", label, alloca_toprint(-1, asg->value, asg->valuelen));
case RHIZOME_MANIFEST_INVALID:
DEBUGF(rhizome, "Manifest invalid field: %s=%s", label, alloca_toprint(-1, asg->value, asg->valuelen));
if (reason)
strbuf_sprintf(reason, "Manifest invalid field: %s=%s", label, alloca_toprint(-1, asg->value, asg->valuelen));
result = RHIZOME_ADD_FILE_INVALID;
goto error;
case RHIZOME_MANIFEST_MALFORMED:
DEBUGF(rhizome, "Manifest malformed field: %s=%s", label, alloca_toprint(-1, asg->value, asg->valuelen));
if (reason)
strbuf_sprintf(reason, "Manifest malformed field: %s=%s", label, alloca_toprint(-1, asg->value, asg->valuelen));
result = RHIZOME_ADD_FILE_INVALID;
goto error;
case RHIZOME_MANIFEST_OVERFLOW:
DEBUGF(rhizome, "Too many fields in manifest at: %s=%s", label, alloca_toprint(-1, asg->value, asg->valuelen));
if (reason)
strbuf_sprintf(reason, "Too many fields in manifest at: %s=%s", label, alloca_toprint(-1, asg->value, asg->valuelen));
result = RHIZOME_ADD_FILE_INVALID;
goto error;
case RHIZOME_MANIFEST_ERROR:
result = rhizome_bundle_result_sprintf(RHIZOME_BUNDLE_STATUS_ERROR,
"Error updating manifest field: %s=%s",
label, alloca_toprint(-1, asg->value, asg->valuelen));
goto error;
case RHIZOME_MANIFEST_OK:
status_ok = 1;
break;
case RHIZOME_MANIFEST_SYNTAX_ERROR:
result = rhizome_bundle_result_sprintf(RHIZOME_BUNDLE_STATUS_INVALID,
"Manifest syntax error: %s=%s",
label, alloca_toprint(-1, asg->value, asg->valuelen));
goto error;
case RHIZOME_MANIFEST_DUPLICATE_FIELD:
// We already deleted the field, so if this happens, its a logic bug.
FATALF("Duplicate field should not occur: %s=%s", label, alloca_toprint(-1, asg->value, asg->valuelen));
case RHIZOME_MANIFEST_INVALID:
result = rhizome_bundle_result_sprintf(RHIZOME_BUNDLE_STATUS_INVALID,
"Manifest invalid field: %s=%s",
label, alloca_toprint(-1, asg->value, asg->valuelen));
goto error;
case RHIZOME_MANIFEST_MALFORMED:
result = rhizome_bundle_result_sprintf(RHIZOME_BUNDLE_STATUS_INVALID,
"Manifest malformed field: %s=%s",
label, alloca_toprint(-1, asg->value, asg->valuelen));
goto error;
case RHIZOME_MANIFEST_OVERFLOW:
result = rhizome_bundle_result_sprintf(RHIZOME_BUNDLE_STATUS_INVALID,
"Too many fields in manifest at: %s=%s",
label, alloca_toprint(-1, asg->value, asg->valuelen));
goto error;
}
if (!status_ok)
FATALF("status = %d", status);
@ -292,22 +293,20 @@ enum rhizome_add_file_result rhizome_manifest_add_file(int appending,
}
}
if (appending && !new_manifest->is_journal) {
cause = "Cannot append to a non-journal";
DEBUG(rhizome, cause);
result = RHIZOME_ADD_FILE_REQUIRES_JOURNAL;
result = rhizome_bundle_result_static(RHIZOME_BUNDLE_STATUS_INVALID,
"Cannot append to a non-journal");
goto error;
}
if (!appending && new_manifest->is_journal) {
cause = "Cannot add a journal bundle (use append instead)";
DEBUG(rhizome, cause);
result = RHIZOME_ADD_FILE_INVALID_FOR_JOURNAL;
result = rhizome_bundle_result_static(RHIZOME_BUNDLE_STATUS_INVALID,
"Cannot add a journal bundle (use append instead)");
goto error;
}
if (bsk) {
if (new_manifest->has_id) {
if (!rhizome_apply_bundle_secret(new_manifest, bsk)) {
WHY(cause = "Supplied bundle secret does not match Bundle Id");
result = RHIZOME_ADD_FILE_WRONG_SECRET;
result = rhizome_bundle_result_static(RHIZOME_BUNDLE_STATUS_READONLY,
"Supplied bundle secret does not match Bundle Id");
goto error;
}
} else {
@ -320,19 +319,29 @@ enum rhizome_add_file_result rhizome_manifest_add_file(int appending,
WARNF("Manifest 'service' field not supplied - setting to '%s'", RHIZOME_SERVICE_FILE);
rhizome_manifest_set_service(new_manifest, RHIZOME_SERVICE_FILE);
}
if ((cause = rhizome_fill_manifest(new_manifest, file_path, author ? author : NULL)) != NULL)
goto error;
*mout = new_manifest;
return RHIZOME_ADD_FILE_OK;
result = rhizome_fill_manifest(new_manifest, file_path, author);
error:
assert(result != RHIZOME_ADD_FILE_OK);
if (cause && reason)
strbuf_puts(reason, cause);
if (new_manifest && new_manifest != m && new_manifest != existing_manifest)
rhizome_manifest_free(new_manifest);
if (existing_manifest)
rhizome_manifest_free(existing_manifest);
return result;
switch (result.status) {
case RHIZOME_BUNDLE_STATUS_NEW:
*mout = new_manifest;
return result;
case RHIZOME_BUNDLE_STATUS_ERROR:
case RHIZOME_BUNDLE_STATUS_SAME:
case RHIZOME_BUNDLE_STATUS_DUPLICATE:
case RHIZOME_BUNDLE_STATUS_OLD:
case RHIZOME_BUNDLE_STATUS_INVALID:
case RHIZOME_BUNDLE_STATUS_FAKE:
case RHIZOME_BUNDLE_STATUS_INCONSISTENT:
case RHIZOME_BUNDLE_STATUS_NO_ROOM:
case RHIZOME_BUNDLE_STATUS_READONLY:
case RHIZOME_BUNDLE_STATUS_BUSY:
if (new_manifest && new_manifest != m && new_manifest != existing_manifest)
rhizome_manifest_free(new_manifest);
if (existing_manifest)
rhizome_manifest_free(existing_manifest);
return result;
}
FATALF("result.status = %d", (int)result.status);
}
/* Import a bundle from a pair of files, one containing the manifest and the optional other
@ -648,3 +657,133 @@ const char *rhizome_payload_status_message_nonnull(enum rhizome_payload_status s
const char *message = rhizome_payload_status_message(status);
return message ? message : "Invalid";
}
void rhizome_bundle_result_free(struct rhizome_bundle_result *resultp)
{
if (resultp->free) {
resultp->free((void *)resultp->message);
}
*resultp = INVALID_RHIZOME_BUNDLE_RESULT;
}
static const char *rhizome_bundle_status_symbol(enum rhizome_bundle_status status)
{
switch (status) {
case RHIZOME_BUNDLE_STATUS_NEW: return "NEW";
case RHIZOME_BUNDLE_STATUS_SAME: return "SAME";
case RHIZOME_BUNDLE_STATUS_DUPLICATE: return "DUPLICATE";
case RHIZOME_BUNDLE_STATUS_OLD: return "OLD";
case RHIZOME_BUNDLE_STATUS_INVALID: return "INVALID";
case RHIZOME_BUNDLE_STATUS_FAKE: return "FAKE";
case RHIZOME_BUNDLE_STATUS_INCONSISTENT: return "INCONSISTENT";
case RHIZOME_BUNDLE_STATUS_NO_ROOM: return "NO_ROOM";
case RHIZOME_BUNDLE_STATUS_READONLY: return "READONLY";
case RHIZOME_BUNDLE_STATUS_BUSY: return "BUSY";
case RHIZOME_BUNDLE_STATUS_ERROR: return "ERROR";
}
FATALF("status=%d", (int)status);
}
static void log_rhizome_bundle_result(struct __sourceloc __whence, struct rhizome_bundle_result result)
{
switch (result.status) {
case RHIZOME_BUNDLE_STATUS_NEW:
case RHIZOME_BUNDLE_STATUS_SAME:
case RHIZOME_BUNDLE_STATUS_DUPLICATE:
case RHIZOME_BUNDLE_STATUS_OLD:
case RHIZOME_BUNDLE_STATUS_INVALID:
case RHIZOME_BUNDLE_STATUS_FAKE:
case RHIZOME_BUNDLE_STATUS_INCONSISTENT:
case RHIZOME_BUNDLE_STATUS_NO_ROOM:
case RHIZOME_BUNDLE_STATUS_READONLY:
DEBUG(rhizome, alloca_rhizome_bundle_result(result));
return;
case RHIZOME_BUNDLE_STATUS_BUSY:
WARN(alloca_rhizome_bundle_result(result));
return;
case RHIZOME_BUNDLE_STATUS_ERROR:
WHY(alloca_rhizome_bundle_result(result));
return;
}
FATAL(alloca_rhizome_bundle_result(result));
}
struct rhizome_bundle_result _rhizome_bundle_result(struct __sourceloc __whence, enum rhizome_bundle_status status)
{
struct rhizome_bundle_result result = INVALID_RHIZOME_BUNDLE_RESULT;
result.status = status;
log_rhizome_bundle_result(__whence, result);
return result;
}
struct rhizome_bundle_result _rhizome_bundle_result_static(struct __sourceloc __whence, enum rhizome_bundle_status status, const char *message)
{
struct rhizome_bundle_result result = INVALID_RHIZOME_BUNDLE_RESULT;
result.status = status;
result.message = message;
log_rhizome_bundle_result(__whence, result);
return result;
}
struct rhizome_bundle_result _rhizome_bundle_result_strdup(struct __sourceloc __whence, enum rhizome_bundle_status status, const char *message)
{
assert(message != NULL);
struct rhizome_bundle_result result = INVALID_RHIZOME_BUNDLE_RESULT;
result.status = status;
result.message = str_edup(message);
result.free = free;
log_rhizome_bundle_result(__whence, result);
return result;
}
struct rhizome_bundle_result _rhizome_bundle_result_sprintf(struct __sourceloc __whence, enum rhizome_bundle_status status, const char *fmt, ...)
{
struct rhizome_bundle_result result = INVALID_RHIZOME_BUNDLE_RESULT;
result.status = status;
va_list ap;
va_start(ap, fmt);
strbuf sb;
STRBUF_ALLOCA_FIT(sb, 200, (strbuf_vsprintf(sb, fmt, ap)));
va_end(ap);
result.message = str_edup(strbuf_str(sb));
result.free = free;
log_rhizome_bundle_result(__whence, result);
return result;
}
const char *rhizome_bundle_result_message(struct rhizome_bundle_result result)
{
return result.message ? result.message : rhizome_bundle_status_message(result.status);
}
const char *rhizome_bundle_result_message_nonnull(struct rhizome_bundle_result result)
{
return result.message ? result.message : rhizome_bundle_status_message_nonnull(result.status);
}
strbuf strbuf_append_rhizome_bundle_result(strbuf sb, struct rhizome_bundle_result result)
{
switch (result.status) {
case RHIZOME_BUNDLE_STATUS_NEW:
case RHIZOME_BUNDLE_STATUS_SAME:
case RHIZOME_BUNDLE_STATUS_DUPLICATE:
case RHIZOME_BUNDLE_STATUS_OLD:
case RHIZOME_BUNDLE_STATUS_INVALID:
case RHIZOME_BUNDLE_STATUS_FAKE:
case RHIZOME_BUNDLE_STATUS_INCONSISTENT:
case RHIZOME_BUNDLE_STATUS_NO_ROOM:
case RHIZOME_BUNDLE_STATUS_READONLY:
case RHIZOME_BUNDLE_STATUS_BUSY:
case RHIZOME_BUNDLE_STATUS_ERROR:
strbuf_puts(sb, "RHIZOME_BUNDLE_STATUS_");
strbuf_puts(sb, rhizome_bundle_status_symbol(result.status));
if (result.message) {
strbuf_puts(sb, " ");
strbuf_toprint_quoted(sb, "``", result.message);
}
return sb;
}
strbuf_sprintf(sb, "Invalid rhizome_bundle_status (%d)", (int)result.status);
return sb;
}

View File

@ -421,6 +421,40 @@ enum rhizome_bundle_status {
const char *rhizome_bundle_status_message(enum rhizome_bundle_status);
const char *rhizome_bundle_status_message_nonnull(enum rhizome_bundle_status);
// Encapsulate a status enum value and an optional text message to assist with
// diagnostics. Useful as a return value from functions that can fail in all
// sorts of ways.
struct rhizome_bundle_result {
enum rhizome_bundle_status status;
const char *message;
void (*free)(void *); // call r.free(r.message) before destroying r
};
#define INVALID_RHIZOME_BUNDLE_RESULT ((struct rhizome_bundle_result){ .status = INVALID_RHIZOME_BUNDLE_STATUS, .message = NULL, .free = NULL })
// Call this before discarding a struct rhizome_bundle_result.
void rhizome_bundle_result_free(struct rhizome_bundle_result *);
// Convenience functions for constructing a struct rhizome_bundle_result and
// logging errors and debug messages in the process.
struct rhizome_bundle_result _rhizome_bundle_result(struct __sourceloc, enum rhizome_bundle_status);
struct rhizome_bundle_result _rhizome_bundle_result_static(struct __sourceloc, enum rhizome_bundle_status, const char *);
struct rhizome_bundle_result _rhizome_bundle_result_strdup(struct __sourceloc, enum rhizome_bundle_status, const char *);
struct rhizome_bundle_result _rhizome_bundle_result_sprintf(struct __sourceloc, enum rhizome_bundle_status, const char *fmt, ...);
#define rhizome_bundle_result(status) _rhizome_bundle_result(__WHENCE__, status)
#define rhizome_bundle_result_static(status, str) _rhizome_bundle_result_static(__WHENCE__, status, str);
#define rhizome_bundle_result_strdup(status, str) _rhizome_bundle_result_strdup(__WHENCE__, status, str);
#define rhizome_bundle_result_sprintf(status, fmt, ...) _rhizome_bundle_result_sprintf(__WHENCE__, status, fmt, ## __VA_ARGS__);
// Functions for extracting information from a struct rhizome_bundle_result.
const char *rhizome_bundle_result_message(struct rhizome_bundle_result); // NULL only if invalid
const char *rhizome_bundle_result_message_nonnull(struct rhizome_bundle_result);
// Function to assist logging struct rhizome_bundle_result.
#define alloca_rhizome_bundle_result(result) strbuf_str(strbuf_append_rhizome_bundle_result(strbuf_alloca(50 + (result.message ? strlen(result.message) : 0)), result))
strbuf strbuf_append_rhizome_bundle_result(strbuf, struct rhizome_bundle_result);
enum rhizome_payload_status {
RHIZOME_PAYLOAD_STATUS_ERROR = -1,
RHIZOME_PAYLOAD_STATUS_EMPTY = 0, // payload is empty (zero length)
@ -458,30 +492,19 @@ rhizome_manifest *_rhizome_new_manifest(struct __sourceloc);
int rhizome_store_manifest(rhizome_manifest *m);
int rhizome_store_file(rhizome_manifest *m,const unsigned char *key);
enum rhizome_add_file_result {
RHIZOME_ADD_FILE_ERROR = -1,
RHIZOME_ADD_FILE_OK = 0, // manifest created successfully
RHIZOME_ADD_FILE_INVALID, // manifest not created due to invalid input
RHIZOME_ADD_FILE_BUSY, // manifest not created because database busy
RHIZOME_ADD_FILE_REQUIRES_JOURNAL, // operation is only valid for a journal
RHIZOME_ADD_FILE_INVALID_FOR_JOURNAL, // operation is not valid for a journal
RHIZOME_ADD_FILE_WRONG_SECRET, // incorrect bundle secret supplied
};
enum rhizome_add_file_result rhizome_manifest_add_file(int appending,
rhizome_manifest *m,
rhizome_manifest **mout,
const rhizome_bid_t *bid,
const rhizome_bk_t *bsk,
const sid_t *author,
const char *file_path,
unsigned nassignments,
const struct rhizome_manifest_field_assignment *assignments,
strbuf reason
);
struct rhizome_bundle_result rhizome_manifest_add_file(int appending,
rhizome_manifest *m,
rhizome_manifest **mout,
const rhizome_bid_t *bid,
const rhizome_bk_t *bsk,
const sid_t *author,
const char *file_path,
unsigned nassignments,
const struct rhizome_manifest_field_assignment *assignments);
int rhizome_bundle_import_files(rhizome_manifest *m, rhizome_manifest **m_out, const char *manifest_path, const char *filepath);
int rhizome_manifest_set_name_from_path(rhizome_manifest *m, const char *filepath);
const char * rhizome_fill_manifest(rhizome_manifest *m, const char *filepath, const sid_t *authorSidp);
struct rhizome_bundle_result rhizome_fill_manifest(rhizome_manifest *m, const char *filepath, const sid_t *authorSidp);
int rhizome_apply_bundle_secret(rhizome_manifest *, const rhizome_bk_t *);
int rhizome_manifest_add_bundle_key(rhizome_manifest *);

View File

@ -1480,13 +1480,13 @@ int rhizome_manifest_set_name_from_path(rhizome_manifest *m, const char *filepat
* - create an ID if there is none, otherwise authenticate the existing one
* - if service is file, then use the payload file's basename for "name"
*
* Return NULL if successful, otherwise a pointer to a static text string describing the reason for
* the failure (always an internal/unrecoverable error).
* Return a rhizome_bundle_status code together with a pointer to a text string describing the
* reason for the failure (always an internal/unrecoverable error). The string is accompanied by a
* pointer to a "free" method (eg, free(3)) that must be called to release the string before the
* pointer is discarded.
*/
const char * rhizome_fill_manifest(rhizome_manifest *m, const char *filepath, const sid_t *authorSidp)
struct rhizome_bundle_result rhizome_fill_manifest(rhizome_manifest *m, const char *filepath, const sid_t *authorSidp)
{
const char *reason = NULL;
/* Set version of manifest from current time if not already set. */
if (m->version == 0)
rhizome_manifest_set_version(m, gettime_ms());
@ -1513,8 +1513,7 @@ const char * rhizome_fill_manifest(rhizome_manifest *m, const char *filepath, co
// If there is no Bundle Id, then create a new bundle Id and secret from scratch.
DEBUG(rhizome, "creating new bundle");
if (rhizome_manifest_createid(m) == -1) {
WHY(reason = "Could not bind manifest to an ID");
return reason;
return rhizome_bundle_result_static(RHIZOME_BUNDLE_STATUS_ERROR, "Could not bind manifest to an ID");
}
// fall through to set the BK field...
case NEW_BUNDLE_ID:
@ -1540,12 +1539,32 @@ const char * rhizome_fill_manifest(rhizome_manifest *m, const char *filepath, co
}
if (!valid_haveSecret)
FATALF("haveSecret = %d", m->haveSecret);
int valid_authorship = 0;
switch (m->authorship) {
case ANONYMOUS:
assert(!authorSidp);
valid_authorship = 1;
break;
case AUTHOR_AUTHENTIC:
valid_authorship = 1;
break;
case AUTHOR_UNKNOWN:
return rhizome_bundle_result_static(RHIZOME_BUNDLE_STATUS_READONLY, "Author is not in keyring");
case AUTHOR_IMPOSTOR:
return rhizome_bundle_result_static(RHIZOME_BUNDLE_STATUS_READONLY, "Incorrect author");
case AUTHOR_NOT_CHECKED:
case AUTHOR_LOCAL:
FATALF("logic error (bug): m->authorship = %d", m->authorship);
case AUTHENTICATION_ERROR:
return rhizome_bundle_result_static(RHIZOME_BUNDLE_STATUS_ERROR, "Error authenticating author");
}
if (!valid_authorship)
FATALF("m->authorship = %d", (int)m->authorship);
/* Service field must already be set.
*/
if (m->service == NULL) {
WHYF(reason = "Missing 'service' field");
return reason;
return rhizome_bundle_result_static(RHIZOME_BUNDLE_STATUS_INVALID, "Missing 'service' field");
}
DEBUGF(rhizome, "manifest contains service=%s", m->service);
@ -1579,7 +1598,7 @@ const char * rhizome_fill_manifest(rhizome_manifest *m, const char *filepath, co
rhizome_manifest_set_crypt(m, PAYLOAD_ENCRYPTED);
}
return NULL;
return rhizome_bundle_result(RHIZOME_BUNDLE_STATUS_NEW);
}
/* Work out the authorship status of the bundle without performing any cryptographic checks.

View File

@ -180,6 +180,7 @@ static int app_rhizome_add_file(const struct cli_parsed *parsed, struct cli_cont
int ret = -1;
rhizome_manifest *m = NULL;
struct rhizome_bundle_result result = INVALID_RHIZOME_BUNDLE_RESULT;
if (rhizome_opendb() == -1)
goto finish;
@ -205,39 +206,16 @@ static int app_rhizome_add_file(const struct cli_parsed *parsed, struct cli_cont
/* Create an in-memory manifest for the file being added.
*/
rhizome_manifest *mout = NULL;
enum rhizome_add_file_result result = rhizome_manifest_add_file(appending, m, &mout,
bundleIdHex ? &bid : NULL,
bsktext ? &bsk : NULL,
authorSidHex ? &authorSid : NULL,
filepath,
nfields, fields,
NULL);
int result_valid = 0;
switch (result) {
case RHIZOME_ADD_FILE_ERROR:
ret = -1;
goto finish;
case RHIZOME_ADD_FILE_OK:
result_valid = 1;
break;
case RHIZOME_ADD_FILE_INVALID:
ret = RHIZOME_BUNDLE_STATUS_INVALID; // TODO separate enum for CLI return codes
goto finish;
case RHIZOME_ADD_FILE_BUSY:
ret = RHIZOME_BUNDLE_STATUS_BUSY; // TODO separate enum for CLI return codes
goto finish;
case RHIZOME_ADD_FILE_REQUIRES_JOURNAL:
ret = RHIZOME_BUNDLE_STATUS_INVALID; // TODO separate enum for CLI return codes
goto finish;
case RHIZOME_ADD_FILE_INVALID_FOR_JOURNAL:
ret = RHIZOME_BUNDLE_STATUS_INVALID; // TODO separate enum for CLI return codes
goto finish;
case RHIZOME_ADD_FILE_WRONG_SECRET:
ret = RHIZOME_BUNDLE_STATUS_READONLY; // TODO separate enum for CLI return codes
result = rhizome_manifest_add_file(appending, m, &mout,
bundleIdHex ? &bid : NULL,
bsktext ? &bsk : NULL,
authorSidHex ? &authorSid : NULL,
filepath,
nfields, fields);
if (result.status != RHIZOME_BUNDLE_STATUS_NEW) {
ret = result.status; // TODO separate enum for CLI return codes
goto finish;
}
if (!result_valid)
FATALF("result = %d", result);
assert(mout != NULL);
if (mout != m) {
rhizome_manifest_free(m);
@ -260,50 +238,50 @@ static int app_rhizome_add_file(const struct cli_parsed *parsed, struct cli_cont
DEBUGF(rhizome, "rhizome_store_payload_file() returned %d %s", pstatus, rhizome_payload_status_message(pstatus));
}
}
enum rhizome_bundle_status status = RHIZOME_BUNDLE_STATUS_ERROR;
rhizome_bundle_result_free(&result);
int pstatus_valid = 0;
switch (pstatus) {
case RHIZOME_PAYLOAD_STATUS_EMPTY:
case RHIZOME_PAYLOAD_STATUS_STORED:
case RHIZOME_PAYLOAD_STATUS_NEW:
pstatus_valid = 1;
status = RHIZOME_BUNDLE_STATUS_NEW;
result.status = RHIZOME_BUNDLE_STATUS_NEW;
break;
case RHIZOME_PAYLOAD_STATUS_TOO_BIG:
case RHIZOME_PAYLOAD_STATUS_EVICTED:
pstatus_valid = 1;
status = RHIZOME_BUNDLE_STATUS_NO_ROOM;
result.status = RHIZOME_BUNDLE_STATUS_NO_ROOM;
INFO("Insufficient space to store payload");
break;
case RHIZOME_PAYLOAD_STATUS_ERROR:
pstatus_valid = 1;
status = RHIZOME_BUNDLE_STATUS_ERROR;
result.status = RHIZOME_BUNDLE_STATUS_ERROR;
break;
case RHIZOME_PAYLOAD_STATUS_WRONG_SIZE:
case RHIZOME_PAYLOAD_STATUS_WRONG_HASH:
pstatus_valid = 1;
status = RHIZOME_BUNDLE_STATUS_INCONSISTENT;
result.status = RHIZOME_BUNDLE_STATUS_INCONSISTENT;
break;
case RHIZOME_PAYLOAD_STATUS_CRYPTO_FAIL:
pstatus_valid = 1;
status = RHIZOME_BUNDLE_STATUS_READONLY;
result.status = RHIZOME_BUNDLE_STATUS_READONLY;
break;
}
if (!pstatus_valid)
FATALF("pstatus = %d", pstatus);
if (status == RHIZOME_BUNDLE_STATUS_NEW) {
if (result.status == RHIZOME_BUNDLE_STATUS_NEW) {
if (!rhizome_manifest_validate(m) || m->malformed)
status = RHIZOME_BUNDLE_STATUS_INVALID;
result.status = RHIZOME_BUNDLE_STATUS_INVALID;
else {
status = rhizome_manifest_finalise(m, &mout, !force_new);
result.status = rhizome_manifest_finalise(m, &mout, !force_new);
if (mout && mout != m && !rhizome_manifest_validate(mout)) {
WHYF("Stored manifest id=%s is invalid -- overwriting", alloca_tohex_rhizome_bid_t(mout->cryptoSignPublic));
status = RHIZOME_BUNDLE_STATUS_NEW;
result.status = RHIZOME_BUNDLE_STATUS_NEW;
}
}
}
int status_valid = 0;
switch (status) {
switch (result.status) {
case RHIZOME_BUNDLE_STATUS_NEW:
if (mout && mout != m)
rhizome_manifest_free(mout);
@ -334,11 +312,12 @@ static int app_rhizome_add_file(const struct cli_parsed *parsed, struct cli_cont
// a valuable tool for the developer.
}
if (!status_valid)
FATALF("status=%d", status);
FATALF("result.status=%d", result.status);
if (mout && mout != m)
rhizome_manifest_free(mout);
ret = status;
ret = result.status;
finish:
rhizome_bundle_result_free(&result);
rhizome_manifest_free(m);
keyring_free(keyring);
keyring = NULL;

View File

@ -243,10 +243,6 @@ void rhizome_authenticate_author(rhizome_manifest *m)
{
IN();
DEBUGF(rhizome, "authenticate author for bid=%s", m->has_id ? alloca_tohex_rhizome_bid_t(m->cryptoSignPublic) : "(none)");
if (!m->has_bundle_key) {
DEBUG(rhizome, " no BK field");
RETURNVOID;
}
switch (m->authorship) {
case ANONYMOUS:
DEBUGF(rhizome, " manifest[%d] author unknown", m->manifest_record_number);

View File

@ -248,13 +248,15 @@ static int rhizome_direct_addfile_end(struct http_request *hr)
if (m->service == NULL)
rhizome_manifest_set_service(m, RHIZOME_SERVICE_FILE);
const sid_t *author = is_sid_t_any(config.rhizome.api.addfile.default_author) ? NULL : &config.rhizome.api.addfile.default_author;
const char *reason = rhizome_fill_manifest(m, r->u.direct_import.data_file_name, author);
if (reason) {
struct rhizome_bundle_result result = rhizome_fill_manifest(m, r->u.direct_import.data_file_name, author);
if (result.status != RHIZOME_BUNDLE_STATUS_NEW) {
rhizome_manifest_free(m);
rhizome_direct_clear_temporary_files(r);
http_request_simple_response(&r->http, 500, alloca_sprintf(-1, "Internal Error: %s", reason));
http_request_simple_response(&r->http, 500, result.message);
rhizome_bundle_result_free(&result);
return 0;
}
rhizome_bundle_result_free(&result);
rhizome_manifest_set_crypt(m, PAYLOAD_CLEAR);
// import file contents
// TODO, stream file into database

View File

@ -70,96 +70,98 @@ static int strn_to_list_token(const char *str, uint64_t *rowidp, const char **af
return 1;
}
static int http_request_rhizome_response(struct httpd_request *r, uint16_t result, const char *message, const char *payload_status_message)
static int http_request_rhizome_response(struct httpd_request *r, uint16_t http_status, const char *http_reason)
{
uint16_t rhizome_result = 0;
switch (r->bundle_status) {
uint16_t rhizome_http_status = 0;
switch (r->bundle_result.status) {
case RHIZOME_BUNDLE_STATUS_SAME:
case RHIZOME_BUNDLE_STATUS_DUPLICATE:
rhizome_result = 200; // OK
rhizome_http_status = 200; // OK
break;
case RHIZOME_BUNDLE_STATUS_NEW:
rhizome_result = 201; // Created
rhizome_http_status = 201; // Created
break;
case RHIZOME_BUNDLE_STATUS_NO_ROOM:
case RHIZOME_BUNDLE_STATUS_OLD:
rhizome_result = 202; // Accepted
rhizome_http_status = 202; // Accepted
break;
case RHIZOME_BUNDLE_STATUS_FAKE:
case RHIZOME_BUNDLE_STATUS_READONLY:
rhizome_result = 419; // Authentication Timeout
rhizome_http_status = 419; // Authentication Timeout
break;
case RHIZOME_BUNDLE_STATUS_INVALID:
case RHIZOME_BUNDLE_STATUS_INCONSISTENT:
rhizome_result = 422; // Unprocessable Entity
rhizome_http_status = 422; // Unprocessable Entity
break;
case RHIZOME_BUNDLE_STATUS_BUSY:
rhizome_result = 423; // Locked
rhizome_http_status = 423; // Locked
break;
case RHIZOME_BUNDLE_STATUS_ERROR:
rhizome_result = 500;
rhizome_http_status = 500;
break;
}
if (rhizome_result) {
if (rhizome_http_status) {
r->http.response.result_extra[0].label = "rhizome_bundle_status_code";
r->http.response.result_extra[0].value.type = JSON_INTEGER;
r->http.response.result_extra[0].value.u.integer = r->bundle_status;
const char *status_message = rhizome_bundle_status_message(r->bundle_status);
r->http.response.result_extra[0].value.u.integer = r->bundle_result.status;
const char *status_message = rhizome_bundle_result_message(r->bundle_result);
if (status_message) {
r->http.response.result_extra[1].label = "rhizome_bundle_status_message";
r->http.response.result_extra[1].value.type = JSON_STRING_NULTERM;
r->http.response.result_extra[1].value.u.string.content = status_message;
}
if (rhizome_result > result) {
result = rhizome_result;
message = NULL;
if (rhizome_http_status > http_status) {
http_status = rhizome_http_status;
if (!http_reason)
http_reason = status_message;
}
}
rhizome_result = 0;
rhizome_http_status = 0;
switch (r->payload_status) {
case RHIZOME_PAYLOAD_STATUS_STORED:
case RHIZOME_PAYLOAD_STATUS_EMPTY:
rhizome_result = 200;
rhizome_http_status = 200;
break;
case RHIZOME_PAYLOAD_STATUS_NEW:
rhizome_result = 201;
rhizome_http_status = 201;
break;
case RHIZOME_PAYLOAD_STATUS_TOO_BIG:
case RHIZOME_PAYLOAD_STATUS_EVICTED:
rhizome_result = 202; // Accepted
rhizome_http_status = 202; // Accepted
break;
case RHIZOME_PAYLOAD_STATUS_CRYPTO_FAIL:
rhizome_result = 419; // Authentication Timeout
rhizome_http_status = 419; // Authentication Timeout
break;
case RHIZOME_PAYLOAD_STATUS_WRONG_SIZE:
case RHIZOME_PAYLOAD_STATUS_WRONG_HASH:
rhizome_result = 422; // Unprocessable Entity
rhizome_http_status = 422; // Unprocessable Entity
break;
case RHIZOME_PAYLOAD_STATUS_ERROR:
rhizome_result = 500;
rhizome_http_status = 500;
break;
}
if (rhizome_result) {
if (rhizome_http_status) {
r->http.response.result_extra[2].label = "rhizome_payload_status_code";
r->http.response.result_extra[2].value.type = JSON_INTEGER;
r->http.response.result_extra[2].value.u.integer = r->payload_status;
const char *status_message = payload_status_message ? payload_status_message : rhizome_payload_status_message(r->payload_status);
const char *status_message = rhizome_payload_status_message(r->payload_status);
if (status_message) {
r->http.response.result_extra[3].label = "rhizome_payload_status_message";
r->http.response.result_extra[3].value.type = JSON_STRING_NULTERM;
r->http.response.result_extra[3].value.u.string.content = status_message;
}
if (rhizome_result > result) {
result = rhizome_result;
message = NULL;
if (rhizome_http_status > http_status) {
http_status = rhizome_http_status;
if (!http_reason)
http_reason = status_message;
}
}
if (result == 0) {
result = 500;
message = "Result logic error";
if (http_status == 0) {
http_status = 500;
http_reason = "Invalid result";
}
http_request_simple_response(&r->http, result, message);
return result;
http_request_simple_response(&r->http, http_status, http_reason);
return http_status;
}
static HTTP_CONTENT_GENERATOR restful_rhizome_bundlelist_json_content;
@ -411,60 +413,49 @@ static int insert_make_manifest(httpd_request *r)
if (!r->u.insert.received_manifest)
return http_response_form_part(r, 400, "Missing", PART_MANIFEST, NULL, 0);
if ((r->manifest = rhizome_new_manifest()) == NULL)
return http_request_rhizome_response(r, 429, "Manifest table full", NULL); // Too Many Requests
return http_request_rhizome_response(r, 429, "Manifest table full"); // Too Many Requests
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;
rhizome_manifest *mout = NULL;
int n = rhizome_manifest_parse(r->manifest);
switch (n) {
case 0:
if (!r->manifest->malformed)
break;
// fall through
if (r->manifest->malformed) {
r->bundle_result = rhizome_bundle_result_sprintf(RHIZOME_BUNDLE_STATUS_INVALID, "Malformed manifest: %s", r->manifest->malformed);
} else {
r->bundle_result = rhizome_manifest_add_file(r->u.insert.appending, r->manifest, &mout,
r->u.insert.received_bundleid ? &r->bid : NULL,
r->u.insert.received_secret ? &r->u.insert.bundle_secret : NULL,
r->u.insert.received_author ? &r->u.insert.author : NULL,
NULL, 0, NULL);
}
break;
case 1:
rhizome_manifest_free(r->manifest);
r->manifest = NULL;
r->bundle_status = RHIZOME_BUNDLE_STATUS_INVALID;
return http_request_rhizome_response(r, 422, "Malformed manifest", NULL); // Unprocessable Entity
r->bundle_result = rhizome_bundle_result_static(RHIZOME_BUNDLE_STATUS_INVALID, "Invalid manifest");
break;
default:
WHYF("rhizome_manifest_parse() returned %d", n);
// fall through
case -1:
r->bundle_status = RHIZOME_BUNDLE_STATUS_ERROR;
r->bundle_result = rhizome_bundle_result_static(RHIZOME_BUNDLE_STATUS_ERROR, "Error while parsing manifest");
break;
}
rhizome_manifest *mout = NULL;
char message[150];
enum rhizome_add_file_result result = rhizome_manifest_add_file(r->u.insert.appending, r->manifest, &mout,
r->u.insert.received_bundleid ? &r->bid: NULL,
r->u.insert.received_secret ? &r->u.insert.bundle_secret : NULL,
r->u.insert.received_author ? &r->u.insert.author: NULL,
NULL, 0, NULL, strbuf_local_buf(message));
int result_valid = 0;
switch (result) {
case RHIZOME_ADD_FILE_ERROR:
return http_request_rhizome_response(r, 500, message, NULL);
case RHIZOME_ADD_FILE_OK:
result_valid = 1;
switch (r->bundle_result.status) {
case RHIZOME_BUNDLE_STATUS_NEW:
case RHIZOME_BUNDLE_STATUS_OLD:
case RHIZOME_BUNDLE_STATUS_SAME:
case RHIZOME_BUNDLE_STATUS_DUPLICATE:
case RHIZOME_BUNDLE_STATUS_NO_ROOM:
break;
case RHIZOME_ADD_FILE_INVALID:
r->bundle_status = RHIZOME_BUNDLE_STATUS_INVALID; // TODO separate enum for CLI return codes
return http_request_rhizome_response(r, 422, message, NULL); // Unprocessable Entity
case RHIZOME_ADD_FILE_BUSY:
r->bundle_status = RHIZOME_BUNDLE_STATUS_BUSY; // TODO separate enum for CLI return codes
return http_request_rhizome_response(r, 423, message, NULL); // Locked
case RHIZOME_ADD_FILE_REQUIRES_JOURNAL:
r->bundle_status = RHIZOME_BUNDLE_STATUS_INVALID; // TODO separate enum for CLI return codes
return http_request_rhizome_response(r, 422, message, NULL); // Unprocessable Entity
case RHIZOME_ADD_FILE_INVALID_FOR_JOURNAL:
r->bundle_status = RHIZOME_BUNDLE_STATUS_INVALID; // TODO separate enum for CLI return codes
return http_request_rhizome_response(r, 422, message, NULL); // Unprocessable Entity
case RHIZOME_ADD_FILE_WRONG_SECRET:
r->bundle_status = RHIZOME_BUNDLE_STATUS_READONLY; // TODO separate enum for CLI return codes
return http_request_rhizome_response(r, 419, message, NULL); // Authentication Timeout
case RHIZOME_BUNDLE_STATUS_INCONSISTENT:
case RHIZOME_BUNDLE_STATUS_INVALID:
case RHIZOME_BUNDLE_STATUS_BUSY:
case RHIZOME_BUNDLE_STATUS_FAKE:
case RHIZOME_BUNDLE_STATUS_READONLY:
case RHIZOME_BUNDLE_STATUS_ERROR:
return http_request_rhizome_response(r, 0, NULL);
}
if (!result_valid)
FATALF("result = %d", result);
assert(mout != NULL);
if (mout != r->manifest) {
rhizome_manifest_free(r->manifest);
@ -540,7 +531,7 @@ static int insert_mime_part_header(struct http_request *hr, const struct mime_pa
r->payload_status = rhizome_write_open_journal(&r->u.insert.write, r->manifest, 0, RHIZOME_SIZE_UNSET);
if (r->payload_status == RHIZOME_PAYLOAD_STATUS_ERROR) {
WHYF("rhizome_write_open_journal() returned %d %s", r->payload_status, rhizome_payload_status_message(r->payload_status));
return http_request_rhizome_response(r, 500, "Error in payload open for write (journal)", NULL);
return http_request_rhizome_response(r, 500, "Error in payload open for write (journal)");
}
} else {
// Note: r->manifest->filesize can be RHIZOME_SIZE_UNSET at this point, if the manifest did
@ -548,7 +539,7 @@ static int insert_mime_part_header(struct http_request *hr, const struct mime_pa
r->payload_status = rhizome_write_open_manifest(&r->u.insert.write, r->manifest);
if (r->payload_status == RHIZOME_PAYLOAD_STATUS_ERROR) {
WHYF("rhizome_write_open_manifest() returned %d %s", r->payload_status, rhizome_payload_status_message(r->payload_status));
return http_request_rhizome_response(r, 500, "Error in payload open for write", NULL);
return http_request_rhizome_response(r, 500, "Error in payload open for write");
}
}
switch (r->payload_status) {
@ -597,7 +588,7 @@ static int insert_mime_part_body(struct http_request *hr, char *buf, size_t len)
switch (r->payload_status) {
case RHIZOME_PAYLOAD_STATUS_NEW:
if (rhizome_write_buffer(&r->u.insert.write, (unsigned char *)buf, len) == -1)
return http_request_rhizome_response(r, 500, "Error in payload write", NULL);
return http_request_rhizome_response(r, 500, "Error in payload write");
break;
case RHIZOME_PAYLOAD_STATUS_STORED:
// TODO: calculate payload hash so it can be compared with stored payload
@ -690,55 +681,50 @@ static int restful_rhizome_insert_end(struct http_request *hr)
status_valid = 1;
break;
case RHIZOME_PAYLOAD_STATUS_WRONG_SIZE:
r->bundle_status = RHIZOME_BUNDLE_STATUS_INCONSISTENT;
{
strbuf msg = strbuf_alloca(200);
strbuf_sprintf(msg, "Payload size (%"PRIu64") contradicts manifest (filesize=%"PRIu64")", r->u.insert.payload_size, r->manifest->filesize);
return http_request_rhizome_response(r, 422, "Inconsistent filesize", strbuf_str(msg)); // Unprocessable Entity
}
r->bundle_result = rhizome_bundle_result_sprintf(RHIZOME_BUNDLE_STATUS_INCONSISTENT,
"Payload size (%"PRIu64") contradicts manifest (filesize=%"PRIu64")",
r->u.insert.payload_size, r->manifest->filesize);
return http_request_rhizome_response(r, 0, "Inconsistent filesize");
case RHIZOME_PAYLOAD_STATUS_WRONG_HASH:
r->bundle_status = RHIZOME_BUNDLE_STATUS_INCONSISTENT;
{
strbuf msg = strbuf_alloca(200);
strbuf_sprintf(msg, "Payload hash (%s) contradicts manifest (filehash=%s)",
alloca_tohex_rhizome_filehash_t(r->u.insert.write.id),
alloca_tohex_rhizome_filehash_t(r->manifest->filehash));
return http_request_rhizome_response(r, 422, "Inconsistent filehash", strbuf_str(msg)); // Unprocessable Entity
}
r->bundle_result = rhizome_bundle_result_sprintf(RHIZOME_BUNDLE_STATUS_INCONSISTENT,
"Payload hash (%s) contradicts manifest (filehash=%s)",
alloca_tohex_rhizome_filehash_t(r->u.insert.write.id),
alloca_tohex_rhizome_filehash_t(r->manifest->filehash));
return http_request_rhizome_response(r, 0, "Inconsistent filehash");
case RHIZOME_PAYLOAD_STATUS_CRYPTO_FAIL:
r->bundle_status = RHIZOME_BUNDLE_STATUS_READONLY;
return http_request_rhizome_response(r, 419, "Missing bundle secret", NULL); // Authentication Timeout
r->bundle_result = rhizome_bundle_result(RHIZOME_BUNDLE_STATUS_READONLY);
return http_request_rhizome_response(r, 0, NULL);
case RHIZOME_PAYLOAD_STATUS_TOO_BIG:
r->bundle_status = RHIZOME_BUNDLE_STATUS_NO_ROOM;
return http_request_rhizome_response(r, 202, "Bundle too big", NULL); // Accepted
r->bundle_result = rhizome_bundle_result(RHIZOME_BUNDLE_STATUS_NO_ROOM);
return http_request_rhizome_response(r, 0, NULL);
case RHIZOME_PAYLOAD_STATUS_EVICTED:
r->bundle_status = RHIZOME_BUNDLE_STATUS_NO_ROOM;
return http_request_rhizome_response(r, 202, "Bundle evicted", NULL); // Accepted
r->bundle_result = rhizome_bundle_result(RHIZOME_BUNDLE_STATUS_NO_ROOM);
return http_request_rhizome_response(r, 0, NULL);
case RHIZOME_PAYLOAD_STATUS_ERROR:
return http_request_rhizome_response(r, 500, "Payload store error", NULL);
return http_request_rhizome_response(r, 500, "Payload store error");
}
if (!status_valid)
FATALF("rhizome_finish_store() returned status = %d", r->payload_status);
// Finalise the manifest and add it to the store.
const char *invalid_reason = rhizome_manifest_validate_reason(r->manifest);
if (invalid_reason) {
r->bundle_status = RHIZOME_BUNDLE_STATUS_INVALID;
return http_request_rhizome_response(r, 422, invalid_reason, NULL); // Unprocessable Entity
r->bundle_result = rhizome_bundle_result_static(RHIZOME_BUNDLE_STATUS_INVALID, invalid_reason);
return http_request_rhizome_response(r, 0, NULL);
}
if (r->manifest->malformed) {
r->bundle_status = RHIZOME_BUNDLE_STATUS_INVALID;
return http_request_rhizome_response(r, 422, r->manifest->malformed, NULL); // Unprocessable Entity
r->bundle_result = rhizome_bundle_result_static(RHIZOME_BUNDLE_STATUS_INVALID, r->manifest->malformed);
return http_request_rhizome_response(r, 0, NULL);
}
if (!r->manifest->haveSecret) {
r->bundle_status = RHIZOME_BUNDLE_STATUS_READONLY;
return http_request_rhizome_response(r, 419, "Missing bundle secret", NULL); // Authentication Timeout
r->bundle_result = rhizome_bundle_result_static(RHIZOME_BUNDLE_STATUS_READONLY, "Missing bundle secret");
return http_request_rhizome_response(r, 0, NULL);
}
rhizome_manifest *mout = NULL;
r->bundle_status = rhizome_manifest_finalise(r->manifest, &mout, !r->u.insert.force_new);
rhizome_bundle_result_free(&r->bundle_result);
r->bundle_result.status = rhizome_manifest_finalise(r->manifest, &mout, !r->u.insert.force_new);
int result = 500;
const char *message = NULL;
DEBUGF(rhizome, "r->bundle_status=%d", r->bundle_status);
switch (r->bundle_status) {
DEBUGF(rhizome, "r->bundle_result = %s", alloca_rhizome_bundle_result(r->bundle_result));
switch (r->bundle_result.status) {
case RHIZOME_BUNDLE_STATUS_NEW:
if (mout && mout != r->manifest)
rhizome_manifest_free(mout);
@ -754,7 +740,7 @@ static int restful_rhizome_insert_end(struct http_request *hr)
result = 201;
break;
case RHIZOME_BUNDLE_STATUS_ERROR:
message = "Error in manifest finalise";
r->bundle_result.message = "Error in manifest finalise";
// fall through
case RHIZOME_BUNDLE_STATUS_INVALID:
case RHIZOME_BUNDLE_STATUS_FAKE:
@ -766,10 +752,10 @@ static int restful_rhizome_insert_end(struct http_request *hr)
rhizome_manifest_free(mout);
rhizome_manifest_free(r->manifest);
r->manifest = NULL;
return http_request_rhizome_response(r, 0, message, NULL);
return http_request_rhizome_response(r, 0, NULL);
}
if (result == 500)
FATALF("rhizome_manifest_finalise() returned status = %d", r->bundle_status);
FATALF("rhizome_manifest_finalise() returned status = %d", r->bundle_result.status);
rhizome_authenticate_author(r->manifest);
http_request_response_static(&r->http, result, "rhizome-manifest/text",
(const char *)r->manifest->manifestdata, r->manifest->manifest_all_bytes
@ -810,9 +796,9 @@ static int restful_rhizome_(httpd_request *r, const char *remainder)
if (r->http.verb != HTTP_VERB_GET)
return 405;
if ((r->manifest = rhizome_new_manifest()) == NULL)
return http_request_rhizome_response(r, 429, "Manifest table full", NULL); // Too Many Requests
r->bundle_status = rhizome_retrieve_manifest(&bid, r->manifest);
switch(r->bundle_status){
return http_request_rhizome_response(r, 429, "Manifest table full"); // Too Many Requests
r->bundle_result.status = rhizome_retrieve_manifest(&bid, r->manifest);
switch(r->bundle_result.status){
case RHIZOME_BUNDLE_STATUS_SAME:
rhizome_authenticate_author(r->manifest);
break;
@ -822,13 +808,13 @@ static int restful_rhizome_(httpd_request *r, const char *remainder)
break;
case RHIZOME_BUNDLE_STATUS_BUSY:
rhizome_manifest_free(r->manifest);
return http_request_rhizome_response(r, 423, "Database busy", NULL); // Locked
return http_request_rhizome_response(r, 0, NULL);
case RHIZOME_BUNDLE_STATUS_ERROR:
rhizome_manifest_free(r->manifest);
r->manifest = NULL;
return http_request_rhizome_response(r, 500, "Manifest retrieve error", NULL);
return http_request_rhizome_response(r, 500, "Manifest retrieve error");
default: // should not return others
FATALF("rhizome_retrieve_manifest() returned status = %d", r->bundle_status);
FATALF("rhizome_retrieve_manifest() returned status = %d", r->bundle_result.status);
}
return handler(r, remainder);
}
@ -838,7 +824,7 @@ static int restful_rhizome_bid_rhm(httpd_request *r, const char *remainder)
if (*remainder)
return 404;
if (r->manifest == NULL)
return http_request_rhizome_response(r, 404, "Bundle not found", NULL); // Not Found
return http_request_rhizome_response(r, 404, "Bundle not found"); // Not Found
http_request_response_static(&r->http, 200, "rhizome-manifest/text",
(const char *)r->manifest->manifestdata, r->manifest->manifest_all_bytes
);
@ -850,7 +836,7 @@ static int restful_rhizome_bid_raw_bin(httpd_request *r, const char *remainder)
if (*remainder)
return 404;
if (r->manifest == NULL)
return http_request_rhizome_response(r, 404, "Bundle not found", NULL); // Not Found
return http_request_rhizome_response(r, 404, "Bundle not found"); // Not Found
if (r->manifest->filesize == 0) {
http_request_response_static(&r->http, 200, CONTENT_TYPE_BLOB, "", 0);
return 1;
@ -867,7 +853,7 @@ static int restful_rhizome_bid_decrypted_bin(httpd_request *r, const char *remai
if (*remainder)
return 404;
if (r->manifest == NULL)
return http_request_rhizome_response(r, 404, "Bundle not found", NULL); // Not Found
return http_request_rhizome_response(r, 404, "Bundle not found"); // Not Found
if (r->manifest->filesize == 0) {
// TODO use Content Type from manifest (once it is implemented)
http_request_response_static(&r->http, 200, CONTENT_TYPE_BLOB, "", 0);
@ -885,7 +871,7 @@ static int rhizome_response_content_init_read_state(httpd_request *r)
{
if (r->u.read_state.length == RHIZOME_SIZE_UNSET && rhizome_read(&r->u.read_state, NULL, 0)) {
rhizome_read_close(&r->u.read_state);
return http_request_rhizome_response(r, 404, "Payload not found", NULL);
return http_request_rhizome_response(r, 404, "Payload not found");
}
assert(r->u.read_state.length != RHIZOME_SIZE_UNSET);
int ret = http_response_init_content_range(r, r->u.read_state.length);
@ -906,14 +892,14 @@ int rhizome_response_content_init_filehash(httpd_request *r, const rhizome_fileh
case RHIZOME_PAYLOAD_STATUS_STORED:
return rhizome_response_content_init_read_state(r);
case RHIZOME_PAYLOAD_STATUS_NEW:
return http_request_rhizome_response(r, 404, "Payload not found", NULL);
return http_request_rhizome_response(r, 404, "Payload not found");
case RHIZOME_PAYLOAD_STATUS_ERROR:
case RHIZOME_PAYLOAD_STATUS_WRONG_SIZE:
case RHIZOME_PAYLOAD_STATUS_WRONG_HASH:
case RHIZOME_PAYLOAD_STATUS_CRYPTO_FAIL:
case RHIZOME_PAYLOAD_STATUS_TOO_BIG:
case RHIZOME_PAYLOAD_STATUS_EVICTED:
return http_request_rhizome_response(r, 500, "Payload read error", NULL);
return http_request_rhizome_response(r, 500, "Payload read error");
}
FATALF("rhizome_open_read() returned status = %d", r->payload_status);
}
@ -930,15 +916,15 @@ int rhizome_response_content_init_payload(httpd_request *r, rhizome_manifest *m)
case RHIZOME_PAYLOAD_STATUS_STORED:
return rhizome_response_content_init_read_state(r);
case RHIZOME_PAYLOAD_STATUS_NEW:
return http_request_rhizome_response(r, 404, "Payload not found", NULL);
return http_request_rhizome_response(r, 404, "Payload not found");
case RHIZOME_PAYLOAD_STATUS_CRYPTO_FAIL:
return http_request_rhizome_response(r, 419, NULL, NULL); // Authentication Timeout
return http_request_rhizome_response(r, 419, "Payload decryption error"); // Authentication Timeout
case RHIZOME_PAYLOAD_STATUS_ERROR:
case RHIZOME_PAYLOAD_STATUS_WRONG_SIZE:
case RHIZOME_PAYLOAD_STATUS_WRONG_HASH:
case RHIZOME_PAYLOAD_STATUS_TOO_BIG:
case RHIZOME_PAYLOAD_STATUS_EVICTED:
return http_request_rhizome_response(r, 500, "Payload read error", NULL);
return http_request_rhizome_response(r, 500, "Payload read error");
}
FATALF("rhizome_open_decrypt_read() returned status = %d", r->payload_status);
}
@ -974,14 +960,30 @@ int rhizome_payload_content(struct http_request *hr, unsigned char *buf, size_t
static void render_manifest_headers(struct http_request *hr, strbuf sb)
{
httpd_request *r = (httpd_request *) hr;
const char *status_message;
if ((status_message = rhizome_bundle_status_message(r->bundle_status))) {
strbuf_sprintf(sb, "Serval-Rhizome-Result-Bundle-Status-Code: %d\r\n", r->bundle_status);
strbuf_sprintf(sb, "Serval-Rhizome-Result-Bundle-Status-Message: %s\r\n", status_message);
switch (r->bundle_result.status) {
case RHIZOME_BUNDLE_STATUS_NEW:
case RHIZOME_BUNDLE_STATUS_SAME:
case RHIZOME_BUNDLE_STATUS_DUPLICATE:
case RHIZOME_BUNDLE_STATUS_OLD:
case RHIZOME_BUNDLE_STATUS_INVALID:
case RHIZOME_BUNDLE_STATUS_FAKE:
case RHIZOME_BUNDLE_STATUS_INCONSISTENT:
case RHIZOME_BUNDLE_STATUS_NO_ROOM:
case RHIZOME_BUNDLE_STATUS_READONLY:
case RHIZOME_BUNDLE_STATUS_BUSY:
case RHIZOME_BUNDLE_STATUS_ERROR:
strbuf_sprintf(sb, "Serval-Rhizome-Result-Bundle-Status-Code: %d\r\n", r->bundle_result.status);
strbuf_puts(sb, "Serval-Rhizome-Result-Bundle-Status-Message: ");
strbuf_json_string(sb, rhizome_bundle_result_message_nonnull(r->bundle_result));
strbuf_puts(sb, "\r\n");
break;
}
if ((status_message = rhizome_payload_status_message(r->payload_status))) {
const char *status_message = rhizome_payload_status_message(r->payload_status);
if (status_message) {
strbuf_sprintf(sb, "Serval-Rhizome-Result-Payload-Status-Code: %d\r\n", r->payload_status);
strbuf_sprintf(sb, "Serval-Rhizome-Result-Payload-Status-Message: %s\r\n", status_message);
strbuf_puts(sb, "Serval-Rhizome-Result-Payload-Status-Message: ");
strbuf_json_string(sb, status_message);
strbuf_puts(sb, "\r\n");
}
rhizome_manifest *m = r->manifest;
if (m) {

View File

@ -808,7 +808,7 @@ setup_AddUpdateNoAuthorWithSecret() {
setup_AddUpdateNoAuthor
}
test_AddUpdateNoAuthorWithSecret() {
executeOk_servald rhizome add file $SIDA file1_2 file1_2.manifest "$file1_secret"
executeOk_servald rhizome add file '' file1_2 file1_2.manifest "$file1_secret"
tfw_cat --stderr
# Rhizome store contents have new payload, but it has lost its author (no BK
# field any more).
@ -1083,7 +1083,7 @@ setup_JournalAddCreate() {
test_JournalAddCreate() {
# TODO: servald should return a status code reserved for this case, instead
# of the 4 error (which means "invalid manifest")
execute --exit-status=4 $servald rhizome add file $SIDA file1 file1.manifest
execute --exit-status=4 --stderr $servald rhizome add file $SIDA file1 file1.manifest
tfw_cat --stdout --stderr
}

View File

@ -483,7 +483,7 @@ test_RhizomePayloadDecryptedForeign() {
assertGrep --matches=1 --ignore-case http.headers$n "^Serval-Rhizome-Result-Bundle-Status-Code: 1$CR\$"
assertGrep --matches=1 --ignore-case http.headers$n "^Serval-Rhizome-Result-Bundle-Status-Message: .*bundle already in store.*$CR\$"
assertGrep --matches=1 --ignore-case http.headers$n "^Serval-Rhizome-Result-Payload-Status-Code: 5$CR\$"
assertGrep --matches=1 --ignore-case http.headers$n "^Serval-Rhizome-Result-Payload-Status-Message: .*incorrect bundle secret*$CR\$"
assertGrep --matches=1 --ignore-case http.headers$n "^Serval-Rhizome-Result-Payload-Status-Message: .*incorrect bundle secret.*$CR\$"
}
doc_RhizomePayloadDecryptedNonexistManifest="HTTP RESTful fetch Rhizome decrypted payload for non-existent manifest"