summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2019-11-15 09:39:31 +0100
committerChristian Grothoff <christian@grothoff.org>2019-11-15 09:39:31 +0100
commit9badf80eb4228a9c009839a4856710127efe8601 (patch)
treeec62e28f2ab3b90ca374537a9b2730b1fecdbb93 /src
parentd0e22221b9fd3627d63e484b8957337d2638a871 (diff)
downloadsync-9badf80eb4228a9c009839a4856710127efe8601.tar.gz
sync-9badf80eb4228a9c009839a4856710127efe8601.tar.bz2
sync-9badf80eb4228a9c009839a4856710127efe8601.zip
implement GET logic
Diffstat (limited to 'src')
-rw-r--r--src/include/sync_database_plugin.h19
-rw-r--r--src/include/sync_service.h2
-rw-r--r--src/sync/sync-httpd.c27
-rw-r--r--src/sync/sync-httpd_backup.c170
-rw-r--r--src/sync/sync-httpd_backup.h10
-rw-r--r--src/syncdb/plugin_syncdb_postgres.c108
6 files changed, 306 insertions, 30 deletions
diff --git a/src/include/sync_database_plugin.h b/src/include/sync_database_plugin.h
index cdc65e9..d5759ee 100644
--- a/src/include/sync_database_plugin.h
+++ b/src/include/sync_database_plugin.h
@@ -38,7 +38,7 @@ enum SYNC_DB_QueryStatus
SYNC_DB_OLD_BACKUP_MISSMATCH = -4,
/**
- * Account is unpaid.
+ * Account is unpaid / does not exist.
*/
SYNC_DB_PAYMENT_REQUIRED = -3,
@@ -153,12 +153,28 @@ struct SYNC_DatabasePlugin
size_t backup_size,
const void *backup);
+
+ /**
+ * Lookup an account and associated backup meta data.
+ *
+ * @param cls closure
+ * @param account_pub account to store @a backup under
+ * @param backup_hash[OUT] set to hash of @a backup
+ * @return transaction status
+ */
+ enum SYNC_DB_QueryStatus
+ (*lookup_account_TR)(void *cls,
+ const struct SYNC_AccountPublicKeyP *account_pub,
+ struct GNUNET_HashCode *backup_hash);
+
+
/**
* Obtain backup.
*
* @param cls closure
* @param account_pub account to store @a backup under
* @param account_sig[OUT] set to signature affirming storage request
+ * @param prev_hash[OUT] set to hash of the previous @a backup (all zeros if none)
* @param backup_hash[OUT] set to hash of @a backup
* @param backup_size[OUT] set to number of bytes in @a backup
* @param backup[OUT] set to raw data to backup, caller MUST FREE
@@ -167,6 +183,7 @@ struct SYNC_DatabasePlugin
(*lookup_backup_TR)(void *cls,
const struct SYNC_AccountPublicKeyP *account_pub,
struct SYNC_AccountSignatureP *account_sig,
+ struct GNUNET_HashCode *prev_hash,
struct GNUNET_HashCode *backup_hash,
size_t *backup_size,
void **backup);
diff --git a/src/include/sync_service.h b/src/include/sync_service.h
index e99d0c3..20bbe67 100644
--- a/src/include/sync_service.h
+++ b/src/include/sync_service.h
@@ -46,7 +46,7 @@ struct SYNC_AccountPublicKeyP
/**
* We use EdDSA.
*/
- struct GNUNET_CRYPTO_EddsaPrivateKey eddsa_priv;
+ struct GNUNET_CRYPTO_EddsaPublicKey eddsa_pub;
};
diff --git a/src/sync/sync-httpd.c b/src/sync/sync-httpd.c
index d7f6eb5..98a2b24 100644
--- a/src/sync/sync-httpd.c
+++ b/src/sync/sync-httpd.c
@@ -175,7 +175,10 @@ url_handler (void *cls,
struct TM_HandlerContext *hc;
struct GNUNET_AsyncScopeId aid;
const char *correlation_id = NULL;
+ struct SYNC_AccountPublicKeyP account_pub;
+ (void) cls;
+ (void) version;
hc = *con_cls;
if (NULL == hc)
@@ -197,7 +200,6 @@ url_handler (void *cls,
{
aid = hc->async_scope_id;
}
-
GNUNET_SCHEDULER_begin_async_scope (&aid);
if (NULL != correlation_id)
@@ -211,22 +213,25 @@ url_handler (void *cls,
"Handling request (%s) for URL '%s'\n",
method,
url);
- if (0 == strncmp (url,
- "/backup/",
- strlen ("/backup/")))
+
+ if (GNUNET_OK ==
+ GNUNET_CRYPTO_eddsa_public_key_from_string (url,
+ strlen (url),
+ &account_pub.eddsa_pub))
{
- // return handle_policy (...);
- if (0 == strcmp (method, MHD_HTTP_METHOD_GET))
+ if (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_GET))
{
return sync_handler_backup_get (connection,
- url,
+ &account_pub,
con_cls);
}
- if (0 == strcmp (method, MHD_HTTP_METHOD_POST))
+ if (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_POST))
{
return sync_handler_backup_post (connection,
con_cls,
- url,
+ &account_pub,
upload_data,
upload_data_size);
}
@@ -238,8 +243,8 @@ url_handler (void *cls,
if ( (0 == strcmp (url,
rh->url)) &&
( (NULL == rh->method) ||
- (0 == strcmp (method,
- rh->method)) ) )
+ (0 == strcasecmp (method,
+ rh->method)) ) )
{
int ret;
diff --git a/src/sync/sync-httpd_backup.c b/src/sync/sync-httpd_backup.c
index 86ba955..61d9151 100644
--- a/src/sync/sync-httpd_backup.c
+++ b/src/sync/sync-httpd_backup.c
@@ -21,26 +21,182 @@
#include "platform.h"
#include "sync-httpd.h"
#include <gnunet/gnunet_util_lib.h>
+#include "sync-httpd_backup.h"
+#include "sync-httpd_responses.h"
/**
* @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @param account public key of the account the request is for
+ * @param[in,out] con_cls the connection's closure (can be updated)
* @return MHD result code
*/
int
sync_handler_backup_get (struct MHD_Connection *connection,
- const char *url,
+ const struct SYNC_AccountPublicKeyP *account,
void **con_cls)
{
- return MHD_NO;
+ struct SYNC_AccountSignatureP account_sig;
+ struct GNUNET_HashCode backup_hash;
+ struct GNUNET_HashCode prev_hash;
+ size_t backup_size;
+ void *backup;
+ enum SYNC_DB_QueryStatus qs;
+ struct MHD_Response *resp;
+ const char *inm;
+ struct GNUNET_HashCode inm_h;
+ int ret;
+
+ inm = MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+ MHD_HTTP_HEADER_IF_NONE_MATCH);
+ qs = db->lookup_account_TR (db->cls,
+ account,
+ &backup_hash);
+ switch (qs)
+ {
+ case SYNC_DB_OLD_BACKUP_MISSMATCH:
+ GNUNET_break (0);
+ return SH_RESPONSE_reply_internal_error (connection,
+ TALER_EC_INTERNAL_INVARIANT_FAILURE,
+ "unexpected return status (backup missmatch)");
+ case SYNC_DB_PAYMENT_REQUIRED:
+ return SH_RESPONSE_reply_not_found (connection,
+ TALER_EC_SYNC_ACCOUNT_UNKNOWN,
+ "account");
+ case SYNC_DB_HARD_ERROR:
+ GNUNET_break (0);
+ return SH_RESPONSE_reply_internal_error (connection,
+ TALER_EC_SYNC_DB_FETCH_ERROR,
+ "hard database failure");
+ case SYNC_DB_SOFT_ERROR:
+ GNUNET_break (0);
+ return SH_RESPONSE_reply_internal_error (connection,
+ TALER_EC_SYNC_DB_FETCH_ERROR,
+ "soft database failure");
+ case SYNC_DB_NO_RESULTS:
+ resp = MHD_create_response_from_buffer (0,
+ NULL,
+ MHD_RESPMEM_PERSISTENT);
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_NO_CONTENT,
+ resp);
+ MHD_destroy_response (resp);
+ return ret;
+ case SYNC_DB_ONE_RESULT:
+ if (NULL != inm)
+ {
+ if (GNUNET_OK !=
+ GNUNET_STRINGS_string_to_data (inm,
+ strlen (inm),
+ &inm_h,
+ sizeof (inm_h)))
+ {
+ GNUNET_break_op (0);
+ return SH_RESPONSE_reply_bad_request (connection,
+ TALER_EC_SYNC_BAD_ETAG,
+ "Etag is not a base32-encoded SHA-512 hash");
+ }
+ if (0 == GNUNET_memcmp (&inm_h,
+ &backup_hash))
+ {
+ resp = MHD_create_response_from_buffer (0,
+ NULL,
+ MHD_RESPMEM_PERSISTENT);
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_NOT_MODIFIED,
+ resp);
+ MHD_destroy_response (resp);
+ return ret;
+ }
+ }
+ /* We have a result, should fetch and return it! */
+ break;
+ }
+
+ qs = db->lookup_backup_TR (db->cls,
+ account,
+ &account_sig,
+ &prev_hash,
+ &backup_hash,
+ &backup_size,
+ &backup);
+ switch (qs)
+ {
+ case SYNC_DB_OLD_BACKUP_MISSMATCH:
+ GNUNET_break (0);
+ return SH_RESPONSE_reply_internal_error (connection,
+ TALER_EC_INTERNAL_INVARIANT_FAILURE,
+ "unexpected return status (backup missmatch)");
+ case SYNC_DB_PAYMENT_REQUIRED:
+ GNUNET_break (0);
+ return SH_RESPONSE_reply_internal_error (connection,
+ TALER_EC_INTERNAL_INVARIANT_FAILURE,
+ "unexpected return status (payment required)");
+ case SYNC_DB_HARD_ERROR:
+ GNUNET_break (0);
+ return SH_RESPONSE_reply_internal_error (connection,
+ TALER_EC_SYNC_DB_FETCH_ERROR,
+ "hard database failure");
+ case SYNC_DB_SOFT_ERROR:
+ GNUNET_break (0);
+ return SH_RESPONSE_reply_internal_error (connection,
+ TALER_EC_SYNC_DB_FETCH_ERROR,
+ "soft database failure");
+ case SYNC_DB_NO_RESULTS:
+ GNUNET_break (0);
+ /* Note: can theoretically happen due to non-transactional nature if
+ the backup expired / was gc'ed JUST between the two SQL calls.
+ But too rare to handle properly, as doing a transaction would be
+ expensive. Just admit to failure ;-) */
+ return SH_RESPONSE_reply_internal_error (connection,
+ TALER_EC_SYNC_DB_FETCH_ERROR,
+ "unexpected empty result set (try again?)");
+ case SYNC_DB_ONE_RESULT:
+ /* interesting case below */
+ break;
+ }
+ resp = MHD_create_response_from_buffer (backup_size,
+ backup,
+ MHD_RESPMEM_MUST_FREE);
+ {
+ char *sig_s;
+ char *prev_s;
+ char *etag;
+
+ sig_s = GNUNET_STRINGS_data_to_string_alloc (&account_sig,
+ sizeof (account_sig));
+ prev_s = GNUNET_STRINGS_data_to_string_alloc (&prev_hash,
+ sizeof (prev_hash));
+ etag = GNUNET_STRINGS_data_to_string_alloc (&backup_hash,
+ sizeof (backup_hash));
+ GNUNET_break (MHD_YES ==
+ MHD_add_response_header (resp,
+ "X-Sync-Signature",
+ sig_s));
+ GNUNET_break (MHD_YES ==
+ MHD_add_response_header (resp,
+ "X-Sync-Previous",
+ prev_s));
+ GNUNET_break (MHD_YES ==
+ MHD_add_response_header (resp,
+ MHD_HTTP_HEADER_ETAG,
+ etag));
+ GNUNET_free (etag);
+ GNUNET_free (prev_s);
+ GNUNET_free (sig_s);
+ }
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_NOT_MODIFIED,
+ resp);
+ MHD_destroy_response (resp);
+ return ret;
}
/**
* @param connection the MHD connection to handle
* @param[in,out] connection_cls the connection's closure (can be updated)
+ * @param account public key of the account the request is for
* @param upload_data upload data
* @param[in,out] upload_data_size number of bytes (left) in @a upload_data
* @return MHD result code
@@ -48,9 +204,11 @@ sync_handler_backup_get (struct MHD_Connection *connection,
int
sync_handler_backup_post (struct MHD_Connection *connection,
void **con_cls,
- const char *url,
+ const struct SYNC_AccountPublicKeyP *account,
const char *upload_data,
size_t *upload_data_size)
{
+ struct SYNC_AccountSignatureP account_sig;
+
return MHD_NO;
}
diff --git a/src/sync/sync-httpd_backup.h b/src/sync/sync-httpd_backup.h
index 1ba7408..02f31be 100644
--- a/src/sync/sync-httpd_backup.h
+++ b/src/sync/sync-httpd_backup.h
@@ -24,20 +24,20 @@
/**
* @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @param account public key of the account the request is for
+ * @param[in,out] con_cls the connection's closure (can be updated)
* @return MHD result code
*/
int
sync_handler_backup_get (struct MHD_Connection *connection,
- const char *url,
+ const struct SYNC_AccountPublicKeyP *account,
void **con_cls);
/**
* @param connection the MHD connection to handle
* @param[in,out] connection_cls the connection's closure (can be updated)
+ * @param account public key of the account the request is for
* @param upload_data upload data
* @param[in,out] upload_data_size number of bytes (left) in @a upload_data
* @return MHD result code
@@ -45,7 +45,7 @@ sync_handler_backup_get (struct MHD_Connection *connection,
int
sync_handler_backup_post (struct MHD_Connection *connection,
void **con_cls,
- const char *url,
+ const struct SYNC_AccountPublicKeyP *account,
const char *upload_data,
size_t *upload_data_size);
diff --git a/src/syncdb/plugin_syncdb_postgres.c b/src/syncdb/plugin_syncdb_postgres.c
index 1d89b5d..aba58d8 100644
--- a/src/syncdb/plugin_syncdb_postgres.c
+++ b/src/syncdb/plugin_syncdb_postgres.c
@@ -258,6 +258,7 @@ postgres_store_backup (void *cls,
struct PostgresClosure *pg = cls;
enum GNUNET_DB_QueryStatus qs;
struct GNUNET_HashCode bh;
+ static struct GNUNET_HashCode no_previous_hash;
check_connection (pg);
postgres_preflight (pg);
@@ -265,6 +266,7 @@ postgres_store_backup (void *cls,
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (account_pub),
GNUNET_PQ_query_param_auto_from_type (account_sig),
+ GNUNET_PQ_query_param_auto_from_type (&no_previous_hash),
GNUNET_PQ_query_param_auto_from_type (backup_hash),
GNUNET_PQ_query_param_fixed_size (backup,
backup_size),
@@ -404,6 +406,7 @@ postgres_update_backup (void *cls,
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (backup_hash),
GNUNET_PQ_query_param_auto_from_type (account_sig),
+ GNUNET_PQ_query_param_auto_from_type (old_backup_hash),
GNUNET_PQ_query_param_fixed_size (backup,
backup_size),
GNUNET_PQ_query_param_auto_from_type (account_pub),
@@ -520,11 +523,96 @@ postgres_update_backup (void *cls,
/**
+ * Lookup an account and associated backup meta data.
+ *
+ * @param cls closure
+ * @param account_pub account to store @a backup under
+ * @param backup_hash[OUT] set to hash of @a backup
+ * @return transaction status
+ */
+static enum SYNC_DB_QueryStatus
+postgres_lookup_account (void *cls,
+ const struct SYNC_AccountPublicKeyP *account_pub,
+ struct GNUNET_HashCode *backup_hash)
+{
+ struct PostgresClosure *pg = cls;
+ enum GNUNET_DB_QueryStatus qs;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (account_pub),
+ GNUNET_PQ_query_param_end
+ };
+
+ check_connection (pg);
+ postgres_preflight (pg);
+ {
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_auto_from_type ("backup_hash",
+ backup_hash),
+ GNUNET_PQ_result_spec_end
+ };
+
+ qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+ "backup_select_hash",
+ params,
+ rs);
+ }
+ switch (qs)
+ {
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ return SYNC_DB_HARD_ERROR;
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ GNUNET_break (0);
+ return SYNC_DB_SOFT_ERROR;
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ break; /* handle interesting case below */
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ return SYNC_DB_ONE_RESULT;
+ default:
+ GNUNET_break (0);
+ return SYNC_DB_HARD_ERROR;
+ }
+
+ /* check if account exists */
+ {
+ struct GNUNET_TIME_Absolute expiration;
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_auto_from_type ("expiration_date",
+ &expiration),
+ GNUNET_PQ_result_spec_end
+ };
+
+ qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+ "account_select",
+ params,
+ rs);
+ }
+ switch (qs)
+ {
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ return SYNC_DB_HARD_ERROR;
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ GNUNET_break (0);
+ return SYNC_DB_SOFT_ERROR;
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ /* indicates: no account */
+ return SYNC_DB_PAYMENT_REQUIRED;
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ /* indicates: no backup */
+ return SYNC_DB_NO_RESULTS;
+ default:
+ GNUNET_break (0);
+ return SYNC_DB_HARD_ERROR;
+ }
+}
+
+
+/**
* Obtain backup.
*
* @param cls closure
* @param account_pub account to store @a backup under
* @param account_sig[OUT] set to signature affirming storage request
+ * @param prev_hash[OUT] set to hash of previous @a backup, all zeros if none
* @param backup_hash[OUT] set to hash of @a backup
* @param backup_size[OUT] set to number of bytes in @a backup
* @param backup[OUT] set to raw data to backup, caller MUST FREE
@@ -533,6 +621,7 @@ static enum SYNC_DB_QueryStatus
postgres_lookup_backup (void *cls,
const struct SYNC_AccountPublicKeyP *account_pub,
struct SYNC_AccountSignatureP *account_sig,
+ struct GNUNET_HashCode *prev_hash,
struct GNUNET_HashCode *backup_hash,
size_t *backup_size,
void **backup)
@@ -546,6 +635,8 @@ postgres_lookup_backup (void *cls,
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_auto_from_type ("account_sig",
account_sig),
+ GNUNET_PQ_result_spec_auto_from_type ("prev_hash",
+ prev_hash),
GNUNET_PQ_result_spec_auto_from_type ("backup_hash",
backup_hash),
GNUNET_PQ_result_spec_variable_size ("data",
@@ -724,6 +815,7 @@ libsync_plugin_db_postgres_init (void *cls)
"("
"account_pub BYTEA PRIMARY KEY REFERENCES accounts (account_pub),"
"account_sig BYTEA NOT NULL CHECK (length(account_sig)=64),"
+ "prev_hash BYTEA NOT NULL CHECK (length(prev_hash)=64),"
"backup_hash BYTEA NOT NULL CHECK (length(backup_hash)=64),"
"data BYTEA NOT NULL"
");"),
@@ -766,22 +858,24 @@ libsync_plugin_db_postgres_init (void *cls)
"INSERT INTO backups "
"(account_pub"
",account_sig"
+ ",prev_hash"
",backup_hash"
",data"
") VALUES "
- "($1,$2,$3,$4);",
- 4),
+ "($1,$2,$3,$4,$5);",
+ 5),
GNUNET_PQ_make_prepare ("backup_update",
"UPDATE backups "
" SET"
" backup_hash=$1"
",account_sig=$2"
- ",data=$3"
+ ",prev_hash=$3"
+ ",data=$4"
" WHERE"
- " account_pub=$4"
+ " account_pub=$5"
" AND"
- " backup_hash=$5;",
- 5),
+ " backup_hash=$6;",
+ 6),
GNUNET_PQ_make_prepare ("backup_select_hash",
"SELECT "
" backup_hash "
@@ -793,6 +887,7 @@ libsync_plugin_db_postgres_init (void *cls)
GNUNET_PQ_make_prepare ("backup_select",
"SELECT "
" account_sig"
+ ",prev_hash"
",backup_hash"
",data "
"FROM"
@@ -822,6 +917,7 @@ libsync_plugin_db_postgres_init (void *cls)
plugin->drop_tables = &postgres_drop_tables;
plugin->gc = &postgres_gc;
plugin->store_backup_TR = &postgres_store_backup;
+ plugin->lookup_account_TR = &postgres_lookup_account;
plugin->lookup_backup_TR = &postgres_lookup_backup;
plugin->update_backup_TR = &postgres_update_backup;
plugin->increment_lifetime_TR = &postgres_increment_lifetime;