cash2ecash

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

bank_api_get_config.c (8012B)


      1 #include <microhttpd.h>
      2 #include "bank_api_get_config.h"
      3 #include "bank_api_curl_defaults.h"
      4 #include "taler/taler_json_lib.h"
      5 
      6 /**
      7  * Which revision of the Taler core-bank protocol is implemented
      8  * by this library?  Used to determine compatibility.
      9  */
     10 #define TALER_PROTOCOL_CURRENT 12
     11 
     12 /**
     13  * How many revisions back are we compatible to?
     14  */
     15 #define TALER_PROTOCOL_AGE 0
     16 
     17 
     18 /**
     19  * Log error related to CURL operations.
     20  *
     21  * @param type log level
     22  * @param function which function failed to run
     23  * @param code what was the curl error code
     24  */
     25 #define CURL_STRERROR(type, function, code)      \
     26         GNUNET_log (type, \
     27                     "Curl function `%s' has failed at `%s:%d' with error: %s", \
     28                     function, __FILE__, __LINE__, curl_easy_strerror (code));
     29 
     30 
     31 /**
     32  * Handle for the get config request.
     33  */
     34 struct TALER_BANK_GetConfigHandle
     35 {
     36       /**
     37    * The context of this handle
     38    */
     39   struct GNUNET_CURL_Context *ctx;
     40 
     41   /**
     42    * Function to call with the ,
     43    * NULL if this has already been done.
     44    */
     45   TALER_BANK_ConfigCallback config_cb;
     46 
     47   /**
     48    * Closure to pass to
     49    */
     50   void *config_cb_cls;
     51 
     52   /**
     53    * Data for the request to get the /config of a bank,
     54    * NULL once we are past stage #MHS_INIT.
     55    */
     56   struct GNUNET_CURL_Job *job;
     57 
     58   /**
     59    * The whole request line
     60    */
     61   char *job_url;
     62 };
     63 
     64 
     65 /**
     66  * Decode the JSON in @a resp_obj from the /config response
     67  *
     68  * @param[in] resp_obj JSON object to parse
     69  * @param[in,out] vi where to store the results we decoded
     70  * @param[out] vc where to store config compatibility data
     71  * @return #TALER_EC_NONE on success
     72  */
     73 static enum TALER_ErrorCode
     74 decode_config_json (const json_t *resp_obj,
     75                     struct TALER_BANK_ConfigInformation *vi,
     76                     enum TALER_BANK_VersionCompatibility *vc)
     77 {
     78   struct TALER_JSON_ProtocolVersion pv;
     79   const char *ver;
     80   bool bank_name_missing;
     81 
     82   struct GNUNET_JSON_Specification spec[] = {
     83     TALER_JSON_spec_version ("version",
     84                              &pv),
     85     GNUNET_JSON_spec_string ("version",
     86                              &ver),
     87     GNUNET_JSON_spec_string ("currency",
     88                                  &vi->currency),
     89     GNUNET_JSON_spec_mark_optional (
     90     GNUNET_JSON_spec_string ("bank_name",
     91                               &vi->bank_name),
     92                               &bank_name_missing),
     93     GNUNET_JSON_spec_end ()
     94   };
     95 
     96   if (JSON_OBJECT != json_typeof (resp_obj))
     97   {
     98     GNUNET_break_op (0);
     99     return TALER_EC_GENERIC_JSON_INVALID;
    100   }
    101   if (GNUNET_OK !=
    102       GNUNET_JSON_parse (resp_obj,
    103                          spec,
    104                          NULL, NULL))
    105   {
    106     GNUNET_break_op (0);
    107     return TALER_EC_GENERIC_JSON_INVALID;
    108   }
    109 
    110   /* Set default fot optional values */
    111   if (bank_name_missing)
    112   {
    113     vi->bank_name = GNUNET_strdup("Taler Bank");
    114   }
    115 
    116   /* Version comparison */
    117   vi->version = ver;
    118   *vc = TALER_BANK_VC_MATCH;
    119   if (TALER_PROTOCOL_CURRENT < pv.current)
    120   {
    121     *vc |= TALER_BANK_VC_NEWER;
    122     if (TALER_PROTOCOL_CURRENT < pv.current - pv.age)
    123       *vc |= TALER_BANK_VC_INCOMPATIBLE;
    124   }
    125   if (TALER_PROTOCOL_CURRENT > pv.current)
    126   {
    127     *vc |= TALER_BANK_VC_OLDER;
    128     if (TALER_PROTOCOL_CURRENT - TALER_PROTOCOL_AGE > pv.current)
    129       *vc |= TALER_BANK_VC_INCOMPATIBLE;
    130   }
    131 
    132   struct GNUNET_JSON_Specification spec2[] = {
    133     GNUNET_JSON_spec_mark_optional (
    134     TALER_JSON_spec_amount ("min_wire_transfer_amount",
    135                                 vi->currency,
    136                                 &vi->min_wire_transfer_amount),
    137                                 NULL),
    138     GNUNET_JSON_spec_mark_optional (
    139     TALER_JSON_spec_amount ("max_wire_transfer_amount",
    140                              vi->currency,
    141                              &vi->max_wire_transfer_amount),
    142                              NULL),
    143     TALER_JSON_spec_currency_specification ("currency_specification",
    144                                             vi->currency,
    145                                             &vi->currency_specification),
    146     GNUNET_JSON_spec_mark_optional (
    147     TALER_JSON_spec_amount("wire_transfer_fees",
    148                             vi->currency,
    149                             &vi->wire_transfer_fees),
    150                             NULL),      
    151     GNUNET_JSON_spec_end ()
    152   };
    153 
    154   if (GNUNET_OK !=
    155       GNUNET_JSON_parse (resp_obj,
    156                          spec2,
    157                          NULL, NULL))
    158   {
    159     GNUNET_break_op (0);
    160     return TALER_EC_GENERIC_JSON_INVALID;
    161   }
    162 
    163   return TALER_EC_NONE;
    164 }
    165 
    166 
    167 /**
    168  * Callback used when http reply arived to a /config request.
    169  *
    170  * @param cls the `struct TALER_BANK_GetConfigHandle`
    171  * @param response_code HTTP response code or 0 on error
    172  * @param  gresp_obj JSON result, NULL on error, must be a `const json_t *`
    173  */
    174 static void
    175 response_cb(void *cls,
    176         long response_code,
    177         const void *gresp_obj)
    178 {
    179     struct TALER_BANK_GetConfigHandle *bank = cls;
    180     const json_t *resp_obj = gresp_obj;
    181     
    182     struct TALER_BANK_ConfigResponse cr = {
    183         .hr.response = resp_obj,
    184         .hr.http_status = (unsigned int)response_code
    185     };
    186     
    187     bank->job = NULL; //job was successfull, curl job cancel not needed anymore in cleanup
    188     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    189               "Received config from URL `%s' with status %ld.\n",
    190               bank->job_url,
    191               response_code);
    192     
    193     switch (response_code)
    194   {
    195   case 0:
    196     GNUNET_break_op (0);
    197     cr.hr.ec = TALER_EC_INVALID;
    198     break;
    199   case MHD_HTTP_OK:
    200     if (NULL == resp_obj)
    201     {
    202       GNUNET_break_op (0);
    203       cr.hr.http_status = 0;
    204       cr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    205       break;
    206     }
    207     cr.hr.ec = decode_config_json (resp_obj,
    208                                    &cr.details.ok.configi,
    209                                    &cr.details.ok.version_compa);
    210     if (TALER_EC_NONE != cr.hr.ec)
    211     {
    212       GNUNET_break_op (0);
    213       cr.hr.http_status = 0;
    214       break;
    215     }
    216     break;
    217   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    218     cr.hr.ec = TALER_JSON_get_error_code (resp_obj);
    219     cr.hr.hint = TALER_JSON_get_error_hint (resp_obj);
    220     break;
    221   default:
    222     cr.hr.ec = TALER_JSON_get_error_code (resp_obj);
    223     cr.hr.hint = TALER_JSON_get_error_hint (resp_obj);
    224     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    225                 "Unexpected response code %u/%d\n",
    226                 (unsigned int) response_code,
    227                 (int) cr.hr.ec);
    228     break;
    229   }
    230 
    231     bank->config_cb (bank->config_cb_cls, &cr);
    232     TALER_BANK_get_config_cancel(bank);
    233 }
    234 
    235 
    236 struct TALER_BANK_GetConfigHandle *
    237 TALER_BANK_get_config ( struct GNUNET_CURL_Context *ctx,
    238                         const char *url,
    239                         TALER_BANK_ConfigCallback config_cb,
    240                         void *config_cb_cls)
    241 {
    242     struct TALER_BANK_GetConfigHandle *bank;
    243     CURL *eh;
    244 
    245     bank = GNUNET_new(struct TALER_BANK_GetConfigHandle);
    246     bank->config_cb = config_cb;
    247     bank->config_cb_cls = config_cb_cls;
    248     bank->ctx = ctx;
    249     bank->job_url = TALER_url_join(url,"config",NULL);
    250     if(NULL == bank->job_url)
    251     {
    252         GNUNET_break(0);
    253         GNUNET_free(bank);
    254         return NULL;
    255     }
    256 
    257     GNUNET_log( GNUNET_ERROR_TYPE_INFO,
    258                 "Requesting bank config with URL `%s'.\n",
    259                 bank->job_url);
    260     eh = TALER_BANK_curl_easy_get_(bank->job_url);
    261     if(NULL == eh)
    262     {
    263         GNUNET_break(0);
    264         TALER_BANK_get_config_cancel(bank);
    265         return NULL;
    266     }
    267     bank->job = GNUNET_CURL_job_add(bank->ctx,
    268                                     eh,
    269                                     &response_cb,
    270                                     bank);
    271     if(NULL == bank->job)
    272     {
    273         GNUNET_break(0);
    274         TALER_BANK_get_config_cancel(bank);
    275         return NULL;
    276     }
    277             
    278     return bank;
    279 }
    280 
    281 
    282 
    283 void
    284 TALER_BANK_get_config_cancel ( struct TALER_BANK_GetConfigHandle *bank)
    285 {
    286     if(NULL != bank->job)
    287     {
    288         GNUNET_CURL_job_cancel(bank->job);
    289         bank->job = NULL;
    290     }
    291     GNUNET_free(bank->job_url);
    292     GNUNET_free(bank);
    293 }   
    294