taler-merchant-setup-reserve.c (12394B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2020 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it under the 6 terms of the GNU General Public License as published by the Free Software 7 Foundation; either version 3, or (at your option) any later version. 8 9 TALER is distributed in the hope that it will be useful, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 13 You should have received a copy of the GNU General Public License along with 14 TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file merchant-tools/taler-merchant-setup-reserve.c 18 * @brief Create reserve for tipping 19 * @author Christian Grothoff 20 */ 21 #include "platform.h" 22 #include <taler/taler_util.h> 23 #include <microhttpd.h> 24 #include <gnunet/gnunet_util_lib.h> 25 #include "taler_merchant_service.h" 26 27 /** 28 * How often do we try before giving up? 29 */ 30 #define MAX_TRIES 30 31 32 /** 33 * Return value from main(). 34 */ 35 static int global_ret; 36 37 /** 38 * Initial amount the reserve will be filled with. 39 */ 40 static struct TALER_Amount initial_amount; 41 42 /** 43 * Base URL of the merchant (with instance) to create the reserve for. 44 */ 45 static char *merchant_base_url; 46 47 /** 48 * Base URL of the exchange to create the reserve at. 49 */ 50 static char *exchange_base_url; 51 52 /** 53 * Wire method to use. 54 */ 55 static char *wire_method; 56 57 /** 58 * Operation handle. 59 */ 60 static struct TALER_MERCHANT_PostReservesHandle *prh; 61 62 /** 63 * Our context for making HTTP requests. 64 */ 65 static struct GNUNET_CURL_Context *ctx; 66 67 /** 68 * Reschedule context. 69 */ 70 static struct GNUNET_CURL_RescheduleContext *rc; 71 72 /** 73 * Username and password to use for client authentication 74 * (optional). 75 */ 76 static char *userpass; 77 78 /** 79 * Type of the client's TLS certificate (optional). 80 */ 81 static char *certtype; 82 83 /** 84 * File with the client's TLS certificate (optional). 85 */ 86 static char *certfile; 87 88 /** 89 * File with the client's TLS private key (optional). 90 */ 91 static char *keyfile; 92 93 /** 94 * This value goes in the Authorization:-header. 95 */ 96 static char *apikey; 97 98 /** 99 * Passphrase to decrypt client's TLS private key file (optional). 100 */ 101 static char *keypass; 102 103 /** 104 * How often have we tried? 105 */ 106 static unsigned int tries; 107 108 /** 109 * Task to do the main work. 110 */ 111 static struct GNUNET_SCHEDULER_Task *task; 112 113 114 /** 115 * Shutdown task (invoked when the process is being terminated) 116 * 117 * @param cls NULL 118 */ 119 static void 120 do_shutdown (void *cls) 121 { 122 if (NULL != task) 123 { 124 GNUNET_SCHEDULER_cancel (task); 125 task = NULL; 126 } 127 if (NULL != ctx) 128 { 129 GNUNET_CURL_fini (ctx); 130 ctx = NULL; 131 } 132 if (NULL != rc) 133 { 134 GNUNET_CURL_gnunet_rc_destroy (rc); 135 rc = NULL; 136 } 137 if (NULL != prh) 138 { 139 TALER_MERCHANT_reserves_post_cancel (prh); 140 prh = NULL; 141 } 142 } 143 144 145 /** 146 * Function that makes the call to setup the reserve. 147 * 148 * @param cls NULL 149 */ 150 static void 151 do_request (void *cls); 152 153 154 /** 155 * Callbacks of this type are used to work the result of submitting a 156 * POST /reserves request to a merchant 157 * 158 * @param cls closure 159 * @param prr response details 160 */ 161 static void 162 result_cb (void *cls, 163 const struct TALER_MERCHANT_PostReservesResponse *prr) 164 { 165 (void) cls; 166 prh = NULL; 167 switch (prr->hr.http_status) 168 { 169 case MHD_HTTP_OK: 170 { 171 char res_str[sizeof (prr->details.ok.reserve_pub) * 2 + 1]; 172 173 GNUNET_STRINGS_data_to_string (&prr->details.ok.reserve_pub, 174 sizeof (prr->details.ok.reserve_pub), 175 res_str, 176 sizeof (res_str)); 177 for (unsigned int i = 0; i<prr->details.ok.accounts_len; i++) 178 { 179 const struct TALER_EXCHANGE_WireAccount *wa 180 = &prr->details.ok.accounts[i]; 181 const char *payto_uri = wa->payto_uri; 182 bool skip = false; 183 184 for (unsigned int j = 0; j<wa->credit_restrictions_length; j++) 185 if (TALER_EXCHANGE_AR_DENY == 186 wa->credit_restrictions[j].type) 187 skip = true; 188 if (skip) 189 continue; 190 if (NULL != strchr (payto_uri, '?')) 191 fprintf (stdout, 192 "%s&message=%s\n", 193 payto_uri, 194 res_str); 195 else 196 fprintf (stdout, 197 "%s?message=%s\n", 198 payto_uri, 199 res_str); 200 if (NULL != wa->conversion_url) 201 fprintf (stdout, 202 "\tConversion needed: %s\n", 203 wa->conversion_url); 204 for (unsigned int j = 0; j<wa->credit_restrictions_length; j++) 205 { 206 const struct TALER_EXCHANGE_AccountRestriction *cr 207 = &wa->credit_restrictions[j]; 208 209 switch (cr->type) 210 { 211 case TALER_EXCHANGE_AR_INVALID: 212 GNUNET_assert (0); 213 break; 214 case TALER_EXCHANGE_AR_DENY: 215 GNUNET_assert (0); 216 break; 217 case TALER_EXCHANGE_AR_REGEX: 218 fprintf (stdout, 219 "\tCredit restriction: %s (%s)\n", 220 cr->details.regex.human_hint, 221 cr->details.regex.posix_egrep); 222 break; 223 } 224 } 225 } 226 } 227 break; 228 case MHD_HTTP_CONFLICT: 229 fprintf (stderr, 230 "Conflict trying to setup reserve: %u/%d\nHint: %s\n", 231 prr->hr.http_status, 232 (int) prr->hr.ec, 233 prr->hr.hint); 234 global_ret = 1; 235 break; 236 case MHD_HTTP_INTERNAL_SERVER_ERROR: 237 case MHD_HTTP_BAD_GATEWAY: 238 tries++; 239 if (tries < MAX_TRIES) 240 { 241 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 242 "Merchant failed, will try again.\n"); 243 task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, 244 &do_request, 245 NULL); 246 return; 247 } 248 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 249 "Merchant failed too often (%u/%d), giving up\n", 250 prr->hr.http_status, 251 prr->hr.ec); 252 global_ret = 1; 253 break; 254 default: 255 fprintf (stderr, 256 "Unexpected backend failure: %u/%d\nHint: %s\n", 257 prr->hr.http_status, 258 (int) prr->hr.ec, 259 prr->hr.hint); 260 global_ret = 1; 261 break; 262 } 263 GNUNET_SCHEDULER_shutdown (); 264 } 265 266 267 /** 268 * Main function that will be run. 269 * 270 * @param cls closure 271 * @param args remaining command-line arguments 272 * @param cfgfile name of the configuration file used (for saving, can be NULL!) 273 * @param config configuration 274 */ 275 static void 276 run (void *cls, 277 char *const *args, 278 const char *cfgfile, 279 const struct GNUNET_CONFIGURATION_Handle *config) 280 { 281 /* setup HTTP client event loop */ 282 ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, 283 &rc); 284 rc = GNUNET_CURL_gnunet_rc_create (ctx); 285 if (NULL != userpass) 286 GNUNET_CURL_set_userpass (ctx, 287 userpass); 288 if (NULL != keyfile) 289 GNUNET_CURL_set_tlscert (ctx, 290 certtype, 291 certfile, 292 keyfile, 293 keypass); 294 if (NULL != apikey) 295 { 296 char *auth_header; 297 298 GNUNET_asprintf (&auth_header, 299 "%s: %s", 300 MHD_HTTP_HEADER_AUTHORIZATION, 301 apikey); 302 if (GNUNET_OK != 303 GNUNET_CURL_append_header (ctx, 304 auth_header)) 305 { 306 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 307 "Failed so set %s header, trying without\n", 308 MHD_HTTP_HEADER_AUTHORIZATION); 309 } 310 GNUNET_free (auth_header); 311 } 312 /* setup termination logic */ 313 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, 314 NULL); 315 task = GNUNET_SCHEDULER_add_now (&do_request, 316 NULL); 317 } 318 319 320 static void 321 do_request (void *cls) 322 { 323 (void) cls; 324 task = NULL; 325 /* run actual (async) operation */ 326 prh = TALER_MERCHANT_reserves_post (ctx, 327 merchant_base_url, 328 &initial_amount, 329 exchange_base_url, 330 wire_method, 331 &result_cb, 332 NULL); 333 if (NULL == prh) 334 { 335 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 336 "Failed to begin operation with merchant backend!\n"); 337 global_ret = 2; 338 GNUNET_SCHEDULER_shutdown (); 339 return; 340 } 341 } 342 343 344 /** 345 * The main function for setting up reserves for tipping. 346 * 347 * @param argc number of arguments from the command line 348 * @param argv command line arguments 349 * @return 0 ok, 1 on error 350 */ 351 int 352 main (int argc, 353 char *const *argv) 354 { 355 struct GNUNET_GETOPT_CommandLineOption options[] = { 356 GNUNET_GETOPT_option_mandatory ( 357 TALER_getopt_get_amount ('a', 358 "amount", 359 "VALUE", 360 "amount to be transferred into the reserve", 361 &initial_amount)), 362 GNUNET_GETOPT_option_string ('A', 363 "auth", 364 "USERNAME:PASSWORD", 365 "use the given USERNAME and PASSWORD for client authentication", 366 &userpass), 367 GNUNET_GETOPT_option_string ('C', 368 "cert", 369 "CERTFILE", 370 "name of the TLS client certificate file", 371 &certfile), 372 GNUNET_GETOPT_option_mandatory ( 373 GNUNET_GETOPT_option_string ('e', 374 "exchange-url", 375 "URL", 376 "base URL of the exchange to create the reserve at", 377 &exchange_base_url)), 378 GNUNET_GETOPT_option_string ('k', 379 "key", 380 "KEYFILE", 381 "file with the private TLS key for TLS client authentication", 382 &keyfile), 383 GNUNET_GETOPT_option_mandatory ( 384 GNUNET_GETOPT_option_string ('m', 385 "merchant-url", 386 "URL", 387 "base URL of the merchant backend's REST API", 388 &merchant_base_url)), 389 GNUNET_GETOPT_option_string ('p', 390 "pass", 391 "KEYFILEPASSPHRASE", 392 "passphrase needed to decrypt the TLS client private key file", 393 &keypass), 394 GNUNET_GETOPT_option_string ('K', 395 "apikey", 396 "APIKEY", 397 "API key to use in the HTTP request", 398 &apikey), 399 GNUNET_GETOPT_option_string ('t', 400 "type", 401 "CERTTYPE", 402 "type of the TLS client certificate, defaults to PEM if not specified", 403 &certtype), 404 GNUNET_GETOPT_option_version (PACKAGE_VERSION "-" VCS_VERSION), 405 GNUNET_GETOPT_option_mandatory ( 406 GNUNET_GETOPT_option_string ('w', 407 "wire-method", 408 "METHOD", 409 "wire method to use for the wire transfer (i.e. IBAN)", 410 &wire_method)), 411 GNUNET_GETOPT_OPTION_END 412 }; 413 enum GNUNET_GenericReturnValue ret; 414 415 /* force linker to link against libtalerutil; if we do 416 not do this, the linker may "optimize" libtalerutil 417 away and skip #TALER_OS_init(), which we do need */ 418 (void) TALER_project_data_default (); 419 ret = GNUNET_PROGRAM_run ( 420 argc, argv, 421 "taler-merchant-setup-reserve", 422 gettext_noop ("Setup reserve for tipping"), 423 options, 424 &run, NULL); 425 if (GNUNET_SYSERR == ret) 426 return 3; 427 if (GNUNET_NO == ret) 428 return 0; 429 return global_ret; 430 } 431 432 433 /* end of taler-merchant-setup-reserve.c */