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