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) { if (current_httpd_requests == NULL) {
assert(current_httpd_request_count == 0); assert(current_httpd_request_count == 0);
} }
rhizome_bundle_result_free(&r->bundle_result);
if (r->manifest) { if (r->manifest) {
rhizome_manifest_free(r->manifest); rhizome_manifest_free(r->manifest);
r->manifest = NULL; r->manifest = NULL;
@ -273,7 +274,7 @@ void httpd_server_poll(struct sched_ent *alarm)
current_httpd_requests = request; current_httpd_requests = request;
++current_httpd_request_count; ++current_httpd_request_count;
request->payload_status = INVALID_RHIZOME_PAYLOAD_STATUS; // will cause FATAL unless set 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) if (peerip)
request->http.client_sockaddr_in = *peerip; request->http.client_sockaddr_in = *peerip;
request->http.uuid = http_request_uuid_counter; request->http.uuid = http_request_uuid_counter;

View File

@ -61,7 +61,7 @@ typedef struct httpd_request
*/ */
rhizome_manifest *manifest; rhizome_manifest *manifest;
enum rhizome_payload_status payload_status; 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. /* 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) { if (m->haveSecret == NEW_BUNDLE_ID) {
rhizome_manifest_set_service(m, RHIZOME_SERVICE_FILE); rhizome_manifest_set_service(m, RHIZOME_SERVICE_FILE);
rhizome_manifest_set_name(m, ""); rhizome_manifest_set_name(m, "");
if (rhizome_fill_manifest(m, NULL, my_sidp) != NULL) struct rhizome_bundle_result result = rhizome_fill_manifest(m, NULL, my_sidp);
return WHY("Invalid conversation manifest"); 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. // The 'meshms' automated test depends on this message; do not alter.
DEBUGF(meshms, "MESHMS CONVERSATION BUNDLE bid=%s secret=%s", DEBUGF(meshms, "MESHMS CONVERSATION BUNDLE bid=%s secret=%s",
alloca_tohex_rhizome_bid_t(m->cryptoSignPublic), 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; 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", DEBUGF(meshms, "Creating ply for my_sid=%s them=%s",
alloca_tohex_sid_t(conv->them), 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_recipient(m, &conv->them);
rhizome_manifest_set_filesize(m, 0); rhizome_manifest_set_filesize(m, 0);
rhizome_manifest_set_tail(m, 0); rhizome_manifest_set_tail(m, 0);
if (rhizome_fill_manifest(m, NULL, my_sid) != NULL) struct rhizome_bundle_result result = rhizome_fill_manifest(m, NULL, my_sid);
return -1; 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->haveSecret);
assert(m->payloadEncryption == PAYLOAD_ENCRYPTED); assert(m->payloadEncryption == PAYLOAD_ENCRYPTED);
conv->my_ply.bundle_id = m->cryptoSignPublic; 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; status = MESHMS_STATUS_PROTOCOL_FAULT;
goto end; goto end;
} }
} else if (create_ply(my_sid, conv, m) == -1) { } else {
goto end; 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->haveSecret);
assert(m->authorship == AUTHOR_AUTHENTIC); assert(m->authorship == AUTHOR_AUTHENTIC);
enum rhizome_payload_status pstatus = rhizome_append_journal_buffer(m, 0, buffer, len); 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; goto end;
}
enum rhizome_bundle_status bstatus = rhizome_manifest_finalise(m, &mout, 1); enum rhizome_bundle_status bstatus = rhizome_manifest_finalise(m, &mout, 1);
DEBUGF(meshms, "bstatus=%d", bstatus); DEBUGF(meshms, "bstatus=%d", bstatus);
switch (bstatus) { switch (bstatus) {

295
rhizome.c
View File

@ -1,6 +1,6 @@
/* /*
Serval DNA - Rhizome entry points 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 Copyright (C) 2011-2012 Paul Gardner-Stephen
This program is free software; you can redistribute it and/or 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 "serval.h"
#include "conf.h" #include "conf.h"
#include "str.h" #include "str.h"
#include "strbuf_helpers.h"
#include "mem.h"
#include "rhizome.h" #include "rhizome.h"
#include "httpd.h" #include "httpd.h"
#include "dataformats.h" #include "dataformats.h"
#include "log.h"
#include "debug.h"
int is_rhizome_enabled() int is_rhizome_enabled()
{ {
@ -144,7 +148,7 @@ int rhizome_fetch_delay_ms()
* *
* @author Andrew Bettison <andrew@servalproject.com> * @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 *m,
rhizome_manifest **mout, rhizome_manifest **mout,
const rhizome_bid_t *bid, 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 sid_t *author,
const char *file_path, const char *file_path,
unsigned nassignments, unsigned nassignments,
const struct rhizome_manifest_field_assignment *assignments, const struct rhizome_manifest_field_assignment *assignments)
strbuf reason
)
{ {
const char *cause = NULL; struct rhizome_bundle_result result = INVALID_RHIZOME_BUNDLE_RESULT; // must be set before returning
enum rhizome_add_file_result result = RHIZOME_ADD_FILE_ERROR;
rhizome_manifest *existing_manifest = NULL; rhizome_manifest *existing_manifest = NULL;
rhizome_manifest *new_manifest = NULL; rhizome_manifest *new_manifest = NULL;
assert(m != 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, // If appending to a journal, caller must not supply 'version', 'filesize' or 'filehash' fields,
// because these will be calculated by the journal append logic. // because these will be calculated by the journal append logic.
if (appending) { if (appending) {
if (m->version) if (m->version) {
DEBUG(rhizome, cause = "Cannot set 'version' field in journal append"); result = rhizome_bundle_result_static(RHIZOME_BUNDLE_STATUS_INVALID, "Cannot set 'version' field in journal append");
else if (m->filesize != RHIZOME_SIZE_UNSET) goto error;
DEBUG(rhizome, cause = "Cannot set 'filesize' field in journal append"); }
else if (m->has_filehash) else if (m->filesize != RHIZOME_SIZE_UNSET) {
DEBUG(rhizome, cause = "Cannot set 'filehash' field in journal append"); result = rhizome_bundle_result_static(RHIZOME_BUNDLE_STATUS_INVALID, "Cannot set 'filesize' field in journal append");
if (cause) { goto error;
result = RHIZOME_ADD_FILE_INVALID_FOR_JOURNAL; }
else if (m->has_filehash) {
result = rhizome_bundle_result_static(RHIZOME_BUNDLE_STATUS_INVALID, "Cannot set 'filehash' field in journal append");
goto error; goto error;
} }
} }
if (bid) { if (bid) {
DEBUGF(rhizome, "Reading manifest from database: id=%s", alloca_tohex_rhizome_bid_t(*bid)); DEBUGF(rhizome, "Reading manifest from database: id=%s", alloca_tohex_rhizome_bid_t(*bid));
if ((existing_manifest = rhizome_new_manifest()) == NULL) { 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; goto error;
} }
enum rhizome_bundle_status status = rhizome_retrieve_manifest(bid, existing_manifest); result.status = rhizome_retrieve_manifest(bid, existing_manifest);
switch (status) { switch (result.status) {
case RHIZOME_BUNDLE_STATUS_NEW: case RHIZOME_BUNDLE_STATUS_NEW:
// No manifest with that bundle ID exists in the store, so we are building a bundle from // No manifest with that bundle ID exists in the store, so we are building a bundle from
// scratch. // scratch.
@ -204,18 +207,20 @@ enum rhizome_add_file_result rhizome_manifest_add_file(int appending,
rhizome_manifest_del_filehash(existing_manifest); rhizome_manifest_del_filehash(existing_manifest);
} }
if (rhizome_manifest_overwrite(existing_manifest, m) == -1) { 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; goto error;
} }
new_manifest = existing_manifest; new_manifest = existing_manifest;
existing_manifest = NULL; existing_manifest = NULL;
break; break;
case RHIZOME_BUNDLE_STATUS_BUSY: case RHIZOME_BUNDLE_STATUS_BUSY:
WARN(cause = "Existing manifest not retrieved due to Rhizome store locking"); result = rhizome_bundle_result_static(RHIZOME_BUNDLE_STATUS_BUSY,
result = RHIZOME_ADD_FILE_BUSY; "Existing manifest not retrieved due to Rhizome store locking");
goto error; goto error;
case RHIZOME_BUNDLE_STATUS_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; goto error;
case RHIZOME_BUNDLE_STATUS_DUPLICATE: case RHIZOME_BUNDLE_STATUS_DUPLICATE:
case RHIZOME_BUNDLE_STATUS_OLD: 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_INCONSISTENT:
case RHIZOME_BUNDLE_STATUS_NO_ROOM: case RHIZOME_BUNDLE_STATUS_NO_ROOM:
case RHIZOME_BUNDLE_STATUS_READONLY: 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. // 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); enum rhizome_manifest_parse_status status = rhizome_manifest_parse_field(new_manifest, asg->label, asg->labellen, asg->value, asg->valuelen);
int status_ok = 0; int status_ok = 0;
switch (status) { switch (status) {
case RHIZOME_MANIFEST_ERROR: case RHIZOME_MANIFEST_ERROR:
WHYF("Fatal error updating manifest field"); result = rhizome_bundle_result_sprintf(RHIZOME_BUNDLE_STATUS_ERROR,
if (reason) "Error updating manifest field: %s=%s",
strbuf_sprintf(reason, "Fatal error updating manifest field: %s=%s", label, alloca_toprint(-1, asg->value, asg->valuelen)); label, alloca_toprint(-1, asg->value, asg->valuelen));
goto error; goto error;
case RHIZOME_MANIFEST_OK: case RHIZOME_MANIFEST_OK:
status_ok = 1; status_ok = 1;
break; break;
case RHIZOME_MANIFEST_SYNTAX_ERROR: case RHIZOME_MANIFEST_SYNTAX_ERROR:
DEBUGF(rhizome, "Manifest syntax error: %s=%s", label, alloca_toprint(-1, asg->value, asg->valuelen)); result = rhizome_bundle_result_sprintf(RHIZOME_BUNDLE_STATUS_INVALID,
if (reason) "Manifest syntax error: %s=%s",
strbuf_sprintf(reason, "Manifest syntax error: %s=%s", label, alloca_toprint(-1, asg->value, asg->valuelen)); label, alloca_toprint(-1, asg->value, asg->valuelen));
result = RHIZOME_ADD_FILE_INVALID; goto error;
goto error; case RHIZOME_MANIFEST_DUPLICATE_FIELD:
case RHIZOME_MANIFEST_DUPLICATE_FIELD: // We already deleted the field, so if this happens, its a logic bug.
// 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));
FATALF("Duplicate field should not occur: %s=%s", label, alloca_toprint(-1, asg->value, asg->valuelen)); case RHIZOME_MANIFEST_INVALID:
case RHIZOME_MANIFEST_INVALID: result = rhizome_bundle_result_sprintf(RHIZOME_BUNDLE_STATUS_INVALID,
DEBUGF(rhizome, "Manifest invalid field: %s=%s", label, alloca_toprint(-1, asg->value, asg->valuelen)); "Manifest invalid field: %s=%s",
if (reason) label, alloca_toprint(-1, asg->value, asg->valuelen));
strbuf_sprintf(reason, "Manifest invalid field: %s=%s", label, alloca_toprint(-1, asg->value, asg->valuelen)); goto error;
result = RHIZOME_ADD_FILE_INVALID; case RHIZOME_MANIFEST_MALFORMED:
goto error; result = rhizome_bundle_result_sprintf(RHIZOME_BUNDLE_STATUS_INVALID,
case RHIZOME_MANIFEST_MALFORMED: "Manifest malformed field: %s=%s",
DEBUGF(rhizome, "Manifest malformed field: %s=%s", label, alloca_toprint(-1, asg->value, asg->valuelen)); label, alloca_toprint(-1, asg->value, asg->valuelen));
if (reason) goto error;
strbuf_sprintf(reason, "Manifest malformed field: %s=%s", label, alloca_toprint(-1, asg->value, asg->valuelen)); case RHIZOME_MANIFEST_OVERFLOW:
result = RHIZOME_ADD_FILE_INVALID; result = rhizome_bundle_result_sprintf(RHIZOME_BUNDLE_STATUS_INVALID,
goto error; "Too many fields in manifest at: %s=%s",
case RHIZOME_MANIFEST_OVERFLOW: label, alloca_toprint(-1, asg->value, asg->valuelen));
DEBUGF(rhizome, "Too many fields in manifest at: %s=%s", label, alloca_toprint(-1, asg->value, asg->valuelen)); goto error;
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;
} }
if (!status_ok) if (!status_ok)
FATALF("status = %d", status); 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) { if (appending && !new_manifest->is_journal) {
cause = "Cannot append to a non-journal"; result = rhizome_bundle_result_static(RHIZOME_BUNDLE_STATUS_INVALID,
DEBUG(rhizome, cause); "Cannot append to a non-journal");
result = RHIZOME_ADD_FILE_REQUIRES_JOURNAL;
goto error; goto error;
} }
if (!appending && new_manifest->is_journal) { if (!appending && new_manifest->is_journal) {
cause = "Cannot add a journal bundle (use append instead)"; result = rhizome_bundle_result_static(RHIZOME_BUNDLE_STATUS_INVALID,
DEBUG(rhizome, cause); "Cannot add a journal bundle (use append instead)");
result = RHIZOME_ADD_FILE_INVALID_FOR_JOURNAL;
goto error; goto error;
} }
if (bsk) { if (bsk) {
if (new_manifest->has_id) { if (new_manifest->has_id) {
if (!rhizome_apply_bundle_secret(new_manifest, bsk)) { if (!rhizome_apply_bundle_secret(new_manifest, bsk)) {
WHY(cause = "Supplied bundle secret does not match Bundle Id"); result = rhizome_bundle_result_static(RHIZOME_BUNDLE_STATUS_READONLY,
result = RHIZOME_ADD_FILE_WRONG_SECRET; "Supplied bundle secret does not match Bundle Id");
goto error; goto error;
} }
} else { } 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); WARNF("Manifest 'service' field not supplied - setting to '%s'", RHIZOME_SERVICE_FILE);
rhizome_manifest_set_service(new_manifest, 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) result = rhizome_fill_manifest(new_manifest, file_path, author);
goto error;
*mout = new_manifest;
return RHIZOME_ADD_FILE_OK;
error: error:
assert(result != RHIZOME_ADD_FILE_OK); switch (result.status) {
if (cause && reason) case RHIZOME_BUNDLE_STATUS_NEW:
strbuf_puts(reason, cause); *mout = new_manifest;
if (new_manifest && new_manifest != m && new_manifest != existing_manifest) return result;
rhizome_manifest_free(new_manifest); case RHIZOME_BUNDLE_STATUS_ERROR:
if (existing_manifest) case RHIZOME_BUNDLE_STATUS_SAME:
rhizome_manifest_free(existing_manifest); case RHIZOME_BUNDLE_STATUS_DUPLICATE:
return result; 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 /* 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); const char *message = rhizome_payload_status_message(status);
return message ? message : "Invalid"; 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(enum rhizome_bundle_status);
const char *rhizome_bundle_status_message_nonnull(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 { enum rhizome_payload_status {
RHIZOME_PAYLOAD_STATUS_ERROR = -1, RHIZOME_PAYLOAD_STATUS_ERROR = -1,
RHIZOME_PAYLOAD_STATUS_EMPTY = 0, // payload is empty (zero length) 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_manifest(rhizome_manifest *m);
int rhizome_store_file(rhizome_manifest *m,const unsigned char *key); int rhizome_store_file(rhizome_manifest *m,const unsigned char *key);
enum rhizome_add_file_result { struct rhizome_bundle_result rhizome_manifest_add_file(int appending,
RHIZOME_ADD_FILE_ERROR = -1, rhizome_manifest *m,
RHIZOME_ADD_FILE_OK = 0, // manifest created successfully rhizome_manifest **mout,
RHIZOME_ADD_FILE_INVALID, // manifest not created due to invalid input const rhizome_bid_t *bid,
RHIZOME_ADD_FILE_BUSY, // manifest not created because database busy const rhizome_bk_t *bsk,
RHIZOME_ADD_FILE_REQUIRES_JOURNAL, // operation is only valid for a journal const sid_t *author,
RHIZOME_ADD_FILE_INVALID_FOR_JOURNAL, // operation is not valid for a journal const char *file_path,
RHIZOME_ADD_FILE_WRONG_SECRET, // incorrect bundle secret supplied unsigned nassignments,
}; const struct rhizome_manifest_field_assignment *assignments);
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
);
int rhizome_bundle_import_files(rhizome_manifest *m, rhizome_manifest **m_out, const char *manifest_path, const char *filepath); 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); 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_apply_bundle_secret(rhizome_manifest *, const rhizome_bk_t *);
int rhizome_manifest_add_bundle_key(rhizome_manifest *); 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 * - 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" * - 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 * Return a rhizome_bundle_status code together with a pointer to a text string describing the
* the failure (always an internal/unrecoverable error). * 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. */ /* Set version of manifest from current time if not already set. */
if (m->version == 0) if (m->version == 0)
rhizome_manifest_set_version(m, gettime_ms()); 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. // If there is no Bundle Id, then create a new bundle Id and secret from scratch.
DEBUG(rhizome, "creating new bundle"); DEBUG(rhizome, "creating new bundle");
if (rhizome_manifest_createid(m) == -1) { if (rhizome_manifest_createid(m) == -1) {
WHY(reason = "Could not bind manifest to an ID"); return rhizome_bundle_result_static(RHIZOME_BUNDLE_STATUS_ERROR, "Could not bind manifest to an ID");
return reason;
} }
// fall through to set the BK field... // fall through to set the BK field...
case NEW_BUNDLE_ID: case NEW_BUNDLE_ID:
@ -1540,12 +1539,32 @@ const char * rhizome_fill_manifest(rhizome_manifest *m, const char *filepath, co
} }
if (!valid_haveSecret) if (!valid_haveSecret)
FATALF("haveSecret = %d", m->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. /* Service field must already be set.
*/ */
if (m->service == NULL) { if (m->service == NULL) {
WHYF(reason = "Missing 'service' field"); return rhizome_bundle_result_static(RHIZOME_BUNDLE_STATUS_INVALID, "Missing 'service' field");
return reason;
} }
DEBUGF(rhizome, "manifest contains service=%s", m->service); 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); 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. /* 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; int ret = -1;
rhizome_manifest *m = NULL; rhizome_manifest *m = NULL;
struct rhizome_bundle_result result = INVALID_RHIZOME_BUNDLE_RESULT;
if (rhizome_opendb() == -1) if (rhizome_opendb() == -1)
goto finish; 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. /* Create an in-memory manifest for the file being added.
*/ */
rhizome_manifest *mout = NULL; rhizome_manifest *mout = NULL;
enum rhizome_add_file_result result = rhizome_manifest_add_file(appending, m, &mout, result = rhizome_manifest_add_file(appending, m, &mout,
bundleIdHex ? &bid : NULL, bundleIdHex ? &bid : NULL,
bsktext ? &bsk : NULL, bsktext ? &bsk : NULL,
authorSidHex ? &authorSid : NULL, authorSidHex ? &authorSid : NULL,
filepath, filepath,
nfields, fields, nfields, fields);
NULL); if (result.status != RHIZOME_BUNDLE_STATUS_NEW) {
int result_valid = 0; ret = result.status; // TODO separate enum for CLI return codes
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
goto finish; goto finish;
} }
if (!result_valid)
FATALF("result = %d", result);
assert(mout != NULL); assert(mout != NULL);
if (mout != m) { if (mout != m) {
rhizome_manifest_free(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)); 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; int pstatus_valid = 0;
switch (pstatus) { switch (pstatus) {
case RHIZOME_PAYLOAD_STATUS_EMPTY: case RHIZOME_PAYLOAD_STATUS_EMPTY:
case RHIZOME_PAYLOAD_STATUS_STORED: case RHIZOME_PAYLOAD_STATUS_STORED:
case RHIZOME_PAYLOAD_STATUS_NEW: case RHIZOME_PAYLOAD_STATUS_NEW:
pstatus_valid = 1; pstatus_valid = 1;
status = RHIZOME_BUNDLE_STATUS_NEW; result.status = RHIZOME_BUNDLE_STATUS_NEW;
break; break;
case RHIZOME_PAYLOAD_STATUS_TOO_BIG: case RHIZOME_PAYLOAD_STATUS_TOO_BIG:
case RHIZOME_PAYLOAD_STATUS_EVICTED: case RHIZOME_PAYLOAD_STATUS_EVICTED:
pstatus_valid = 1; pstatus_valid = 1;
status = RHIZOME_BUNDLE_STATUS_NO_ROOM; result.status = RHIZOME_BUNDLE_STATUS_NO_ROOM;
INFO("Insufficient space to store payload"); INFO("Insufficient space to store payload");
break; break;
case RHIZOME_PAYLOAD_STATUS_ERROR: case RHIZOME_PAYLOAD_STATUS_ERROR:
pstatus_valid = 1; pstatus_valid = 1;
status = RHIZOME_BUNDLE_STATUS_ERROR; result.status = RHIZOME_BUNDLE_STATUS_ERROR;
break; break;
case RHIZOME_PAYLOAD_STATUS_WRONG_SIZE: case RHIZOME_PAYLOAD_STATUS_WRONG_SIZE:
case RHIZOME_PAYLOAD_STATUS_WRONG_HASH: case RHIZOME_PAYLOAD_STATUS_WRONG_HASH:
pstatus_valid = 1; pstatus_valid = 1;
status = RHIZOME_BUNDLE_STATUS_INCONSISTENT; result.status = RHIZOME_BUNDLE_STATUS_INCONSISTENT;
break; break;
case RHIZOME_PAYLOAD_STATUS_CRYPTO_FAIL: case RHIZOME_PAYLOAD_STATUS_CRYPTO_FAIL:
pstatus_valid = 1; pstatus_valid = 1;
status = RHIZOME_BUNDLE_STATUS_READONLY; result.status = RHIZOME_BUNDLE_STATUS_READONLY;
break; break;
} }
if (!pstatus_valid) if (!pstatus_valid)
FATALF("pstatus = %d", pstatus); FATALF("pstatus = %d", pstatus);
if (status == RHIZOME_BUNDLE_STATUS_NEW) { if (result.status == RHIZOME_BUNDLE_STATUS_NEW) {
if (!rhizome_manifest_validate(m) || m->malformed) if (!rhizome_manifest_validate(m) || m->malformed)
status = RHIZOME_BUNDLE_STATUS_INVALID; result.status = RHIZOME_BUNDLE_STATUS_INVALID;
else { 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)) { if (mout && mout != m && !rhizome_manifest_validate(mout)) {
WHYF("Stored manifest id=%s is invalid -- overwriting", alloca_tohex_rhizome_bid_t(mout->cryptoSignPublic)); 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; int status_valid = 0;
switch (status) { switch (result.status) {
case RHIZOME_BUNDLE_STATUS_NEW: case RHIZOME_BUNDLE_STATUS_NEW:
if (mout && mout != m) if (mout && mout != m)
rhizome_manifest_free(mout); 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. // a valuable tool for the developer.
} }
if (!status_valid) if (!status_valid)
FATALF("status=%d", status); FATALF("result.status=%d", result.status);
if (mout && mout != m) if (mout && mout != m)
rhizome_manifest_free(mout); rhizome_manifest_free(mout);
ret = status; ret = result.status;
finish: finish:
rhizome_bundle_result_free(&result);
rhizome_manifest_free(m); rhizome_manifest_free(m);
keyring_free(keyring); keyring_free(keyring);
keyring = NULL; keyring = NULL;

View File

@ -243,10 +243,6 @@ void rhizome_authenticate_author(rhizome_manifest *m)
{ {
IN(); IN();
DEBUGF(rhizome, "authenticate author for bid=%s", m->has_id ? alloca_tohex_rhizome_bid_t(m->cryptoSignPublic) : "(none)"); 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) { switch (m->authorship) {
case ANONYMOUS: case ANONYMOUS:
DEBUGF(rhizome, " manifest[%d] author unknown", m->manifest_record_number); 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) if (m->service == NULL)
rhizome_manifest_set_service(m, RHIZOME_SERVICE_FILE); 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 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); struct rhizome_bundle_result result = rhizome_fill_manifest(m, r->u.direct_import.data_file_name, author);
if (reason) { if (result.status != RHIZOME_BUNDLE_STATUS_NEW) {
rhizome_manifest_free(m); rhizome_manifest_free(m);
rhizome_direct_clear_temporary_files(r); 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; return 0;
} }
rhizome_bundle_result_free(&result);
rhizome_manifest_set_crypt(m, PAYLOAD_CLEAR); rhizome_manifest_set_crypt(m, PAYLOAD_CLEAR);
// import file contents // import file contents
// TODO, stream file into database // 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; 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; uint16_t rhizome_http_status = 0;
switch (r->bundle_status) { switch (r->bundle_result.status) {
case RHIZOME_BUNDLE_STATUS_SAME: case RHIZOME_BUNDLE_STATUS_SAME:
case RHIZOME_BUNDLE_STATUS_DUPLICATE: case RHIZOME_BUNDLE_STATUS_DUPLICATE:
rhizome_result = 200; // OK rhizome_http_status = 200; // OK
break; break;
case RHIZOME_BUNDLE_STATUS_NEW: case RHIZOME_BUNDLE_STATUS_NEW:
rhizome_result = 201; // Created rhizome_http_status = 201; // Created
break; break;
case RHIZOME_BUNDLE_STATUS_NO_ROOM: case RHIZOME_BUNDLE_STATUS_NO_ROOM:
case RHIZOME_BUNDLE_STATUS_OLD: case RHIZOME_BUNDLE_STATUS_OLD:
rhizome_result = 202; // Accepted rhizome_http_status = 202; // Accepted
break; break;
case RHIZOME_BUNDLE_STATUS_FAKE: case RHIZOME_BUNDLE_STATUS_FAKE:
case RHIZOME_BUNDLE_STATUS_READONLY: case RHIZOME_BUNDLE_STATUS_READONLY:
rhizome_result = 419; // Authentication Timeout rhizome_http_status = 419; // Authentication Timeout
break; break;
case RHIZOME_BUNDLE_STATUS_INVALID: case RHIZOME_BUNDLE_STATUS_INVALID:
case RHIZOME_BUNDLE_STATUS_INCONSISTENT: case RHIZOME_BUNDLE_STATUS_INCONSISTENT:
rhizome_result = 422; // Unprocessable Entity rhizome_http_status = 422; // Unprocessable Entity
break; break;
case RHIZOME_BUNDLE_STATUS_BUSY: case RHIZOME_BUNDLE_STATUS_BUSY:
rhizome_result = 423; // Locked rhizome_http_status = 423; // Locked
break; break;
case RHIZOME_BUNDLE_STATUS_ERROR: case RHIZOME_BUNDLE_STATUS_ERROR:
rhizome_result = 500; rhizome_http_status = 500;
break; 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].label = "rhizome_bundle_status_code";
r->http.response.result_extra[0].value.type = JSON_INTEGER; r->http.response.result_extra[0].value.type = JSON_INTEGER;
r->http.response.result_extra[0].value.u.integer = r->bundle_status; r->http.response.result_extra[0].value.u.integer = r->bundle_result.status;
const char *status_message = rhizome_bundle_status_message(r->bundle_status); const char *status_message = rhizome_bundle_result_message(r->bundle_result);
if (status_message) { if (status_message) {
r->http.response.result_extra[1].label = "rhizome_bundle_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.type = JSON_STRING_NULTERM;
r->http.response.result_extra[1].value.u.string.content = status_message; r->http.response.result_extra[1].value.u.string.content = status_message;
} }
if (rhizome_result > result) { if (rhizome_http_status > http_status) {
result = rhizome_result; http_status = rhizome_http_status;
message = NULL; if (!http_reason)
http_reason = status_message;
} }
} }
rhizome_result = 0; rhizome_http_status = 0;
switch (r->payload_status) { switch (r->payload_status) {
case RHIZOME_PAYLOAD_STATUS_STORED: case RHIZOME_PAYLOAD_STATUS_STORED:
case RHIZOME_PAYLOAD_STATUS_EMPTY: case RHIZOME_PAYLOAD_STATUS_EMPTY:
rhizome_result = 200; rhizome_http_status = 200;
break; break;
case RHIZOME_PAYLOAD_STATUS_NEW: case RHIZOME_PAYLOAD_STATUS_NEW:
rhizome_result = 201; rhizome_http_status = 201;
break; break;
case RHIZOME_PAYLOAD_STATUS_TOO_BIG: case RHIZOME_PAYLOAD_STATUS_TOO_BIG:
case RHIZOME_PAYLOAD_STATUS_EVICTED: case RHIZOME_PAYLOAD_STATUS_EVICTED:
rhizome_result = 202; // Accepted rhizome_http_status = 202; // Accepted
break; break;
case RHIZOME_PAYLOAD_STATUS_CRYPTO_FAIL: case RHIZOME_PAYLOAD_STATUS_CRYPTO_FAIL:
rhizome_result = 419; // Authentication Timeout rhizome_http_status = 419; // Authentication Timeout
break; break;
case RHIZOME_PAYLOAD_STATUS_WRONG_SIZE: case RHIZOME_PAYLOAD_STATUS_WRONG_SIZE:
case RHIZOME_PAYLOAD_STATUS_WRONG_HASH: case RHIZOME_PAYLOAD_STATUS_WRONG_HASH:
rhizome_result = 422; // Unprocessable Entity rhizome_http_status = 422; // Unprocessable Entity
break; break;
case RHIZOME_PAYLOAD_STATUS_ERROR: case RHIZOME_PAYLOAD_STATUS_ERROR:
rhizome_result = 500; rhizome_http_status = 500;
break; 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].label = "rhizome_payload_status_code";
r->http.response.result_extra[2].value.type = JSON_INTEGER; r->http.response.result_extra[2].value.type = JSON_INTEGER;
r->http.response.result_extra[2].value.u.integer = r->payload_status; 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) { if (status_message) {
r->http.response.result_extra[3].label = "rhizome_payload_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.type = JSON_STRING_NULTERM;
r->http.response.result_extra[3].value.u.string.content = status_message; r->http.response.result_extra[3].value.u.string.content = status_message;
} }
if (rhizome_result > result) { if (rhizome_http_status > http_status) {
result = rhizome_result; http_status = rhizome_http_status;
message = NULL; if (!http_reason)
http_reason = status_message;
} }
} }
if (result == 0) { if (http_status == 0) {
result = 500; http_status = 500;
message = "Result logic error"; http_reason = "Invalid result";
} }
http_request_simple_response(&r->http, result, message); http_request_simple_response(&r->http, http_status, http_reason);
return result; return http_status;
} }
static HTTP_CONTENT_GENERATOR restful_rhizome_bundlelist_json_content; 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) if (!r->u.insert.received_manifest)
return http_response_form_part(r, 400, "Missing", PART_MANIFEST, NULL, 0); return http_response_form_part(r, 400, "Missing", PART_MANIFEST, NULL, 0);
if ((r->manifest = rhizome_new_manifest()) == NULL) 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); assert(r->u.insert.manifest.length <= sizeof r->manifest->manifestdata);
memcpy(r->manifest->manifestdata, r->u.insert.manifest.buffer, r->u.insert.manifest.length); memcpy(r->manifest->manifestdata, r->u.insert.manifest.buffer, r->u.insert.manifest.length);
r->manifest->manifest_all_bytes = 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); int n = rhizome_manifest_parse(r->manifest);
switch (n) { switch (n) {
case 0: case 0:
if (!r->manifest->malformed) if (r->manifest->malformed) {
break; r->bundle_result = rhizome_bundle_result_sprintf(RHIZOME_BUNDLE_STATUS_INVALID, "Malformed manifest: %s", r->manifest->malformed);
// fall through } 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: case 1:
rhizome_manifest_free(r->manifest); r->bundle_result = rhizome_bundle_result_static(RHIZOME_BUNDLE_STATUS_INVALID, "Invalid manifest");
r->manifest = NULL; break;
r->bundle_status = RHIZOME_BUNDLE_STATUS_INVALID;
return http_request_rhizome_response(r, 422, "Malformed manifest", NULL); // Unprocessable Entity
default: default:
WHYF("rhizome_manifest_parse() returned %d", n); WHYF("rhizome_manifest_parse() returned %d", n);
// fall through // fall through
case -1: 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; break;
} }
rhizome_manifest *mout = NULL; switch (r->bundle_result.status) {
char message[150]; case RHIZOME_BUNDLE_STATUS_NEW:
enum rhizome_add_file_result result = rhizome_manifest_add_file(r->u.insert.appending, r->manifest, &mout, case RHIZOME_BUNDLE_STATUS_OLD:
r->u.insert.received_bundleid ? &r->bid: NULL, case RHIZOME_BUNDLE_STATUS_SAME:
r->u.insert.received_secret ? &r->u.insert.bundle_secret : NULL, case RHIZOME_BUNDLE_STATUS_DUPLICATE:
r->u.insert.received_author ? &r->u.insert.author: NULL, case RHIZOME_BUNDLE_STATUS_NO_ROOM:
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;
break; break;
case RHIZOME_ADD_FILE_INVALID: case RHIZOME_BUNDLE_STATUS_INCONSISTENT:
r->bundle_status = RHIZOME_BUNDLE_STATUS_INVALID; // TODO separate enum for CLI return codes case RHIZOME_BUNDLE_STATUS_INVALID:
return http_request_rhizome_response(r, 422, message, NULL); // Unprocessable Entity case RHIZOME_BUNDLE_STATUS_BUSY:
case RHIZOME_ADD_FILE_BUSY: case RHIZOME_BUNDLE_STATUS_FAKE:
r->bundle_status = RHIZOME_BUNDLE_STATUS_BUSY; // TODO separate enum for CLI return codes case RHIZOME_BUNDLE_STATUS_READONLY:
return http_request_rhizome_response(r, 423, message, NULL); // Locked case RHIZOME_BUNDLE_STATUS_ERROR:
case RHIZOME_ADD_FILE_REQUIRES_JOURNAL: return http_request_rhizome_response(r, 0, NULL);
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
} }
if (!result_valid)
FATALF("result = %d", result);
assert(mout != NULL); assert(mout != NULL);
if (mout != r->manifest) { if (mout != r->manifest) {
rhizome_manifest_free(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); 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) { 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)); 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 { } else {
// Note: r->manifest->filesize can be RHIZOME_SIZE_UNSET at this point, if the manifest did // 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); r->payload_status = rhizome_write_open_manifest(&r->u.insert.write, r->manifest);
if (r->payload_status == RHIZOME_PAYLOAD_STATUS_ERROR) { 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)); 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) { 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) { switch (r->payload_status) {
case RHIZOME_PAYLOAD_STATUS_NEW: case RHIZOME_PAYLOAD_STATUS_NEW:
if (rhizome_write_buffer(&r->u.insert.write, (unsigned char *)buf, len) == -1) 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; break;
case RHIZOME_PAYLOAD_STATUS_STORED: case RHIZOME_PAYLOAD_STATUS_STORED:
// TODO: calculate payload hash so it can be compared with stored payload // 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; status_valid = 1;
break; break;
case RHIZOME_PAYLOAD_STATUS_WRONG_SIZE: case RHIZOME_PAYLOAD_STATUS_WRONG_SIZE:
r->bundle_status = RHIZOME_BUNDLE_STATUS_INCONSISTENT; r->bundle_result = rhizome_bundle_result_sprintf(RHIZOME_BUNDLE_STATUS_INCONSISTENT,
{ "Payload size (%"PRIu64") contradicts manifest (filesize=%"PRIu64")",
strbuf msg = strbuf_alloca(200); r->u.insert.payload_size, r->manifest->filesize);
strbuf_sprintf(msg, "Payload size (%"PRIu64") contradicts manifest (filesize=%"PRIu64")", r->u.insert.payload_size, r->manifest->filesize); return http_request_rhizome_response(r, 0, "Inconsistent filesize");
return http_request_rhizome_response(r, 422, "Inconsistent filesize", strbuf_str(msg)); // Unprocessable Entity
}
case RHIZOME_PAYLOAD_STATUS_WRONG_HASH: case RHIZOME_PAYLOAD_STATUS_WRONG_HASH:
r->bundle_status = RHIZOME_BUNDLE_STATUS_INCONSISTENT; r->bundle_result = rhizome_bundle_result_sprintf(RHIZOME_BUNDLE_STATUS_INCONSISTENT,
{ "Payload hash (%s) contradicts manifest (filehash=%s)",
strbuf msg = strbuf_alloca(200); alloca_tohex_rhizome_filehash_t(r->u.insert.write.id),
strbuf_sprintf(msg, "Payload hash (%s) contradicts manifest (filehash=%s)", alloca_tohex_rhizome_filehash_t(r->manifest->filehash));
alloca_tohex_rhizome_filehash_t(r->u.insert.write.id), return http_request_rhizome_response(r, 0, "Inconsistent filehash");
alloca_tohex_rhizome_filehash_t(r->manifest->filehash));
return http_request_rhizome_response(r, 422, "Inconsistent filehash", strbuf_str(msg)); // Unprocessable Entity
}
case RHIZOME_PAYLOAD_STATUS_CRYPTO_FAIL: case RHIZOME_PAYLOAD_STATUS_CRYPTO_FAIL:
r->bundle_status = RHIZOME_BUNDLE_STATUS_READONLY; r->bundle_result = rhizome_bundle_result(RHIZOME_BUNDLE_STATUS_READONLY);
return http_request_rhizome_response(r, 419, "Missing bundle secret", NULL); // Authentication Timeout return http_request_rhizome_response(r, 0, NULL);
case RHIZOME_PAYLOAD_STATUS_TOO_BIG: case RHIZOME_PAYLOAD_STATUS_TOO_BIG:
r->bundle_status = RHIZOME_BUNDLE_STATUS_NO_ROOM; r->bundle_result = rhizome_bundle_result(RHIZOME_BUNDLE_STATUS_NO_ROOM);
return http_request_rhizome_response(r, 202, "Bundle too big", NULL); // Accepted return http_request_rhizome_response(r, 0, NULL);
case RHIZOME_PAYLOAD_STATUS_EVICTED: case RHIZOME_PAYLOAD_STATUS_EVICTED:
r->bundle_status = RHIZOME_BUNDLE_STATUS_NO_ROOM; r->bundle_result = rhizome_bundle_result(RHIZOME_BUNDLE_STATUS_NO_ROOM);
return http_request_rhizome_response(r, 202, "Bundle evicted", NULL); // Accepted return http_request_rhizome_response(r, 0, NULL);
case RHIZOME_PAYLOAD_STATUS_ERROR: 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) if (!status_valid)
FATALF("rhizome_finish_store() returned status = %d", r->payload_status); FATALF("rhizome_finish_store() returned status = %d", r->payload_status);
// Finalise the manifest and add it to the store. // Finalise the manifest and add it to the store.
const char *invalid_reason = rhizome_manifest_validate_reason(r->manifest); const char *invalid_reason = rhizome_manifest_validate_reason(r->manifest);
if (invalid_reason) { if (invalid_reason) {
r->bundle_status = RHIZOME_BUNDLE_STATUS_INVALID; r->bundle_result = rhizome_bundle_result_static(RHIZOME_BUNDLE_STATUS_INVALID, invalid_reason);
return http_request_rhizome_response(r, 422, invalid_reason, NULL); // Unprocessable Entity return http_request_rhizome_response(r, 0, NULL);
} }
if (r->manifest->malformed) { if (r->manifest->malformed) {
r->bundle_status = RHIZOME_BUNDLE_STATUS_INVALID; r->bundle_result = rhizome_bundle_result_static(RHIZOME_BUNDLE_STATUS_INVALID, r->manifest->malformed);
return http_request_rhizome_response(r, 422, r->manifest->malformed, NULL); // Unprocessable Entity return http_request_rhizome_response(r, 0, NULL);
} }
if (!r->manifest->haveSecret) { if (!r->manifest->haveSecret) {
r->bundle_status = RHIZOME_BUNDLE_STATUS_READONLY; r->bundle_result = rhizome_bundle_result_static(RHIZOME_BUNDLE_STATUS_READONLY, "Missing bundle secret");
return http_request_rhizome_response(r, 419, "Missing bundle secret", NULL); // Authentication Timeout return http_request_rhizome_response(r, 0, NULL);
} }
rhizome_manifest *mout = 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; int result = 500;
const char *message = NULL; DEBUGF(rhizome, "r->bundle_result = %s", alloca_rhizome_bundle_result(r->bundle_result));
DEBUGF(rhizome, "r->bundle_status=%d", r->bundle_status); switch (r->bundle_result.status) {
switch (r->bundle_status) {
case RHIZOME_BUNDLE_STATUS_NEW: case RHIZOME_BUNDLE_STATUS_NEW:
if (mout && mout != r->manifest) if (mout && mout != r->manifest)
rhizome_manifest_free(mout); rhizome_manifest_free(mout);
@ -754,7 +740,7 @@ static int restful_rhizome_insert_end(struct http_request *hr)
result = 201; result = 201;
break; break;
case RHIZOME_BUNDLE_STATUS_ERROR: case RHIZOME_BUNDLE_STATUS_ERROR:
message = "Error in manifest finalise"; r->bundle_result.message = "Error in manifest finalise";
// fall through // fall through
case RHIZOME_BUNDLE_STATUS_INVALID: case RHIZOME_BUNDLE_STATUS_INVALID:
case RHIZOME_BUNDLE_STATUS_FAKE: 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(mout);
rhizome_manifest_free(r->manifest); rhizome_manifest_free(r->manifest);
r->manifest = NULL; r->manifest = NULL;
return http_request_rhizome_response(r, 0, message, NULL); return http_request_rhizome_response(r, 0, NULL);
} }
if (result == 500) 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); rhizome_authenticate_author(r->manifest);
http_request_response_static(&r->http, result, "rhizome-manifest/text", http_request_response_static(&r->http, result, "rhizome-manifest/text",
(const char *)r->manifest->manifestdata, r->manifest->manifest_all_bytes (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) if (r->http.verb != HTTP_VERB_GET)
return 405; return 405;
if ((r->manifest = rhizome_new_manifest()) == NULL) 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
r->bundle_status = rhizome_retrieve_manifest(&bid, r->manifest); r->bundle_result.status = rhizome_retrieve_manifest(&bid, r->manifest);
switch(r->bundle_status){ switch(r->bundle_result.status){
case RHIZOME_BUNDLE_STATUS_SAME: case RHIZOME_BUNDLE_STATUS_SAME:
rhizome_authenticate_author(r->manifest); rhizome_authenticate_author(r->manifest);
break; break;
@ -822,13 +808,13 @@ static int restful_rhizome_(httpd_request *r, const char *remainder)
break; break;
case RHIZOME_BUNDLE_STATUS_BUSY: case RHIZOME_BUNDLE_STATUS_BUSY:
rhizome_manifest_free(r->manifest); 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: case RHIZOME_BUNDLE_STATUS_ERROR:
rhizome_manifest_free(r->manifest); rhizome_manifest_free(r->manifest);
r->manifest = NULL; 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 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); return handler(r, remainder);
} }
@ -838,7 +824,7 @@ static int restful_rhizome_bid_rhm(httpd_request *r, const char *remainder)
if (*remainder) if (*remainder)
return 404; return 404;
if (r->manifest == NULL) 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", http_request_response_static(&r->http, 200, "rhizome-manifest/text",
(const char *)r->manifest->manifestdata, r->manifest->manifest_all_bytes (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) if (*remainder)
return 404; return 404;
if (r->manifest == NULL) 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) { if (r->manifest->filesize == 0) {
http_request_response_static(&r->http, 200, CONTENT_TYPE_BLOB, "", 0); http_request_response_static(&r->http, 200, CONTENT_TYPE_BLOB, "", 0);
return 1; return 1;
@ -867,7 +853,7 @@ static int restful_rhizome_bid_decrypted_bin(httpd_request *r, const char *remai
if (*remainder) if (*remainder)
return 404; return 404;
if (r->manifest == NULL) 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) { if (r->manifest->filesize == 0) {
// TODO use Content Type from manifest (once it is implemented) // TODO use Content Type from manifest (once it is implemented)
http_request_response_static(&r->http, 200, CONTENT_TYPE_BLOB, "", 0); 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)) { if (r->u.read_state.length == RHIZOME_SIZE_UNSET && rhizome_read(&r->u.read_state, NULL, 0)) {
rhizome_read_close(&r->u.read_state); 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); assert(r->u.read_state.length != RHIZOME_SIZE_UNSET);
int ret = http_response_init_content_range(r, r->u.read_state.length); 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: case RHIZOME_PAYLOAD_STATUS_STORED:
return rhizome_response_content_init_read_state(r); return rhizome_response_content_init_read_state(r);
case RHIZOME_PAYLOAD_STATUS_NEW: 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_ERROR:
case RHIZOME_PAYLOAD_STATUS_WRONG_SIZE: case RHIZOME_PAYLOAD_STATUS_WRONG_SIZE:
case RHIZOME_PAYLOAD_STATUS_WRONG_HASH: case RHIZOME_PAYLOAD_STATUS_WRONG_HASH:
case RHIZOME_PAYLOAD_STATUS_CRYPTO_FAIL: case RHIZOME_PAYLOAD_STATUS_CRYPTO_FAIL:
case RHIZOME_PAYLOAD_STATUS_TOO_BIG: case RHIZOME_PAYLOAD_STATUS_TOO_BIG:
case RHIZOME_PAYLOAD_STATUS_EVICTED: 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); 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: case RHIZOME_PAYLOAD_STATUS_STORED:
return rhizome_response_content_init_read_state(r); return rhizome_response_content_init_read_state(r);
case RHIZOME_PAYLOAD_STATUS_NEW: 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: 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_ERROR:
case RHIZOME_PAYLOAD_STATUS_WRONG_SIZE: case RHIZOME_PAYLOAD_STATUS_WRONG_SIZE:
case RHIZOME_PAYLOAD_STATUS_WRONG_HASH: case RHIZOME_PAYLOAD_STATUS_WRONG_HASH:
case RHIZOME_PAYLOAD_STATUS_TOO_BIG: case RHIZOME_PAYLOAD_STATUS_TOO_BIG:
case RHIZOME_PAYLOAD_STATUS_EVICTED: 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); 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) static void render_manifest_headers(struct http_request *hr, strbuf sb)
{ {
httpd_request *r = (httpd_request *) hr; httpd_request *r = (httpd_request *) hr;
const char *status_message; switch (r->bundle_result.status) {
if ((status_message = rhizome_bundle_status_message(r->bundle_status))) { case RHIZOME_BUNDLE_STATUS_NEW:
strbuf_sprintf(sb, "Serval-Rhizome-Result-Bundle-Status-Code: %d\r\n", r->bundle_status); case RHIZOME_BUNDLE_STATUS_SAME:
strbuf_sprintf(sb, "Serval-Rhizome-Result-Bundle-Status-Message: %s\r\n", status_message); 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-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; rhizome_manifest *m = r->manifest;
if (m) { if (m) {

View File

@ -808,7 +808,7 @@ setup_AddUpdateNoAuthorWithSecret() {
setup_AddUpdateNoAuthor setup_AddUpdateNoAuthor
} }
test_AddUpdateNoAuthorWithSecret() { 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 tfw_cat --stderr
# Rhizome store contents have new payload, but it has lost its author (no BK # Rhizome store contents have new payload, but it has lost its author (no BK
# field any more). # field any more).
@ -1083,7 +1083,7 @@ setup_JournalAddCreate() {
test_JournalAddCreate() { test_JournalAddCreate() {
# TODO: servald should return a status code reserved for this case, instead # TODO: servald should return a status code reserved for this case, instead
# of the 4 error (which means "invalid manifest") # 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 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-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-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-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" doc_RhizomePayloadDecryptedNonexistManifest="HTTP RESTful fetch Rhizome decrypted payload for non-existent manifest"