diff options
Diffstat (limited to 'src/backend/taler-merchant-httpd_track-deposit.c')
-rw-r--r-- | src/backend/taler-merchant-httpd_track-deposit.c | 316 |
1 files changed, 279 insertions, 37 deletions
diff --git a/src/backend/taler-merchant-httpd_track-deposit.c b/src/backend/taler-merchant-httpd_track-deposit.c index 970bd576..48b7e2eb 100644 --- a/src/backend/taler-merchant-httpd_track-deposit.c +++ b/src/backend/taler-merchant-httpd_track-deposit.c @@ -87,19 +87,123 @@ struct DepositTrackContext unsigned int response_code; /** - * + * Error message. */ - json_t *json; + const char *error; /** - * Error message. + * Response to return upon resume. */ - const char *error; + struct MHD_Response *response; + /** + * Handle for operation to lookup /keys (and auditors) from + * the exchange used for this transaction; NULL if no operation is + * pending. + */ + struct TMH_EXCHANGES_FindOperation *fo; + + /** + * Task run on timeout. + */ + struct GNUNET_SCHEDULER_Task *timeout_task; + + /** + * URI of the exchange. + */ + char *uri; }; /** + * Free the @a rctx. + * + * @param rctx data to free + */ +static void +free_deposit_track_context (struct DepositTrackContext *rctx) +{ + if (NULL != rctx->fo) + { + TMH_EXCHANGES_find_exchange_cancel (rctx->fo); + rctx->fo = NULL; + } + if (NULL != rctx->timeout_task) + { + GNUNET_SCHEDULER_cancel (rctx->timeout_task); + rctx->timeout_task = NULL; + } + GNUNET_free (rctx); +} + + +/** + * Resume the given /track/deposit operation and send the given response. + * Stores the response in the @a rctx and signals MHD to resume + * the connection. Also ensures MHD runs immediately. + * + * @param rctx deposit tracking context + * @param response_code response code to use + * @param response response data to send back + */ +static void +resume_track_deposit_with_response (struct DepositTrackContext *rctx, + unsigned int response_code, + struct MHD_Response *response) +{ + rctx->response_code = response_code; + rctx->response = response; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Resuming /track/transaction handling as exchange interaction is done (%u)\n", + response_code); + if (NULL != rctx->timeout_task) + { + GNUNET_SCHEDULER_cancel (rctx->timeout_task); + rctx->timeout_task = NULL; + } + MHD_resume_connection (rctx->connection); + TMH_trigger_daemon (); /* we resumed, kick MHD */ +} + + +/** + * Custom cleanup routine for a `struct DepositTrackContext`. + * + * @param hc the `struct DepositTrackContext` to clean up. + */ +static void +track_deposit_cleanup (struct TM_HandlerContext *hc) +{ + struct DepositTrackContext *rctx = (struct DepositTrackContext *) hc; + + free_deposit_track_context (rctx); +} + + +/** + * Function called with information about a coin that was deposited. + * Verify that it matches the information claimed by the exchange. + * + * @param cls closure FIXME! + * @param transaction_id of the contract + * @param coin_pub public key of the coin + * @param amount_with_fee amount the exchange will deposit for this coin + * @param deposit_fee fee the exchange will charge for this coin + * @param exchange_proof proof from exchange that coin was accepted + */ +static void +check_deposit (void *cls, + uint64_t transaction_id, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + const struct TALER_Amount *amount_with_fee, + const struct TALER_Amount *deposit_fee, + const json_t *exchange_proof) +{ + GNUNET_break (0); // not implemented! +} + + +/** * Function called with detailed wire transfer data, including all * of the coin transactions that were combined into the wire transfer. * @@ -124,56 +228,136 @@ wire_deposit_cb (void *cls, const struct TALER_WireDepositDetails *details) { struct DepositTrackContext *rctx = cls; - // unsigned int i; + unsigned int i; + int ret; rctx->wdh = NULL; - if (NULL == total_amount) + if (MHD_HTTP_OK != http_status) { - rctx->error = "failed to obtain /wire/deposit response from exchange"; - rctx->json = json_incref ((json_t *) json); - rctx->response_code = http_status; - MHD_resume_connection (rctx->connection); + resume_track_deposit_with_response + (rctx, + MHD_HTTP_FAILED_DEPENDENCY, + TMH_RESPONSE_make_json_pack ("{s:I, s:O}", + "exchange_status", (json_int_t) http_status, + "details", json)); return; } + + if (GNUNET_OK != + db->store_transfer_to_proof (db->cls, + rctx->uri, + &rctx->wtid, + json)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to persist wire transfer proof in DB\n"); + } + rctx->details_length = details_length; rctx->details = GNUNET_new_array (details_length, struct TALER_WireDepositDetails); memcpy (rctx->details, details, details_length * sizeof (struct TALER_WireDepositDetails)); - GNUNET_break (0); - /* FIXME: now check that these details match what we have in - our database... */ + for (i=0;i<details_length;i++) + { + ret = db->find_payments_by_id (rctx, + details[i].transaction_id, + &check_deposit, + rctx); + if (GNUNET_SYSERR == ret) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to verify existing payment data in DB\n"); + } + if (GNUNET_NO == ret) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to find payment data in DB\n"); + } + /* FIXME: check result of "check_deposit"! */ + ret = db->store_coin_to_transfer (db->cls, + details[i].transaction_id, + &details[i].coin_pub, + &rctx->wtid); + if (GNUNET_OK != ret) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to persist coin to wire transfer mapping in DB\n"); + } + } + /* FIXME: might want a more custom response here... */ + resume_track_deposit_with_response + (rctx, + MHD_HTTP_OK, + TMH_RESPONSE_make_json_pack ("{s:I, s:O}", + "exchange_status", (json_int_t) http_status, + "details", json)); +} +/** + * Function called with the result of our exchange lookup. + * + * @param cls the `struct DepositTrackContext` + * @param eh NULL if exchange was not found to be acceptable + * @param exchange_trusted #GNUNET_YES if this exchange is trusted by config + */ +static void +process_track_deposit_with_exchange (void *cls, + struct TALER_EXCHANGE_Handle *eh, + int exchange_trusted) +{ + struct DepositTrackContext *rctx = cls; + + rctx->fo = NULL; + rctx->eh = eh; + rctx->wdh = TALER_EXCHANGE_wire_deposits (eh, + &rctx->wtid, + &wire_deposit_cb, + rctx); } /** - * Function called with information about who is auditing - * a particular exchange and what key the exchange is using. + * Handle a timeout for the processing of the track deposit request. * * @param cls closure - * @param keys information about the various keys used - * by the exchange, NULL if /keys failed */ static void -cert_cb (void *cls, - const struct TALER_EXCHANGE_Keys *keys) +handle_track_deposit_timeout (void *cls) { struct DepositTrackContext *rctx = cls; - if (NULL == keys) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Resuming /track/deposit with error after timeout\n"); + rctx->timeout_task = NULL; + + if (NULL != rctx->fo) { - rctx->error = "failed to obtain /keys from exchange"; - rctx->response_code = MHD_HTTP_SERVICE_UNAVAILABLE; - MHD_resume_connection (rctx->connection); - return; + TMH_EXCHANGES_find_exchange_cancel (rctx->fo); + rctx->fo = NULL; } - rctx->wdh = TALER_EXCHANGE_wire_deposits (rctx->eh, - &rctx->wtid, - &wire_deposit_cb, - rctx); + resume_track_deposit_with_response (rctx, + MHD_HTTP_SERVICE_UNAVAILABLE, + TMH_RESPONSE_make_internal_error ("exchange not reachable")); +} + + +/** + * Function called with information about a wire transfer identifier. + * Generate a response based on the given @a proof. + * + * @param cls closure + * @param proof proof from exchange about what the wire transfer was for + */ +static void +proof_cb (void *cls, + const json_t *proof) +{ + struct DepositTrackContext *rctx = cls; + + GNUNET_break (0); // not implemented! } @@ -199,18 +383,52 @@ MH_handler_track_deposit (struct TMH_RequestHandler *rh, struct DepositTrackContext *rctx; const char *str; const char *uri; + int ret; - rctx = *connection_cls; - if (NULL != rctx) + if (NULL == *connection_cls) { - // ... + rctx = GNUNET_new (struct DepositTrackContext); + rctx->hc.cc = &track_deposit_cleanup; + rctx->connection = connection; + *connection_cls = rctx; } + else + { + /* not first call, recover state */ + rctx = *connection_cls; + } + + if (0 != rctx->response_code) + { + /* We are *done* processing the request, just queue the response (!) */ + if (UINT_MAX == rctx->response_code) + { + GNUNET_break (0); + return MHD_NO; /* hard error */ + } + ret = MHD_queue_response (connection, + rctx->response_code, + rctx->response); + if (NULL != rctx->response) + { + MHD_destroy_response (rctx->response); + rctx->response = NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Queueing response (%u) for /track/deposit (%s).\n", + (unsigned int) rctx->response_code, + ret ? "OK" : "FAILED"); + return ret; + } + uri = MHD_lookup_connection_value (connection, MHD_GET_ARGUMENT_KIND, "exchange"); if (NULL == uri) return TMH_RESPONSE_reply_external_error (connection, "exchange argument missing"); + rctx->uri = GNUNET_strdup (uri); + str = MHD_lookup_connection_value (connection, MHD_GET_ARGUMENT_KIND, "wtid"); @@ -222,15 +440,39 @@ MH_handler_track_deposit (struct TMH_RequestHandler *rh, strlen (str), &rctx->wtid, sizeof (rctx->wtid))) + { return TMH_RESPONSE_reply_external_error (connection, "wtid argument malformed"); - rctx->eh = TALER_EXCHANGE_connect (NULL /* FIXME */, - uri, - &cert_cb, - rctx, - TALER_EXCHANGE_OPTION_END); + } + + /* FIXME: check if reply is already in database! */ + ret = db->find_proof_by_wtid (db->cls, + rctx->uri, + &rctx->wtid, + &proof_cb, + rctx); + if (0 != rctx->response_code) + { + ret = MHD_queue_response (connection, + rctx->response_code, + rctx->response); + if (NULL != rctx->response) + { + MHD_destroy_response (rctx->response); + rctx->response = NULL; + } + return ret; + } - GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Suspending /track/deposit handling while working with the exchange\n"); + MHD_suspend_connection (connection); + rctx->fo = TMH_EXCHANGES_find_exchange (uri, + &process_track_deposit_with_exchange, + rctx); + rctx->timeout_task = GNUNET_SCHEDULER_add_delayed (TRACK_TIMEOUT, + &handle_track_deposit_timeout, + rctx); return MHD_NO; } |