cash2ecash

cash2ecash: cash acceptor that issues digital cash (experimental)
Log | Files | Refs | Submodules | README | LICENSE

bank_api_account_withdrawal.c (17892B)


      1 #include <curl/curl.h>
      2 #include <curl/easy.h>
      3 #include <gnunet/gnunet_common.h>
      4 #include <jansson.h>
      5 #include <microhttpd.h>
      6 #include <stdbool.h>
      7 #include <stdio.h>
      8 #include <taler/platform.h>
      9 #include "taler_bank_service_cash2ecash.h"
     10 #include <taler/taler_curl_lib.h>
     11 #include <gnunet/gnunet_json_lib.h>
     12 #include <taler/taler_json_lib.h>
     13 #include "bank_api_common.h"
     14 
     15 
     16 
     17 
     18 
     19 struct TALER_BANK_AccountWithdrawalHandle
     20 {
     21   /**
     22    *The url for this request.
     23    */
     24   char *request_url;
     25 
     26   /**
     27      POST context.
     28   */
     29   struct TALER_CURL_PostContext post_ctx;
     30 
     31   /**
     32    * Handle for the request.
     33    */
     34   struct GNUNET_CURL_Job *job;
     35 
     36   /**
     37    * Function to call with the result.
     38    */
     39   TALER_BANK_AccountWithdrawalCallback cb;
     40 
     41   /**
     42    * Closure for @a cb.
     43    */
     44   void *cb_cls;
     45 };
     46 
     47 
     48 
     49 
     50 
     51 
     52 /**
     53  * Function called when we're done processing the
     54  * HTTP /account/$ACC/withdrawals request.
     55  *
     56  * @param cls the `struct TALER_BANK_AccountWithdrawalHandle`
     57  * @param response_code HTTP response code, 0 on error
     58  * @param response parsed JSON result, NULL on error
     59  */
     60 static void
     61 handle_account_withdrawal_finished (void *cls,
     62                                long response_code,
     63                                const void *response)
     64 {
     65   struct TALER_BANK_AccountWithdrawalHandle *aai = cls;
     66   const json_t *j = response;
     67   struct TALER_BANK_AccountWithdrawalResponse ir = {
     68     .http_status = response_code,
     69     .response = response
     70   };
     71 
     72   aai->job = NULL;
     73   switch (response_code)
     74   {
     75   case 0:
     76     ir.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     77     break;
     78   case MHD_HTTP_OK:
     79     {
     80       struct GNUNET_JSON_Specification spec[] = {
     81         GNUNET_JSON_spec_string ("withdrawal_id",
     82                                  &ir.details.ok.withdrawal_id),
     83         GNUNET_JSON_spec_string ("taler_withdraw_uri",
     84                                     &ir.details.ok.taler_withdraw_uri),
     85         GNUNET_JSON_spec_end ()
     86       };
     87 
     88       if (GNUNET_OK !=
     89           GNUNET_JSON_parse (j,
     90                              spec,
     91                              NULL, NULL))
     92       {
     93         GNUNET_break_op (0);
     94         ir.http_status = 0;
     95         ir.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     96         break;
     97       }
     98     }
     99     break;
    100   case MHD_HTTP_BAD_REQUEST:
    101     /* This should never happen, either us or the bank is buggy
    102        (or API version conflict); just pass JSON reply to the application */
    103     GNUNET_break_op (0);
    104     ir.ec = TALER_JSON_get_error_code (j);
    105     break;
    106   case MHD_HTTP_FORBIDDEN:
    107     /* Access denied */
    108     ir.ec = TALER_JSON_get_error_code (j);
    109     break;
    110   case MHD_HTTP_UNAUTHORIZED:
    111     /* Nothing really to verify, bank says invalid or missing credentials; we should
    112        pass the JSON reply to the application */
    113     ir.ec = TALER_JSON_get_error_code (j);
    114     break;
    115   case MHD_HTTP_NOT_FOUND:
    116     /* Nothing really to verify, maybe account really does not exist.
    117        We should pass the JSON reply to the application */
    118     ir.ec = TALER_JSON_get_error_code (j);
    119     break;
    120   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    121     /* Server had an internal issue; we should retry, but this API
    122        leaves this to the application */
    123     ir.ec = TALER_JSON_get_error_code (j);
    124     break;
    125   case MHD_HTTP_CONFLICT:
    126     /* The account does not have sufficient funds */
    127     ir.ec = TALER_JSON_get_error_code (j);
    128     break;
    129   default:
    130     /* unexpected response code */
    131     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    132                 "Unexpected response code %u\n",
    133                 (unsigned int) response_code);
    134     GNUNET_break (0);
    135     ir.ec = TALER_JSON_get_error_code (j);
    136     break;
    137   }
    138   aai->cb (aai->cb_cls,
    139            &ir);
    140   TALER_BANK_account_withdrawal_cancel (aai);
    141 }
    142 
    143 
    144 
    145 
    146 struct TALER_BANK_AccountWithdrawalHandle *
    147 TALER_BANK_account_withdrawal (
    148   struct GNUNET_CURL_Context *ctx,
    149   const struct TALER_BANK_AuthenticationData *auth,
    150   const char *account_name,
    151   const struct TALER_Amount *amount,
    152   const struct TALER_Amount *suggested_amount,
    153   const bool *no_amount_to_wallet,
    154   TALER_BANK_AccountWithdrawalCallback res_cb,
    155   void *res_cb_cls)
    156 {
    157   struct TALER_BANK_AccountWithdrawalHandle *awh;
    158   json_t *withdrawal_req;
    159   CURL *eh;
    160 
    161   withdrawal_req = GNUNET_JSON_PACK(
    162       GNUNET_JSON_pack_allow_null(TALER_JSON_pack_amount("amount", amount)),
    163       GNUNET_JSON_pack_allow_null(
    164           TALER_JSON_pack_amount("suggested_amount", suggested_amount)),
    165       GNUNET_JSON_pack_allow_null(
    166 	  GNUNET_JSON_pack_bool("no_amount_to_wallet", *no_amount_to_wallet)));
    167   if (NULL == withdrawal_req)
    168   {
    169     GNUNET_break (0);
    170     return NULL;
    171   }
    172   awh = GNUNET_new (struct TALER_BANK_AccountWithdrawalHandle);
    173   awh->cb = res_cb;
    174   awh->cb_cls = res_cb_cls;
    175   {
    176     char *path;
    177 
    178     GNUNET_asprintf (&path,
    179                      "accounts/%s/withdrawals",
    180                      account_name);
    181     awh->request_url = TALER_url_join (auth->wire_gateway_url,
    182                                        path,
    183                                        NULL);
    184     GNUNET_free (path);
    185   }
    186   if (NULL == awh->request_url)
    187   {
    188     GNUNET_free (awh);
    189     json_decref (withdrawal_req);
    190     return NULL;
    191   }
    192   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    193               "Posting withdrawal request at `%s'\n",
    194               awh->request_url);
    195   awh->post_ctx.headers
    196     = curl_slist_append (
    197         awh->post_ctx.headers,
    198         "Content-Type: application/json");
    199 
    200   eh = curl_easy_init ();
    201   if ( (NULL == eh) ||
    202        (GNUNET_OK !=
    203         TALER_BANK_setup_auth_ (eh,
    204                                 auth)) ||
    205        (CURLE_OK !=
    206         curl_easy_setopt (eh,
    207                           CURLOPT_URL,
    208                           awh->request_url)) ||
    209        (GNUNET_OK !=
    210         TALER_curl_easy_post (&awh->post_ctx,
    211                               eh,
    212                               withdrawal_req)) )
    213   {
    214     GNUNET_break (0);
    215     TALER_BANK_account_withdrawal_cancel (awh);
    216     if (NULL != eh)
    217       curl_easy_cleanup (eh);
    218     json_decref (withdrawal_req);
    219     return NULL;
    220   }
    221   json_decref (withdrawal_req);
    222   awh->job = GNUNET_CURL_job_add2 (ctx,
    223                                    eh,
    224                                    awh->post_ctx.headers,
    225                                    &handle_account_withdrawal_finished,
    226                                    awh);
    227   GNUNET_assert (NULL != awh->job);
    228   return awh;
    229 }
    230 
    231 void
    232 TALER_BANK_account_withdrawal_cancel (
    233   struct TALER_BANK_AccountWithdrawalHandle *awh)
    234 {
    235   if (NULL != awh->job)
    236   {
    237     GNUNET_CURL_job_cancel (awh->job);
    238     awh->job = NULL;
    239   }
    240   TALER_curl_easy_post_finished (&awh->post_ctx);
    241   GNUNET_free (awh->request_url);
    242   GNUNET_free (awh);
    243 }
    244 
    245 
    246 
    247 
    248 /* ********************* /accounts/$ACC/withdrawals/$WITHDRAWAL_ID/confirm *********************** */
    249 
    250 struct TALER_BANK_AccountWithdrawalConfirmHandle
    251 {
    252   /**
    253    *The url for this request.
    254    */
    255   char *request_url;
    256 
    257   /**
    258      POST context.
    259   */
    260   struct TALER_CURL_PostContext post_ctx;
    261 
    262   /**
    263    * Handle for the request.
    264    */
    265   struct GNUNET_CURL_Job *job;
    266 
    267   /**
    268    * Function to call with the result.
    269    */
    270   TALER_BANK_AccountWithdrawalConfirmCallback cb;
    271 
    272   /**
    273    * Closure for @a cb.
    274    */
    275   void *cb_cls;
    276 };
    277 
    278 /**
    279  * Function called when we're done processing the
    280  * HTTP /account/$ACC/withdrawals/$WITHDRAWAL_ID/confirm request.
    281  *
    282  * @param cls the `struct TALER_BANK_AccountWithdrawalConfirmHandle`
    283  * @param response_code HTTP response code, 0 on error
    284  * @param response parsed JSON result, NULL on error
    285  */
    286 static void
    287 handle_account_withdrawal_confirm_finished (void *cls,
    288                                long response_code,
    289                                const void *response)
    290 {
    291   struct TALER_BANK_AccountWithdrawalConfirmHandle *aai = cls;
    292   const json_t *j = response;
    293   struct TALER_BANK_AccountWithdrawalConfirmResponse ir = {
    294     .http_status = response_code,
    295     .response = response
    296   };
    297 
    298   aai->job = NULL;
    299   switch (response_code)
    300   {
    301   case 0:
    302     ir.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    303     break;
    304   case MHD_HTTP_ACCEPTED:
    305     fprintf(stderr, "There is no implementation for 2FA yet");
    306     ir.ec =TALER_JSON_get_error_code(j);
    307     break;
    308   case MHD_HTTP_NO_CONTENT:
    309     break;
    310   case MHD_HTTP_BAD_REQUEST:
    311     /* This should never happen, either us or the bank is buggy
    312        (or API version conflict); just pass JSON reply to the application */
    313     GNUNET_break_op (0);
    314     ir.ec = TALER_JSON_get_error_code (j);
    315     break;
    316   case MHD_HTTP_FORBIDDEN:
    317     /* Access denied */
    318     ir.ec = TALER_JSON_get_error_code (j);
    319     break;
    320   case MHD_HTTP_UNAUTHORIZED:
    321     /* Nothing really to verify, bank says invalid or missing credentials; we should
    322        pass the JSON reply to the application */
    323     ir.ec = TALER_JSON_get_error_code (j);
    324     break;
    325   case MHD_HTTP_NOT_FOUND:
    326     /* Nothing really to verify, maybe account really does not exist.
    327        We should pass the JSON reply to the application */
    328     ir.ec = TALER_JSON_get_error_code (j);
    329     break;
    330   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    331     /* Server had an internal issue; we should retry, but this API
    332        leaves this to the application */
    333     ir.ec = TALER_JSON_get_error_code (j);
    334     break;
    335   case MHD_HTTP_CONFLICT:
    336     /* The account does not have sufficient funds */
    337     ir.ec = TALER_JSON_get_error_code (j);
    338     break;
    339   default:
    340     /* unexpected response code */
    341     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    342                 "Unexpected response code %u\n",
    343                 (unsigned int) response_code);
    344     GNUNET_break (0);
    345     ir.ec = TALER_JSON_get_error_code (j);
    346     break;
    347   }
    348   aai->cb (aai->cb_cls,
    349            &ir);
    350   TALER_BANK_account_withdrawal_confirm_cancel (aai);
    351 }
    352 
    353 
    354 
    355 
    356 struct TALER_BANK_AccountWithdrawalConfirmHandle *
    357 TALER_BANK_account_withdrawal_confirm (
    358   struct GNUNET_CURL_Context *ctx,
    359   const struct TALER_BANK_AuthenticationData *auth,
    360   const char *account_name,
    361   const char *withdrawal_id,
    362   const struct TALER_Amount *amount,
    363   TALER_BANK_AccountWithdrawalConfirmCallback res_cb,
    364   void *res_cb_cls)
    365 {
    366   struct TALER_BANK_AccountWithdrawalConfirmHandle *awch;
    367   json_t *wconfirm_req;
    368   CURL *eh;
    369 
    370   wconfirm_req = GNUNET_JSON_PACK(
    371     GNUNET_JSON_pack_allow_null(
    372       TALER_JSON_pack_amount("amount",
    373 			     amount)));
    374   if (NULL == wconfirm_req)
    375   {
    376     GNUNET_break (0);
    377     return NULL;
    378   }
    379   awch = GNUNET_new (struct TALER_BANK_AccountWithdrawalConfirmHandle);
    380   awch->cb = res_cb;
    381   awch->cb_cls = res_cb_cls;
    382   {
    383     char *path;
    384 
    385     GNUNET_asprintf (&path,
    386                      "accounts/%s/withdrawals/%s/confirm",
    387                      account_name, withdrawal_id);
    388     awch->request_url = TALER_url_join (auth->wire_gateway_url,
    389                                        path,
    390                                        NULL);
    391     GNUNET_free (path);
    392   }
    393   if (NULL == awch->request_url)
    394   {
    395     GNUNET_free (awch);
    396     json_decref (wconfirm_req);
    397     return NULL;
    398   }
    399   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    400               "Posting withdrawal confirmation request at `%s'\n",
    401               awch->request_url);
    402   awch->post_ctx.headers
    403     = curl_slist_append (
    404         awch->post_ctx.headers,
    405         "Content-Type: application/json");
    406 
    407   eh = curl_easy_init ();
    408   if ( (NULL == eh) ||
    409        (GNUNET_OK !=
    410         TALER_BANK_setup_auth_ (eh,
    411                                 auth)) ||
    412        (CURLE_OK !=
    413         curl_easy_setopt (eh,
    414                           CURLOPT_URL,
    415                           awch->request_url)) ||
    416        (GNUNET_OK !=
    417         TALER_curl_easy_post (&awch->post_ctx,
    418                               eh,
    419                               wconfirm_req)) )
    420   {
    421     GNUNET_break (0);
    422     TALER_BANK_account_withdrawal_confirm_cancel (awch);
    423     if (NULL != eh)
    424       curl_easy_cleanup (eh);
    425     json_decref (wconfirm_req);
    426     return NULL;
    427   }
    428   json_decref (wconfirm_req);
    429   awch->job = GNUNET_CURL_job_add2 (ctx,
    430                                    eh,
    431                                    awch->post_ctx.headers,
    432                                    &handle_account_withdrawal_confirm_finished,
    433                                    awch);
    434   GNUNET_assert (NULL != awch->job);
    435   return awch;
    436 }
    437 
    438 void
    439 TALER_BANK_account_withdrawal_confirm_cancel (
    440   struct TALER_BANK_AccountWithdrawalConfirmHandle *awch)
    441 {
    442   if (NULL != awch->job)
    443   {
    444     GNUNET_CURL_job_cancel (awch->job);
    445     awch->job = NULL;
    446   }
    447   TALER_curl_easy_post_finished (&awch->post_ctx);
    448   GNUNET_free (awch->request_url);
    449   GNUNET_free (awch);
    450 }
    451 
    452 /* ********************* /withdrawals/$WITHDRAWAL_ID *********************** */
    453 
    454 struct TALER_BANK_WithdrawalIDInfoHandle
    455 {
    456   /**
    457    *The url for this request.
    458    */
    459   char *request_url;
    460 
    461   /**
    462      POST context.
    463   */
    464   struct TALER_CURL_PostContext post_ctx;
    465 
    466   /**
    467    * Handle for the request.
    468    */
    469   struct GNUNET_CURL_Job *job;
    470 
    471   /**
    472    * Function to call with the result.
    473    */
    474   TALER_BANK_WithdrawalIDInfoCallback cb;
    475 
    476   /**
    477    * Closure for @a cb.
    478    */
    479   void *cb_cls;
    480 };
    481 
    482 /**
    483  * Function called when we're done processing the
    484  * HTTP /withdrawals/$WITHDRAWAL_ID request.
    485  *
    486  * @param cls the `struct TALER_BANK_WithdrawalIDInfoandle`
    487  * @param response_code HTTP response code, 0 on error
    488  * @param response parsed JSON result, NULL on error
    489  */
    490 static void
    491 handle_withdrawalID_info_finished (void *cls,
    492                                long response_code,
    493                                const void *response)
    494 {
    495   struct TALER_BANK_WithdrawalIDInfoHandle *aai = cls;
    496   const json_t *j = response;
    497   struct TALER_BANK_WithdrawalIDInfoResponse ir = {
    498     .http_status = response_code,
    499     .response = response
    500   };
    501 
    502   aai->job = NULL;
    503   switch (response_code)
    504   {
    505   case 0:
    506     ir.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    507     break;
    508   case MHD_HTTP_OK:
    509     {
    510       struct GNUNET_JSON_Specification spec[] = {
    511         GNUNET_JSON_spec_string ("status",
    512                                  &ir.details.ok.status),
    513 	GNUNET_JSON_spec_mark_optional(
    514 	TALER_JSON_spec_amount_any("amount",
    515 				   &ir.details.ok.amount), NULL),
    516 	GNUNET_JSON_spec_mark_optional(
    517 	TALER_JSON_spec_amount_any("suggested_amount",
    518 				   &ir.details.ok.suggested_amount), NULL),
    519 	GNUNET_JSON_spec_mark_optional(
    520 	GNUNET_JSON_spec_bool("no_amount_to_wallet", &ir.details.ok.no_amount_to_wallet), NULL),
    521         GNUNET_JSON_spec_string ("username",
    522 				 &ir.details.ok.username),
    523 	GNUNET_JSON_spec_mark_optional(
    524 	GNUNET_JSON_spec_string ("selected_reserve_pub",
    525 				 &ir.details.ok.selected_reserve_pub), NULL),
    526 	GNUNET_JSON_spec_mark_optional(
    527 	GNUNET_JSON_spec_string ("selected_exchange_account",
    528 				 &ir.details.ok.selected_exchange_account), NULL),
    529         GNUNET_JSON_spec_end ()
    530       };
    531 
    532       if (GNUNET_OK !=
    533           GNUNET_JSON_parse (j,
    534                              spec,
    535                              NULL, NULL))
    536 	{
    537 	  GNUNET_break_op (0);
    538 	  ir.http_status = 0;
    539 	  ir.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    540 	  break;
    541 	}
    542     }
    543     break;
    544   case MHD_HTTP_BAD_REQUEST:
    545     /* This should never happen, either us or the bank is buggy
    546        (or API version conflict); just pass JSON reply to the application */
    547     GNUNET_break_op (0);
    548     ir.ec = TALER_JSON_get_error_code (j);
    549     break;
    550   case MHD_HTTP_NOT_FOUND:
    551     /* Nothing really to verify, maybe withdrawal id really does not exist.
    552        We should pass the JSON reply to the application */
    553     ir.ec = TALER_JSON_get_error_code (j);
    554     break;
    555   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    556     /* Server had an internal issue; we should retry, but this API
    557        leaves this to the application */
    558     ir.ec = TALER_JSON_get_error_code (j);
    559     break;
    560   default:
    561     /* unexpected response code */
    562     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    563                 "Unexpected response code %u\n",
    564                 (unsigned int) response_code);
    565     GNUNET_break (0);
    566     ir.ec = TALER_JSON_get_error_code (j);
    567     break;
    568   }
    569   aai->cb (aai->cb_cls,
    570            &ir);
    571   TALER_BANK_withdrawalID_info_cancel (aai);
    572 }
    573 
    574 
    575 
    576 
    577 struct TALER_BANK_WithdrawalIDInfoHandle *
    578 TALER_BANK_withdrawalID_info (
    579   struct GNUNET_CURL_Context *ctx,
    580   const struct TALER_BANK_AuthenticationData *auth,
    581   const char *withdrawal_id,
    582   TALER_BANK_WithdrawalIDInfoCallback res_cb,
    583   void *res_cb_cls)
    584 {
    585   struct TALER_BANK_WithdrawalIDInfoHandle *awch;
    586   CURL *eh;
    587 
    588   awch = GNUNET_new (struct TALER_BANK_WithdrawalIDInfoHandle);
    589   awch->cb = res_cb;
    590   awch->cb_cls = res_cb_cls;
    591   {
    592     char *path;
    593 
    594     GNUNET_asprintf (&path,
    595                      "withdrawals/%s",
    596                      withdrawal_id);
    597     awch->request_url = TALER_url_join (auth->wire_gateway_url,
    598                                        path,
    599                                        NULL);
    600     GNUNET_free (path);
    601   }
    602   if (NULL == awch->request_url)
    603   {
    604     GNUNET_free (awch);
    605     return NULL;
    606   }
    607   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    608               "Posting withdrawal id info request at `%s'\n",
    609               awch->request_url);
    610 
    611   eh = curl_easy_init ();
    612   if ( (NULL == eh) ||
    613        (CURLE_OK !=
    614         curl_easy_setopt (eh,
    615                           CURLOPT_URL,
    616                           awch->request_url)) ||
    617        (CURLE_OK !=
    618 	curl_easy_setopt(eh, CURLOPT_HTTPGET, 1)))
    619   {
    620     GNUNET_break (0);
    621     TALER_BANK_withdrawalID_info_cancel (awch);
    622     if (NULL != eh)
    623       curl_easy_cleanup (eh);
    624     return NULL;
    625   }
    626   awch->job = GNUNET_CURL_job_add (ctx,
    627                                    eh,
    628                                    &handle_withdrawalID_info_finished,
    629                                    awch);
    630   GNUNET_assert (NULL != awch->job);
    631   return awch;
    632 }
    633 
    634 void
    635 TALER_BANK_withdrawalID_info_cancel (
    636   struct TALER_BANK_WithdrawalIDInfoHandle *awch)
    637 {
    638   if (NULL != awch->job)
    639   {
    640     GNUNET_CURL_job_cancel (awch->job);
    641     awch->job = NULL;
    642   }
    643   
    644   TALER_curl_easy_post_finished (&awch->post_ctx); //might not work; are there any headers?
    645   /*Inside easy post finished
    646   curl_slist_free_all (ctx->headers);
    647   ctx->headers = NULL;
    648   GNUNET_free (ctx->json_enc);
    649   ctx->json_enc = NULL;
    650   */
    651   GNUNET_free (awch->request_url);
    652   GNUNET_free (awch);
    653 }
    654 
    655