diff --git a/rhizome.h b/rhizome.h index 35fbf1ad..5489a73a 100644 --- a/rhizome.h +++ b/rhizome.h @@ -242,6 +242,18 @@ typedef struct rhizome_manifest */ bool_t has_author; + /* Local authorship. Useful for dividing bundle lists between "sent" and + * "inbox" views. + */ + enum rhizome_bundle_authorship { + AUTHOR_NOT_CHECKED = 0, + AUTHOR_ERROR, // author check failed, don't try again + AUTHOR_UNKNOWN, // author is not a local identity + AUTHOR_LOCAL, // author is in keyring (unlocked) but not verified + AUTHOR_IMPOSTOR, // author is a local identity but fails verification + AUTHOR_AUTHENTIC // a local identity is the verified author + } authorship; + /* time-to-live in hops of this manifest. */ int ttl; @@ -432,6 +444,7 @@ int rhizome_clean_payload(const char *fileidhex); int rhizome_store_file(rhizome_manifest *m,const unsigned char *key); int rhizome_bundle_import_files(rhizome_manifest *m, const char *manifest_path, const char *filepath); int rhizome_fill_manifest(rhizome_manifest *m, const char *filepath, const sid_t *authorSidp, rhizome_bk_t *bsk); +int rhizome_lookup_author(rhizome_manifest *m); int rhizome_manifest_verify(rhizome_manifest *m); int rhizome_manifest_check_sanity(rhizome_manifest *m_in); diff --git a/rhizome_bundle.c b/rhizome_bundle.c index 6d7816ea..477dbf9b 100644 --- a/rhizome_bundle.c +++ b/rhizome_bundle.c @@ -735,22 +735,18 @@ void _rhizome_manifest_free(struct __sourceloc __whence, rhizome_manifest *m) if (!m) return; int mid=m->manifest_record_number; - if (m!=&manifests[mid]) { - WHYF("%s(): asked to free manifest %p, which claims to be manifest slot #%d (%p), but isn't", - __FUNCTION__, m, mid, &manifests[mid] + if (m!=&manifests[mid]) + FATALF("%s(): asked to free manifest %p, which claims to be manifest slot #%d (%p), but isn't", + __FUNCTION__, m, mid, &manifests[mid] ); - exit(-1); - } - if (manifest_free[mid]) { - WHYF("%s(): asked to free manifest slot #%d (%p), which was already freed at %s:%d:%s()", - __FUNCTION__, mid, m, - manifest_free_whence[mid].file, - manifest_free_whence[mid].line, - manifest_free_whence[mid].function - ); - exit(-1); - } + if (manifest_free[mid]) + FATALF("%s(): asked to free manifest slot #%d (%p), which was already freed at %s:%d:%s()", + __FUNCTION__, mid, m, + manifest_free_whence[mid].file, + manifest_free_whence[mid].line, + manifest_free_whence[mid].function + ); /* Free variable and signature blocks. */ unsigned i; @@ -984,3 +980,33 @@ int rhizome_fill_manifest(rhizome_manifest *m, const char *filepath, const sid_t return 0; } + +int rhizome_lookup_author(rhizome_manifest *m) +{ + switch (m->authorship) { + case AUTHOR_NOT_CHECKED: + if (m->has_author) { + int cn = 0, in = 0, kp = 0; + if (keyring_find_sid(keyring, &cn, &in, &kp, &m->author)) { + m->authorship = AUTHOR_LOCAL; + return 1; + } + } + if (m->has_sender) { + int cn = 0, in = 0, kp = 0; + if (keyring_find_sid(keyring, &cn, &in, &kp, &m->sender)) { + m->author = m->sender; + m->authorship = AUTHOR_LOCAL; + return 1; + } + } + case AUTHOR_ERROR: + case AUTHOR_UNKNOWN: + case AUTHOR_IMPOSTOR: + return 0; + case AUTHOR_LOCAL: + case AUTHOR_AUTHENTIC: + return 1; + } + FATAL("m->authorship = %d", m->authorship); +} diff --git a/rhizome_database.c b/rhizome_database.c index f3024925..b9ef4f79 100644 --- a/rhizome_database.c +++ b/rhizome_database.c @@ -1408,63 +1408,158 @@ rollback: return -1; } -int rhizome_list_manifests(struct cli_context *context, const char *service, const char *name, - const char *sender_hex, const char *recipient_hex, +struct rhizome_list_cursor { + // Query parameters that narrow the set of listed bundles. + const char *service; + const char *name; + sid_t sender; + sid_t recipient; + unsigned limit; + unsigned offset; + // Set by calling the next() function. + rhizome_manifest *manifest; + size_t row; + int64_t rowid; + // Private state. + sqlite3_stmt *_statement; +}; + +/* Fill in the parameters in the cursor struct prior to calling this function. + * + * @author Andrew Bettison + */ +static int rhizome_list_open(sqlite_retry_state *retry, struct rhizome_list_cursor *cursor) +{ + IN(); + strbuf b = strbuf_alloca(1024); + strbuf_sprintf(b, "SELECT id, manifest, version, inserttime, author, rowid FROM manifests WHERE 1=1"); + if (cursor->service) + strbuf_puts(b, " AND service = @service"); + if (cursor->name) + strbuf_puts(b, " AND name like @name"); + if (!is_sid_t_any(cursor->sender)) + strbuf_puts(b, " AND sender = @sender"); + if (!is_sid_t_any(cursor->recipient)) + strbuf_puts(b, " AND recipient = @recipient"); + strbuf_puts(b, " ORDER BY inserttime DESC OFFSET @offset"); + if (strbuf_overrun(b)) + RETURN(WHYF("SQL command too long: %s", strbuf_str(b))); + cursor->_statement = sqlite_prepare_bind(retry, strbuf_str(b)); + if (!statement) + RETURN(-1); + int ret = 0; + if (cursor->service && *cursor->service && sqlite_bind(retry, statement, NAMED|STATIC_TEXT, "service", cursor->service, END) == -1) + goto failure; + if (cursor->name && *cursor->name && sqlite_bind(retry, statement, NAMED|STATIC_TEXT, "name", cursor->name, END) == -1) + goto failure; + if (!is_sid_t_any(cursor->sender) && sqlite_bind(retry, statement, NAMED|SID_T, "sender", &cursor->sender, END) == -1) + goto failure; + if (!is_sid_t_any(cursor->recipient) && sqlite_bind(retry, statement, NAMED|SID_T, "recipient", &cursor->recipient, END) == -1) + goto failure; + if (sqlite_bind(retry, statement, NAMED|INT, "offset", cursor->offset + cursor->_row, END) == -1) + goto cleanup; + cursor->manifest = NULL; + RETURN(0); + OUT(); +failure: + sqlite3_finalize(cursor->_statement); + cursor->_statement = NULL; + RETURN(-1); + OUT(); +} + +static int rhizome_list_next(sqlite_retry_state *retry, struct rhizome_list_cursor *cursor) +{ + IN(); + if (cursor->_statement == NULL && rhizome_list_open(retry, cursor) == -1) + RETURN(-1); + if (cursor->limit && cursor->_row >= cursor->limit) + RETURN(NULL); + while (sqlite_step_retry(retry, cursor->_statement) == SQLITE_ROW) { + if (cursor->manifest) { + rhizome_manifest_free(cursor->manifest); + cursor->manifest = NULL; + } + assert(sqlite3_column_count(cursor->_statement) == 6); + assert(sqlite3_column_type(cursor->_statement, 0) == SQLITE_TEXT); + assert(sqlite3_column_type(cursor->_statement, 1) == SQLITE_BLOB); + assert(sqlite3_column_type(cursor->_statement, 2) == SQLITE_INTEGER); + assert(sqlite3_column_type(cursor->_statement, 3) == SQLITE_INTEGER); + assert(sqlite3_column_type(cursor->_statement, 4) == SQLITE_TEXT || sqlite3_column_type(cursor->_statement, 4) == SQLITE_NULL); + assert(sqlite3_column_type(cursor->_statement, 5) == SQLITE_INTEGER); + rhizome_manifest *m = cursor->manifest = rhizome_new_manifest(); + if (m == NULL) + RETURN(-1); + const char *q_manifestid = (const char *) sqlite3_column_text(cursor->_statement, 0); + const char *manifestblob = (char *) sqlite3_column_blob(cursor->_statement, 1); + size_t manifestblobsize = sqlite3_column_bytes(cursor->_statement, 1); // must call after sqlite3_column_blob() + int64_t q_version = sqlite3_column_int64(cursor->_statement, 2); + int64_t q_inserttime = sqlite3_column_int64(cursor->_statement, 3); + const char *q_author = (const char *) sqlite3_column_text(cursor->_statement, 4); + int64_t rowid = sqlite3_column_int64(cursor->_statement, 5); + if (rhizome_read_manifest_file(m, manifestblob, manifestblobsize) == -1) { + WHYF("MANIFESTS row id=%s has invalid manifest blob -- skipped", q_manifestid); + continue; + } + const sid_t *author = NULL; + if (q_author) { + author = alloca(sizeof *author); + if (str_to_sid_t(author, q_author) == -1) { + WHYF("MANIFESTS row id=%s has invalid author column %s -- skipped", q_manifestid, alloca_str_toprint(q_author)); + continue; + } + } + if (rhizome_fill_manifest(m, NULL, author, NULL) == -1) { + WHYF("MANIFESTS row id=%s has invalid manifest -- skipped", q_manifestid); + continue; + } + if (m->version != q_version) { + WHYF("MANIFESTS row id=%s version=%"PRId64" does not match manifest blob version=%"PRId64" -- skipped", + q_manifestid, q_version, m->version); + continue; + } + if (cursor->service && !(m->service && strcasecmp(cursor->service, m->service) == 0)) + continue; + if (!is_sid_t_any(cursor->sender) && !(m->has_sender && cmp_sid_t(&cursor->sender, &m->sender) == 0)) + continue; + if (!is_sid_t_any(cursor->recipient) && !(m->has_recipient && cmp_sid_t(&cursor->recipient, &m->recipient) == 0)) + continue; + rhizome_lookup_author(m); + // Don't do rhizome_verify_author(m); too CPU expensive for a listing. Save that for when + // the bundle is extracted or exported. + ++cursor->_row; + RETURN(1); + } + RETURN(0); + OUT(); +} + +static void rhizome_list_release(struct rhizome_list_cursor *) +{ + if (cursor->manifest) { + rhizome_manifest_free(cursor->manifest); + cursor->manifest = NULL; + } + if (cursor->_statement) { + sqlite3_finalize(cursor->_statement); + cursor->_statement = NULL; + } +} + +int rhizome_list_manifests(struct cli_context *context, const char *service, const char *name, + const char *sender_hex, const char *recipient_hex, int limit, int offset, char count_rows) { IN(); - sid_t sender; - sid_t recipient; - strbuf b = strbuf_alloca(1024); - strbuf_sprintf(b, "SELECT id, manifest, version, inserttime, author, rowid FROM manifests WHERE 1=1"); - - if (service && *service) - strbuf_sprintf(b, " AND service = ?1"); - if (name && *name) - strbuf_sprintf(b, " AND name like ?2"); - if (sender_hex && *sender_hex) { - if (str_to_sid_t(&sender, sender_hex) == -1) - RETURN(WHYF("Invalid sender SID: %s", sender_hex)); - strbuf_sprintf(b, " AND sender = ?3"); - } - if (recipient_hex && *recipient_hex) { - if (str_to_sid_t(&recipient, recipient_hex) == -1) - RETURN(WHYF("Invalid recipient SID: %s", recipient_hex)); - strbuf_sprintf(b, " AND recipient = ?4"); - } - - strbuf_sprintf(b, " ORDER BY inserttime DESC"); - - if (offset) - strbuf_sprintf(b, " OFFSET %u", offset); - - if (strbuf_overrun(b)) - RETURN(WHYF("SQL command too long: %s", strbuf_str(b))); - + struct rhizome_list_cursor cursor; + bzero(&cursor, sizeof cursor); + if (sender_hex && *sender_hex && str_to_sid_t(&cursor->sender, sender_hex) == -1) + RETURN(WHYF("Invalid sender SID: %s", sender_hex)); + if (recipient_hex && *recipient_hex && str_to_sid_t(&cursor->recipient, recipient_hex) == -1) + RETURN(WHYF("Invalid recipient SID: %s", recipient_hex)); sqlite_retry_state retry = SQLITE_RETRY_STATE_DEFAULT; - sqlite3_stmt *statement = sqlite_prepare(&retry, strbuf_str(b)); - if (!statement) + if (rhizome_list_open(&retry, &cursor) == -1) RETURN(-1); - - int ret = 0; - - if (service && *service) - ret = sqlite3_bind_text(statement, 1, service, -1, SQLITE_STATIC); - if (ret==SQLITE_OK && name && *name) - ret = sqlite3_bind_text(statement, 2, name, -1, SQLITE_STATIC); - if (ret==SQLITE_OK && sender_hex && *sender_hex) - ret = sqlite3_bind_text(statement, 3, sender_hex, -1, SQLITE_STATIC); - if (ret==SQLITE_OK && recipient_hex && *recipient_hex) - ret = sqlite3_bind_text(statement, 4, recipient_hex, -1, SQLITE_STATIC); - - if (ret!=SQLITE_OK){ - ret = WHYF("Failed to bind parameters: %s", sqlite3_errmsg(rhizome_db)); - goto cleanup; - } - - ret=0; - size_t rows = 0; - const char *names[]={ "_id", "service", @@ -1481,93 +1576,21 @@ int rhizome_list_manifests(struct cli_context *context, const char *service, con "name" }; cli_columns(context, 13, names); - - while (sqlite_step_retry(&retry, statement) == SQLITE_ROW) { - ++rows; - if (limit>0 && rows>limit) - break; - if (!( sqlite3_column_count(statement) == 6 - && sqlite3_column_type(statement, 0) == SQLITE_TEXT - && sqlite3_column_type(statement, 1) == SQLITE_BLOB - && sqlite3_column_type(statement, 2) == SQLITE_INTEGER - && sqlite3_column_type(statement, 3) == SQLITE_INTEGER - && ( sqlite3_column_type(statement, 4) == SQLITE_TEXT - || sqlite3_column_type(statement, 4) == SQLITE_NULL - ) - )) { - ret = WHY("Incorrect statement column"); - break; - } - rhizome_manifest *m = rhizome_new_manifest(); - if (m == NULL) { - ret = WHY("Out of manifests"); - break; - } - const char *q_manifestid = (const char *) sqlite3_column_text(statement, 0); - const char *manifestblob = (char *) sqlite3_column_blob(statement, 1); - size_t manifestblobsize = sqlite3_column_bytes(statement, 1); // must call after sqlite3_column_blob() - int64_t q_version = sqlite3_column_int64(statement, 2); - int64_t q_inserttime = sqlite3_column_int64(statement, 3); - const char *q_author = (const char *) sqlite3_column_text(statement, 4); - int64_t rowid = sqlite3_column_int64(statement, 5); - - if (rhizome_read_manifest_file(m, manifestblob, manifestblobsize) == -1) { - WARNF("MANIFESTS row id=%s has invalid manifest blob -- skipped", q_manifestid); - } else { - - if (m->version != q_version) - WARNF("MANIFESTS row id=%s version=%"PRId64" does not match manifest blob.version=%"PRId64, - q_manifestid, q_version, m->version); - int match = 1; - - if (service && service[0] && !(m->service && strcasecmp(m->service, service) == 0)) - match = 0; - if (match && sender_hex && sender_hex[0]) { - if (!(m->has_sender && cmp_sid_t(&sender, &m->sender) == 0)) - match = 0; - } - if (match && recipient_hex && recipient_hex[0]) { - if (!(m->has_recipient && cmp_sid_t(&recipient, &m->recipient) == 0)) - match = 0; - } - - if (match) { - int from_here = 0; - - if (q_author) { - sid_t author; - if (str_to_sid_t(&author, q_author) == -1) { - WARNF("MANIFESTS row id=%s has invalid author=%s -- ignored", q_manifestid, alloca_str_toprint(q_author)); - } else { - rhizome_manifest_set_author(m, &author); - int cn = 0, in = 0, kp = 0; - from_here = keyring_find_sid(keyring, &cn, &in, &kp, &m->author); - } - } - if (!from_here && m->has_sender) { - if (config.debug.rhizome) - DEBUGF("blob.sender=%s", alloca_tohex_sid_t(m->sender)); - int cn = 0, in = 0, kp = 0; - from_here = keyring_find_sid(keyring, &cn, &in, &kp, &m->sender); - } - - cli_put_long(context, rowid, ":"); - cli_put_string(context, m->service, ":"); - cli_put_hexvalue(context, m->cryptoSignPublic.binary, sizeof m->cryptoSignPublic.binary, ":"); - cli_put_long(context, m->version, ":"); - cli_put_long(context, m->has_date ? m->date : 0, ":"); - cli_put_long(context, q_inserttime, ":"); - cli_put_hexvalue(context, m->has_author ? m->author.binary : NULL, sizeof m->author.binary, ":"); - cli_put_long(context, from_here, ":"); - assert(m->filesize != RHIZOME_SIZE_UNSET); - cli_put_long(context, m->filesize, ":"); - cli_put_hexvalue(context, m->filesize ? m->filehash.binary : NULL, sizeof m->filehash.binary, ":"); - cli_put_hexvalue(context, m->has_sender ? m->sender.binary : NULL, sizeof m->sender.binary, ":"); - cli_put_hexvalue(context, m->has_recipient ? m->recipient.binary : NULL, sizeof m->recipient.binary, ":"); - cli_put_string(context, m->name, "\n"); - } - } - if (m) rhizome_manifest_free(m); + while (rhizome_list_next(&retry, &cursor) == 1) { + cli_put_long(context, cursor.rowid, ":"); + cli_put_string(context, m->service, ":"); + cli_put_hexvalue(context, m->cryptoSignPublic.binary, sizeof m->cryptoSignPublic.binary, ":"); + cli_put_long(context, m->version, ":"); + cli_put_long(context, m->has_date ? m->date : 0, ":"); + cli_put_long(context, q_inserttime, ":"); + cli_put_hexvalue(context, m->has_author ? m->author.binary : NULL, sizeof m->author.binary, ":"); + cli_put_long(context, from_here, ":"); + assert(m->filesize != RHIZOME_SIZE_UNSET); + cli_put_long(context, m->filesize, ":"); + cli_put_hexvalue(context, m->filesize ? m->filehash.binary : NULL, sizeof m->filehash.binary, ":"); + cli_put_hexvalue(context, m->has_sender ? m->sender.binary : NULL, sizeof m->sender.binary, ":"); + cli_put_hexvalue(context, m->has_recipient ? m->recipient.binary : NULL, sizeof m->recipient.binary, ":"); + cli_put_string(context, m->name, "\n"); } if (ret==0 && count_rows){