diff options
author | Christian Grothoff <christian@grothoff.org> | 2019-11-17 00:13:00 +0100 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2019-11-17 00:13:00 +0100 |
commit | 8b1e276a316d29a752b26cdc070330e688aea221 (patch) | |
tree | 203a9de2a95edae1dae9979ec4296e63b3bb61e9 | |
parent | 9badf80eb4228a9c009839a4856710127efe8601 (diff) | |
download | sync-8b1e276a316d29a752b26cdc070330e688aea221.tar.gz sync-8b1e276a316d29a752b26cdc070330e688aea221.tar.bz2 sync-8b1e276a316d29a752b26cdc070330e688aea221.zip |
breaking the build badly, big hacking
-rw-r--r-- | configure.ac | 25 | ||||
-rw-r--r-- | src/include/sync_database_plugin.h | 80 | ||||
-rw-r--r-- | src/include/sync_service.h | 30 | ||||
-rw-r--r-- | src/sync/Makefile.am | 2 | ||||
-rw-r--r-- | src/sync/sync-httpd.c | 227 | ||||
-rw-r--r-- | src/sync/sync-httpd.h | 21 | ||||
-rw-r--r-- | src/sync/sync-httpd_backup.c | 144 | ||||
-rw-r--r-- | src/sync/sync-httpd_backup.h | 30 | ||||
-rw-r--r-- | src/sync/sync-httpd_responses.h | 14 | ||||
-rw-r--r-- | src/sync/sync.conf | 11 | ||||
-rw-r--r-- | src/syncdb/plugin_syncdb_postgres.c | 195 |
11 files changed, 665 insertions, 114 deletions
diff --git a/configure.ac b/configure.ac index 37784fb..cf38528 100644 --- a/configure.ac +++ b/configure.ac @@ -46,6 +46,28 @@ CFLAGS="-Wall $CFLAGS" # Checks for header files. AC_CHECK_HEADERS([stdint.h stdlib.h string.h unistd.h]) + +# Check for Taler's libtalermerchant +libtalermerchant=0 +AC_MSG_CHECKING([for libtalermerchant]) +AC_ARG_WITH(merchant, + [AS_HELP_STRING([--with-merchant=PFX], [base of Taler MERCHANT installation])], + [AC_MSG_RESULT([given as $with_merchant])], + [AC_MSG_RESULT(not given) + with_merchant=yes]) +AS_CASE([$with_merchant], + [yes], [], + [no], [AC_MSG_ERROR([--with-merchant is required])], + [LDFLAGS="-L$with_merchant/lib $LDFLAGS" + CPPFLAGS="-I$with_merchant/include $CPPFLAGS $POSTGRESQL_CPPFLAGS"]) + +AC_CHECK_HEADERS([taler/taler_merchant_service.h], + [AC_CHECK_LIB([talermerchant], [TALER_MERCHANT_poll_payment], libtalermerchant=1)], + [], [#ifdef HAVE_TALER_PLATFORM_H + #include <taler/platform.h> + #endif]) + + # Check for GNUnet's libgnunetutil. libgnunetutil=0 AC_MSG_CHECKING([for libgnunetutil]) @@ -102,6 +124,9 @@ AC_CHECK_HEADERS([gnunet/gnunet_pq_lib.h], #endif]) AM_CONDITIONAL(HAVE_GNUNETPQ, test x$libgnunetpq = x1) + + + # check for libmicrohttpd microhttpd=0 AC_MSG_CHECKING([for microhttpd]) diff --git a/src/include/sync_database_plugin.h b/src/include/sync_database_plugin.h index d5759ee..ac88828 100644 --- a/src/include/sync_database_plugin.h +++ b/src/include/sync_database_plugin.h @@ -65,6 +65,24 @@ enum SYNC_DB_QueryStatus /** + * Function called on all pending payments. + * + * @param cls closure + * @param account_pub which account is the order for + * @param timestamp for how long have we been waiting + * @param order_id order id in the backend + * @param amount how much is the order for + */ +typedef void +(*SYNC_DB_PaymentPendingIterator)(void *cls, + const struct + SYNC_AccountPublicKeyP *account_pub, + struct GNUNET_TIME_Absolute timestamp, + const char *order_id, + const struct TALER_Amount *amount); + + +/** * Handle to interact with the database. * * Functions ending with "_TR" run their OWN transaction scope @@ -104,12 +122,16 @@ struct SYNC_DatabasePlugin * truth and financial records older than @a fin_expire. * * @param cls closure - * @param expire backups older than the given time stamp should be garbage collected + * @param expire_backups backups older than the given time stamp should be garbage collected + * @param expire_pending_payments payments still pending from since before + * this value should be garbage collected * @return transaction status */ enum SYNC_DB_QueryStatus (*gc)(void *cls, - struct GNUNET_TIME_Absolute expire); + struct GNUNET_TIME_Absolute expire, + struct GNUNET_TIME_Absolute expire_pending_payments); + /** * Store backup. Only applicable for the FIRST backup under @@ -132,6 +154,55 @@ struct SYNC_DatabasePlugin size_t backup_size, const void *backup); + + /** + * Store payment. Used to begin a payment, not indicative + * that the payment actually was made. (That is done + * when we increment the account's lifetime.) + * + * @param cls closure + * @param account_pub account to store @a backup under + * @param order_id order we created + * @param amount how much we asked for + * @return transaction status + */ + enum SYNC_DB_QueryStatus + (*store_payment_TR)(void *cls, + const struct SYNC_AccountPublicKeyP *account_pub, + const char *order_id, + const struct TALER_Amount *amount); + + + /** + * Lookup pending payments. + * + * @param cls closure + * @param it iterator to call on all pending payments + * @param it_cls closure for @a it + * @return transaction status + */ + enum SYNC_DB_QueryStatus + (*lookup_pending_payments_TR)(void *cls, + SYNC_DB_PaymentPendingIterator it, + void *it_cls); + + + /** + * Lookup pending payments by account. + * + * @param cls closure + * @param account_pub account to look for pending payments under + * @param it iterator to call on all pending payments + * @param it_cls closure for @a it + * @return transaction status + */ + enum SYNC_DB_QueryStatus + (*lookup_pending_payments_by_account_TR)(void *cls, + const struct + SYNC_AccountPublicKeyP *account_pub, + SYNC_DB_PaymentPendingIterator it, + void *it_cls); + /** * Update backup. * @@ -189,16 +260,19 @@ struct SYNC_DatabasePlugin void **backup); /** - * Increment account lifetime. + * Increment account lifetime and mark the associated payment + * as successful. * * @param cls closure * @param account_pub which account received a payment + * @param order_id order which was paid, must be unique and match pending payment * @param lifetime for how long is the account now paid (increment) * @return transaction status */ enum SYNC_DB_QueryStatus (*increment_lifetime_TR)(void *cls, const struct SYNC_AccountPublicKeyP *account_pub, + const char *order_id, struct GNUNET_TIME_Relative lifetime); }; diff --git a/src/include/sync_service.h b/src/include/sync_service.h index 20bbe67..595df3c 100644 --- a/src/include/sync_service.h +++ b/src/include/sync_service.h @@ -50,6 +50,36 @@ struct SYNC_AccountPublicKeyP }; +GNUNET_NETWORK_STRUCT_BEGIN + + +/** + * Data signed by the account public key of a sync client to + * authorize the upload of the backup. + */ +struct SYNC_UploadSignaturePS +{ + /** + * Set to #TALER_SIGNATURE_SYNC_BACKUP_UPLOAD. + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + + /** + * Hash of the previous backup, all zeros for none. + */ + struct GNUNET_HashCode old_backup_hash GNUNET_PACKED; + + /** + * Hash of the new backup. + */ + struct GNUNET_HashCode new_backup_hash GNUNET_PACKED; + +}; + + +GNUNET_NETWORK_STRUCT_END + + /** * Signature made with an account's public key. */ diff --git a/src/sync/Makefile.am b/src/sync/Makefile.am index 0bb6703..40c8aba 100644 --- a/src/sync/Makefile.am +++ b/src/sync/Makefile.am @@ -12,6 +12,7 @@ bin_PROGRAMS = \ sync_httpd_SOURCES = \ sync-httpd.c sync-httpd.h \ sync-httpd_backup.c sync-httpd_backup.h \ + sync-httpd_backup_post.c \ sync-httpd_mhd.c sync-httpd_mhd.h \ sync-httpd_parsing.c sync-httpd_parsing.h \ sync-httpd_responses.c sync-httpd_responses.h \ @@ -20,6 +21,7 @@ sync_httpd_LDADD = \ $(top_builddir)/src/syncdb/libsyncdb.la \ -lmicrohttpd \ -ljansson \ + -ltalermerchant \ -ltalerjson \ -ltalerutil \ -lgnunetcurl \ diff --git a/src/sync/sync-httpd.c b/src/sync/sync-httpd.c index 98a2b24..a0bd692 100644 --- a/src/sync/sync-httpd.c +++ b/src/sync/sync-httpd.c @@ -35,6 +35,58 @@ #define UNIX_BACKLOG 500 /** + * How long do we hold an BACKEND connection if + * we are awaiting payment before giving up (only + * used when resuming). + */ +#define CHECK_BACKEND_PAYMENT_TIMEOUT GNUNET_TIME_relative_multiply ( \ + GNUNET_TIME_UNIT_SECONDS, 15) + + +/** + * Context we use to check for payments outside of HTTP requests. + */ +struct PaymentContext +{ + /** + * The asyncronous operation. + */ + struct TALER_MERCHANT_CheckPaymentOperation *cpo; + + /** + * Kept in a DLL. + */ + struct PaymentContext *next; + + /** + * Kept in a DLL. + */ + struct PaymentContext *prev; + + /** + * Order ID of the payment. + */ + char *order_id; + + /** + * Account the payment is about. + */ + struct SYNC_AccountPublicKeyP account_pub; +}; + + +/** + * Head of active payments. + */ +static struct PaymentContext *pc_head; + +/** + * Tail of active payments. + */ +static struct PaymentContext *pc_tail; + + +/** * The port we are running on */ static long long unsigned port; @@ -55,6 +107,26 @@ unsigned long long int SH_upload_limit_mb; struct TALER_Amount SH_annual_fee; /** + * Our Taler backend to process payments. + */ +char *MH_backend_url; + +/** + * Our own base URL + */ +char *MH_my_base_url; + +/** + * Our context for making HTTP requests. + */ +struct GNUNET_CURL_Context *MH_ctx; + +/** + * Reschedule context for #MH_ctx. + */ +static struct GNUNET_CURL_RescheduleContext *rc; + +/** * Task running the HTTP server. */ static struct GNUNET_SCHEDULER_Task *mhd_task; @@ -223,8 +295,7 @@ url_handler (void *cls, MHD_HTTP_METHOD_GET)) { return sync_handler_backup_get (connection, - &account_pub, - con_cls); + &account_pub); } if (0 == strcasecmp (method, MHD_HTTP_METHOD_POST)) @@ -281,6 +352,29 @@ url_handler (void *cls, static void do_shutdown (void *cls) { + struct PaymentContext *pc; + + (void) cls; + while (NULL != (pc = pc_head)) + { + TALER_MERCHANT_check_payment_cancel (pc->cpo); + GNUNET_CONTAINER_DLL_remove (pc_head, + pc_tail, + pc); + GNUNET_free (pc->order_id); + GNUNET_free (pc); + } + if (NULL != SH_ctx) + { + GNUNET_CURL_fini (SH_ctx); + SH_ctx = NULL; + } + if (NULL != rc) + { + GNUNET_CURL_gnunet_rc_destroy (rc); + rc = NULL; + } + SH_resume_all_bc (); if (NULL != mhd_task) { GNUNET_SCHEDULER_cancel (mhd_task); @@ -321,6 +415,8 @@ handle_mhd_completion_callback (void *cls, { struct TM_HandlerContext *hc = *con_cls; + (void) cls; + (void) connection; if (NULL == hc) return; GNUNET_log (GNUNET_ERROR_TYPE_INFO, @@ -355,6 +451,7 @@ static int triggered; static void run_daemon (void *cls) { + (void) cls; mhd_task = NULL; do { triggered = 0; @@ -387,6 +484,89 @@ SH_trigger_daemon () /** + * Callback to process a GET /check-payment request + * + * @param cls our `struct PaymentContext` + * @param http_status HTTP status code for this request + * @param obj raw response body + * @param paid #GNUNET_YES if the payment is settled, #GNUNET_NO if not + * settled, $GNUNET_SYSERR on error + * (note that refunded payments are returned as paid!) + * @param refunded #GNUNET_YES if there is at least on refund on this payment, + * #GNUNET_NO if refunded, #GNUNET_SYSERR or error + * @param refunded_amount amount that was refunded, NULL if there + * was no refund + * @param taler_pay_uri the URI that instructs the wallets to process + * the payment + */ +static void +check_payment_cb (void *cls, + unsigned int http_status, + const json_t *obj, + int paid, + int refunded, + struct TALER_Amount *refund_amount, + const char *taler_pay_uri) +{ + struct PaymentContext *pc = cls; + + /* refunds are not supported, verify */ + pc->cpo = NULL; + if (paid) + { + enum SYNC_DB_QueryStatus qs; + + qs = db->increment_lifetime_TR (db->cls, + &pc->account, + pc->order_id, + GNUNET_TIME_UNIT_YEARS); /* always annual */ + GNUNET_break (0 > qs); + } + GNUNET_CONTAINER_DLL_remove (pc_head, + pc_tail, + pc); + GNUNET_free (pc->order_id); + GNUNET_free (pc); +} + + +/** + * Function called on all pending payments. Talks to our + * backend to see if any of them are done yet. + * + * @param cls closure + * @param account_pub which account is the order for + * @param timestamp for how long have we been waiting + * @param order_id order id in the backend + * @param amount how much is the order for + */ +static void +check_on_payments_cb (void *cls, + const struct SYNC_AccountPublicKeyP *account_pub, + struct GNUNET_TIME_Absolute timestamp, + const char *order_id, + const struct TALER_Amount *amount) +{ + struct PaymentContext *pc; + + (void) amount; + pc = GNUNET_new (struct PaymentContext); + pc->account_pub = *account_pub; + pc->order_id = GNUNET_strdup (order_id); + GNUNET_CONTAINER_DLL_insert (pc_head, + pc_tail, + pc); + pc->cpo = TALER_MERCHANT_check_payment (MH_ctx, + MH_backend_url, + order_id, + NULL /* our payments are NOT session-bound */, + CHECK_BACKEND_PAYMENT_TIMEOUT, + &check_payment_cb, + pc); +} + + +/** * Function that queries MHD's select sets and * starts the task waiting for them. * @@ -489,6 +669,36 @@ run (void *cls, GNUNET_SCHEDULER_shutdown (); return; } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (config, + "sync", + "PAYMENT_BACKEND_URL", + &MH_backend_url)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "sync", + "PAYMENT_BACKEND_URL"); + GNUNET_SCHEDULER_shutdown (); + return; + } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (config, + "sync", + "BASE_URL", + &MH_my_url)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "sync", + "BASE_URL"); + GNUNET_SCHEDULER_shutdown (); + return; + } + + /* setup HTTP client event loop */ + MH_ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, + &rc); + rc = GNUNET_CURL_gnunet_rc_create (MH_ctx); + if (NULL == (db = SYNC_DB_plugin_load (config))) @@ -497,6 +707,19 @@ run (void *cls, return; } + /* TODO: maybe make this conditional on a command-line option? + Might be expensive, and is strictly speaking not required. */ + { + /* we might have been down for a while, catch up on + all payments that happened in the meantime */ + enum SYNC_DB_QueryStatus qs; + + db->lookup_pending_payments_TR (db->cls, + &check_on_payments_cb, + NULL); + GNUNET_break (qs >= 0); + } + { const char *choices[] = {"tcp", "unix", diff --git a/src/sync/sync-httpd.h b/src/sync/sync-httpd.h index c2e76a3..c000075 100644 --- a/src/sync/sync-httpd.h +++ b/src/sync/sync-httpd.h @@ -115,11 +115,6 @@ struct TM_HandlerContext TM_ContextCleanup cc; /** - * Which request handler is handling this request? - */ - const struct SH_RequestHandler *rh; - - /** * Asynchronous request context id. */ struct GNUNET_AsyncScopeId async_scope_id; @@ -147,6 +142,21 @@ extern unsigned long long SH_upload_limit_mb; extern struct TALER_Amount SH_annual_fee; /** + * Our Taler backend to process payments. + */ +extern char *MH_backend_url; + +/** + * Our own base URL + */ +extern char *MH_my_base_url; + +/** + * Our context for making HTTP requests. + */ +extern struct GNUNET_CURL_Context *MH_ctx; + +/** * Kick MHD to run now, to be called after MHD_resume_connection(). * Basically, we need to explicitly resume MHD's event loop whenever * we made progress serving a request. This function re-schedules @@ -155,4 +165,5 @@ extern struct TALER_Amount SH_annual_fee; void SH_trigger_daemon (void); + #endif diff --git a/src/sync/sync-httpd_backup.c b/src/sync/sync-httpd_backup.c index 61d9151..65506da 100644 --- a/src/sync/sync-httpd_backup.c +++ b/src/sync/sync-httpd_backup.c @@ -25,30 +25,21 @@ #include "sync-httpd_responses.h" /** + * Handle request on @a connection for retrieval of the latest + * backup of @a account. + * * @param connection the MHD connection to handle * @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 struct SYNC_AccountPublicKeyP *account, - void **con_cls) + const struct SYNC_AccountPublicKeyP *account) { - 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); @@ -74,44 +65,86 @@ sync_handler_backup_get (struct MHD_Connection *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); + { + struct MHD_Response *resp; + + 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)) + const char *inm; + + inm = MHD_lookup_connection_value (connection, + MHD_HEADER_KIND, + MHD_HTTP_HEADER_IF_NONE_MATCH); + if (NULL != inm) { - 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; + struct GNUNET_HashCode inm_h; + + 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_IF_NONE_MATCH, + "Etag does not include 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; } + return SH_return_backup (connection, + account, + MHD_HTTP_OK); +} + + +/** + * Return the current backup of @a account on @a connection + * using @a default_http_status on success. + * + * @param connection MHD connection to use + * @param account account to query + * @param default_http_status HTTP status to queue response + * with on success (#MHD_HTTP_OK or #MHD_HTTP_CONFLICT) + * @return MHD result code + */ +int +SH_return_backup (struct MHD_Connection *connection, + const struct SYNC_AccountPublicKeyP *account, + unsigned int default_http_status) +{ + enum SYNC_DB_QueryStatus qs; + struct MHD_Response *resp; + int ret; + struct SYNC_AccountSignatureP account_sig; + struct GNUNET_HashCode backup_hash; + struct GNUNET_HashCode prev_hash; + size_t backup_size; + void *backup; qs = db->lookup_backup_TR (db->cls, account, @@ -171,11 +204,11 @@ sync_handler_backup_get (struct MHD_Connection *connection, sizeof (backup_hash)); GNUNET_break (MHD_YES == MHD_add_response_header (resp, - "X-Sync-Signature", + "Sync-Signature", sig_s)); GNUNET_break (MHD_YES == MHD_add_response_header (resp, - "X-Sync-Previous", + "Sync-Previous", prev_s)); GNUNET_break (MHD_YES == MHD_add_response_header (resp, @@ -186,29 +219,8 @@ sync_handler_backup_get (struct MHD_Connection *connection, GNUNET_free (sig_s); } ret = MHD_queue_response (connection, - MHD_HTTP_NOT_MODIFIED, + default_http_status, 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 - */ -int -sync_handler_backup_post (struct MHD_Connection *connection, - void **con_cls, - 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 02f31be..6326266 100644 --- a/src/sync/sync-httpd_backup.h +++ b/src/sync/sync-httpd_backup.h @@ -23,15 +23,39 @@ #include <microhttpd.h> /** + * Service is shutting down, resume all MHD connections NOW. + */ +void +SH_resume_all_bc (void); + + +/** + * Return the current backup of @a account on @a connection + * using @a default_http_status on success. + * + * @param connection MHD connection to use + * @param account account to query + * @param default_http_status HTTP status to queue response + * with on success (#MHD_HTTP_OK or #MHD_HTTP_CONFLICT) + * @return MHD result code + */ +int +SH_return_backup (struct MHD_Connection *connection, + const struct SYNC_AccountPublicKeyP *account, + unsigned int default_http_status); + + +/** + * Handle request on @a connection for retrieval of the latest + * backup of @a account. + * * @param connection the MHD connection to handle * @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 struct SYNC_AccountPublicKeyP *account, - void **con_cls); + const struct SYNC_AccountPublicKeyP *account); /** diff --git a/src/sync/sync-httpd_responses.h b/src/sync/sync-httpd_responses.h index c072590..4ff4002 100644 --- a/src/sync/sync-httpd_responses.h +++ b/src/sync/sync-httpd_responses.h @@ -30,7 +30,6 @@ #include <gnunet/gnunet_util_lib.h> - /** * Make JSON response object. * @@ -67,8 +66,6 @@ SH_RESPONSE_make_json_pack (const char *fmt, ...); - - /** * Function to call to handle the request by building a JSON * reply from a format string and varargs. @@ -112,8 +109,6 @@ SH_RESPONSE_reply_internal_error (struct MHD_Connection *connection, const char *hint); - - /** * Send a response indicating that the request was too big. * @@ -124,7 +119,6 @@ int SH_RESPONSE_reply_request_too_large (struct MHD_Connection *connection); - /** * Send a response indicating that we did not find the @a object * needed for the reply. @@ -142,7 +136,6 @@ SH_RESPONSE_reply_rc (struct MHD_Connection *connection, const char *msg); - /** * Send a response indicating that the JSON was malformed. * @@ -153,7 +146,6 @@ int SH_RESPONSE_reply_invalid_json (struct MHD_Connection *connection); - /** * Send a response indicating that we did not find the @a object * needed for the reply. @@ -169,7 +161,6 @@ SH_RESPONSE_reply_not_found (struct MHD_Connection *connection, const char *object); - /** * Send a response indicating that the request was malformed. * @@ -184,8 +175,6 @@ SH_RESPONSE_reply_bad_request (struct MHD_Connection *connection, const char *issue); - - /** * Add headers we want to return in every response. * Useful for testing, like if we want to always close @@ -197,7 +186,6 @@ void SH_RESPONSE_add_global_headers (struct MHD_Response *response); - /** * Send a response indicating an external error. * @@ -212,8 +200,6 @@ SH_RESPONSE_reply_external_error (struct MHD_Connection *connection, const char *hint); - - /** * Send a response indicating a missing argument. * diff --git a/src/sync/sync.conf b/src/sync/sync.conf index 601241e..459c5c6 100644 --- a/src/sync/sync.conf +++ b/src/sync/sync.conf @@ -10,7 +10,7 @@ SERVE = tcp # Which HTTP port does the backend listen on? Only used if "SERVE" is 'tcp'. -PORT = 9966 +PORT = 9967 # Which IP address should we bind to? i.e. 127.0.0.1 or ::1 for loopback. # Can also be given as a hostname. We will bind to the wildcard (dual-stack) @@ -31,6 +31,15 @@ ANNUAL_FEE = TESTKUDOS:0.1 # Upload limit per backup, in megabytes UPLOAD_LIMIT_MB = 16 +# Public base URL of the SYNC service itself. Used for the +# fulfillment URL. +BASE_URL = https://localhost:9967/ + +# Base URL of our payment backend +PAYMENT_BACKEND_URL = http://localhost:9966/ + + + # Configuration for postgres database. [syncdb-postgres] CONFIG = postgres:///sync diff --git a/src/syncdb/plugin_syncdb_postgres.c b/src/syncdb/plugin_syncdb_postgres.c index aba58d8..68d5461 100644 --- a/src/syncdb/plugin_syncdb_postgres.c +++ b/src/syncdb/plugin_syncdb_postgres.c @@ -63,6 +63,7 @@ postgres_drop_tables (void *cls) struct PostgresClosure *pg = cls; struct GNUNET_PQ_ExecuteStatement es[] = { GNUNET_PQ_make_try_execute ("DROP TABLE IF EXISTS accounts CASCADE;"), + GNUNET_PQ_make_try_execute ("DROP TABLE IF EXISTS payments;"), GNUNET_PQ_make_try_execute ("DROP TABLE IF EXISTS backups;"), GNUNET_PQ_EXECUTE_STATEMENT_END }; @@ -212,25 +213,95 @@ commit_transaction (void *cls) * truth and financial records older than @a fin_expire. * * @param cls closure - * @param expire backups older than the given time stamp should be garbage collected + * @param expire_backups backups older than the given time stamp should be garbage collected + * @param expire_pending_payments payments still pending from since before + * this value should be garbage collected * @return transaction status */ static enum SYNC_DB_QueryStatus postgres_gc (void *cls, - struct GNUNET_TIME_Absolute expire) + struct GNUNET_TIME_Absolute expire_backups, + struct GNUNET_TIME_Absolute expire_pending_payments) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { - TALER_PQ_query_param_absolute_time (&expire), + TALER_PQ_query_param_absolute_time (&expire_backups), GNUNET_PQ_query_param_end }; + struct GNUNET_PQ_QueryParam params2[] = { + TALER_PQ_query_param_absolute_time (&expire_pending_payments), + GNUNET_PQ_query_param_end + }; + enum SYNC_DB_QueryStatus qs; check_connection (pg); postgres_preflight (pg); - return (enum SYNC_DB_QueryStatus) - GNUNET_PQ_eval_prepared_non_select (pg->conn, - "gc", - params); + qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, + "gc_accounts", + params); + if (qs < 0) + return qs; + return GNUNET_PQ_eval_prepared_non_select (pg->conn, + "gc_pending_payments", + params2); +} + + +/** + * Store payment. Used to begin a payment, not indicative + * that the payment actually was made. (That is done + * when we increment the account's lifetime.) + * + * @param cls closure + * @param account_pub account to store @a backup under + * @param order_id order we created + * @param amount how much we asked for + * @return transaction status + */ +static enum SYNC_DB_QueryStatus +postgres_store_payment (void *cls, + const struct SYNC_AccountPublicKeyP *account_pub, + const char *order_id, + const struct TALER_Amount *amount) +{ + // FIXME: use payment_insert +} + + +/** + * Lookup pending payments. + * + * @param cls closure + * @param it iterator to call on all pending payments + * @param it_cls closure for @a it + * @return transaction status + */ +static enum SYNC_DB_QueryStatus +postgres_lookup_pending_payments (void *cls, + SYNC_DB_PaymentPendingIterator it, + void *it_cls) +{ + // FIXME: use payments_select +} + + +/** + * Lookup pending payments by account. + * + * @param cls closure + * @param account_pub account to look for pending payments under + * @param it iterator to call on all pending payments + * @param it_cls closure for @a it + * @return transaction status + */ +static enum SYNC_DB_QueryStatus +postgres_lookup_pending_payments_by_account (void *cls, + const struct + SYNC_AccountPublicKeyP *account_pub, + SYNC_DB_PaymentPendingIterator it, + void *it_cls) +{ + // FIXME: use payments_select_by_account } @@ -674,12 +745,14 @@ postgres_lookup_backup (void *cls, * * @param cls closure * @param account_pub which account received a payment + * @param order_id order which was paid, must be unique and match pending payment * @param lifetime for how long is the account now paid (increment) * @return transaction status */ static enum SYNC_DB_QueryStatus postgres_increment_lifetime (void *cls, const struct SYNC_AccountPublicKeyP *account_pub, + const char *order_id, struct GNUNET_TIME_Relative lifetime) { struct PostgresClosure *pg = cls; @@ -694,6 +767,25 @@ postgres_increment_lifetime (void *cls, GNUNET_break (0); return GNUNET_DB_STATUS_HARD_ERROR; } + + { + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (order_id), + GNUNET_PQ_query_param_auto_from_type (account_pub), + GNUNET_PQ_query_param_end + }; + + qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, + "payment_done", + params); + if (0 >= qs) + { + /* payment made before, or unknown, or error => no further action! */ + rollback (pg); + return qs; + } + } + { struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_auto_from_type (account_pub), @@ -807,33 +899,62 @@ libsync_plugin_db_postgres_init (void *cls) The contract terms will change (nonce will be added) when moved to the contract terms table */ GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS accounts" - "(" - "account_pub BYTEA PRIMARY KEY CHECK (length(account_pub)=32)," - "expiration_date INT8 NOT NULL" + "(account_pub BYTEA PRIMARY KEY CHECK (length(account_pub)=32)" + ",expiration_date INT8 NOT NULL" + ");"), + GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS payments" + "(account_pub BYTEA CHECK (length(account_pub)=32)," + ",order_id VARCHAR PRIMARY KEY" + ",timestamp INT8 NOT NULL" + ",amount_val INT8 NOT NULL" /* amount we were paid */ + ",amount_frac INT4 NOT NULL" + ",paid BOOLEAN NOT NULL DEFAULT FALSE" ");"), GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS backups" - "(" - "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" + "(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" ");"), /* index for gc */ GNUNET_PQ_make_try_execute ( "CREATE INDEX accounts_expire ON " "accounts (expiration_date);"), + GNUNET_PQ_make_try_execute ( + "CREATE INDEX payments_timestamp ON " + "payments (paid,timestamp);"), GNUNET_PQ_EXECUTE_STATEMENT_END }; struct GNUNET_PQ_PreparedStatement ps[] = { GNUNET_PQ_make_prepare ("account_insert", "INSERT INTO accounts " - "(" - "account_pub," - "expiration_date" + "(account_pub," + ",expiration_date" ") VALUES " "($1,$2);", 2), + GNUNET_PQ_make_prepare ("payment_insert", + "INSERT INTO payments " + "(account_pub" + ",order_id" + ",timestamp" + ",amount_val" + ",amount_frac" + ") VALUES " + "($1,$2,$3,$4,$5);", + 5), + GNUNET_PQ_make_prepare ("payment_done", + "UPDATE payments " + "SET" + " paid=TRUE " + "WHERE" + " order_id=$1" + " AND" + " account_pub=$2" + " AND" + " paid=FALSE;", + 2), GNUNET_PQ_make_prepare ("account_update", "UPDATE accounts " "SET" @@ -849,11 +970,41 @@ libsync_plugin_db_postgres_init (void *cls) "WHERE" " account_pub=$1;", 1), - GNUNET_PQ_make_prepare ("gc", + GNUNET_PQ_make_prepare ("payments_select", + "SELECT" + " account_pub" + ",order_id" + ",amount_val" + ",amount_frac" + "FROM" + " payments " + "WHERE" + " paid=FALSE;", + 0), + GNUNET_PQ_make_prepare ("payments_select_by_account", + "SELECT" + " order_id" + ",amount_val" + ",amount_frac" + "FROM" + " payments " + "WHERE" + " paid=FALSE" + " AND" + " account_pub=$1;", + 1), + GNUNET_PQ_make_prepare ("gc_accounts", "DELETE FROM accounts " "WHERE" " expiration_date < $1;", 1), + GNUNET_PQ_make_prepare ("gc_accounts", + "DELETE FROM payments " + "WHERE" + " paid=FALSE" + " AND" + " timestamp < $1;", + 1), GNUNET_PQ_make_prepare ("backup_insert", "INSERT INTO backups " "(account_pub" @@ -916,6 +1067,10 @@ libsync_plugin_db_postgres_init (void *cls) plugin->cls = pg; plugin->drop_tables = &postgres_drop_tables; plugin->gc = &postgres_gc; + plugin->store_payment_TR = &postgres_store_payment; + plugin->lookup_pending_payments_TR = &postgres_lookup_pending_payments; + plugin->lookup_pending_payments_by_account_TR = + &postgres_lookup_pending_payments_by_account; plugin->store_backup_TR = &postgres_store_backup; plugin->lookup_account_TR = &postgres_lookup_account; plugin->lookup_backup_TR = &postgres_lookup_backup; |