summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2019-11-17 00:13:00 +0100
committerChristian Grothoff <christian@grothoff.org>2019-11-17 00:13:00 +0100
commit8b1e276a316d29a752b26cdc070330e688aea221 (patch)
tree203a9de2a95edae1dae9979ec4296e63b3bb61e9
parent9badf80eb4228a9c009839a4856710127efe8601 (diff)
downloadsync-8b1e276a316d29a752b26cdc070330e688aea221.tar.gz
sync-8b1e276a316d29a752b26cdc070330e688aea221.tar.bz2
sync-8b1e276a316d29a752b26cdc070330e688aea221.zip
breaking the build badly, big hacking
-rw-r--r--configure.ac25
-rw-r--r--src/include/sync_database_plugin.h80
-rw-r--r--src/include/sync_service.h30
-rw-r--r--src/sync/Makefile.am2
-rw-r--r--src/sync/sync-httpd.c227
-rw-r--r--src/sync/sync-httpd.h21
-rw-r--r--src/sync/sync-httpd_backup.c144
-rw-r--r--src/sync/sync-httpd_backup.h30
-rw-r--r--src/sync/sync-httpd_responses.h14
-rw-r--r--src/sync/sync.conf11
-rw-r--r--src/syncdb/plugin_syncdb_postgres.c195
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;