exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

taler-exchange-offline.c (162377B)


      1 /*
      2    This file is part of TALER
      3    Copyright (C) 2020-2026 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 taler-exchange-offline.c
     18  * @brief Support for operations involving the exchange's offline master key.
     19  * @author Christian Grothoff
     20  */
     21 #include "platform.h"
     22 #include <gnunet/gnunet_json_lib.h>
     23 #include <gnunet/gnunet_util_lib.h>
     24 #include "taler/taler_json_lib.h"
     25 
     26 struct AmlStaffRequest;
     27 #define TALER_EXCHANGE_POST_MANAGEMENT_AML_OFFICERS_RESULT_CLOSURE \
     28         struct AmlStaffRequest
     29 #include "taler/exchange/post-management-aml-officers.h"
     30 
     31 struct DenomRevocationRequest;
     32 #define TALER_EXCHANGE_POST_MANAGEMENT_DENOMINATIONS_REVOKE_RESULT_CLOSURE \
     33         struct DenomRevocationRequest
     34 #include \
     35   "taler/exchange/post-management-denominations-H_DENOM_PUB-revoke.h"
     36 
     37 struct SignkeyRevocationRequest;
     38 #define TALER_EXCHANGE_POST_MANAGEMENT_SIGNKEYS_REVOKE_RESULT_CLOSURE \
     39         struct SignkeyRevocationRequest
     40 #include \
     41   "taler/exchange/post-management-signkeys-EXCHANGE_PUB-revoke.h"
     42 
     43 struct AuditorAddRequest;
     44 #define TALER_EXCHANGE_POST_MANAGEMENT_AUDITORS_RESULT_CLOSURE \
     45         struct AuditorAddRequest
     46 #include "taler/exchange/post-management-auditors.h"
     47 
     48 struct AuditorDelRequest;
     49 #define TALER_EXCHANGE_POST_MANAGEMENT_AUDITORS_DISABLE_RESULT_CLOSURE \
     50         struct AuditorDelRequest
     51 #include "taler/exchange/post-management-auditors-AUDITOR_PUB-disable.h"
     52 
     53 struct WireAddRequest;
     54 #define TALER_EXCHANGE_POST_MANAGEMENT_WIRE_RESULT_CLOSURE \
     55         struct WireAddRequest
     56 #include "taler/exchange/post-management-wire.h"
     57 
     58 struct WireDelRequest;
     59 #define TALER_EXCHANGE_POST_MANAGEMENT_WIRE_DISABLE_RESULT_CLOSURE \
     60         struct WireDelRequest
     61 #include "taler/exchange/post-management-wire-disable.h"
     62 
     63 struct WireFeeRequest;
     64 #define TALER_EXCHANGE_POST_MANAGEMENT_WIRE_FEES_RESULT_CLOSURE \
     65         struct WireFeeRequest
     66 #include "taler/exchange/post-management-wire-fee.h"
     67 
     68 struct GlobalFeeRequest;
     69 #define TALER_EXCHANGE_POST_MANAGEMENT_GLOBAL_FEES_RESULT_CLOSURE \
     70         struct GlobalFeeRequest
     71 #include "taler/exchange/post-management-global-fees.h"
     72 
     73 struct DrainProfitsRequest;
     74 #define TALER_EXCHANGE_POST_MANAGEMENT_DRAIN_RESULT_CLOSURE \
     75         struct DrainProfitsRequest
     76 #include "taler/exchange/post-management-drain.h"
     77 
     78 struct UploadKeysRequest;
     79 #define TALER_EXCHANGE_POST_MANAGEMENT_KEYS_RESULT_CLOSURE \
     80         struct UploadKeysRequest
     81 #include "taler/exchange/post-management-keys.h"
     82 
     83 struct PartnerAddRequest;
     84 #define TALER_EXCHANGE_POST_MANAGEMENT_PARTNERS_RESULT_CLOSURE \
     85         struct PartnerAddRequest
     86 #include "taler/exchange/post-management-partners.h"
     87 
     88 #define TALER_EXCHANGE_GET_MANAGEMENT_KEYS_RESULT_CLOSURE \
     89         char *const
     90 #include "taler/exchange/get-management-keys.h"
     91 
     92 #include <microhttpd.h>
     93 #include <regex.h>
     94 
     95 
     96 /**
     97  * Name of the input for the 'sign' and 'show' operation.
     98  * The last component --by convention-- identifies the protocol version
     99  * and should be incremented whenever the JSON format of the 'argument' changes.
    100  */
    101 #define OP_INPUT_KEYS "exchange-input-keys-0"
    102 
    103 /**
    104  * Name of the operation to 'disable auditor'
    105  * The last component --by convention-- identifies the protocol version
    106  * and should be incremented whenever the JSON format of the 'argument' changes.
    107  */
    108 #define OP_DISABLE_AUDITOR "exchange-disable-auditor-0"
    109 
    110 /**
    111  * Name of the operation to 'enable auditor'
    112  * The last component --by convention-- identifies the protocol version
    113  * and should be incremented whenever the JSON format of the 'argument' changes.
    114  */
    115 #define OP_ENABLE_AUDITOR "exchange-enable-auditor-0"
    116 
    117 /**
    118  * Name of the operation to 'enable wire'
    119  * The last component --by convention-- identifies the protocol version
    120  * and should be incremented whenever the JSON format of the 'argument' changes.
    121  */
    122 #define OP_ENABLE_WIRE "exchange-enable-wire-0"
    123 
    124 /**
    125  * Name of the operation to 'disable wire'
    126  * The last component --by convention-- identifies the protocol version
    127  * and should be incremented whenever the JSON format of the 'argument' changes.
    128  */
    129 #define OP_DISABLE_WIRE "exchange-disable-wire-0"
    130 
    131 /**
    132  * Name of the operation to set a 'wire-fee'
    133  * The last component --by convention-- identifies the protocol version
    134  * and should be incremented whenever the JSON format of the 'argument' changes.
    135  */
    136 #define OP_SET_WIRE_FEE "exchange-set-wire-fee-0"
    137 
    138 /**
    139  * Name of the operation to set a 'global-fee'
    140  * The last component --by convention-- identifies the protocol version
    141  * and should be incremented whenever the JSON format of the 'argument' changes.
    142  */
    143 #define OP_SET_GLOBAL_FEE "exchange-set-global-fee-0"
    144 
    145 /**
    146  * Name of the operation to 'upload' key signatures
    147  * The last component --by convention-- identifies the protocol version
    148  * and should be incremented whenever the JSON format of the 'argument' changes.
    149  */
    150 #define OP_UPLOAD_SIGS "exchange-upload-sigs-0"
    151 
    152 /**
    153  * Name of the operation to 'revoke-denomination' key
    154  * The last component --by convention-- identifies the protocol version
    155  * and should be incremented whenever the JSON format of the 'argument' changes.
    156  */
    157 #define OP_REVOKE_DENOMINATION "exchange-revoke-denomination-0"
    158 
    159 /**
    160  * Name of the operation to 'revoke-signkey'
    161  * The last component --by convention-- identifies the protocol version
    162  * and should be incremented whenever the JSON format of the 'argument' changes.
    163  */
    164 #define OP_REVOKE_SIGNKEY "exchange-revoke-signkey-0"
    165 
    166 /**
    167  * Show the offline signing key.
    168  * The last component --by convention-- identifies the protocol version
    169  * and should be incremented whenever the JSON format of the 'argument' changes.
    170  */
    171 #define OP_SETUP "exchange-setup-0"
    172 
    173 /**
    174  * Generate message to drain profits.
    175  */
    176 #define OP_DRAIN_PROFITS "exchange-drain-profits-0"
    177 
    178 /**
    179  * Setup AML staff.
    180  */
    181 #define OP_UPDATE_AML_STAFF "exchange-add-aml-staff-0"
    182 
    183 /**
    184  * Setup partner exchange for wad transfers.
    185  */
    186 #define OP_ADD_PARTNER "exchange-add-partner-0"
    187 
    188 /**
    189  * Our private key, initialized in #load_offline_key().
    190  */
    191 static struct TALER_MasterPrivateKeyP master_priv;
    192 
    193 /**
    194  * Our public key, initialized in #load_offline_key().
    195  */
    196 static struct TALER_MasterPublicKeyP master_pub;
    197 
    198 /**
    199  * Our context for making HTTP requests.
    200  */
    201 static struct GNUNET_CURL_Context *ctx;
    202 
    203 /**
    204  * Reschedule context for #ctx.
    205  */
    206 static struct GNUNET_CURL_RescheduleContext *rc;
    207 
    208 /**
    209  * Handle to the exchange's configuration
    210  */
    211 static const struct GNUNET_CONFIGURATION_Handle *kcfg;
    212 
    213 /**
    214  * Return value from main().
    215  */
    216 static int global_ret;
    217 
    218 /**
    219  * Input to consume.
    220  */
    221 static json_t *in;
    222 
    223 /**
    224  * Array of actions to perform.
    225  */
    226 static json_t *out;
    227 
    228 /**
    229  * Currency we have configured.
    230  */
    231 static char *currency;
    232 
    233 /**
    234  * URL of the exchange we are interacting with
    235  * as per our configuration.
    236  */
    237 static char *CFG_exchange_url;
    238 
    239 /**
    240  * A subcommand supported by this program.
    241  */
    242 struct SubCommand
    243 {
    244   /**
    245    * Name of the command.
    246    */
    247   const char *name;
    248 
    249   /**
    250    * Help text for the command.
    251    */
    252   const char *help;
    253 
    254   /**
    255    * Function implementing the command.
    256    *
    257    * @param args subsequent command line arguments (char **)
    258    */
    259   void (*cb)(char *const *args);
    260 };
    261 
    262 
    263 /**
    264  * Data structure for denomination revocation requests.
    265  */
    266 struct DenomRevocationRequest
    267 {
    268 
    269   /**
    270    * Kept in a DLL.
    271    */
    272   struct DenomRevocationRequest *next;
    273 
    274   /**
    275    * Kept in a DLL.
    276    */
    277   struct DenomRevocationRequest *prev;
    278 
    279   /**
    280    * Operation handle.
    281    */
    282   struct TALER_EXCHANGE_PostManagementDenominationsRevokeHandle *h;
    283 
    284   /**
    285    * Array index of the associated command.
    286    */
    287   size_t idx;
    288 };
    289 
    290 
    291 /**
    292  * Data structure for signkey revocation requests.
    293  */
    294 struct SignkeyRevocationRequest
    295 {
    296 
    297   /**
    298    * Kept in a DLL.
    299    */
    300   struct SignkeyRevocationRequest *next;
    301 
    302   /**
    303    * Kept in a DLL.
    304    */
    305   struct SignkeyRevocationRequest *prev;
    306 
    307   /**
    308    * Operation handle.
    309    */
    310   struct TALER_EXCHANGE_PostManagementSignkeysRevokeHandle *h;
    311 
    312   /**
    313    * Array index of the associated command.
    314    */
    315   size_t idx;
    316 };
    317 
    318 
    319 /**
    320  * Data structure for auditor add requests.
    321  */
    322 struct AuditorAddRequest
    323 {
    324 
    325   /**
    326    * Kept in a DLL.
    327    */
    328   struct AuditorAddRequest *next;
    329 
    330   /**
    331    * Kept in a DLL.
    332    */
    333   struct AuditorAddRequest *prev;
    334 
    335   /**
    336    * Operation handle.
    337    */
    338   struct TALER_EXCHANGE_PostManagementAuditorsHandle *h;
    339 
    340   /**
    341    * Array index of the associated command.
    342    */
    343   size_t idx;
    344 };
    345 
    346 
    347 /**
    348  * Data structure for auditor del requests.
    349  */
    350 struct AuditorDelRequest
    351 {
    352 
    353   /**
    354    * Kept in a DLL.
    355    */
    356   struct AuditorDelRequest *next;
    357 
    358   /**
    359    * Kept in a DLL.
    360    */
    361   struct AuditorDelRequest *prev;
    362 
    363   /**
    364    * Operation handle.
    365    */
    366   struct TALER_EXCHANGE_PostManagementAuditorsDisableHandle *h;
    367 
    368   /**
    369    * Array index of the associated command.
    370    */
    371   size_t idx;
    372 };
    373 
    374 
    375 /**
    376  * Data structure for wire add requests.
    377  */
    378 struct WireAddRequest
    379 {
    380 
    381   /**
    382    * Kept in a DLL.
    383    */
    384   struct WireAddRequest *next;
    385 
    386   /**
    387    * Kept in a DLL.
    388    */
    389   struct WireAddRequest *prev;
    390 
    391   /**
    392    * Operation handle.
    393    */
    394   struct TALER_EXCHANGE_PostManagementWireHandle *h;
    395 
    396   /**
    397    * Array index of the associated command.
    398    */
    399   size_t idx;
    400 };
    401 
    402 
    403 /**
    404  * Data structure for wire del requests.
    405  */
    406 struct WireDelRequest
    407 {
    408 
    409   /**
    410    * Kept in a DLL.
    411    */
    412   struct WireDelRequest *next;
    413 
    414   /**
    415    * Kept in a DLL.
    416    */
    417   struct WireDelRequest *prev;
    418 
    419   /**
    420    * Operation handle.
    421    */
    422   struct TALER_EXCHANGE_PostManagementWireDisableHandle *h;
    423 
    424   /**
    425    * Array index of the associated command.
    426    */
    427   size_t idx;
    428 };
    429 
    430 
    431 /**
    432  * Data structure for announcing wire fees.
    433  */
    434 struct WireFeeRequest
    435 {
    436 
    437   /**
    438    * Kept in a DLL.
    439    */
    440   struct WireFeeRequest *next;
    441 
    442   /**
    443    * Kept in a DLL.
    444    */
    445   struct WireFeeRequest *prev;
    446 
    447   /**
    448    * Operation handle.
    449    */
    450   struct TALER_EXCHANGE_PostManagementWireFeesHandle *h;
    451 
    452   /**
    453    * Array index of the associated command.
    454    */
    455   size_t idx;
    456 };
    457 
    458 
    459 /**
    460  * Data structure for draining profits.
    461  */
    462 struct DrainProfitsRequest
    463 {
    464 
    465   /**
    466    * Kept in a DLL.
    467    */
    468   struct DrainProfitsRequest *next;
    469 
    470   /**
    471    * Kept in a DLL.
    472    */
    473   struct DrainProfitsRequest *prev;
    474 
    475   /**
    476    * Operation handle.
    477    */
    478   struct TALER_EXCHANGE_PostManagementDrainHandle *h;
    479 
    480   /**
    481    * Array index of the associated command.
    482    */
    483   size_t idx;
    484 };
    485 
    486 
    487 /**
    488  * Data structure for announcing global fees.
    489  */
    490 struct GlobalFeeRequest
    491 {
    492 
    493   /**
    494    * Kept in a DLL.
    495    */
    496   struct GlobalFeeRequest *next;
    497 
    498   /**
    499    * Kept in a DLL.
    500    */
    501   struct GlobalFeeRequest *prev;
    502 
    503   /**
    504    * Operation handle.
    505    */
    506   struct TALER_EXCHANGE_PostManagementGlobalFeesHandle *h;
    507 
    508   /**
    509    * Array index of the associated command.
    510    */
    511   size_t idx;
    512 };
    513 
    514 
    515 /**
    516  * Ongoing /keys request.
    517  */
    518 struct UploadKeysRequest
    519 {
    520   /**
    521    * Kept in a DLL.
    522    */
    523   struct UploadKeysRequest *next;
    524 
    525   /**
    526    * Kept in a DLL.
    527    */
    528   struct UploadKeysRequest *prev;
    529 
    530   /**
    531    * Operation handle.
    532    */
    533   struct TALER_EXCHANGE_PostManagementKeysHandle *h;
    534 
    535   /**
    536    * Operation index.
    537    */
    538   size_t idx;
    539 };
    540 
    541 
    542 /**
    543  * Data structure for AML staff requests.
    544  */
    545 struct AmlStaffRequest
    546 {
    547 
    548   /**
    549    * Kept in a DLL.
    550    */
    551   struct AmlStaffRequest *next;
    552 
    553   /**
    554    * Kept in a DLL.
    555    */
    556   struct AmlStaffRequest *prev;
    557 
    558   /**
    559    * Operation handle.
    560    */
    561   struct TALER_EXCHANGE_PostManagementAmlOfficersHandle *h;
    562 
    563   /**
    564    * Array index of the associated command.
    565    */
    566   size_t idx;
    567 };
    568 
    569 
    570 /**
    571  * Data structure for partner add requests.
    572  */
    573 struct PartnerAddRequest
    574 {
    575 
    576   /**
    577    * Kept in a DLL.
    578    */
    579   struct PartnerAddRequest *next;
    580 
    581   /**
    582    * Kept in a DLL.
    583    */
    584   struct PartnerAddRequest *prev;
    585 
    586   /**
    587    * Operation handle.
    588    */
    589   struct TALER_EXCHANGE_PostManagementPartnersHandle *h;
    590 
    591   /**
    592    * Array index of the associated command.
    593    */
    594   size_t idx;
    595 };
    596 
    597 
    598 /**
    599  * Next work item to perform.
    600  */
    601 static struct GNUNET_SCHEDULER_Task *nxt;
    602 
    603 /**
    604  * Handle for #do_download.
    605  */
    606 static struct TALER_EXCHANGE_GetManagementKeysHandle *mgkh;
    607 
    608 /**
    609  * Active AML staff change requests.
    610  */
    611 static struct AmlStaffRequest *asr_head;
    612 
    613 /**
    614  * Active AML staff change requests.
    615  */
    616 static struct AmlStaffRequest *asr_tail;
    617 
    618 /**
    619  * Active partner add requests.
    620  */
    621 static struct PartnerAddRequest *par_head;
    622 
    623 /**
    624  * Active partner add requests.
    625  */
    626 static struct PartnerAddRequest *par_tail;
    627 
    628 /**
    629  * Active denomiantion revocation requests.
    630  */
    631 static struct DenomRevocationRequest *drr_head;
    632 
    633 /**
    634  * Active denomiantion revocation requests.
    635  */
    636 static struct DenomRevocationRequest *drr_tail;
    637 
    638 /**
    639  * Active signkey revocation requests.
    640  */
    641 static struct SignkeyRevocationRequest *srr_head;
    642 
    643 /**
    644  * Active signkey revocation requests.
    645  */
    646 static struct SignkeyRevocationRequest *srr_tail;
    647 
    648 /**
    649  * Active auditor add requests.
    650  */
    651 static struct AuditorAddRequest *aar_head;
    652 
    653 /**
    654  * Active auditor add requests.
    655  */
    656 static struct AuditorAddRequest *aar_tail;
    657 
    658 /**
    659  * Active auditor del requests.
    660  */
    661 static struct AuditorDelRequest *adr_head;
    662 
    663 /**
    664  * Active auditor del requests.
    665  */
    666 static struct AuditorDelRequest *adr_tail;
    667 
    668 /**
    669  * Active wire add requests.
    670  */
    671 static struct WireAddRequest *war_head;
    672 
    673 /**
    674  * Active wire add requests.
    675  */
    676 static struct WireAddRequest *war_tail;
    677 
    678 /**
    679  * Active wire del requests.
    680  */
    681 static struct WireDelRequest *wdr_head;
    682 
    683 /**
    684  * Active wire del requests.
    685  */
    686 static struct WireDelRequest *wdr_tail;
    687 
    688 /**
    689  * Active wire fee requests.
    690  */
    691 static struct WireFeeRequest *wfr_head;
    692 
    693 /**
    694  * Active wire fee requests.
    695  */
    696 static struct WireFeeRequest *wfr_tail;
    697 
    698 /**
    699  * Active global fee requests.
    700  */
    701 static struct GlobalFeeRequest *gfr_head;
    702 
    703 /**
    704  * Active global fee requests.
    705  */
    706 static struct GlobalFeeRequest *gfr_tail;
    707 
    708 /**
    709  * Active keys upload requests.
    710  */
    711 static struct UploadKeysRequest *ukr_head;
    712 
    713 /**
    714  * Active keys upload requests.
    715  */
    716 static struct UploadKeysRequest *ukr_tail;
    717 
    718 /**
    719  * Active drain profits requests.
    720  */
    721 struct DrainProfitsRequest *dpr_head;
    722 
    723 /**
    724  * Active drain profits requests.
    725  */
    726 static struct DrainProfitsRequest *dpr_tail;
    727 
    728 
    729 /**
    730  * Shutdown task. Invoked when the application is being terminated.
    731  *
    732  * @param cls NULL
    733  */
    734 static void
    735 do_shutdown (void *cls)
    736 {
    737   (void) cls;
    738 
    739   {
    740     struct AmlStaffRequest *asr;
    741 
    742     while (NULL != (asr = asr_head))
    743     {
    744       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    745                   "Aborting incomplete AML staff update #%u\n",
    746                   (unsigned int) asr->idx);
    747       TALER_EXCHANGE_post_management_aml_officers_cancel (asr->h);
    748       GNUNET_CONTAINER_DLL_remove (asr_head,
    749                                    asr_tail,
    750                                    asr);
    751       GNUNET_free (asr);
    752     }
    753   }
    754   {
    755     struct PartnerAddRequest *par;
    756 
    757     while (NULL != (par = par_head))
    758     {
    759       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    760                   "Aborting incomplete partner add request #%u\n",
    761                   (unsigned int) par->idx);
    762       TALER_EXCHANGE_post_management_partners_cancel (par->h);
    763       GNUNET_CONTAINER_DLL_remove (par_head,
    764                                    par_tail,
    765                                    par);
    766       GNUNET_free (par);
    767     }
    768   }
    769   {
    770     struct DenomRevocationRequest *drr;
    771 
    772     while (NULL != (drr = drr_head))
    773     {
    774       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    775                   "Aborting incomplete denomination revocation #%u\n",
    776                   (unsigned int) drr->idx);
    777       TALER_EXCHANGE_post_management_denominations_revoke_cancel (drr->h);
    778       GNUNET_CONTAINER_DLL_remove (drr_head,
    779                                    drr_tail,
    780                                    drr);
    781       GNUNET_free (drr);
    782     }
    783   }
    784   {
    785     struct SignkeyRevocationRequest *srr;
    786 
    787     while (NULL != (srr = srr_head))
    788     {
    789       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    790                   "Aborting incomplete signkey revocation #%u\n",
    791                   (unsigned int) srr->idx);
    792       TALER_EXCHANGE_post_management_signkeys_revoke_cancel (srr->h);
    793       GNUNET_CONTAINER_DLL_remove (srr_head,
    794                                    srr_tail,
    795                                    srr);
    796       GNUNET_free (srr);
    797     }
    798   }
    799 
    800   {
    801     struct AuditorAddRequest *aar;
    802 
    803     while (NULL != (aar = aar_head))
    804     {
    805       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    806                   "Aborting incomplete auditor add #%u\n",
    807                   (unsigned int) aar->idx);
    808       TALER_EXCHANGE_post_management_auditors_cancel (aar->h);
    809       GNUNET_CONTAINER_DLL_remove (aar_head,
    810                                    aar_tail,
    811                                    aar);
    812       GNUNET_free (aar);
    813     }
    814   }
    815   {
    816     struct AuditorDelRequest *adr;
    817 
    818     while (NULL != (adr = adr_head))
    819     {
    820       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    821                   "Aborting incomplete auditor del #%u\n",
    822                   (unsigned int) adr->idx);
    823       TALER_EXCHANGE_post_management_auditors_disable_cancel (adr->h);
    824       GNUNET_CONTAINER_DLL_remove (adr_head,
    825                                    adr_tail,
    826                                    adr);
    827       GNUNET_free (adr);
    828     }
    829   }
    830   {
    831     struct WireAddRequest *war;
    832 
    833     while (NULL != (war = war_head))
    834     {
    835       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    836                   "Aborting incomplete wire add #%u\n",
    837                   (unsigned int) war->idx);
    838       TALER_EXCHANGE_post_management_wire_cancel (war->h);
    839       GNUNET_CONTAINER_DLL_remove (war_head,
    840                                    war_tail,
    841                                    war);
    842       GNUNET_free (war);
    843     }
    844   }
    845   {
    846     struct WireDelRequest *wdr;
    847 
    848     while (NULL != (wdr = wdr_head))
    849     {
    850       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    851                   "Aborting incomplete wire del #%u\n",
    852                   (unsigned int) wdr->idx);
    853       TALER_EXCHANGE_post_management_wire_disable_cancel (wdr->h);
    854       GNUNET_CONTAINER_DLL_remove (wdr_head,
    855                                    wdr_tail,
    856                                    wdr);
    857       GNUNET_free (wdr);
    858     }
    859   }
    860   {
    861     struct WireFeeRequest *wfr;
    862 
    863     while (NULL != (wfr = wfr_head))
    864     {
    865       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    866                   "Aborting incomplete wire fee #%u\n",
    867                   (unsigned int) wfr->idx);
    868       TALER_EXCHANGE_post_management_wire_fees_cancel (wfr->h);
    869       GNUNET_CONTAINER_DLL_remove (wfr_head,
    870                                    wfr_tail,
    871                                    wfr);
    872       GNUNET_free (wfr);
    873     }
    874   }
    875   {
    876     struct GlobalFeeRequest *gfr;
    877 
    878     while (NULL != (gfr = gfr_head))
    879     {
    880       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    881                   "Aborting incomplete global fee #%u\n",
    882                   (unsigned int) gfr->idx);
    883       TALER_EXCHANGE_post_management_global_fees_cancel (gfr->h);
    884       GNUNET_CONTAINER_DLL_remove (gfr_head,
    885                                    gfr_tail,
    886                                    gfr);
    887       GNUNET_free (gfr);
    888     }
    889   }
    890   {
    891     struct UploadKeysRequest *ukr;
    892 
    893     while (NULL != (ukr = ukr_head))
    894     {
    895       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    896                   "Aborting incomplete key signature upload #%u\n",
    897                   (unsigned int) ukr->idx);
    898       TALER_EXCHANGE_post_management_keys_cancel (ukr->h);
    899       GNUNET_CONTAINER_DLL_remove (ukr_head,
    900                                    ukr_tail,
    901                                    ukr);
    902       GNUNET_free (ukr);
    903     }
    904   }
    905 
    906   {
    907     struct DrainProfitsRequest *dpr;
    908 
    909     while (NULL != (dpr = dpr_head))
    910     {
    911       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    912                   "Aborting incomplete drain profits request #%u\n",
    913                   (unsigned int) dpr->idx);
    914       TALER_EXCHANGE_post_management_drain_cancel (dpr->h);
    915       GNUNET_CONTAINER_DLL_remove (dpr_head,
    916                                    dpr_tail,
    917                                    dpr);
    918       GNUNET_free (dpr);
    919     }
    920   }
    921 
    922   if (NULL != out)
    923   {
    924     if (EXIT_SUCCESS == global_ret)
    925       json_dumpf (out,
    926                   stdout,
    927                   JSON_INDENT (2));
    928     json_decref (out);
    929     out = NULL;
    930   }
    931   if (NULL != in)
    932   {
    933     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    934                 "Input not consumed!\n");
    935     json_decref (in);
    936     in = NULL;
    937   }
    938   if (NULL != nxt)
    939   {
    940     GNUNET_SCHEDULER_cancel (nxt);
    941     nxt = NULL;
    942   }
    943   if (NULL != mgkh)
    944   {
    945     TALER_EXCHANGE_get_management_keys_cancel (mgkh);
    946     mgkh = NULL;
    947   }
    948   if (NULL != ctx)
    949   {
    950     GNUNET_CURL_fini (ctx);
    951     ctx = NULL;
    952   }
    953   if (NULL != rc)
    954   {
    955     GNUNET_CURL_gnunet_rc_destroy (rc);
    956     rc = NULL;
    957   }
    958 }
    959 
    960 
    961 /**
    962  * Test if we should shut down because all tasks are done.
    963  */
    964 static void
    965 test_shutdown (void)
    966 {
    967   if ( (NULL == drr_head) &&
    968        (NULL == par_head) &&
    969        (NULL == asr_head) &&
    970        (NULL == srr_head) &&
    971        (NULL == aar_head) &&
    972        (NULL == adr_head) &&
    973        (NULL == war_head) &&
    974        (NULL == wdr_head) &&
    975        (NULL == wfr_head) &&
    976        (NULL == gfr_head) &&
    977        (NULL == ukr_head) &&
    978        (NULL == dpr_head) &&
    979        (NULL == mgkh) &&
    980        (NULL == nxt) )
    981     GNUNET_SCHEDULER_shutdown ();
    982 }
    983 
    984 
    985 /**
    986  * Function to continue processing the next command.
    987  *
    988  * @param cls must be a `char *const*` with the array of
    989  *        command-line arguments to process next
    990  */
    991 static void
    992 work (void *cls);
    993 
    994 
    995 /**
    996  * Function to schedule job to process the next command.
    997  *
    998  * @param args the array of command-line arguments to process next
    999  */
   1000 static void
   1001 next (char *const *args)
   1002 {
   1003   GNUNET_assert (NULL == nxt);
   1004   if (NULL == args[0])
   1005   {
   1006     test_shutdown ();
   1007     return;
   1008   }
   1009   nxt = GNUNET_SCHEDULER_add_now (&work,
   1010                                   (void *) args);
   1011 }
   1012 
   1013 
   1014 /**
   1015  * Add an operation to the #out JSON array for processing later.
   1016  *
   1017  * @param op_name name of the operation
   1018  * @param op_value values for the operation (consumed)
   1019  */
   1020 static void
   1021 output_operation (const char *op_name,
   1022                   json_t *op_value)
   1023 {
   1024   json_t *action;
   1025 
   1026   GNUNET_break (NULL != op_value);
   1027   if (NULL == out)
   1028   {
   1029     out = json_array ();
   1030     GNUNET_assert (NULL != out);
   1031   }
   1032   action = GNUNET_JSON_PACK (
   1033     GNUNET_JSON_pack_string ("operation",
   1034                              op_name),
   1035     GNUNET_JSON_pack_object_steal ("arguments",
   1036                                    op_value));
   1037   GNUNET_assert (0 ==
   1038                  json_array_append_new (out,
   1039                                         action));
   1040 }
   1041 
   1042 
   1043 /**
   1044  * Information about a subroutine for an upload.
   1045  */
   1046 struct UploadHandler
   1047 {
   1048   /**
   1049    * Key to trigger this subroutine.
   1050    */
   1051   const char *key;
   1052 
   1053   /**
   1054    * Function implementing an upload.
   1055    *
   1056    * @param exchange_url URL of the exchange
   1057    * @param idx index of the operation we are performing
   1058    * @param value arguments to drive the upload.
   1059    */
   1060   void (*cb)(const char *exchange_url,
   1061              size_t idx,
   1062              const json_t *value);
   1063 
   1064 };
   1065 
   1066 
   1067 /**
   1068  * Load the offline key (if not yet done). Triggers shutdown on failure.
   1069  *
   1070  * @param do_create #GNUNET_YES if the key may be created
   1071  * @return #GNUNET_OK on success
   1072  */
   1073 static enum GNUNET_GenericReturnValue
   1074 load_offline_key (int do_create)
   1075 {
   1076   static bool done;
   1077   int ret;
   1078   char *fn;
   1079 
   1080   if (done)
   1081     return GNUNET_OK;
   1082   if (GNUNET_OK !=
   1083       GNUNET_CONFIGURATION_get_value_filename (kcfg,
   1084                                                "exchange-offline",
   1085                                                "MASTER_PRIV_FILE",
   1086                                                &fn))
   1087   {
   1088     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   1089                                "exchange-offline",
   1090                                "MASTER_PRIV_FILE");
   1091     test_shutdown ();
   1092     return GNUNET_SYSERR;
   1093   }
   1094   if (GNUNET_YES !=
   1095       GNUNET_DISK_file_test (fn))
   1096     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1097                 "Exchange master private key `%s' does not exist yet, creating it!\n",
   1098                 fn);
   1099   ret = GNUNET_CRYPTO_eddsa_key_from_file (fn,
   1100                                            do_create,
   1101                                            &master_priv.eddsa_priv);
   1102   if (GNUNET_SYSERR == ret)
   1103   {
   1104     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1105                 "Failed to initialize master key from file `%s': %s\n",
   1106                 fn,
   1107                 "could not create file");
   1108     GNUNET_free (fn);
   1109     test_shutdown ();
   1110     return GNUNET_SYSERR;
   1111   }
   1112   GNUNET_free (fn);
   1113   GNUNET_CRYPTO_eddsa_key_get_public (&master_priv.eddsa_priv,
   1114                                       &master_pub.eddsa_pub);
   1115   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1116               "Using master public key %s\n",
   1117               TALER_B2S (&master_pub));
   1118   done = true;
   1119   return GNUNET_OK;
   1120 }
   1121 
   1122 
   1123 /**
   1124  * Function called with information about the post revocation operation result.
   1125  *
   1126  * @param drr the revocation request
   1127  * @param dr response data
   1128  */
   1129 static void
   1130 denom_revocation_cb (
   1131   struct DenomRevocationRequest *drr,
   1132   const struct TALER_EXCHANGE_PostManagementDenominationsRevokeResponse *dr)
   1133 {
   1134   const struct TALER_EXCHANGE_HttpResponse *hr = &dr->hr;
   1135 
   1136   if (MHD_HTTP_NO_CONTENT != hr->http_status)
   1137   {
   1138     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1139                 "Upload failed for command %u with status %u: %s (%s)\n",
   1140                 (unsigned int) drr->idx,
   1141                 hr->http_status,
   1142                 hr->hint,
   1143                 TALER_JSON_get_error_hint (hr->reply));
   1144     global_ret = EXIT_FAILURE;
   1145   }
   1146   GNUNET_CONTAINER_DLL_remove (drr_head,
   1147                                drr_tail,
   1148                                drr);
   1149   GNUNET_free (drr);
   1150   test_shutdown ();
   1151 }
   1152 
   1153 
   1154 /**
   1155  * Upload denomination revocation request data.
   1156  *
   1157  * @param exchange_url base URL of the exchange
   1158  * @param idx index of the operation we are performing (for logging)
   1159  * @param value arguments for denomination revocation
   1160  */
   1161 static void
   1162 upload_denom_revocation (const char *exchange_url,
   1163                          size_t idx,
   1164                          const json_t *value)
   1165 {
   1166   struct TALER_MasterSignatureP master_sig;
   1167   struct TALER_DenominationHashP h_denom_pub;
   1168   struct DenomRevocationRequest *drr;
   1169   const char *err_name;
   1170   unsigned int err_line;
   1171   struct GNUNET_JSON_Specification spec[] = {
   1172     GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
   1173                                  &h_denom_pub),
   1174     GNUNET_JSON_spec_fixed_auto ("master_sig",
   1175                                  &master_sig),
   1176     GNUNET_JSON_spec_end ()
   1177   };
   1178 
   1179   if (GNUNET_OK !=
   1180       GNUNET_JSON_parse (value,
   1181                          spec,
   1182                          &err_name,
   1183                          &err_line))
   1184   {
   1185     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1186                 "Invalid input for denomination revocation: %s#%u at %u (skipping)\n",
   1187                 err_name,
   1188                 err_line,
   1189                 (unsigned int) idx);
   1190     json_dumpf (value,
   1191                 stderr,
   1192                 JSON_INDENT (2));
   1193     global_ret = EXIT_FAILURE;
   1194     GNUNET_SCHEDULER_shutdown ();
   1195     return;
   1196   }
   1197   drr = GNUNET_new (struct DenomRevocationRequest);
   1198   drr->idx = idx;
   1199   drr->h =
   1200     TALER_EXCHANGE_post_management_denominations_revoke_create (ctx,
   1201                                                                 exchange_url,
   1202                                                                 &h_denom_pub,
   1203                                                                 &master_sig);
   1204   TALER_EXCHANGE_post_management_denominations_revoke_start (drr->h,
   1205                                                              &
   1206                                                              denom_revocation_cb,
   1207                                                              drr);
   1208   GNUNET_CONTAINER_DLL_insert (drr_head,
   1209                                drr_tail,
   1210                                drr);
   1211 }
   1212 
   1213 
   1214 /**
   1215  * Function called with information about the post revocation operation result.
   1216  *
   1217  * @param srr the revocation request
   1218  * @param sr response data
   1219  */
   1220 static void
   1221 signkey_revocation_cb (
   1222   struct SignkeyRevocationRequest *srr,
   1223   const struct TALER_EXCHANGE_PostManagementSignkeysRevokeResponse *sr)
   1224 {
   1225   const struct TALER_EXCHANGE_HttpResponse *hr = &sr->hr;
   1226 
   1227   if (MHD_HTTP_NO_CONTENT != hr->http_status)
   1228   {
   1229     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1230                 "Upload failed for command %u with status %u: %s (%s)\n",
   1231                 (unsigned int) srr->idx,
   1232                 hr->http_status,
   1233                 hr->hint,
   1234                 TALER_JSON_get_error_hint (hr->reply));
   1235     global_ret = EXIT_FAILURE;
   1236   }
   1237   GNUNET_CONTAINER_DLL_remove (srr_head,
   1238                                srr_tail,
   1239                                srr);
   1240   GNUNET_free (srr);
   1241   test_shutdown ();
   1242 }
   1243 
   1244 
   1245 /**
   1246  * Upload signkey revocation request data.
   1247  *
   1248  * @param exchange_url base URL of the exchange
   1249  * @param idx index of the operation we are performing (for logging)
   1250  * @param value arguments for denomination revocation
   1251  */
   1252 static void
   1253 upload_signkey_revocation (const char *exchange_url,
   1254                            size_t idx,
   1255                            const json_t *value)
   1256 {
   1257   struct TALER_MasterSignatureP master_sig;
   1258   struct TALER_ExchangePublicKeyP exchange_pub;
   1259   struct SignkeyRevocationRequest *srr;
   1260   const char *err_name;
   1261   unsigned int err_line;
   1262   struct GNUNET_JSON_Specification spec[] = {
   1263     GNUNET_JSON_spec_fixed_auto ("exchange_pub",
   1264                                  &exchange_pub),
   1265     GNUNET_JSON_spec_fixed_auto ("master_sig",
   1266                                  &master_sig),
   1267     GNUNET_JSON_spec_end ()
   1268   };
   1269 
   1270   if (GNUNET_OK !=
   1271       GNUNET_JSON_parse (value,
   1272                          spec,
   1273                          &err_name,
   1274                          &err_line))
   1275   {
   1276     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1277                 "Invalid input for signkey revocation: %s#%u at %u (skipping)\n",
   1278                 err_name,
   1279                 err_line,
   1280                 (unsigned int) idx);
   1281     json_dumpf (value,
   1282                 stderr,
   1283                 JSON_INDENT (2));
   1284     global_ret = EXIT_FAILURE;
   1285     GNUNET_SCHEDULER_shutdown ();
   1286     return;
   1287   }
   1288   srr = GNUNET_new (struct SignkeyRevocationRequest);
   1289   srr->idx = idx;
   1290   srr->h =
   1291     TALER_EXCHANGE_post_management_signkeys_revoke_create (ctx,
   1292                                                            exchange_url,
   1293                                                            &exchange_pub,
   1294                                                            &master_sig);
   1295   TALER_EXCHANGE_post_management_signkeys_revoke_start (srr->h,
   1296                                                         &signkey_revocation_cb,
   1297                                                         srr);
   1298   GNUNET_CONTAINER_DLL_insert (srr_head,
   1299                                srr_tail,
   1300                                srr);
   1301 }
   1302 
   1303 
   1304 /**
   1305  * Function called with information about the post auditor add operation result.
   1306  *
   1307  * @param aar the auditor add request
   1308  * @param mer response data
   1309  */
   1310 static void
   1311 auditor_add_cb (
   1312   struct AuditorAddRequest *aar,
   1313   const struct TALER_EXCHANGE_PostManagementAuditorsResponse *mer)
   1314 {
   1315   const struct TALER_EXCHANGE_HttpResponse *hr = &mer->hr;
   1316 
   1317   if (MHD_HTTP_NO_CONTENT != hr->http_status)
   1318   {
   1319     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1320                 "Upload failed for command %u with status %u: %s (%s)\n",
   1321                 (unsigned int) aar->idx,
   1322                 hr->http_status,
   1323                 TALER_ErrorCode_get_hint (hr->ec),
   1324                 hr->hint);
   1325     global_ret = EXIT_FAILURE;
   1326   }
   1327   GNUNET_CONTAINER_DLL_remove (aar_head,
   1328                                aar_tail,
   1329                                aar);
   1330   GNUNET_free (aar);
   1331   test_shutdown ();
   1332 }
   1333 
   1334 
   1335 /**
   1336  * Upload auditor add data.
   1337  *
   1338  * @param exchange_url base URL of the exchange
   1339  * @param idx index of the operation we are performing (for logging)
   1340  * @param value arguments for denomination revocation
   1341  */
   1342 static void
   1343 upload_auditor_add (const char *exchange_url,
   1344                     size_t idx,
   1345                     const json_t *value)
   1346 {
   1347   struct TALER_MasterSignatureP master_sig;
   1348   const char *auditor_url;
   1349   const char *auditor_name;
   1350   struct GNUNET_TIME_Timestamp start_time;
   1351   struct TALER_AuditorPublicKeyP auditor_pub;
   1352   struct AuditorAddRequest *aar;
   1353   const char *err_name;
   1354   unsigned int err_line;
   1355   struct GNUNET_JSON_Specification spec[] = {
   1356     TALER_JSON_spec_web_url ("auditor_url",
   1357                              &auditor_url),
   1358     GNUNET_JSON_spec_string ("auditor_name",
   1359                              &auditor_name),
   1360     GNUNET_JSON_spec_timestamp ("validity_start",
   1361                                 &start_time),
   1362     GNUNET_JSON_spec_fixed_auto ("auditor_pub",
   1363                                  &auditor_pub),
   1364     GNUNET_JSON_spec_fixed_auto ("master_sig",
   1365                                  &master_sig),
   1366     GNUNET_JSON_spec_end ()
   1367   };
   1368 
   1369   if (GNUNET_OK !=
   1370       GNUNET_JSON_parse (value,
   1371                          spec,
   1372                          &err_name,
   1373                          &err_line))
   1374   {
   1375     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1376                 "Invalid input for adding auditor: %s#%u at %u (skipping)\n",
   1377                 err_name,
   1378                 err_line,
   1379                 (unsigned int) idx);
   1380     json_dumpf (value,
   1381                 stderr,
   1382                 JSON_INDENT (2));
   1383     global_ret = EXIT_FAILURE;
   1384     GNUNET_SCHEDULER_shutdown ();
   1385     return;
   1386   }
   1387   aar = GNUNET_new (struct AuditorAddRequest);
   1388   aar->idx = idx;
   1389   aar->h =
   1390     TALER_EXCHANGE_post_management_auditors_create (ctx,
   1391                                                     exchange_url,
   1392                                                     &auditor_pub,
   1393                                                     auditor_url,
   1394                                                     auditor_name,
   1395                                                     start_time,
   1396                                                     &master_sig);
   1397   TALER_EXCHANGE_post_management_auditors_start (aar->h,
   1398                                                  &auditor_add_cb,
   1399                                                  aar);
   1400   GNUNET_CONTAINER_DLL_insert (aar_head,
   1401                                aar_tail,
   1402                                aar);
   1403 }
   1404 
   1405 
   1406 /**
   1407  * Function called with information about the post auditor del operation result.
   1408  *
   1409  * @param adr auditor delete request
   1410  * @param mdr response data
   1411  */
   1412 static void
   1413 auditor_del_cb (
   1414   struct AuditorDelRequest *adr,
   1415   const struct TALER_EXCHANGE_PostManagementAuditorsDisableResponse *mdr)
   1416 {
   1417   const struct TALER_EXCHANGE_HttpResponse *hr = &mdr->hr;
   1418 
   1419   if (MHD_HTTP_NO_CONTENT != hr->http_status)
   1420   {
   1421     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1422                 "Upload failed for command %u with status %u: %s (%s)\n",
   1423                 (unsigned int) adr->idx,
   1424                 hr->http_status,
   1425                 TALER_ErrorCode_get_hint (hr->ec),
   1426                 hr->hint);
   1427     global_ret = EXIT_FAILURE;
   1428   }
   1429   GNUNET_CONTAINER_DLL_remove (adr_head,
   1430                                adr_tail,
   1431                                adr);
   1432   GNUNET_free (adr);
   1433   test_shutdown ();
   1434 }
   1435 
   1436 
   1437 /**
   1438  * Upload auditor del data.
   1439  *
   1440  * @param exchange_url base URL of the exchange
   1441  * @param idx index of the operation we are performing (for logging)
   1442  * @param value arguments for denomination revocation
   1443  */
   1444 static void
   1445 upload_auditor_del (const char *exchange_url,
   1446                     size_t idx,
   1447                     const json_t *value)
   1448 {
   1449   struct TALER_AuditorPublicKeyP auditor_pub;
   1450   struct TALER_MasterSignatureP master_sig;
   1451   struct GNUNET_TIME_Timestamp end_time;
   1452   struct AuditorDelRequest *adr;
   1453   const char *err_name;
   1454   unsigned int err_line;
   1455   struct GNUNET_JSON_Specification spec[] = {
   1456     GNUNET_JSON_spec_fixed_auto ("auditor_pub",
   1457                                  &auditor_pub),
   1458     GNUNET_JSON_spec_timestamp ("validity_end",
   1459                                 &end_time),
   1460     GNUNET_JSON_spec_fixed_auto ("master_sig",
   1461                                  &master_sig),
   1462     GNUNET_JSON_spec_end ()
   1463   };
   1464 
   1465   if (GNUNET_OK !=
   1466       GNUNET_JSON_parse (value,
   1467                          spec,
   1468                          &err_name,
   1469                          &err_line))
   1470   {
   1471     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1472                 "Invalid input to disable auditor: %s#%u at %u (skipping)\n",
   1473                 err_name,
   1474                 err_line,
   1475                 (unsigned int) idx);
   1476     json_dumpf (value,
   1477                 stderr,
   1478                 JSON_INDENT (2));
   1479     global_ret = EXIT_FAILURE;
   1480     GNUNET_SCHEDULER_shutdown ();
   1481     return;
   1482   }
   1483   adr = GNUNET_new (struct AuditorDelRequest);
   1484   adr->idx = idx;
   1485   adr->h =
   1486     TALER_EXCHANGE_post_management_auditors_disable_create (ctx,
   1487                                                             exchange_url,
   1488                                                             &auditor_pub,
   1489                                                             end_time,
   1490                                                             &master_sig);
   1491   TALER_EXCHANGE_post_management_auditors_disable_start (adr->h,
   1492                                                          &auditor_del_cb,
   1493                                                          adr);
   1494   GNUNET_CONTAINER_DLL_insert (adr_head,
   1495                                adr_tail,
   1496                                adr);
   1497 }
   1498 
   1499 
   1500 /**
   1501  * Function called with information about the post wire add operation result.
   1502  *
   1503  * @param war the wire add request
   1504  * @param wer response data
   1505  */
   1506 static void
   1507 wire_add_cb (
   1508   struct WireAddRequest *war,
   1509   const struct TALER_EXCHANGE_PostManagementWireResponse *wer)
   1510 {
   1511   const struct TALER_EXCHANGE_HttpResponse *hr = &wer->hr;
   1512 
   1513   if (MHD_HTTP_NO_CONTENT != hr->http_status)
   1514   {
   1515     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1516                 "Upload failed for command %u with status %u: %s (%s)\n",
   1517                 (unsigned int) war->idx,
   1518                 hr->http_status,
   1519                 TALER_ErrorCode_get_hint (hr->ec),
   1520                 hr->hint);
   1521     global_ret = EXIT_FAILURE;
   1522   }
   1523   GNUNET_CONTAINER_DLL_remove (war_head,
   1524                                war_tail,
   1525                                war);
   1526   GNUNET_free (war);
   1527   test_shutdown ();
   1528 }
   1529 
   1530 
   1531 /**
   1532  * Upload wire add data.
   1533  *
   1534  * @param exchange_url base URL of the exchange
   1535  * @param idx index of the operation we are performing (for logging)
   1536  * @param value arguments for denomination revocation
   1537  */
   1538 static void
   1539 upload_wire_add (const char *exchange_url,
   1540                  size_t idx,
   1541                  const json_t *value)
   1542 {
   1543   struct TALER_MasterSignatureP master_sig_add;
   1544   struct TALER_MasterSignatureP master_sig_wire;
   1545   struct TALER_FullPayto payto_uri;
   1546   struct GNUNET_TIME_Timestamp start_time;
   1547   struct WireAddRequest *war;
   1548   const char *err_name;
   1549   const char *conversion_url = NULL;
   1550   const char *open_banking_gateway = NULL;
   1551   const char *prepared_transfer_url = NULL;
   1552   const char *bank_label = NULL;
   1553   int64_t priority = 0;
   1554   const json_t *debit_restrictions;
   1555   const json_t *credit_restrictions;
   1556   unsigned int err_line;
   1557   struct GNUNET_JSON_Specification spec[] = {
   1558     TALER_JSON_spec_full_payto_uri ("payto_uri",
   1559                                     &payto_uri),
   1560     GNUNET_JSON_spec_mark_optional (
   1561       TALER_JSON_spec_web_url ("conversion_url",
   1562                                &conversion_url),
   1563       NULL),
   1564     GNUNET_JSON_spec_mark_optional (
   1565       TALER_JSON_spec_web_url ("open_banking_gateway",
   1566                                &open_banking_gateway),
   1567       NULL),
   1568     GNUNET_JSON_spec_mark_optional (
   1569       TALER_JSON_spec_web_url ("prepared_transfer_url",
   1570                                &prepared_transfer_url),
   1571       NULL),
   1572     GNUNET_JSON_spec_mark_optional (
   1573       GNUNET_JSON_spec_string ("bank_label",
   1574                                &bank_label),
   1575       NULL),
   1576     GNUNET_JSON_spec_int64 ("priority",
   1577                             &priority),
   1578     GNUNET_JSON_spec_array_const ("debit_restrictions",
   1579                                   &debit_restrictions),
   1580     GNUNET_JSON_spec_array_const ("credit_restrictions",
   1581                                   &credit_restrictions),
   1582     GNUNET_JSON_spec_timestamp ("validity_start",
   1583                                 &start_time),
   1584     GNUNET_JSON_spec_fixed_auto ("master_sig_add",
   1585                                  &master_sig_add),
   1586     GNUNET_JSON_spec_fixed_auto ("master_sig_wire",
   1587                                  &master_sig_wire),
   1588     GNUNET_JSON_spec_end ()
   1589   };
   1590 
   1591   if (GNUNET_OK !=
   1592       GNUNET_JSON_parse (value,
   1593                          spec,
   1594                          &err_name,
   1595                          &err_line))
   1596   {
   1597     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1598                 "Invalid input for adding wire account: %s#%u at %u (skipping)\n",
   1599                 err_name,
   1600                 err_line,
   1601                 (unsigned int) idx);
   1602     json_dumpf (value,
   1603                 stderr,
   1604                 JSON_INDENT (2));
   1605     global_ret = EXIT_FAILURE;
   1606     GNUNET_SCHEDULER_shutdown ();
   1607     return;
   1608   }
   1609   {
   1610     char *wire_method;
   1611 
   1612     wire_method = TALER_payto_get_method (payto_uri.full_payto);
   1613     if (NULL == wire_method)
   1614     {
   1615       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1616                   "payto:// URI `%s' is malformed\n",
   1617                   payto_uri.full_payto);
   1618       global_ret = EXIT_FAILURE;
   1619       GNUNET_SCHEDULER_shutdown ();
   1620       return;
   1621     }
   1622     GNUNET_free (wire_method);
   1623   }
   1624   {
   1625     char *msg = TALER_payto_validate (payto_uri);
   1626 
   1627     if (NULL != msg)
   1628     {
   1629       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1630                   "payto URI is malformed: %s\n",
   1631                   msg);
   1632       GNUNET_free (msg);
   1633       GNUNET_SCHEDULER_shutdown ();
   1634       global_ret = EXIT_INVALIDARGUMENT;
   1635       return;
   1636     }
   1637   }
   1638   war = GNUNET_new (struct WireAddRequest);
   1639   war->idx = idx;
   1640   war->h =
   1641     TALER_EXCHANGE_post_management_wire_create (ctx,
   1642                                                 exchange_url,
   1643                                                 payto_uri,
   1644                                                 start_time,
   1645                                                 &master_sig_add,
   1646                                                 &master_sig_wire);
   1647   TALER_EXCHANGE_post_management_wire_set_options (
   1648     war->h,
   1649     TALER_EXCHANGE_post_management_wire_option_bank_label (bank_label),
   1650     TALER_EXCHANGE_post_management_wire_option_conversion_url (conversion_url),
   1651     TALER_EXCHANGE_post_management_wire_option_open_banking_gateway (
   1652       open_banking_gateway),
   1653     TALER_EXCHANGE_post_management_wire_option_prepared_transfer_url (
   1654       prepared_transfer_url),
   1655     TALER_EXCHANGE_post_management_wire_option_debit_restrictions (
   1656       debit_restrictions),
   1657     TALER_EXCHANGE_post_management_wire_option_credit_restrictions (
   1658       credit_restrictions),
   1659     TALER_EXCHANGE_post_management_wire_option_priority (priority));
   1660   TALER_EXCHANGE_post_management_wire_start (war->h,
   1661                                              &wire_add_cb,
   1662                                              war);
   1663   GNUNET_CONTAINER_DLL_insert (war_head,
   1664                                war_tail,
   1665                                war);
   1666 }
   1667 
   1668 
   1669 /**
   1670  * Function called with information about the post wire del operation result.
   1671  *
   1672  * @param wdr request to delete wire account
   1673  * @param wdres response data
   1674  */
   1675 static void
   1676 wire_del_cb (
   1677   struct WireDelRequest *wdr,
   1678   const struct TALER_EXCHANGE_PostManagementWireDisableResponse *wdres)
   1679 {
   1680   const struct TALER_EXCHANGE_HttpResponse *hr = &wdres->hr;
   1681 
   1682   if (MHD_HTTP_NO_CONTENT != hr->http_status)
   1683   {
   1684     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1685                 "Upload failed for command %u with status %u: %s (%s)\n",
   1686                 (unsigned int) wdr->idx,
   1687                 hr->http_status,
   1688                 TALER_ErrorCode_get_hint (hr->ec),
   1689                 hr->hint);
   1690     global_ret = EXIT_FAILURE;
   1691   }
   1692   GNUNET_CONTAINER_DLL_remove (wdr_head,
   1693                                wdr_tail,
   1694                                wdr);
   1695   GNUNET_free (wdr);
   1696   test_shutdown ();
   1697 }
   1698 
   1699 
   1700 /**
   1701  * Upload wire del data.
   1702  *
   1703  * @param exchange_url base URL of the exchange
   1704  * @param idx index of the operation we are performing (for logging)
   1705  * @param value arguments for denomination revocation
   1706  */
   1707 static void
   1708 upload_wire_del (const char *exchange_url,
   1709                  size_t idx,
   1710                  const json_t *value)
   1711 {
   1712   struct TALER_MasterSignatureP master_sig;
   1713   struct TALER_FullPayto payto_uri;
   1714   struct GNUNET_TIME_Timestamp end_time;
   1715   struct WireDelRequest *wdr;
   1716   const char *err_name;
   1717   unsigned int err_line;
   1718   struct GNUNET_JSON_Specification spec[] = {
   1719     TALER_JSON_spec_full_payto_uri ("payto_uri",
   1720                                     &payto_uri),
   1721     GNUNET_JSON_spec_timestamp ("validity_end",
   1722                                 &end_time),
   1723     GNUNET_JSON_spec_fixed_auto ("master_sig",
   1724                                  &master_sig),
   1725     GNUNET_JSON_spec_end ()
   1726   };
   1727 
   1728   if (GNUNET_OK !=
   1729       GNUNET_JSON_parse (value,
   1730                          spec,
   1731                          &err_name,
   1732                          &err_line))
   1733   {
   1734     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1735                 "Invalid input to disable wire account: %s#%u at %u (skipping)\n",
   1736                 err_name,
   1737                 err_line,
   1738                 (unsigned int) idx);
   1739     json_dumpf (value,
   1740                 stderr,
   1741                 JSON_INDENT (2));
   1742     global_ret = EXIT_FAILURE;
   1743     GNUNET_SCHEDULER_shutdown ();
   1744     return;
   1745   }
   1746   wdr = GNUNET_new (struct WireDelRequest);
   1747   wdr->idx = idx;
   1748   wdr->h =
   1749     TALER_EXCHANGE_post_management_wire_disable_create (ctx,
   1750                                                         exchange_url,
   1751                                                         payto_uri,
   1752                                                         end_time,
   1753                                                         &master_sig);
   1754   TALER_EXCHANGE_post_management_wire_disable_start (wdr->h,
   1755                                                      &wire_del_cb,
   1756                                                      wdr);
   1757   GNUNET_CONTAINER_DLL_insert (wdr_head,
   1758                                wdr_tail,
   1759                                wdr);
   1760 }
   1761 
   1762 
   1763 /**
   1764  * Function called with information about the post wire fee operation result.
   1765  *
   1766  * @param wfr the wire fee request
   1767  * @param swr response data
   1768  */
   1769 static void
   1770 wire_fee_cb (
   1771   struct WireFeeRequest *wfr,
   1772   const struct TALER_EXCHANGE_PostManagementWireFeesResponse *swr)
   1773 {
   1774   const struct TALER_EXCHANGE_HttpResponse *hr = &swr->hr;
   1775 
   1776   if (MHD_HTTP_NO_CONTENT != hr->http_status)
   1777   {
   1778     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1779                 "Upload failed for command %u with status %u: %s (%s)\n",
   1780                 (unsigned int) wfr->idx,
   1781                 hr->http_status,
   1782                 TALER_ErrorCode_get_hint (hr->ec),
   1783                 hr->hint);
   1784     global_ret = EXIT_FAILURE;
   1785   }
   1786   GNUNET_CONTAINER_DLL_remove (wfr_head,
   1787                                wfr_tail,
   1788                                wfr);
   1789   GNUNET_free (wfr);
   1790   test_shutdown ();
   1791 }
   1792 
   1793 
   1794 /**
   1795  * Upload wire fee.
   1796  *
   1797  * @param exchange_url base URL of the exchange
   1798  * @param idx index of the operation we are performing (for logging)
   1799  * @param value arguments for denomination revocation
   1800  */
   1801 static void
   1802 upload_wire_fee (const char *exchange_url,
   1803                  size_t idx,
   1804                  const json_t *value)
   1805 {
   1806   struct TALER_MasterSignatureP master_sig;
   1807   const char *wire_method;
   1808   struct WireFeeRequest *wfr;
   1809   const char *err_name;
   1810   unsigned int err_line;
   1811   struct TALER_WireFeeSet fees;
   1812   struct GNUNET_TIME_Timestamp start_time;
   1813   struct GNUNET_TIME_Timestamp end_time;
   1814   struct GNUNET_JSON_Specification spec[] = {
   1815     GNUNET_JSON_spec_string ("wire_method",
   1816                              &wire_method),
   1817     TALER_JSON_spec_amount ("wire_fee",
   1818                             currency,
   1819                             &fees.wire),
   1820     TALER_JSON_spec_amount ("closing_fee",
   1821                             currency,
   1822                             &fees.closing),
   1823     GNUNET_JSON_spec_timestamp ("start_time",
   1824                                 &start_time),
   1825     GNUNET_JSON_spec_timestamp ("end_time",
   1826                                 &end_time),
   1827     GNUNET_JSON_spec_fixed_auto ("master_sig",
   1828                                  &master_sig),
   1829     GNUNET_JSON_spec_end ()
   1830   };
   1831 
   1832   if (GNUNET_OK !=
   1833       GNUNET_JSON_parse (value,
   1834                          spec,
   1835                          &err_name,
   1836                          &err_line))
   1837   {
   1838     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1839                 "Invalid input to set wire fee: %s#%u at %u (skipping)\n",
   1840                 err_name,
   1841                 err_line,
   1842                 (unsigned int) idx);
   1843     json_dumpf (value,
   1844                 stderr,
   1845                 JSON_INDENT (2));
   1846     global_ret = EXIT_FAILURE;
   1847     GNUNET_SCHEDULER_shutdown ();
   1848     return;
   1849   }
   1850   wfr = GNUNET_new (struct WireFeeRequest);
   1851   wfr->idx = idx;
   1852   wfr->h =
   1853     TALER_EXCHANGE_post_management_wire_fees_create (ctx,
   1854                                                      exchange_url,
   1855                                                      wire_method,
   1856                                                      start_time,
   1857                                                      end_time,
   1858                                                      &fees,
   1859                                                      &master_sig);
   1860   TALER_EXCHANGE_post_management_wire_fees_start (wfr->h,
   1861                                                   &wire_fee_cb,
   1862                                                   wfr);
   1863   GNUNET_CONTAINER_DLL_insert (wfr_head,
   1864                                wfr_tail,
   1865                                wfr);
   1866 }
   1867 
   1868 
   1869 /**
   1870  * Function called with information about the post global fee operation result.
   1871  *
   1872  * @param gfr the global fee request
   1873  * @param gr response data
   1874  */
   1875 static void
   1876 global_fee_cb (
   1877   struct GlobalFeeRequest *gfr,
   1878   const struct TALER_EXCHANGE_PostManagementGlobalFeesResponse *gr)
   1879 {
   1880   const struct TALER_EXCHANGE_HttpResponse *hr = &gr->hr;
   1881 
   1882   if (MHD_HTTP_NO_CONTENT != hr->http_status)
   1883   {
   1884     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1885                 "Upload failed for command %u with status %u: %s (%s)\n",
   1886                 (unsigned int) gfr->idx,
   1887                 hr->http_status,
   1888                 TALER_ErrorCode_get_hint (hr->ec),
   1889                 hr->hint);
   1890     global_ret = EXIT_FAILURE;
   1891   }
   1892   GNUNET_CONTAINER_DLL_remove (gfr_head,
   1893                                gfr_tail,
   1894                                gfr);
   1895   GNUNET_free (gfr);
   1896   test_shutdown ();
   1897 }
   1898 
   1899 
   1900 /**
   1901  * Upload global fee.
   1902  *
   1903  * @param exchange_url base URL of the exchange
   1904  * @param idx index of the operation we are performing (for logging)
   1905  * @param value arguments for denomination revocation
   1906  */
   1907 static void
   1908 upload_global_fee (const char *exchange_url,
   1909                    size_t idx,
   1910                    const json_t *value)
   1911 {
   1912   struct TALER_MasterSignatureP master_sig;
   1913   struct GlobalFeeRequest *gfr;
   1914   const char *err_name;
   1915   unsigned int err_line;
   1916   struct TALER_GlobalFeeSet fees;
   1917   struct GNUNET_TIME_Timestamp start_time;
   1918   struct GNUNET_TIME_Timestamp end_time;
   1919   struct GNUNET_TIME_Relative purse_timeout;
   1920   struct GNUNET_TIME_Relative history_expiration;
   1921   uint32_t purse_account_limit;
   1922   struct GNUNET_JSON_Specification spec[] = {
   1923     TALER_JSON_spec_amount ("history_fee",
   1924                             currency,
   1925                             &fees.history),
   1926     TALER_JSON_spec_amount ("account_fee",
   1927                             currency,
   1928                             &fees.account),
   1929     TALER_JSON_spec_amount ("purse_fee",
   1930                             currency,
   1931                             &fees.purse),
   1932     GNUNET_JSON_spec_relative_time ("purse_timeout",
   1933                                     &purse_timeout),
   1934     GNUNET_JSON_spec_relative_time ("history_expiration",
   1935                                     &history_expiration),
   1936     GNUNET_JSON_spec_uint32 ("purse_account_limit",
   1937                              &purse_account_limit),
   1938     GNUNET_JSON_spec_timestamp ("start_time",
   1939                                 &start_time),
   1940     GNUNET_JSON_spec_timestamp ("end_time",
   1941                                 &end_time),
   1942     GNUNET_JSON_spec_fixed_auto ("master_sig",
   1943                                  &master_sig),
   1944     GNUNET_JSON_spec_end ()
   1945   };
   1946 
   1947   if (GNUNET_OK !=
   1948       GNUNET_JSON_parse (value,
   1949                          spec,
   1950                          &err_name,
   1951                          &err_line))
   1952   {
   1953     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1954                 "Invalid input to set wire fee: %s#%u at %u (skipping)\n",
   1955                 err_name,
   1956                 err_line,
   1957                 (unsigned int) idx);
   1958     json_dumpf (value,
   1959                 stderr,
   1960                 JSON_INDENT (2));
   1961     global_ret = EXIT_FAILURE;
   1962     GNUNET_SCHEDULER_shutdown ();
   1963     return;
   1964   }
   1965   gfr = GNUNET_new (struct GlobalFeeRequest);
   1966   gfr->idx = idx;
   1967   gfr->h =
   1968     TALER_EXCHANGE_post_management_global_fees_create (ctx,
   1969                                                        exchange_url,
   1970                                                        start_time,
   1971                                                        end_time,
   1972                                                        &fees,
   1973                                                        purse_timeout,
   1974                                                        history_expiration,
   1975                                                        purse_account_limit,
   1976                                                        &master_sig);
   1977   TALER_EXCHANGE_post_management_global_fees_start (gfr->h,
   1978                                                     &global_fee_cb,
   1979                                                     gfr);
   1980   GNUNET_CONTAINER_DLL_insert (gfr_head,
   1981                                gfr_tail,
   1982                                gfr);
   1983 }
   1984 
   1985 
   1986 /**
   1987  * Function called with information about the drain profits operation.
   1988  *
   1989  * @param dpr the drain profits request
   1990  * @param mdr response data
   1991  */
   1992 static void
   1993 drain_profits_cb (
   1994   struct DrainProfitsRequest *dpr,
   1995   const struct TALER_EXCHANGE_PostManagementDrainResponse *mdr)
   1996 {
   1997   const struct TALER_EXCHANGE_HttpResponse *hr = &mdr->hr;
   1998 
   1999   if (MHD_HTTP_NO_CONTENT != hr->http_status)
   2000   {
   2001     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2002                 "Upload failed for command %u with status %u: %s (%s)\n",
   2003                 (unsigned int) dpr->idx,
   2004                 hr->http_status,
   2005                 TALER_ErrorCode_get_hint (hr->ec),
   2006                 hr->hint);
   2007     global_ret = EXIT_FAILURE;
   2008   }
   2009   GNUNET_CONTAINER_DLL_remove (dpr_head,
   2010                                dpr_tail,
   2011                                dpr);
   2012   GNUNET_free (dpr);
   2013   test_shutdown ();
   2014 }
   2015 
   2016 
   2017 /**
   2018  * Upload drain profit action.
   2019  *
   2020  * @param exchange_url base URL of the exchange
   2021  * @param idx index of the operation we are performing (for logging)
   2022  * @param value arguments for drain profits
   2023  */
   2024 static void
   2025 upload_drain (const char *exchange_url,
   2026               size_t idx,
   2027               const json_t *value)
   2028 {
   2029   struct TALER_WireTransferIdentifierRawP wtid;
   2030   struct TALER_MasterSignatureP master_sig;
   2031   const char *err_name;
   2032   unsigned int err_line;
   2033   struct TALER_Amount amount;
   2034   struct GNUNET_TIME_Timestamp date;
   2035   struct TALER_FullPayto payto_uri;
   2036   const char *account_section;
   2037   struct DrainProfitsRequest *dpr;
   2038   struct GNUNET_JSON_Specification spec[] = {
   2039     GNUNET_JSON_spec_fixed_auto ("wtid",
   2040                                  &wtid),
   2041     TALER_JSON_spec_amount ("amount",
   2042                             currency,
   2043                             &amount),
   2044     GNUNET_JSON_spec_timestamp ("date",
   2045                                 &date),
   2046     GNUNET_JSON_spec_string ("account_section",
   2047                              &account_section),
   2048     TALER_JSON_spec_full_payto_uri ("payto_uri",
   2049                                     &payto_uri),
   2050     GNUNET_JSON_spec_fixed_auto ("master_sig",
   2051                                  &master_sig),
   2052     GNUNET_JSON_spec_end ()
   2053   };
   2054 
   2055   if (GNUNET_OK !=
   2056       GNUNET_JSON_parse (value,
   2057                          spec,
   2058                          &err_name,
   2059                          &err_line))
   2060   {
   2061     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2062                 "Invalid input to drain profits: %s#%u at %u (skipping)\n",
   2063                 err_name,
   2064                 err_line,
   2065                 (unsigned int) idx);
   2066     json_dumpf (value,
   2067                 stderr,
   2068                 JSON_INDENT (2));
   2069     global_ret = EXIT_FAILURE;
   2070     GNUNET_SCHEDULER_shutdown ();
   2071     return;
   2072   }
   2073   dpr = GNUNET_new (struct DrainProfitsRequest);
   2074   dpr->idx = idx;
   2075   dpr->h =
   2076     TALER_EXCHANGE_post_management_drain_create (ctx,
   2077                                                  exchange_url,
   2078                                                  &wtid,
   2079                                                  &amount,
   2080                                                  date,
   2081                                                  account_section,
   2082                                                  payto_uri,
   2083                                                  &master_sig);
   2084   TALER_EXCHANGE_post_management_drain_start (dpr->h,
   2085                                               &drain_profits_cb,
   2086                                               dpr);
   2087   GNUNET_CONTAINER_DLL_insert (dpr_head,
   2088                                dpr_tail,
   2089                                dpr);
   2090 }
   2091 
   2092 
   2093 /**
   2094  * Function called with information about the post upload keys operation result.
   2095  *
   2096  * @param ukr the upload keys request
   2097  * @param mr response data
   2098  */
   2099 static void
   2100 keys_cb (
   2101   struct UploadKeysRequest *ukr,
   2102   const struct TALER_EXCHANGE_PostManagementKeysResponse *mr)
   2103 {
   2104   const struct TALER_EXCHANGE_HttpResponse *hr = &mr->hr;
   2105 
   2106   if (MHD_HTTP_NO_CONTENT != hr->http_status)
   2107   {
   2108     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2109                 "Upload failed for command %u with status %u: %s (%s)\n",
   2110                 (unsigned int) ukr->idx,
   2111                 hr->http_status,
   2112                 TALER_ErrorCode_get_hint (hr->ec),
   2113                 hr->hint);
   2114     global_ret = EXIT_FAILURE;
   2115   }
   2116   GNUNET_CONTAINER_DLL_remove (ukr_head,
   2117                                ukr_tail,
   2118                                ukr);
   2119   GNUNET_free (ukr);
   2120   test_shutdown ();
   2121 }
   2122 
   2123 
   2124 /**
   2125  * Upload (denomination and signing) key master signatures.
   2126  *
   2127  * @param exchange_url base URL of the exchange
   2128  * @param idx index of the operation we are performing (for logging)
   2129  * @param value arguments for POSTing keys
   2130  */
   2131 static void
   2132 upload_keys (const char *exchange_url,
   2133              size_t idx,
   2134              const json_t *value)
   2135 {
   2136   struct TALER_EXCHANGE_ManagementPostKeysData pkd;
   2137   struct UploadKeysRequest *ukr;
   2138   const char *err_name;
   2139   unsigned int err_line;
   2140   const json_t *denom_sigs;
   2141   const json_t *signkey_sigs;
   2142   struct GNUNET_JSON_Specification spec[] = {
   2143     GNUNET_JSON_spec_array_const ("denom_sigs",
   2144                                   &denom_sigs),
   2145     GNUNET_JSON_spec_array_const ("signkey_sigs",
   2146                                   &signkey_sigs),
   2147     GNUNET_JSON_spec_end ()
   2148   };
   2149   bool ok = true;
   2150 
   2151   if (GNUNET_OK !=
   2152       GNUNET_JSON_parse (value,
   2153                          spec,
   2154                          &err_name,
   2155                          &err_line))
   2156   {
   2157     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2158                 "Invalid input to 'upload': %s#%u (skipping)\n",
   2159                 err_name,
   2160                 err_line);
   2161     json_dumpf (value,
   2162                 stderr,
   2163                 JSON_INDENT (2));
   2164     global_ret = EXIT_FAILURE;
   2165     GNUNET_SCHEDULER_shutdown ();
   2166     return;
   2167   }
   2168   pkd.num_sign_sigs = json_array_size (signkey_sigs);
   2169   pkd.num_denom_sigs = json_array_size (denom_sigs);
   2170   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2171               "Uploading %u denomination and %u signing key signatures\n",
   2172               pkd.num_denom_sigs,
   2173               pkd.num_sign_sigs);
   2174   pkd.sign_sigs = GNUNET_new_array (
   2175     pkd.num_sign_sigs,
   2176     struct TALER_EXCHANGE_SigningKeySignature);
   2177   pkd.denom_sigs = GNUNET_new_array (
   2178     pkd.num_denom_sigs,
   2179     struct TALER_EXCHANGE_DenominationKeySignature);
   2180   for (unsigned int i = 0; i<pkd.num_sign_sigs; i++)
   2181   {
   2182     struct TALER_EXCHANGE_SigningKeySignature *ss = &pkd.sign_sigs[i];
   2183     json_t *val = json_array_get (signkey_sigs,
   2184                                   i);
   2185     struct GNUNET_JSON_Specification ispec[] = {
   2186       GNUNET_JSON_spec_fixed_auto ("exchange_pub",
   2187                                    &ss->exchange_pub),
   2188       GNUNET_JSON_spec_fixed_auto ("master_sig",
   2189                                    &ss->master_sig),
   2190       GNUNET_JSON_spec_end ()
   2191     };
   2192 
   2193     if (GNUNET_OK !=
   2194         GNUNET_JSON_parse (val,
   2195                            ispec,
   2196                            &err_name,
   2197                            &err_line))
   2198     {
   2199       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2200                   "Invalid input for signkey validity: %s#%u at %u (aborting)\n",
   2201                   err_name,
   2202                   err_line,
   2203                   i);
   2204       json_dumpf (val,
   2205                   stderr,
   2206                   JSON_INDENT (2));
   2207       ok = false;
   2208     }
   2209   }
   2210   for (unsigned int i = 0; i<pkd.num_denom_sigs; i++)
   2211   {
   2212     struct TALER_EXCHANGE_DenominationKeySignature *ds = &pkd.denom_sigs[i];
   2213     json_t *val = json_array_get (denom_sigs,
   2214                                   i);
   2215     struct GNUNET_JSON_Specification ispec[] = {
   2216       GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
   2217                                    &ds->h_denom_pub),
   2218       GNUNET_JSON_spec_fixed_auto ("master_sig",
   2219                                    &ds->master_sig),
   2220       GNUNET_JSON_spec_end ()
   2221     };
   2222 
   2223     if (GNUNET_OK !=
   2224         GNUNET_JSON_parse (val,
   2225                            ispec,
   2226                            &err_name,
   2227                            &err_line))
   2228     {
   2229       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2230                   "Invalid input for denomination validity: %s#%u at %u (aborting)\n",
   2231                   err_name,
   2232                   err_line,
   2233                   i);
   2234       json_dumpf (val,
   2235                   stderr,
   2236                   JSON_INDENT (2));
   2237       ok = false;
   2238     }
   2239   }
   2240 
   2241   if (ok)
   2242   {
   2243     ukr = GNUNET_new (struct UploadKeysRequest);
   2244     ukr->idx = idx;
   2245     ukr->h =
   2246       TALER_EXCHANGE_post_management_keys_create (ctx,
   2247                                                   exchange_url,
   2248                                                   &pkd);
   2249     TALER_EXCHANGE_post_management_keys_start (ukr->h,
   2250                                                &keys_cb,
   2251                                                ukr);
   2252     GNUNET_CONTAINER_DLL_insert (ukr_head,
   2253                                  ukr_tail,
   2254                                  ukr);
   2255   }
   2256   else
   2257   {
   2258     global_ret = EXIT_FAILURE;
   2259     GNUNET_SCHEDULER_shutdown ();
   2260   }
   2261   GNUNET_free (pkd.sign_sigs);
   2262   GNUNET_free (pkd.denom_sigs);
   2263 }
   2264 
   2265 
   2266 
   2267 /**
   2268  * Function called with information about the add partner operation.
   2269  *
   2270  * @param par the request
   2271  * @param apr response data
   2272  */
   2273 static void
   2274 add_partner_cb (
   2275   struct PartnerAddRequest *par,
   2276   const struct TALER_EXCHANGE_PostManagementPartnersResponse *apr)
   2277 {
   2278   const struct TALER_EXCHANGE_HttpResponse *hr = &apr->hr;
   2279 
   2280   if (MHD_HTTP_NO_CONTENT != hr->http_status)
   2281   {
   2282     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2283                 "Upload failed for command %u with status %u: %s (%s)\n",
   2284                 (unsigned int) par->idx,
   2285                 hr->http_status,
   2286                 TALER_ErrorCode_get_hint (hr->ec),
   2287                 hr->hint);
   2288     global_ret = EXIT_FAILURE;
   2289   }
   2290   GNUNET_CONTAINER_DLL_remove (par_head,
   2291                                par_tail,
   2292                                par);
   2293   GNUNET_free (par);
   2294   test_shutdown ();
   2295 }
   2296 
   2297 
   2298 /**
   2299  * Add partner action.
   2300  *
   2301  * @param exchange_url base URL of the exchange
   2302  * @param idx index of the operation we are performing (for logging)
   2303  * @param value arguments for add partner
   2304  */
   2305 static void
   2306 add_partner (const char *exchange_url,
   2307              size_t idx,
   2308              const json_t *value)
   2309 {
   2310   struct TALER_MasterPublicKeyP partner_pub;
   2311   struct GNUNET_TIME_Timestamp start_date;
   2312   struct GNUNET_TIME_Timestamp end_date;
   2313   struct GNUNET_TIME_Relative wad_frequency;
   2314   struct TALER_Amount wad_fee;
   2315   const char *partner_base_url;
   2316   struct TALER_MasterSignatureP master_sig;
   2317   struct PartnerAddRequest *par;
   2318   struct GNUNET_JSON_Specification spec[] = {
   2319     GNUNET_JSON_spec_fixed_auto ("partner_pub",
   2320                                  &partner_pub),
   2321     TALER_JSON_spec_amount ("wad_fee",
   2322                             currency,
   2323                             &wad_fee),
   2324     GNUNET_JSON_spec_relative_time ("wad_frequency",
   2325                                     &wad_frequency),
   2326     GNUNET_JSON_spec_timestamp ("start_date",
   2327                                 &start_date),
   2328     GNUNET_JSON_spec_timestamp ("end_date",
   2329                                 &end_date),
   2330     TALER_JSON_spec_web_url ("partner_base_url",
   2331                              &partner_base_url),
   2332     GNUNET_JSON_spec_fixed_auto ("master_sig",
   2333                                  &master_sig),
   2334     GNUNET_JSON_spec_end ()
   2335   };
   2336   const char *err_name;
   2337   unsigned int err_line;
   2338 
   2339   if (GNUNET_OK !=
   2340       GNUNET_JSON_parse (value,
   2341                          spec,
   2342                          &err_name,
   2343                          &err_line))
   2344   {
   2345     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2346                 "Invalid input to add partner: %s#%u at %u (skipping)\n",
   2347                 err_name,
   2348                 err_line,
   2349                 (unsigned int) idx);
   2350     json_dumpf (value,
   2351                 stderr,
   2352                 JSON_INDENT (2));
   2353     global_ret = EXIT_FAILURE;
   2354     GNUNET_SCHEDULER_shutdown ();
   2355     return;
   2356   }
   2357   par = GNUNET_new (struct PartnerAddRequest);
   2358   par->idx = idx;
   2359   par->h =
   2360     TALER_EXCHANGE_post_management_partners_create (ctx,
   2361                                                     exchange_url,
   2362                                                     &partner_pub,
   2363                                                     start_date,
   2364                                                     end_date,
   2365                                                     wad_frequency,
   2366                                                     &wad_fee,
   2367                                                     partner_base_url,
   2368                                                     &master_sig);
   2369   TALER_EXCHANGE_post_management_partners_start (par->h,
   2370                                                  &add_partner_cb,
   2371                                                  par);
   2372   GNUNET_CONTAINER_DLL_insert (par_head,
   2373                                par_tail,
   2374                                par);
   2375 }
   2376 
   2377 
   2378 /**
   2379  * Function called with information about the AML officer update operation.
   2380  *
   2381  * @param asr the request
   2382  * @param ar response data
   2383  */
   2384 static void
   2385 update_aml_officer_cb (
   2386   TALER_EXCHANGE_POST_MANAGEMENT_AML_OFFICERS_RESULT_CLOSURE *asr,
   2387   const struct TALER_EXCHANGE_PostManagementAmlOfficersResponse *ar)
   2388 {
   2389   const struct TALER_EXCHANGE_HttpResponse *hr = &ar->hr;
   2390 
   2391   if (MHD_HTTP_NO_CONTENT != hr->http_status)
   2392   {
   2393     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2394                 "Upload failed for command %u with status %u: %s (%s)\n",
   2395                 (unsigned int) asr->idx,
   2396                 hr->http_status,
   2397                 TALER_ErrorCode_get_hint (hr->ec),
   2398                 hr->hint);
   2399     global_ret = EXIT_FAILURE;
   2400   }
   2401   GNUNET_CONTAINER_DLL_remove (asr_head,
   2402                                asr_tail,
   2403                                asr);
   2404   GNUNET_free (asr);
   2405   test_shutdown ();
   2406 }
   2407 
   2408 
   2409 /**
   2410  * Upload AML staff action.
   2411  *
   2412  * @param exchange_url base URL of the exchange
   2413  * @param idx index of the operation we are performing (for logging)
   2414  * @param value arguments for AML staff change
   2415  */
   2416 static void
   2417 update_aml_staff (const char *exchange_url,
   2418                   size_t idx,
   2419                   const json_t *value)
   2420 {
   2421   struct TALER_AmlOfficerPublicKeyP officer_pub;
   2422   const char *officer_name;
   2423   struct GNUNET_TIME_Timestamp change_date;
   2424   bool is_active;
   2425   bool read_only;
   2426   struct TALER_MasterSignatureP master_sig;
   2427   struct AmlStaffRequest *asr;
   2428   struct GNUNET_JSON_Specification spec[] = {
   2429     GNUNET_JSON_spec_fixed_auto ("officer_pub",
   2430                                  &officer_pub),
   2431     GNUNET_JSON_spec_timestamp ("change_date",
   2432                                 &change_date),
   2433     GNUNET_JSON_spec_bool ("is_active",
   2434                            &is_active),
   2435     GNUNET_JSON_spec_bool ("read_only",
   2436                            &read_only),
   2437     GNUNET_JSON_spec_string ("officer_name",
   2438                              &officer_name),
   2439     GNUNET_JSON_spec_fixed_auto ("master_sig",
   2440                                  &master_sig),
   2441     GNUNET_JSON_spec_end ()
   2442   };
   2443   const char *err_name;
   2444   unsigned int err_line;
   2445 
   2446   if (GNUNET_OK !=
   2447       GNUNET_JSON_parse (value,
   2448                          spec,
   2449                          &err_name,
   2450                          &err_line))
   2451   {
   2452     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2453                 "Invalid input to AML staff update: %s#%u at %u (skipping)\n",
   2454                 err_name,
   2455                 err_line,
   2456                 (unsigned int) idx);
   2457     json_dumpf (value,
   2458                 stderr,
   2459                 JSON_INDENT (2));
   2460     global_ret = EXIT_FAILURE;
   2461     GNUNET_SCHEDULER_shutdown ();
   2462     return;
   2463   }
   2464   asr = GNUNET_new (struct AmlStaffRequest);
   2465   asr->idx = idx;
   2466   asr->h = TALER_EXCHANGE_post_management_aml_officers_create (
   2467     ctx,
   2468     exchange_url,
   2469     &officer_pub,
   2470     officer_name,
   2471     change_date,
   2472     is_active,
   2473     read_only,
   2474     &master_sig);
   2475   GNUNET_CONTAINER_DLL_insert (asr_head,
   2476                                asr_tail,
   2477                                asr);
   2478   GNUNET_assert (TALER_EC_NONE ==
   2479                  TALER_EXCHANGE_post_management_aml_officers_start (
   2480                    asr->h,
   2481                    &update_aml_officer_cb,
   2482                    asr));
   2483 }
   2484 
   2485 
   2486 /**
   2487  * Perform uploads based on the JSON in #out.
   2488  *
   2489  * @param exchange_url base URL of the exchange to use
   2490  */
   2491 static void
   2492 trigger_upload (const char *exchange_url)
   2493 {
   2494   struct UploadHandler uhs[] = {
   2495     {
   2496       .key = OP_REVOKE_DENOMINATION,
   2497       .cb = &upload_denom_revocation
   2498     },
   2499     {
   2500       .key = OP_REVOKE_SIGNKEY,
   2501       .cb = &upload_signkey_revocation
   2502     },
   2503     {
   2504       .key = OP_ENABLE_AUDITOR,
   2505       .cb = &upload_auditor_add
   2506     },
   2507     {
   2508       .key = OP_DISABLE_AUDITOR,
   2509       .cb = &upload_auditor_del
   2510     },
   2511     {
   2512       .key = OP_ENABLE_WIRE,
   2513       .cb = &upload_wire_add
   2514     },
   2515     {
   2516       .key = OP_DISABLE_WIRE,
   2517       .cb = &upload_wire_del
   2518     },
   2519     {
   2520       .key = OP_SET_WIRE_FEE,
   2521       .cb = &upload_wire_fee
   2522     },
   2523     {
   2524       .key = OP_SET_GLOBAL_FEE,
   2525       .cb = &upload_global_fee
   2526     },
   2527     {
   2528       .key = OP_UPLOAD_SIGS,
   2529       .cb = &upload_keys
   2530     },
   2531     {
   2532       .key = OP_DRAIN_PROFITS,
   2533       .cb = &upload_drain
   2534     },
   2535     {
   2536       .key = OP_UPDATE_AML_STAFF,
   2537       .cb = &update_aml_staff
   2538     },
   2539     {
   2540       .key = OP_ADD_PARTNER,
   2541       .cb = &add_partner
   2542     },
   2543     /* array termination */
   2544     {
   2545       .key = NULL
   2546     }
   2547   };
   2548   size_t index;
   2549   json_t *obj;
   2550 
   2551   json_array_foreach (out, index, obj) {
   2552     bool found = false;
   2553     const char *key;
   2554     const json_t *value;
   2555 
   2556     key = json_string_value (json_object_get (obj, "operation"));
   2557     value = json_object_get (obj, "arguments");
   2558     if (NULL == key)
   2559     {
   2560       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2561                   "Malformed JSON input\n");
   2562       global_ret = EXIT_FAILURE;
   2563       GNUNET_SCHEDULER_shutdown ();
   2564       return;
   2565     }
   2566     /* block of code that uses key and value */
   2567     for (unsigned int i = 0; NULL != uhs[i].key; i++)
   2568     {
   2569       if (0 == strcasecmp (key,
   2570                            uhs[i].key))
   2571       {
   2572 
   2573         found = true;
   2574         uhs[i].cb (exchange_url,
   2575                    index,
   2576                    value);
   2577         break;
   2578       }
   2579     }
   2580     if (! found)
   2581     {
   2582       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2583                   "Upload does not know how to handle `%s'\n",
   2584                   key);
   2585       global_ret = EXIT_FAILURE;
   2586       GNUNET_SCHEDULER_shutdown ();
   2587       return;
   2588     }
   2589   }
   2590 }
   2591 
   2592 
   2593 /**
   2594  * Upload operation result (signatures) to exchange.
   2595  *
   2596  * @param args the array of command-line arguments to process next
   2597  */
   2598 static void
   2599 do_upload (char *const *args)
   2600 {
   2601   (void) args;
   2602   if (NULL != in)
   2603   {
   2604     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2605                 "Downloaded data was not consumed, refusing upload\n");
   2606     GNUNET_SCHEDULER_shutdown ();
   2607     global_ret = EXIT_FAILURE;
   2608     return;
   2609   }
   2610   if (NULL == out)
   2611   {
   2612     json_error_t err;
   2613 
   2614     out = json_loadf (stdin,
   2615                       JSON_REJECT_DUPLICATES,
   2616                       &err);
   2617     if (NULL == out)
   2618     {
   2619       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2620                   "Failed to read JSON input: %s at %d:%s (offset: %d)\n",
   2621                   err.text,
   2622                   err.line,
   2623                   err.source,
   2624                   err.position);
   2625       GNUNET_SCHEDULER_shutdown ();
   2626       global_ret = EXIT_FAILURE;
   2627       return;
   2628     }
   2629   }
   2630   if (! json_is_array (out))
   2631   {
   2632     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2633                 "Error: expected JSON array for `upload` command\n");
   2634     GNUNET_SCHEDULER_shutdown ();
   2635     global_ret = EXIT_FAILURE;
   2636     return;
   2637   }
   2638   if ( (NULL == CFG_exchange_url) &&
   2639        (GNUNET_OK !=
   2640         GNUNET_CONFIGURATION_get_value_string (kcfg,
   2641                                                "exchange",
   2642                                                "BASE_URL",
   2643                                                &CFG_exchange_url)) )
   2644   {
   2645     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   2646                                "exchange",
   2647                                "BASE_URL");
   2648     global_ret = EXIT_NOTCONFIGURED;
   2649     GNUNET_SCHEDULER_shutdown ();
   2650     return;
   2651   }
   2652   trigger_upload (CFG_exchange_url);
   2653   json_decref (out);
   2654   out = NULL;
   2655 }
   2656 
   2657 
   2658 /**
   2659  * Revoke denomination key.
   2660  *
   2661  * @param args the array of command-line arguments to process next;
   2662  *        args[0] must be the hash of the denomination key to revoke
   2663  */
   2664 static void
   2665 do_revoke_denomination_key (char *const *args)
   2666 {
   2667   struct TALER_DenominationHashP h_denom_pub;
   2668   struct TALER_MasterSignatureP master_sig;
   2669 
   2670   if (NULL != in)
   2671   {
   2672     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2673                 "Downloaded data was not consumed, refusing revocation\n");
   2674     GNUNET_SCHEDULER_shutdown ();
   2675     global_ret = EXIT_FAILURE;
   2676     return;
   2677   }
   2678   if ( (NULL == args[0]) ||
   2679        (GNUNET_OK !=
   2680         GNUNET_STRINGS_string_to_data (args[0],
   2681                                        strlen (args[0]),
   2682                                        &h_denom_pub,
   2683                                        sizeof (h_denom_pub))) )
   2684   {
   2685     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2686                 "You must specify a denomination key with this subcommand\n");
   2687     GNUNET_SCHEDULER_shutdown ();
   2688     global_ret = EXIT_INVALIDARGUMENT;
   2689     return;
   2690   }
   2691   if (GNUNET_OK !=
   2692       load_offline_key (GNUNET_NO))
   2693     return;
   2694   TALER_exchange_offline_denomination_revoke_sign (&h_denom_pub,
   2695                                                    &master_priv,
   2696                                                    &master_sig);
   2697   output_operation (OP_REVOKE_DENOMINATION,
   2698                     GNUNET_JSON_PACK (
   2699                       GNUNET_JSON_pack_data_auto ("h_denom_pub",
   2700                                                   &h_denom_pub),
   2701                       GNUNET_JSON_pack_data_auto ("master_sig",
   2702                                                   &master_sig)));
   2703   next (args + 1);
   2704 }
   2705 
   2706 
   2707 /**
   2708  * Revoke signkey.
   2709  *
   2710  * @param args the array of command-line arguments to process next;
   2711  *        args[0] must be the hash of the denomination key to revoke
   2712  */
   2713 static void
   2714 do_revoke_signkey (char *const *args)
   2715 {
   2716   struct TALER_ExchangePublicKeyP exchange_pub;
   2717   struct TALER_MasterSignatureP master_sig;
   2718 
   2719   if (NULL != in)
   2720   {
   2721     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2722                 "Downloaded data was not consumed, refusing revocation\n");
   2723     GNUNET_SCHEDULER_shutdown ();
   2724     global_ret = EXIT_FAILURE;
   2725     return;
   2726   }
   2727   if ( (NULL == args[0]) ||
   2728        (GNUNET_OK !=
   2729         GNUNET_STRINGS_string_to_data (args[0],
   2730                                        strlen (args[0]),
   2731                                        &exchange_pub,
   2732                                        sizeof (exchange_pub))) )
   2733   {
   2734     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2735                 "You must specify an exchange signing key with this subcommand\n");
   2736     GNUNET_SCHEDULER_shutdown ();
   2737     global_ret = EXIT_INVALIDARGUMENT;
   2738     return;
   2739   }
   2740   if (GNUNET_OK !=
   2741       load_offline_key (GNUNET_NO))
   2742     return;
   2743   TALER_exchange_offline_signkey_revoke_sign (&exchange_pub,
   2744                                               &master_priv,
   2745                                               &master_sig);
   2746   output_operation (OP_REVOKE_SIGNKEY,
   2747                     GNUNET_JSON_PACK (
   2748                       GNUNET_JSON_pack_data_auto ("exchange_pub",
   2749                                                   &exchange_pub),
   2750                       GNUNET_JSON_pack_data_auto ("master_sig",
   2751                                                   &master_sig)));
   2752   next (args + 1);
   2753 }
   2754 
   2755 
   2756 /**
   2757  * Add auditor.
   2758  *
   2759  * @param args the array of command-line arguments to process next;
   2760  *        args[0] must be the auditor's public key, args[1] the auditor's
   2761  *        API base URL, and args[2] the auditor's name.
   2762  */
   2763 static void
   2764 do_add_auditor (char *const *args)
   2765 {
   2766   struct TALER_MasterSignatureP master_sig;
   2767   struct TALER_AuditorPublicKeyP auditor_pub;
   2768   struct GNUNET_TIME_Timestamp now;
   2769 
   2770   if (NULL != in)
   2771   {
   2772     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2773                 "Downloaded data was not consumed, not adding auditor\n");
   2774     GNUNET_SCHEDULER_shutdown ();
   2775     global_ret = EXIT_FAILURE;
   2776     return;
   2777   }
   2778   if ( (NULL == args[0]) ||
   2779        (GNUNET_OK !=
   2780         GNUNET_STRINGS_string_to_data (args[0],
   2781                                        strlen (args[0]),
   2782                                        &auditor_pub,
   2783                                        sizeof (auditor_pub))) )
   2784   {
   2785     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2786                 "You must specify an auditor public key as first argument for this subcommand\n");
   2787     GNUNET_SCHEDULER_shutdown ();
   2788     global_ret = EXIT_INVALIDARGUMENT;
   2789     return;
   2790   }
   2791 
   2792   if ( (NULL == args[1]) ||
   2793        (0 != strncmp ("http",
   2794                       args[1],
   2795                       strlen ("http"))) ||
   2796        (NULL == args[2]) )
   2797   {
   2798     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2799                 "You must specify an auditor URI and auditor name as 2nd and 3rd arguments to this subcommand\n");
   2800     GNUNET_SCHEDULER_shutdown ();
   2801     global_ret = EXIT_INVALIDARGUMENT;
   2802     return;
   2803   }
   2804   if (GNUNET_OK !=
   2805       load_offline_key (GNUNET_NO))
   2806     return;
   2807   now = GNUNET_TIME_timestamp_get ();
   2808   TALER_exchange_offline_auditor_add_sign (&auditor_pub,
   2809                                            args[1],
   2810                                            now,
   2811                                            &master_priv,
   2812                                            &master_sig);
   2813   output_operation (OP_ENABLE_AUDITOR,
   2814                     GNUNET_JSON_PACK (
   2815                       GNUNET_JSON_pack_string ("auditor_url",
   2816                                                args[1]),
   2817                       GNUNET_JSON_pack_string ("auditor_name",
   2818                                                args[2]),
   2819                       GNUNET_JSON_pack_timestamp ("validity_start",
   2820                                                   now),
   2821                       GNUNET_JSON_pack_data_auto ("auditor_pub",
   2822                                                   &auditor_pub),
   2823                       GNUNET_JSON_pack_data_auto ("master_sig",
   2824                                                   &master_sig)));
   2825   next (args + 3);
   2826 }
   2827 
   2828 
   2829 /**
   2830  * Disable auditor account.
   2831  *
   2832  * @param args the array of command-line arguments to process next;
   2833  *        args[0] must be the hash of the denomination key to revoke
   2834  */
   2835 static void
   2836 do_del_auditor (char *const *args)
   2837 {
   2838   struct TALER_MasterSignatureP master_sig;
   2839   struct TALER_AuditorPublicKeyP auditor_pub;
   2840   struct GNUNET_TIME_Timestamp now;
   2841 
   2842   if (NULL != in)
   2843   {
   2844     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2845                 "Downloaded data was not consumed, not deleting auditor account\n");
   2846     GNUNET_SCHEDULER_shutdown ();
   2847     global_ret = EXIT_FAILURE;
   2848     return;
   2849   }
   2850   if ( (NULL == args[0]) ||
   2851        (GNUNET_OK !=
   2852         GNUNET_STRINGS_string_to_data (args[0],
   2853                                        strlen (args[0]),
   2854                                        &auditor_pub,
   2855                                        sizeof (auditor_pub))) )
   2856   {
   2857     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2858                 "You must specify an auditor public key as first argument for this subcommand\n");
   2859     GNUNET_SCHEDULER_shutdown ();
   2860     global_ret = EXIT_INVALIDARGUMENT;
   2861     return;
   2862   }
   2863   if (GNUNET_OK !=
   2864       load_offline_key (GNUNET_NO))
   2865     return;
   2866   now = GNUNET_TIME_timestamp_get ();
   2867   TALER_exchange_offline_auditor_del_sign (&auditor_pub,
   2868                                            now,
   2869                                            &master_priv,
   2870                                            &master_sig);
   2871   output_operation (OP_DISABLE_AUDITOR,
   2872                     GNUNET_JSON_PACK (
   2873                       GNUNET_JSON_pack_data_auto ("auditor_pub",
   2874                                                   &auditor_pub),
   2875                       GNUNET_JSON_pack_timestamp ("validity_end",
   2876                                                   now),
   2877                       GNUNET_JSON_pack_data_auto ("master_sig",
   2878                                                   &master_sig)));
   2879   next (args + 1);
   2880 }
   2881 
   2882 
   2883 /**
   2884  * Parse account restriction.
   2885  *
   2886  * @param args the array of command-line arguments to process next
   2887  * @param[in,out] restrictions JSON array to update
   2888  * @return -1 on error, otherwise number of arguments from @a args that were used
   2889  */
   2890 static int
   2891 parse_restriction (char *const *args,
   2892                    json_t *restrictions)
   2893 {
   2894   if (NULL == args[0])
   2895   {
   2896     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2897                 "Restriction TYPE argument missing\n");
   2898     return -1;
   2899   }
   2900   if (0 == strcmp (args[0],
   2901                    "deny"))
   2902   {
   2903     GNUNET_assert (0 ==
   2904                    json_array_append_new (
   2905                      restrictions,
   2906                      GNUNET_JSON_PACK (
   2907                        GNUNET_JSON_pack_string ("type",
   2908                                                 "deny"))));
   2909     return 1;
   2910   }
   2911   if (0 == strcmp (args[0],
   2912                    "regex"))
   2913   {
   2914     json_t *i18n;
   2915     json_error_t err;
   2916 
   2917     if ( (NULL == args[1]) ||
   2918          (NULL == args[2]) ||
   2919          (NULL == args[3]) )
   2920     {
   2921       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2922                   "Mandatory arguments for restriction of type `regex' missing (REGEX, HINT, HINT-I18 required)\n");
   2923       return -1;
   2924     }
   2925     {
   2926       regex_t ex;
   2927 
   2928       if (0 != regcomp (&ex,
   2929                         args[1],
   2930                         REG_NOSUB | REG_EXTENDED))
   2931       {
   2932         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2933                     "Invalid regular expression `%s'\n",
   2934                     args[1]);
   2935         return -1;
   2936       }
   2937       regfree (&ex);
   2938     }
   2939 
   2940     i18n = json_loads (args[3],
   2941                        JSON_REJECT_DUPLICATES,
   2942                        &err);
   2943     if (NULL == i18n)
   2944     {
   2945       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2946                   "Invalid JSON for restriction of type `regex': `%s` at %d\n",
   2947                   args[3],
   2948                   err.position);
   2949       return -1;
   2950     }
   2951     GNUNET_assert (0 ==
   2952                    json_array_append_new (
   2953                      restrictions,
   2954                      GNUNET_JSON_PACK (
   2955                        GNUNET_JSON_pack_string ("type",
   2956                                                 "regex"),
   2957                        GNUNET_JSON_pack_string ("payto_regex",
   2958                                                 args[1]),
   2959                        GNUNET_JSON_pack_string ("human_hint",
   2960                                                 args[2]),
   2961                        GNUNET_JSON_pack_object_steal ("human_hint_i18n",
   2962                                                       i18n)
   2963                        )));
   2964     return 4;
   2965   }
   2966   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2967               "Restriction TYPE `%s' unsupported\n",
   2968               args[0]);
   2969   return -1;
   2970 }
   2971 
   2972 
   2973 /**
   2974  * Add wire account.
   2975  *
   2976  * @param args the array of command-line arguments to process next;
   2977  *        args[0] must be the hash of the denomination key to revoke
   2978  */
   2979 static void
   2980 do_add_wire (char *const *args)
   2981 {
   2982   struct TALER_MasterSignatureP master_sig_add;
   2983   struct TALER_MasterSignatureP master_sig_wire;
   2984   struct GNUNET_TIME_Timestamp now;
   2985   const char *conversion_url = NULL;
   2986   const char *open_banking_gateway = NULL;
   2987   const char *prepared_transfer_url = NULL;
   2988   const char *bank_label = NULL;
   2989   int64_t priority = 0;
   2990   json_t *debit_restrictions;
   2991   json_t *credit_restrictions;
   2992   unsigned int num_args = 1;
   2993   struct TALER_FullPayto payto_uri = {
   2994     .full_payto = args[0]
   2995   };
   2996 
   2997   if (NULL != in)
   2998   {
   2999     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3000                 "Downloaded data was not consumed, not adding wire account\n");
   3001     GNUNET_SCHEDULER_shutdown ();
   3002     global_ret = EXIT_FAILURE;
   3003     return;
   3004   }
   3005   if (NULL == args[0])
   3006   {
   3007     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3008                 "You must specify a payto://-URI with this subcommand\n");
   3009     GNUNET_SCHEDULER_shutdown ();
   3010     global_ret = EXIT_INVALIDARGUMENT;
   3011     return;
   3012   }
   3013   if (GNUNET_OK !=
   3014       load_offline_key (GNUNET_NO))
   3015     return;
   3016   {
   3017     char *msg = TALER_payto_validate (payto_uri);
   3018 
   3019     if (NULL != msg)
   3020     {
   3021       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3022                   "payto URI `%s' is malformed: %s\n",
   3023                   payto_uri.full_payto,
   3024                   msg);
   3025       GNUNET_free (msg);
   3026       GNUNET_SCHEDULER_shutdown ();
   3027       global_ret = EXIT_INVALIDARGUMENT;
   3028       return;
   3029     }
   3030   }
   3031   now = GNUNET_TIME_timestamp_get ();
   3032   {
   3033     char *wire_method;
   3034 
   3035     wire_method = TALER_payto_get_method (payto_uri.full_payto);
   3036     if (NULL == wire_method)
   3037     {
   3038       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3039                   "payto:// URI `%s' is malformed\n",
   3040                   payto_uri.full_payto);
   3041       global_ret = EXIT_INVALIDARGUMENT;
   3042       GNUNET_SCHEDULER_shutdown ();
   3043       return;
   3044     }
   3045     GNUNET_free (wire_method);
   3046   }
   3047   debit_restrictions = json_array ();
   3048   GNUNET_assert (NULL != debit_restrictions);
   3049   credit_restrictions = json_array ();
   3050   GNUNET_assert (NULL != credit_restrictions);
   3051   while (NULL != args[num_args])
   3052   {
   3053     if (0 == strcmp (args[num_args],
   3054                      "conversion-url"))
   3055     {
   3056       num_args++;
   3057       conversion_url = args[num_args];
   3058       if (NULL == conversion_url)
   3059       {
   3060         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3061                     "'conversion-url' requires an argument\n");
   3062         global_ret = EXIT_INVALIDARGUMENT;
   3063         GNUNET_SCHEDULER_shutdown ();
   3064         json_decref (debit_restrictions);
   3065         json_decref (credit_restrictions);
   3066         return;
   3067       }
   3068       if (! TALER_is_web_url (conversion_url))
   3069       {
   3070         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3071                     "'conversion-url' must refer to HTTP(S) endpoint, `%s' is invalid\n",
   3072                     conversion_url);
   3073         global_ret = EXIT_INVALIDARGUMENT;
   3074         GNUNET_SCHEDULER_shutdown ();
   3075         json_decref (debit_restrictions);
   3076         json_decref (credit_restrictions);
   3077         return;
   3078       }
   3079       num_args++;
   3080       continue;
   3081     }
   3082 
   3083     if (0 == strcmp (args[num_args],
   3084                      "open-banking-gateway"))
   3085     {
   3086       num_args++;
   3087       open_banking_gateway = args[num_args];
   3088       if (NULL == open_banking_gateway)
   3089       {
   3090         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3091                     "'open-banking-gateway' requires an argument\n");
   3092         global_ret = EXIT_INVALIDARGUMENT;
   3093         GNUNET_SCHEDULER_shutdown ();
   3094         json_decref (debit_restrictions);
   3095         json_decref (credit_restrictions);
   3096         return;
   3097       }
   3098       if (! TALER_is_web_url (open_banking_gateway))
   3099       {
   3100         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3101                     "'open-banking-gateway' must refer to HTTP(S) endpoint, `%s' is invalid\n",
   3102                     open_banking_gateway);
   3103         global_ret = EXIT_INVALIDARGUMENT;
   3104         GNUNET_SCHEDULER_shutdown ();
   3105         json_decref (debit_restrictions);
   3106         json_decref (credit_restrictions);
   3107         return;
   3108       }
   3109       num_args++;
   3110       continue;
   3111     }
   3112 
   3113     if (0 == strcmp (args[num_args],
   3114                      "prepared-transfer-url"))
   3115     {
   3116       num_args++;
   3117       prepared_transfer_url = args[num_args];
   3118       if (NULL == prepared_transfer_url)
   3119       {
   3120         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3121                     "'prepared-transfer-url' requires an argument\n");
   3122         global_ret = EXIT_INVALIDARGUMENT;
   3123         GNUNET_SCHEDULER_shutdown ();
   3124         json_decref (debit_restrictions);
   3125         json_decref (credit_restrictions);
   3126         return;
   3127       }
   3128       if (! TALER_is_web_url (prepared_transfer_url))
   3129       {
   3130         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3131                     "'prepared-transfer-url' must refer to HTTP(S) endpoint, `%s' is invalid\n",
   3132                     prepared_transfer_url);
   3133         global_ret = EXIT_INVALIDARGUMENT;
   3134         GNUNET_SCHEDULER_shutdown ();
   3135         json_decref (debit_restrictions);
   3136         json_decref (credit_restrictions);
   3137         return;
   3138       }
   3139       num_args++;
   3140       continue;
   3141     }
   3142 
   3143     if (0 == strcmp (args[num_args],
   3144                      "credit-restriction"))
   3145     {
   3146       int iret;
   3147 
   3148       num_args++;
   3149       iret = parse_restriction (&args[num_args],
   3150                                 credit_restrictions);
   3151       if (iret <= 0)
   3152       {
   3153         global_ret = EXIT_INVALIDARGUMENT;
   3154         GNUNET_SCHEDULER_shutdown ();
   3155         json_decref (debit_restrictions);
   3156         json_decref (credit_restrictions);
   3157         return;
   3158       }
   3159       num_args += iret;
   3160       continue;
   3161     }
   3162     if (0 == strcmp (args[num_args],
   3163                      "debit-restriction"))
   3164     {
   3165       int iret;
   3166 
   3167       num_args++;
   3168       iret = parse_restriction (&args[num_args],
   3169                                 debit_restrictions);
   3170       if (iret <= 0)
   3171       {
   3172         global_ret = EXIT_INVALIDARGUMENT;
   3173         GNUNET_SCHEDULER_shutdown ();
   3174         json_decref (debit_restrictions);
   3175         json_decref (credit_restrictions);
   3176         return;
   3177       }
   3178       num_args += iret;
   3179       continue;
   3180     }
   3181     if (0 == strcmp (args[num_args],
   3182                      "display-hint"))
   3183     {
   3184       long long p;
   3185       char dummy;
   3186 
   3187       num_args++;
   3188       if ( (NULL == args[num_args]) ||
   3189            (NULL == args[num_args + 1]) )
   3190       {
   3191         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3192                     "'display-hint' requires at least two arguments (priority and label)\n");
   3193         global_ret = EXIT_INVALIDARGUMENT;
   3194         GNUNET_SCHEDULER_shutdown ();
   3195         json_decref (debit_restrictions);
   3196         json_decref (credit_restrictions);
   3197         return;
   3198       }
   3199       if (1 != sscanf (args[num_args],
   3200                        "%lld%c",
   3201                        &p,
   3202                        &dummy))
   3203       {
   3204         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3205                     "Priority argument `%s' is not a number\n",
   3206                     args[num_args]);
   3207         global_ret = EXIT_INVALIDARGUMENT;
   3208         GNUNET_SCHEDULER_shutdown ();
   3209         json_decref (debit_restrictions);
   3210         json_decref (credit_restrictions);
   3211         return;
   3212       }
   3213       priority = (int64_t) p;
   3214       num_args++;
   3215       bank_label = args[num_args];
   3216       num_args++;
   3217       continue;
   3218     }
   3219     break;
   3220   }
   3221   TALER_exchange_offline_wire_add_sign (payto_uri,
   3222                                         conversion_url,
   3223                                         open_banking_gateway,
   3224                                         prepared_transfer_url,
   3225                                         debit_restrictions,
   3226                                         credit_restrictions,
   3227                                         now,
   3228                                         &master_priv,
   3229                                         &master_sig_add);
   3230   TALER_exchange_wire_signature_make (payto_uri,
   3231                                       conversion_url,
   3232                                       open_banking_gateway,
   3233                                       prepared_transfer_url,
   3234                                       debit_restrictions,
   3235                                       credit_restrictions,
   3236                                       &master_priv,
   3237                                       &master_sig_wire);
   3238   output_operation (OP_ENABLE_WIRE,
   3239                     GNUNET_JSON_PACK (
   3240                       TALER_JSON_pack_full_payto ("payto_uri",
   3241                                                   payto_uri),
   3242                       GNUNET_JSON_pack_array_steal ("debit_restrictions",
   3243                                                     debit_restrictions),
   3244                       GNUNET_JSON_pack_array_steal ("credit_restrictions",
   3245                                                     credit_restrictions),
   3246                       GNUNET_JSON_pack_allow_null (
   3247                         GNUNET_JSON_pack_string ("conversion_url",
   3248                                                  conversion_url)),
   3249                       GNUNET_JSON_pack_allow_null (
   3250                         GNUNET_JSON_pack_string ("open_banking_gateway",
   3251                                                  open_banking_gateway)),
   3252                       GNUNET_JSON_pack_allow_null (
   3253                         GNUNET_JSON_pack_string ("prepared_transfer_url",
   3254                                                  prepared_transfer_url)),
   3255                       GNUNET_JSON_pack_allow_null (
   3256                         GNUNET_JSON_pack_string ("bank_label",
   3257                                                  bank_label)),
   3258                       GNUNET_JSON_pack_int64 ("priority",
   3259                                               priority),
   3260                       GNUNET_JSON_pack_timestamp ("validity_start",
   3261                                                   now),
   3262                       GNUNET_JSON_pack_data_auto ("master_sig_add",
   3263                                                   &master_sig_add),
   3264                       GNUNET_JSON_pack_data_auto ("master_sig_wire",
   3265                                                   &master_sig_wire)));
   3266   next (args + num_args);
   3267 }
   3268 
   3269 
   3270 /**
   3271  * Disable wire account.
   3272  *
   3273  * @param args the array of command-line arguments to process next;
   3274  *        args[0] must be the payto URI of the account to disable
   3275  */
   3276 static void
   3277 do_del_wire (char *const *args)
   3278 {
   3279   struct TALER_MasterSignatureP master_sig;
   3280   struct GNUNET_TIME_Timestamp now;
   3281   struct TALER_FullPayto payto_uri = {
   3282     .full_payto = args[0]
   3283   };
   3284 
   3285   if (NULL != in)
   3286   {
   3287     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3288                 "Downloaded data was not consumed, not deleting wire account\n")
   3289     ;
   3290     GNUNET_SCHEDULER_shutdown ();
   3291     global_ret = EXIT_FAILURE;
   3292     return;
   3293   }
   3294   if (NULL == args[0])
   3295   {
   3296     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3297                 "You must specify a payto://-URI with this subcommand\n");
   3298     GNUNET_SCHEDULER_shutdown ();
   3299     global_ret = EXIT_INVALIDARGUMENT;
   3300     return;
   3301   }
   3302   if (GNUNET_OK !=
   3303       load_offline_key (GNUNET_NO))
   3304     return;
   3305   now = GNUNET_TIME_timestamp_get ();
   3306   TALER_exchange_offline_wire_del_sign (payto_uri,
   3307                                         now,
   3308                                         &master_priv,
   3309                                         &master_sig);
   3310   output_operation (OP_DISABLE_WIRE,
   3311                     GNUNET_JSON_PACK (
   3312                       TALER_JSON_pack_full_payto ("payto_uri",
   3313                                                   payto_uri),
   3314                       GNUNET_JSON_pack_timestamp ("validity_end",
   3315                                                   now),
   3316                       GNUNET_JSON_pack_data_auto ("master_sig",
   3317                                                   &master_sig)));
   3318   next (args + 1);
   3319 }
   3320 
   3321 
   3322 /**
   3323  * Set wire fees for the given year.
   3324  *
   3325  * @param args the array of command-line arguments to process next;
   3326  *        args[0] must be the year, args[1] the wire method, args[2] the wire fee and args[3]
   3327  *        the closing fee.
   3328  */
   3329 static void
   3330 do_set_wire_fee (char *const *args)
   3331 {
   3332   struct TALER_MasterSignatureP master_sig;
   3333   char dummy;
   3334   unsigned int year;
   3335   struct TALER_WireFeeSet fees;
   3336   struct GNUNET_TIME_Timestamp start_time;
   3337   struct GNUNET_TIME_Timestamp end_time;
   3338 
   3339   if (NULL != in)
   3340   {
   3341     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3342                 "Downloaded data was not consumed, not setting wire fee\n");
   3343     GNUNET_SCHEDULER_shutdown ();
   3344     global_ret = EXIT_FAILURE;
   3345     return;
   3346   }
   3347   if ( (NULL == args[0]) ||
   3348        (NULL == args[1]) ||
   3349        (NULL == args[2]) ||
   3350        (NULL == args[3]) ||
   3351        ( (1 != sscanf (args[0],
   3352                        "%u%c",
   3353                        &year,
   3354                        &dummy)) &&
   3355          (0 != strcasecmp ("now",
   3356                            args[0])) ) ||
   3357        (GNUNET_OK !=
   3358         TALER_string_to_amount (args[2],
   3359                                 &fees.wire)) ||
   3360        (GNUNET_OK !=
   3361         TALER_string_to_amount (args[3],
   3362                                 &fees.closing)) )
   3363   {
   3364     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3365                 "You must use YEAR, METHOD, WIRE-FEE, and CLOSING-FEE as arguments for this subcommand\n");
   3366     GNUNET_SCHEDULER_shutdown ();
   3367     global_ret = EXIT_INVALIDARGUMENT;
   3368     return;
   3369   }
   3370   if (0 == strcasecmp ("now",
   3371                        args[0]))
   3372     year = GNUNET_TIME_get_current_year ();
   3373   if (GNUNET_OK !=
   3374       load_offline_key (GNUNET_NO))
   3375     return;
   3376   start_time = GNUNET_TIME_absolute_to_timestamp (
   3377     GNUNET_TIME_year_to_time (year));
   3378   end_time = GNUNET_TIME_absolute_to_timestamp (
   3379     GNUNET_TIME_year_to_time (year + 1));
   3380 
   3381   TALER_exchange_offline_wire_fee_sign (args[1],
   3382                                         start_time,
   3383                                         end_time,
   3384                                         &fees,
   3385                                         &master_priv,
   3386                                         &master_sig);
   3387   output_operation (OP_SET_WIRE_FEE,
   3388                     GNUNET_JSON_PACK (
   3389                       GNUNET_JSON_pack_string ("wire_method",
   3390                                                args[1]),
   3391                       GNUNET_JSON_pack_timestamp ("start_time",
   3392                                                   start_time),
   3393                       GNUNET_JSON_pack_timestamp ("end_time",
   3394                                                   end_time),
   3395                       TALER_JSON_pack_amount ("wire_fee",
   3396                                               &fees.wire),
   3397                       TALER_JSON_pack_amount ("closing_fee",
   3398                                               &fees.closing),
   3399                       GNUNET_JSON_pack_data_auto ("master_sig",
   3400                                                   &master_sig)));
   3401   next (args + 4);
   3402 }
   3403 
   3404 
   3405 /**
   3406  * Set global fees for the given year.
   3407  *
   3408  * @param args the array of command-line arguments to process next;
   3409  *        args[0] must be the year, args[1] the history fee, args[2]
   3410  *        the account fee and args[3] the purse fee. These are followed by args[4] purse timeout,
   3411  *        args[5] history expiration. Last is args[6] the (free) purse account limit.
   3412  */
   3413 static void
   3414 do_set_global_fee (char *const *args)
   3415 {
   3416   struct TALER_MasterSignatureP master_sig;
   3417   char dummy;
   3418   unsigned int year;
   3419   struct TALER_GlobalFeeSet fees;
   3420   struct GNUNET_TIME_Relative purse_timeout;
   3421   struct GNUNET_TIME_Relative history_expiration;
   3422   unsigned int purse_account_limit;
   3423   struct GNUNET_TIME_Timestamp start_time;
   3424   struct GNUNET_TIME_Timestamp end_time;
   3425 
   3426   if (NULL != in)
   3427   {
   3428     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3429                 "Downloaded data was not consumed, not setting global fee\n");
   3430     GNUNET_SCHEDULER_shutdown ();
   3431     global_ret = EXIT_FAILURE;
   3432     return;
   3433   }
   3434   if ( (NULL == args[0]) ||
   3435        (NULL == args[1]) ||
   3436        (NULL == args[2]) ||
   3437        (NULL == args[3]) ||
   3438        (NULL == args[4]) ||
   3439        (NULL == args[5]) ||
   3440        (NULL == args[6]) )
   3441   {
   3442     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3443                 "You must use YEAR, HISTORY-FEE, ACCOUNT-FEE, PURSE-FEE, PURSE-TIMEOUT, HISTORY-EXPIRATION and PURSE-ACCOUNT-LIMIT as arguments for this subcommand\n");
   3444     GNUNET_SCHEDULER_shutdown ();
   3445     global_ret = EXIT_INVALIDARGUMENT;
   3446     return;
   3447   }
   3448   if ( (1 != sscanf (args[0],
   3449                      "%u%c",
   3450                      &year,
   3451                      &dummy)) &&
   3452        (0 != strcasecmp ("now",
   3453                          args[0])) )
   3454   {
   3455     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3456                 "Invalid YEAR given for 'global-fee' subcommand\n");
   3457     GNUNET_SCHEDULER_shutdown ();
   3458     global_ret = EXIT_INVALIDARGUMENT;
   3459     return;
   3460   }
   3461   if ( (GNUNET_OK !=
   3462         TALER_string_to_amount (args[1],
   3463                                 &fees.history)) ||
   3464        (GNUNET_OK !=
   3465         TALER_string_to_amount (args[2],
   3466                                 &fees.account)) ||
   3467        (GNUNET_OK !=
   3468         TALER_string_to_amount (args[3],
   3469                                 &fees.purse)) )
   3470   {
   3471     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3472                 "Invalid amount given for 'global-fee' subcommand\n");
   3473     GNUNET_SCHEDULER_shutdown ();
   3474     global_ret = EXIT_INVALIDARGUMENT;
   3475     return;
   3476   }
   3477   if (GNUNET_OK !=
   3478       GNUNET_STRINGS_fancy_time_to_relative (args[4],
   3479                                              &purse_timeout))
   3480   {
   3481     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3482                 "Invalid purse timeout `%s' given for 'global-fee' subcommand\n",
   3483                 args[4]);
   3484     GNUNET_SCHEDULER_shutdown ();
   3485     global_ret = EXIT_INVALIDARGUMENT;
   3486     return;
   3487   }
   3488   if (GNUNET_OK !=
   3489       GNUNET_STRINGS_fancy_time_to_relative (args[5],
   3490                                              &history_expiration))
   3491   {
   3492     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3493                 "Invalid history expiratoin `%s' given for 'global-fee' subcommand\n",
   3494                 args[5]);
   3495     GNUNET_SCHEDULER_shutdown ();
   3496     global_ret = EXIT_INVALIDARGUMENT;
   3497     return;
   3498   }
   3499   if (1 != sscanf (args[6],
   3500                    "%u%c",
   3501                    &purse_account_limit,
   3502                    &dummy))
   3503   {
   3504     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3505                 "Invalid purse account limit given for 'global-fee' subcommand\n");
   3506     GNUNET_SCHEDULER_shutdown ();
   3507     global_ret = EXIT_INVALIDARGUMENT;
   3508     return;
   3509   }
   3510   if (0 == strcasecmp ("now",
   3511                        args[0]))
   3512     year = GNUNET_TIME_get_current_year ();
   3513   if (GNUNET_OK !=
   3514       load_offline_key (GNUNET_NO))
   3515     return;
   3516   start_time = GNUNET_TIME_absolute_to_timestamp (
   3517     GNUNET_TIME_year_to_time (year));
   3518   end_time = GNUNET_TIME_absolute_to_timestamp (
   3519     GNUNET_TIME_year_to_time (year + 1));
   3520 
   3521   TALER_exchange_offline_global_fee_sign (start_time,
   3522                                           end_time,
   3523                                           &fees,
   3524                                           purse_timeout,
   3525                                           history_expiration,
   3526                                           (uint32_t) purse_account_limit,
   3527                                           &master_priv,
   3528                                           &master_sig);
   3529   output_operation (OP_SET_GLOBAL_FEE,
   3530                     GNUNET_JSON_PACK (
   3531                       GNUNET_JSON_pack_timestamp ("start_time",
   3532                                                   start_time),
   3533                       GNUNET_JSON_pack_timestamp ("end_time",
   3534                                                   end_time),
   3535                       TALER_JSON_pack_amount ("history_fee",
   3536                                               &fees.history),
   3537                       TALER_JSON_pack_amount ("account_fee",
   3538                                               &fees.account),
   3539                       TALER_JSON_pack_amount ("purse_fee",
   3540                                               &fees.purse),
   3541                       GNUNET_JSON_pack_time_rel ("purse_timeout",
   3542                                                  purse_timeout),
   3543                       GNUNET_JSON_pack_time_rel ("history_expiration",
   3544                                                  history_expiration),
   3545                       GNUNET_JSON_pack_uint64 ("purse_account_limit",
   3546                                                (uint32_t) purse_account_limit),
   3547                       GNUNET_JSON_pack_data_auto ("master_sig",
   3548                                                   &master_sig)));
   3549   next (args + 7);
   3550 }
   3551 
   3552 
   3553 /**
   3554  * Drain profits from exchange's escrow account to
   3555  * regular exchange account.
   3556  *
   3557  * @param args the array of command-line arguments to process next;
   3558  *        args[0] must be the amount,
   3559  *        args[1] must be the section of the escrow account to drain
   3560  *        args[2] must be the payto://-URI of the target account
   3561  */
   3562 static void
   3563 do_drain (char *const *args)
   3564 {
   3565   struct TALER_WireTransferIdentifierRawP wtid;
   3566   struct GNUNET_TIME_Timestamp date;
   3567   struct TALER_Amount amount;
   3568   const char *account_section;
   3569   struct TALER_FullPayto payto_uri;
   3570   struct TALER_MasterSignatureP master_sig;
   3571   char *err;
   3572 
   3573   if (NULL != in)
   3574   {
   3575     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3576                 "Downloaded data was not consumed, refusing drain\n");
   3577     GNUNET_SCHEDULER_shutdown ();
   3578     global_ret = EXIT_FAILURE;
   3579     return;
   3580   }
   3581   if ( (NULL == args[0]) ||
   3582        (NULL == args[1]) ||
   3583        (NULL == args[2]) ||
   3584        (GNUNET_OK !=
   3585         TALER_string_to_amount (args[0],
   3586                                 &amount)) )
   3587   {
   3588     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3589                 "Drain requires an amount, section name and target payto://-URI as arguments\n");
   3590     GNUNET_SCHEDULER_shutdown ();
   3591     global_ret = EXIT_INVALIDARGUMENT;
   3592     return;
   3593   }
   3594   if ( (NULL == args[0]) ||
   3595        (NULL == args[1]) ||
   3596        (NULL == args[2]) )
   3597   {
   3598     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3599                 "Drain requires an amount, section name and target payto://-URI as arguments\n");
   3600     GNUNET_SCHEDULER_shutdown ();
   3601     global_ret = EXIT_INVALIDARGUMENT;
   3602     return;
   3603   }
   3604   if (GNUNET_OK !=
   3605       TALER_string_to_amount (args[0],
   3606                               &amount))
   3607   {
   3608     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3609                 "Invalid amount `%s' specified for drain\n",
   3610                 args[0]);
   3611     GNUNET_SCHEDULER_shutdown ();
   3612     global_ret = EXIT_INVALIDARGUMENT;
   3613     return;
   3614   }
   3615   account_section = args[1];
   3616   payto_uri.full_payto = args[2];
   3617   err = TALER_payto_validate (payto_uri);
   3618   if (NULL != err)
   3619   {
   3620     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3621                 "Invalid payto://-URI `%s' specified for drain: %s\n",
   3622                 payto_uri.full_payto,
   3623                 err);
   3624     GNUNET_free (err);
   3625     GNUNET_SCHEDULER_shutdown ();
   3626     global_ret = EXIT_INVALIDARGUMENT;
   3627     return;
   3628   }
   3629   if (GNUNET_OK !=
   3630       load_offline_key (GNUNET_NO))
   3631     return;
   3632   GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
   3633                               &wtid,
   3634                               sizeof (wtid));
   3635   date = GNUNET_TIME_timestamp_get ();
   3636   TALER_exchange_offline_profit_drain_sign (&wtid,
   3637                                             date,
   3638                                             &amount,
   3639                                             account_section,
   3640                                             payto_uri,
   3641                                             &master_priv,
   3642                                             &master_sig);
   3643   output_operation (OP_DRAIN_PROFITS,
   3644                     GNUNET_JSON_PACK (
   3645                       GNUNET_JSON_pack_data_auto ("wtid",
   3646                                                   &wtid),
   3647                       GNUNET_JSON_pack_string ("account_section",
   3648                                                account_section),
   3649                       TALER_JSON_pack_full_payto ("payto_uri",
   3650                                                   payto_uri),
   3651                       TALER_JSON_pack_amount ("amount",
   3652                                               &amount),
   3653                       GNUNET_JSON_pack_timestamp ("date",
   3654                                                   date),
   3655                       GNUNET_JSON_pack_data_auto ("master_sig",
   3656                                                   &master_sig)));
   3657   next (args + 3);
   3658 }
   3659 
   3660 
   3661 /**
   3662  * Add partner.
   3663  *
   3664  * @param args the array of command-line arguments to process next;
   3665  *        args[0] must be the partner's master public key, args[1] the partner's
   3666  *        API base URL, args[2] the wad fee, args[3] the wad frequency, and
   3667  *        args[4] the year (including possibly 'now')
   3668  */
   3669 static void
   3670 do_add_partner (char *const *args)
   3671 {
   3672   struct TALER_MasterPublicKeyP partner_pub;
   3673   struct GNUNET_TIME_Timestamp start_date;
   3674   struct GNUNET_TIME_Timestamp end_date;
   3675   struct GNUNET_TIME_Relative wad_frequency;
   3676   struct TALER_Amount wad_fee;
   3677   const char *partner_base_url;
   3678   struct TALER_MasterSignatureP master_sig;
   3679   char dummy;
   3680   unsigned int year;
   3681 
   3682   if (NULL != in)
   3683   {
   3684     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3685                 "Downloaded data was not consumed, not adding partner\n");
   3686     GNUNET_SCHEDULER_shutdown ();
   3687     global_ret = EXIT_FAILURE;
   3688     return;
   3689   }
   3690   if ( (NULL == args[0]) ||
   3691        (GNUNET_OK !=
   3692         GNUNET_STRINGS_string_to_data (args[0],
   3693                                        strlen (args[0]),
   3694                                        &partner_pub,
   3695                                        sizeof (partner_pub))) )
   3696   {
   3697     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3698                 "You must specify the partner master public key as first argument for this subcommand\n");
   3699     GNUNET_SCHEDULER_shutdown ();
   3700     global_ret = EXIT_INVALIDARGUMENT;
   3701     return;
   3702   }
   3703   if ( (NULL == args[1]) ||
   3704        (0 != strncmp ("http",
   3705                       args[1],
   3706                       strlen ("http"))) )
   3707   {
   3708     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3709                 "You must specify the partner's base URL as the 2nd argument to this subcommand\n");
   3710     GNUNET_SCHEDULER_shutdown ();
   3711     global_ret = EXIT_INVALIDARGUMENT;
   3712     return;
   3713   }
   3714   partner_base_url = args[1];
   3715   if ( (NULL == args[2]) ||
   3716        (GNUNET_OK !=
   3717         TALER_string_to_amount (args[2],
   3718                                 &wad_fee)) )
   3719   {
   3720     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3721                 "Invalid amount `%s' specified for wad fee of partner\n",
   3722                 args[2]);
   3723     GNUNET_SCHEDULER_shutdown ();
   3724     global_ret = EXIT_INVALIDARGUMENT;
   3725     return;
   3726   }
   3727   if ( (NULL == args[3]) ||
   3728        (GNUNET_OK !=
   3729         GNUNET_STRINGS_fancy_time_to_relative (args[3],
   3730                                                &wad_frequency)) )
   3731   {
   3732     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3733                 "Invalid wad frequency `%s' specified for add partner\n",
   3734                 args[3]);
   3735     GNUNET_SCHEDULER_shutdown ();
   3736     global_ret = EXIT_INVALIDARGUMENT;
   3737     return;
   3738   }
   3739   if ( (NULL == args[4]) ||
   3740        ( (1 != sscanf (args[4],
   3741                        "%u%c",
   3742                        &year,
   3743                        &dummy)) &&
   3744          (0 != strcasecmp ("now",
   3745                            args[4])) ) )
   3746   {
   3747     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3748                 "Invalid year `%s' specified for add partner\n",
   3749                 args[4]);
   3750     GNUNET_SCHEDULER_shutdown ();
   3751     global_ret = EXIT_INVALIDARGUMENT;
   3752     return;
   3753   }
   3754   if (0 == strcasecmp ("now",
   3755                        args[4]))
   3756     year = GNUNET_TIME_get_current_year ();
   3757   start_date = GNUNET_TIME_absolute_to_timestamp (
   3758     GNUNET_TIME_year_to_time (year));
   3759   end_date = GNUNET_TIME_absolute_to_timestamp (
   3760     GNUNET_TIME_year_to_time (year + 1));
   3761 
   3762   if (GNUNET_OK !=
   3763       load_offline_key (GNUNET_NO))
   3764     return;
   3765   TALER_exchange_offline_partner_details_sign (&partner_pub,
   3766                                                start_date,
   3767                                                end_date,
   3768                                                wad_frequency,
   3769                                                &wad_fee,
   3770                                                partner_base_url,
   3771                                                &master_priv,
   3772                                                &master_sig);
   3773   output_operation (OP_ADD_PARTNER,
   3774                     GNUNET_JSON_PACK (
   3775                       GNUNET_JSON_pack_string ("partner_base_url",
   3776                                                partner_base_url),
   3777                       GNUNET_JSON_pack_time_rel ("wad_frequency",
   3778                                                  wad_frequency),
   3779                       GNUNET_JSON_pack_timestamp ("start_date",
   3780                                                   start_date),
   3781                       GNUNET_JSON_pack_timestamp ("end_date",
   3782                                                   end_date),
   3783                       GNUNET_JSON_pack_data_auto ("partner_pub",
   3784                                                   &partner_pub),
   3785                       GNUNET_JSON_pack_data_auto ("master_sig",
   3786                                                   &master_sig)));
   3787   next (args + 5);
   3788 }
   3789 
   3790 
   3791 /**
   3792  * Enable or disable AML staff.
   3793  *
   3794  * @param is_active true to enable, false to disable
   3795  * @param args the array of command-line arguments to process next; args[0] must be the AML staff's public key, args[1] the AML staff's legal name, and if @a is_active then args[2] rw (read write) or ro (read only)
   3796  */
   3797 static void
   3798 do_set_aml_staff (bool is_active,
   3799                   char *const *args)
   3800 {
   3801   struct TALER_AmlOfficerPublicKeyP officer_pub;
   3802   const char *officer_name;
   3803   bool read_only;
   3804   struct TALER_MasterSignatureP master_sig;
   3805   struct GNUNET_TIME_Timestamp now
   3806     = GNUNET_TIME_timestamp_get ();
   3807 
   3808   if (NULL != in)
   3809   {
   3810     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3811                 "Downloaded data was not consumed, not updating AML staff status\n");
   3812     GNUNET_SCHEDULER_shutdown ();
   3813     global_ret = EXIT_FAILURE;
   3814     return;
   3815   }
   3816   if ( (NULL == args[0]) ||
   3817        (GNUNET_OK !=
   3818         GNUNET_STRINGS_string_to_data (args[0],
   3819                                        strlen (args[0]),
   3820                                        &officer_pub,
   3821                                        sizeof (officer_pub))) )
   3822   {
   3823     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3824                 "You must specify the AML officer's public key as first argument for this subcommand\n");
   3825     GNUNET_SCHEDULER_shutdown ();
   3826     global_ret = EXIT_INVALIDARGUMENT;
   3827     return;
   3828   }
   3829   if (NULL == args[1])
   3830   {
   3831     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3832                 "You must specify the officer's legal name as the 2nd argument to this subcommand\n");
   3833     GNUNET_SCHEDULER_shutdown ();
   3834     global_ret = EXIT_INVALIDARGUMENT;
   3835     return;
   3836   }
   3837   officer_name = args[1];
   3838   if (is_active)
   3839   {
   3840     if ( (NULL == args[2]) ||
   3841          ( (0 != strcmp (args[2],
   3842                          "ro")) &&
   3843            (0 != strcmp (args[2],
   3844                          "rw")) ) )
   3845     {
   3846       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3847                   "You must specify 'ro' or 'rw' (and not `%s') for the access level\n",
   3848                   args[2]);
   3849       GNUNET_SCHEDULER_shutdown ();
   3850       global_ret = EXIT_INVALIDARGUMENT;
   3851       return;
   3852     }
   3853     read_only = (0 == strcmp (args[2],
   3854                               "ro"));
   3855   }
   3856   else
   3857   {
   3858     read_only = true;
   3859   }
   3860   if (GNUNET_OK !=
   3861       load_offline_key (GNUNET_NO))
   3862     return;
   3863   TALER_exchange_offline_aml_officer_status_sign (&officer_pub,
   3864                                                   officer_name,
   3865                                                   now,
   3866                                                   is_active,
   3867                                                   read_only,
   3868                                                   &master_priv,
   3869                                                   &master_sig);
   3870   output_operation (OP_UPDATE_AML_STAFF,
   3871                     GNUNET_JSON_PACK (
   3872                       GNUNET_JSON_pack_string ("officer_name",
   3873                                                officer_name),
   3874                       GNUNET_JSON_pack_timestamp ("change_date",
   3875                                                   now),
   3876                       GNUNET_JSON_pack_bool ("is_active",
   3877                                              is_active),
   3878                       GNUNET_JSON_pack_bool ("read_only",
   3879                                              read_only),
   3880                       GNUNET_JSON_pack_data_auto ("officer_pub",
   3881                                                   &officer_pub),
   3882                       GNUNET_JSON_pack_data_auto ("master_sig",
   3883                                                   &master_sig)));
   3884   next (args + (is_active ? 3 : 2));
   3885 }
   3886 
   3887 
   3888 /**
   3889  * Disable AML staff.
   3890  *
   3891  * @param args the array of command-line arguments to process next;
   3892  *        args[0] must be the AML staff's public key, args[1] the AML staff's legal name, args[2] rw (read write) or ro (read only)
   3893  */
   3894 static void
   3895 disable_aml_staff (char *const *args)
   3896 {
   3897   do_set_aml_staff (false,
   3898                     args);
   3899 }
   3900 
   3901 
   3902 /**
   3903  * Enable AML staff.
   3904  *
   3905  * @param args the array of command-line arguments to process next;
   3906  *        args[0] must be the AML staff's public key, args[1] the AML staff's legal name, args[2] rw (read write) or ro (read only)
   3907  */
   3908 static void
   3909 enable_aml_staff (char *const *args)
   3910 {
   3911   do_set_aml_staff (true,
   3912                     args);
   3913 }
   3914 
   3915 
   3916 /**
   3917  * Function called with information about future keys.  Dumps the JSON output
   3918  * (on success), either into an internal buffer or to stdout (depending on
   3919  * whether there are subsequent commands).
   3920  *
   3921  * @param args the remaining args
   3922  * @param mgr response data
   3923  */
   3924 static void
   3925 download_cb (char *const *args,
   3926              const struct TALER_EXCHANGE_GetManagementKeysResponse *mgr)
   3927 {
   3928   const struct TALER_EXCHANGE_HttpResponse *hr = &mgr->hr;
   3929 
   3930   mgkh = NULL;
   3931   switch (hr->http_status)
   3932   {
   3933   case MHD_HTTP_OK:
   3934     break;
   3935   default:
   3936     if (0 != hr->http_status)
   3937       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3938                   "Failed to download keys from `%s': %s (HTTP status: %u/%u)\n",
   3939                   CFG_exchange_url,
   3940                   hr->hint,
   3941                   hr->http_status,
   3942                   (unsigned int) hr->ec);
   3943     else
   3944       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3945                   "Failed to download keys from `%s' (no HTTP response)\n",
   3946                   CFG_exchange_url);
   3947     GNUNET_SCHEDULER_shutdown ();
   3948     global_ret = EXIT_FAILURE;
   3949     return;
   3950   }
   3951   in = GNUNET_JSON_PACK (
   3952     GNUNET_JSON_pack_string ("operation",
   3953                              OP_INPUT_KEYS),
   3954     GNUNET_JSON_pack_object_incref ("arguments",
   3955                                     (json_t *) hr->reply));
   3956   if (NULL == args[0])
   3957   {
   3958     json_dumpf (in,
   3959                 stdout,
   3960                 JSON_INDENT (2));
   3961     json_decref (in);
   3962     in = NULL;
   3963   }
   3964   next (args);
   3965 }
   3966 
   3967 
   3968 /**
   3969  * Download future keys.
   3970  *
   3971  * @param args the array of command-line arguments to process next
   3972  */
   3973 static void
   3974 do_download (char *const *args)
   3975 {
   3976   if ( (NULL == CFG_exchange_url) &&
   3977        (GNUNET_OK !=
   3978         GNUNET_CONFIGURATION_get_value_string (kcfg,
   3979                                                "exchange",
   3980                                                "BASE_URL",
   3981                                                &CFG_exchange_url)) )
   3982   {
   3983     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   3984                                "exchange",
   3985                                "BASE_URL");
   3986     GNUNET_SCHEDULER_shutdown ();
   3987     global_ret = EXIT_NOTCONFIGURED;
   3988     return;
   3989   }
   3990   mgkh = TALER_EXCHANGE_get_management_keys_create (ctx,
   3991                                                     CFG_exchange_url);
   3992   TALER_EXCHANGE_get_management_keys_start (mgkh,
   3993                                             &download_cb,
   3994                                             (void *) args);
   3995 }
   3996 
   3997 
   3998 /**
   3999  * Check that the security module keys are the same as before.  If we had no
   4000  * keys in store before, remember them (Trust On First Use).
   4001  *
   4002  * @param secmset security module keys
   4003  * @return #GNUNET_OK if keys match with what we have in store
   4004  *         #GNUNET_NO if we had nothing in store but now do
   4005  *         #GNUNET_SYSERR if keys changed from what we remember or other error
   4006  */
   4007 static enum GNUNET_GenericReturnValue
   4008 tofu_check (const struct TALER_SecurityModulePublicKeySetP *secmset)
   4009 {
   4010   char *fn;
   4011   struct TALER_SecurityModulePublicKeySetP oldset;
   4012   ssize_t ret;
   4013 
   4014   if (GNUNET_OK !=
   4015       GNUNET_CONFIGURATION_get_value_filename (kcfg,
   4016                                                "exchange-offline",
   4017                                                "SECM_TOFU_FILE",
   4018                                                &fn))
   4019   {
   4020     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   4021                                "exchange-offline",
   4022                                "SECM_TOFU_FILE");
   4023     return GNUNET_SYSERR;
   4024   }
   4025   if (GNUNET_OK ==
   4026       GNUNET_DISK_file_test (fn))
   4027   {
   4028     ret = GNUNET_DISK_fn_read (fn,
   4029                                &oldset,
   4030                                sizeof (oldset));
   4031     if (GNUNET_SYSERR != ret)
   4032     {
   4033       if (ret != sizeof (oldset))
   4034       {
   4035         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4036                     "File `%s' corrupt\n",
   4037                     fn);
   4038         GNUNET_free (fn);
   4039         return GNUNET_SYSERR;
   4040       }
   4041       /* TOFU check */
   4042       if (0 != memcmp (&oldset,
   4043                        secmset,
   4044                        sizeof (*secmset)))
   4045       {
   4046         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4047                     "Fatal: security module keys changed (file `%s')!\n",
   4048                     fn);
   4049         GNUNET_free (fn);
   4050         return GNUNET_SYSERR;
   4051       }
   4052       GNUNET_free (fn);
   4053       return GNUNET_OK;
   4054     }
   4055   }
   4056 
   4057   {
   4058     char *key;
   4059 
   4060     /* check against SECMOD-keys pinned in configuration */
   4061     if (GNUNET_OK ==
   4062         GNUNET_CONFIGURATION_get_value_string (kcfg,
   4063                                                "exchange-offline",
   4064                                                "SECM_ESIGN_PUBKEY",
   4065                                                &key))
   4066     {
   4067       struct TALER_SecurityModulePublicKeyP k;
   4068 
   4069       if (GNUNET_OK !=
   4070           GNUNET_STRINGS_string_to_data (key,
   4071                                          strlen (key),
   4072                                          &k,
   4073                                          sizeof (k)))
   4074       {
   4075         GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
   4076                                    "exchange-offline",
   4077                                    "SECM_ESIGN_PUBKEY",
   4078                                    "key malformed");
   4079         GNUNET_free (key);
   4080         GNUNET_free (fn);
   4081         return GNUNET_SYSERR;
   4082       }
   4083       GNUNET_free (key);
   4084       if (0 !=
   4085           GNUNET_memcmp (&k,
   4086                          &secmset->eddsa))
   4087       {
   4088         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4089                     "ESIGN security module key does not match SECM_ESIGN_PUBKEY in configuration\n");
   4090         GNUNET_free (fn);
   4091         return GNUNET_SYSERR;
   4092       }
   4093     }
   4094     if (GNUNET_OK ==
   4095         GNUNET_CONFIGURATION_get_value_string (kcfg,
   4096                                                "exchange-offline",
   4097                                                "SECM_DENOM_PUBKEY",
   4098                                                &key))
   4099     {
   4100       struct TALER_SecurityModulePublicKeyP k;
   4101 
   4102       if (GNUNET_OK !=
   4103           GNUNET_STRINGS_string_to_data (key,
   4104                                          strlen (key),
   4105                                          &k,
   4106                                          sizeof (k)))
   4107       {
   4108         GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
   4109                                    "exchange-offline",
   4110                                    "SECM_DENOM_PUBKEY",
   4111                                    "key malformed");
   4112         GNUNET_free (key);
   4113         GNUNET_free (fn);
   4114         return GNUNET_SYSERR;
   4115       }
   4116       GNUNET_free (key);
   4117       if (0 !=
   4118           GNUNET_memcmp (&k,
   4119                          &secmset->rsa))
   4120       {
   4121         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4122                     "DENOM security module key does not match SECM_DENOM_PUBKEY in configuration\n");
   4123         GNUNET_free (fn);
   4124         return GNUNET_SYSERR;
   4125       }
   4126     }
   4127     if (GNUNET_OK ==
   4128         GNUNET_CONFIGURATION_get_value_string (kcfg,
   4129                                                "exchange-offline",
   4130                                                "SECM_DENOM_CS_PUBKEY",
   4131                                                &key))
   4132     {
   4133       struct TALER_SecurityModulePublicKeyP k;
   4134 
   4135       if (GNUNET_OK !=
   4136           GNUNET_STRINGS_string_to_data (key,
   4137                                          strlen (key),
   4138                                          &k,
   4139                                          sizeof (k)))
   4140       {
   4141         GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
   4142                                    "exchange-offline",
   4143                                    "SECM_DENOM_CS_PUBKEY",
   4144                                    "key malformed");
   4145         GNUNET_free (key);
   4146         GNUNET_free (fn);
   4147         return GNUNET_SYSERR;
   4148       }
   4149       GNUNET_free (key);
   4150       if (0 !=
   4151           GNUNET_memcmp (&k,
   4152                          &secmset->cs))
   4153       {
   4154         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4155                     "DENOM security module key does not match SECM_DENOM_CS_PUBKEY in configuration\n");
   4156         GNUNET_free (fn);
   4157         return GNUNET_SYSERR;
   4158       }
   4159     }
   4160   }
   4161   if (GNUNET_OK !=
   4162       GNUNET_DISK_directory_create_for_file (fn))
   4163   {
   4164     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4165                 "Failed create directory to store key material in file `%s'\n",
   4166                 fn);
   4167     GNUNET_free (fn);
   4168     return GNUNET_SYSERR;
   4169   }
   4170   /* persist keys for future runs */
   4171   if (GNUNET_OK !=
   4172       GNUNET_DISK_fn_write (fn,
   4173                             secmset,
   4174                             sizeof (oldset),
   4175                             GNUNET_DISK_PERM_USER_READ))
   4176   {
   4177     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4178                 "Failed to store key material in file `%s'\n",
   4179                 fn);
   4180     GNUNET_free (fn);
   4181     return GNUNET_SYSERR;
   4182   }
   4183   GNUNET_free (fn);
   4184   return GNUNET_NO;
   4185 }
   4186 
   4187 
   4188 /**
   4189  * Output @a signkeys for human consumption.
   4190  *
   4191  * @param secm_pub security module public key used to sign the denominations
   4192  * @param signkeys keys to output
   4193  * @return #GNUNET_OK on success
   4194  */
   4195 static enum GNUNET_GenericReturnValue
   4196 show_signkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub,
   4197                const json_t *signkeys)
   4198 {
   4199   size_t index;
   4200   json_t *value;
   4201 
   4202   json_array_foreach (signkeys, index, value) {
   4203     const char *err_name;
   4204     unsigned int err_line;
   4205     struct TALER_ExchangePublicKeyP exchange_pub;
   4206     struct TALER_SecurityModuleSignatureP secm_sig;
   4207     struct GNUNET_TIME_Timestamp start_time;
   4208     struct GNUNET_TIME_Timestamp sign_end;
   4209     struct GNUNET_TIME_Timestamp legal_end;
   4210     struct GNUNET_TIME_Relative duration;
   4211     struct GNUNET_JSON_Specification spec[] = {
   4212       GNUNET_JSON_spec_timestamp ("stamp_start",
   4213                                   &start_time),
   4214       GNUNET_JSON_spec_timestamp ("stamp_expire",
   4215                                   &sign_end),
   4216       GNUNET_JSON_spec_timestamp ("stamp_end",
   4217                                   &legal_end),
   4218       GNUNET_JSON_spec_fixed_auto ("key",
   4219                                    &exchange_pub),
   4220       GNUNET_JSON_spec_fixed_auto ("signkey_secmod_sig",
   4221                                    &secm_sig),
   4222       GNUNET_JSON_spec_end ()
   4223     };
   4224 
   4225     if (GNUNET_OK !=
   4226         GNUNET_JSON_parse (value,
   4227                            spec,
   4228                            &err_name,
   4229                            &err_line))
   4230     {
   4231       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4232                   "Invalid input for signing key to 'show': %s#%u at %u (skipping)\n",
   4233                   err_name,
   4234                   err_line,
   4235                   (unsigned int) index);
   4236       json_dumpf (value,
   4237                   stderr,
   4238                   JSON_INDENT (2));
   4239       global_ret = EXIT_FAILURE;
   4240       GNUNET_SCHEDULER_shutdown ();
   4241       return GNUNET_SYSERR;
   4242     }
   4243     duration = GNUNET_TIME_absolute_get_difference (start_time.abs_time,
   4244                                                     sign_end.abs_time);
   4245     if (GNUNET_OK !=
   4246         TALER_exchange_secmod_eddsa_verify (&exchange_pub,
   4247                                             start_time,
   4248                                             duration,
   4249                                             secm_pub,
   4250                                             &secm_sig))
   4251     {
   4252       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4253                   "Invalid security module signature for signing key %s (aborting)\n",
   4254                   TALER_B2S (&exchange_pub));
   4255       global_ret = EXIT_FAILURE;
   4256       GNUNET_SCHEDULER_shutdown ();
   4257       return GNUNET_SYSERR;
   4258     }
   4259     {
   4260       char *legal_end_s;
   4261 
   4262       legal_end_s = GNUNET_strdup (
   4263         GNUNET_TIME_timestamp2s (legal_end));
   4264       printf ("EXCHANGE-KEY %s starting at %s (used for: %s, legal end: %s)\n",
   4265               TALER_B2S (&exchange_pub),
   4266               GNUNET_TIME_timestamp2s (start_time),
   4267               GNUNET_TIME_relative2s (duration,
   4268                                       false),
   4269               legal_end_s);
   4270       GNUNET_free (legal_end_s);
   4271     }
   4272   }
   4273   return GNUNET_OK;
   4274 }
   4275 
   4276 
   4277 /**
   4278  * Output @a denomkeys for human consumption.
   4279  *
   4280  * @param secm_pub_rsa security module public key used to sign the RSA denominations
   4281  * @param secm_pub_cs security module public key used to sign the CS denominations
   4282  * @param denomkeys keys to output
   4283  * @return #GNUNET_OK on success
   4284  */
   4285 static enum GNUNET_GenericReturnValue
   4286 show_denomkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub_rsa,
   4287                 const struct TALER_SecurityModulePublicKeyP *secm_pub_cs,
   4288                 const json_t *denomkeys)
   4289 {
   4290   size_t index;
   4291   json_t *value;
   4292 
   4293   json_array_foreach (denomkeys, index, value) {
   4294     const char *err_name;
   4295     unsigned int err_line;
   4296     const char *section_name;
   4297     struct TALER_DenominationPublicKey denom_pub;
   4298     struct GNUNET_TIME_Timestamp stamp_start;
   4299     struct GNUNET_TIME_Timestamp stamp_expire_withdraw;
   4300     struct GNUNET_TIME_Timestamp stamp_expire_deposit;
   4301     struct GNUNET_TIME_Timestamp stamp_expire_legal;
   4302     struct TALER_Amount coin_value;
   4303     struct TALER_DenomFeeSet fees;
   4304     struct TALER_SecurityModuleSignatureP secm_sig;
   4305     struct GNUNET_JSON_Specification spec[] = {
   4306       GNUNET_JSON_spec_string ("section_name",
   4307                                &section_name),
   4308       TALER_JSON_spec_denom_pub ("denom_pub",
   4309                                  &denom_pub),
   4310       TALER_JSON_spec_amount ("value",
   4311                               currency,
   4312                               &coin_value),
   4313       TALER_JSON_SPEC_DENOM_FEES ("fee",
   4314                                   currency,
   4315                                   &fees),
   4316       GNUNET_JSON_spec_timestamp ("stamp_start",
   4317                                   &stamp_start),
   4318       GNUNET_JSON_spec_timestamp ("stamp_expire_withdraw",
   4319                                   &stamp_expire_withdraw),
   4320       GNUNET_JSON_spec_timestamp ("stamp_expire_deposit",
   4321                                   &stamp_expire_deposit),
   4322       GNUNET_JSON_spec_timestamp ("stamp_expire_legal",
   4323                                   &stamp_expire_legal),
   4324       GNUNET_JSON_spec_fixed_auto ("denom_secmod_sig",
   4325                                    &secm_sig),
   4326       GNUNET_JSON_spec_end ()
   4327     };
   4328     struct GNUNET_TIME_Relative duration;
   4329     struct TALER_DenominationHashP h_denom_pub;
   4330     enum GNUNET_GenericReturnValue ok;
   4331 
   4332     if (GNUNET_OK !=
   4333         GNUNET_JSON_parse (value,
   4334                            spec,
   4335                            &err_name,
   4336                            &err_line))
   4337     {
   4338       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4339                   "Invalid input for denomination key to 'show': %s#%u at %u (skipping)\n",
   4340                   err_name,
   4341                   err_line,
   4342                   (unsigned int) index);
   4343       json_dumpf (value,
   4344                   stderr,
   4345                   JSON_INDENT (2));
   4346       GNUNET_JSON_parse_free (spec);
   4347       global_ret = EXIT_FAILURE;
   4348       GNUNET_SCHEDULER_shutdown ();
   4349       return GNUNET_SYSERR;
   4350     }
   4351     duration = GNUNET_TIME_absolute_get_difference (
   4352       stamp_start.abs_time,
   4353       stamp_expire_withdraw.abs_time);
   4354     TALER_denom_pub_hash (&denom_pub,
   4355                           &h_denom_pub);
   4356     switch (denom_pub.bsign_pub_key->cipher)
   4357     {
   4358     case GNUNET_CRYPTO_BSA_RSA:
   4359       {
   4360         struct TALER_RsaPubHashP h_rsa;
   4361 
   4362         TALER_rsa_pub_hash (denom_pub.bsign_pub_key->details.rsa_public_key,
   4363                             &h_rsa);
   4364         ok = TALER_exchange_secmod_rsa_verify (&h_rsa,
   4365                                                section_name,
   4366                                                stamp_start,
   4367                                                duration,
   4368                                                secm_pub_rsa,
   4369                                                &secm_sig);
   4370       }
   4371       break;
   4372     case GNUNET_CRYPTO_BSA_CS:
   4373       {
   4374         struct TALER_CsPubHashP h_cs;
   4375 
   4376         TALER_cs_pub_hash (&denom_pub.bsign_pub_key->details.cs_public_key,
   4377                            &h_cs);
   4378         ok = TALER_exchange_secmod_cs_verify (&h_cs,
   4379                                               section_name,
   4380                                               stamp_start,
   4381                                               duration,
   4382                                               secm_pub_cs,
   4383                                               &secm_sig);
   4384       }
   4385       break;
   4386     default:
   4387       GNUNET_break (0);
   4388       ok = GNUNET_SYSERR;
   4389       break;
   4390     }
   4391     if (GNUNET_OK != ok)
   4392     {
   4393       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4394                   "Invalid security module signature for denomination key %s (aborting)\n",
   4395                   GNUNET_h2s (&h_denom_pub.hash));
   4396       global_ret = EXIT_FAILURE;
   4397       GNUNET_SCHEDULER_shutdown ();
   4398       return GNUNET_SYSERR;
   4399     }
   4400 
   4401     {
   4402       char *withdraw_fee_s;
   4403       char *deposit_fee_s;
   4404       char *refresh_fee_s;
   4405       char *refund_fee_s;
   4406       char *deposit_s;
   4407       char *legal_s;
   4408 
   4409       withdraw_fee_s = TALER_amount_to_string (&fees.withdraw);
   4410       deposit_fee_s = TALER_amount_to_string (&fees.deposit);
   4411       refresh_fee_s = TALER_amount_to_string (&fees.refresh);
   4412       refund_fee_s = TALER_amount_to_string (&fees.refund);
   4413       deposit_s = GNUNET_strdup (
   4414         GNUNET_TIME_timestamp2s (stamp_expire_deposit));
   4415       legal_s = GNUNET_strdup (
   4416         GNUNET_TIME_timestamp2s (stamp_expire_legal));
   4417 
   4418       printf (
   4419         "DENOMINATION-KEY(%s) %s of value %s starting at %s "
   4420         "(used for: %s, deposit until: %s legal end: %s) with fees %s/%s/%s/%s\n",
   4421         section_name,
   4422         TALER_B2S (&h_denom_pub),
   4423         TALER_amount2s (&coin_value),
   4424         GNUNET_TIME_timestamp2s (stamp_start),
   4425         GNUNET_TIME_relative2s (duration,
   4426                                 false),
   4427         deposit_s,
   4428         legal_s,
   4429         withdraw_fee_s,
   4430         deposit_fee_s,
   4431         refresh_fee_s,
   4432         refund_fee_s);
   4433       GNUNET_free (withdraw_fee_s);
   4434       GNUNET_free (deposit_fee_s);
   4435       GNUNET_free (refresh_fee_s);
   4436       GNUNET_free (refund_fee_s);
   4437       GNUNET_free (deposit_s);
   4438       GNUNET_free (legal_s);
   4439     }
   4440 
   4441     GNUNET_JSON_parse_free (spec);
   4442   }
   4443   return GNUNET_OK;
   4444 }
   4445 
   4446 
   4447 /**
   4448  * Parse the input of exchange keys for the 'show' and 'sign' commands.
   4449  *
   4450  * @param command_name name of the command, for logging
   4451  * @return NULL on error, otherwise the keys details to be free'd by caller
   4452  */
   4453 static json_t *
   4454 parse_keys_input (const char *command_name)
   4455 {
   4456   const char *op_str;
   4457   json_t *keys;
   4458   struct GNUNET_JSON_Specification spec[] = {
   4459     GNUNET_JSON_spec_json ("arguments",
   4460                            &keys),
   4461     GNUNET_JSON_spec_string ("operation",
   4462                              &op_str),
   4463     GNUNET_JSON_spec_end ()
   4464   };
   4465   const char *err_name;
   4466   unsigned int err_line;
   4467 
   4468   if (NULL == in)
   4469   {
   4470     json_error_t err;
   4471 
   4472     in = json_loadf (stdin,
   4473                      JSON_REJECT_DUPLICATES,
   4474                      &err);
   4475     if (NULL == in)
   4476     {
   4477       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4478                   "Failed to read JSON input: %s at %d:%s (offset: %d)\n",
   4479                   err.text,
   4480                   err.line,
   4481                   err.source,
   4482                   err.position);
   4483       global_ret = EXIT_FAILURE;
   4484       GNUNET_SCHEDULER_shutdown ();
   4485       return NULL;
   4486     }
   4487   }
   4488   if (GNUNET_OK !=
   4489       GNUNET_JSON_parse (in,
   4490                          spec,
   4491                          &err_name,
   4492                          &err_line))
   4493   {
   4494     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4495                 "Invalid input to '%s': %s#%u (skipping)\n",
   4496                 command_name,
   4497                 err_name,
   4498                 err_line);
   4499     json_dumpf (in,
   4500                 stderr,
   4501                 JSON_INDENT (2));
   4502     global_ret = EXIT_FAILURE;
   4503     GNUNET_SCHEDULER_shutdown ();
   4504     return NULL;
   4505   }
   4506   if (0 != strcmp (op_str,
   4507                    OP_INPUT_KEYS))
   4508   {
   4509     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4510                 "Invalid input to '%s' : operation is `%s', expected `%s'\n",
   4511                 command_name,
   4512                 op_str,
   4513                 OP_INPUT_KEYS);
   4514     GNUNET_JSON_parse_free (spec);
   4515     return NULL;
   4516   }
   4517   json_decref (in);
   4518   in = NULL;
   4519   return keys;
   4520 }
   4521 
   4522 
   4523 /**
   4524  * Show future keys.
   4525  *
   4526  * @param args the array of command-line arguments to process next
   4527  */
   4528 static void
   4529 do_show (char *const *args)
   4530 {
   4531   json_t *keys;
   4532   const char *err_name;
   4533   unsigned int err_line;
   4534   const json_t *denomkeys;
   4535   const json_t *signkeys;
   4536   struct TALER_MasterPublicKeyP mpub;
   4537   struct TALER_SecurityModulePublicKeySetP secmset;
   4538   struct GNUNET_JSON_Specification spec[] = {
   4539     GNUNET_JSON_spec_array_const ("future_denoms",
   4540                                   &denomkeys),
   4541     GNUNET_JSON_spec_array_const ("future_signkeys",
   4542                                   &signkeys),
   4543     GNUNET_JSON_spec_fixed_auto ("master_pub",
   4544                                  &mpub),
   4545     GNUNET_JSON_spec_fixed_auto ("denom_secmod_public_key",
   4546                                  &secmset.rsa),
   4547     GNUNET_JSON_spec_fixed_auto ("denom_secmod_cs_public_key",
   4548                                  &secmset.cs),
   4549     GNUNET_JSON_spec_fixed_auto ("signkey_secmod_public_key",
   4550                                  &secmset.eddsa),
   4551     GNUNET_JSON_spec_end ()
   4552   };
   4553 
   4554   keys = parse_keys_input ("show");
   4555   if (NULL == keys)
   4556     return;
   4557 
   4558   if (GNUNET_OK !=
   4559       load_offline_key (GNUNET_NO))
   4560     return;
   4561   if (GNUNET_OK !=
   4562       GNUNET_JSON_parse (keys,
   4563                          spec,
   4564                          &err_name,
   4565                          &err_line))
   4566   {
   4567     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4568                 "Invalid input to 'show': %s #%u (skipping)\n",
   4569                 err_name,
   4570                 err_line);
   4571     json_dumpf (in,
   4572                 stderr,
   4573                 JSON_INDENT (2));
   4574     global_ret = EXIT_FAILURE;
   4575     GNUNET_SCHEDULER_shutdown ();
   4576     json_decref (keys);
   4577     return;
   4578   }
   4579   if (0 !=
   4580       GNUNET_memcmp (&master_pub,
   4581                      &mpub))
   4582   {
   4583     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4584                 "Fatal: exchange uses different master key!\n");
   4585     global_ret = EXIT_FAILURE;
   4586     GNUNET_SCHEDULER_shutdown ();
   4587     json_decref (keys);
   4588     return;
   4589   }
   4590   if (GNUNET_SYSERR ==
   4591       tofu_check (&secmset))
   4592   {
   4593     global_ret = EXIT_FAILURE;
   4594     GNUNET_SCHEDULER_shutdown ();
   4595     json_decref (keys);
   4596     return;
   4597   }
   4598   if ( (GNUNET_OK !=
   4599         show_signkeys (&secmset.eddsa,
   4600                        signkeys)) ||
   4601        (GNUNET_OK !=
   4602         show_denomkeys (&secmset.rsa,
   4603                         &secmset.cs,
   4604                         denomkeys)) )
   4605   {
   4606     global_ret = EXIT_FAILURE;
   4607     GNUNET_SCHEDULER_shutdown ();
   4608     json_decref (keys);
   4609     return;
   4610   }
   4611   json_decref (keys);
   4612   next (args);
   4613 }
   4614 
   4615 
   4616 /**
   4617  * Sign @a signkeys with offline key.
   4618  *
   4619  * @param secm_pub security module public key used to sign the denominations
   4620  * @param signkeys keys to output
   4621  * @param[in,out] result array where to output the signatures
   4622  * @return #GNUNET_OK on success
   4623  */
   4624 static enum GNUNET_GenericReturnValue
   4625 sign_signkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub,
   4626                const json_t *signkeys,
   4627                json_t *result)
   4628 {
   4629   size_t index;
   4630   json_t *value;
   4631 
   4632   json_array_foreach (signkeys, index, value) {
   4633     const char *err_name;
   4634     unsigned int err_line;
   4635     struct TALER_ExchangePublicKeyP exchange_pub;
   4636     struct TALER_SecurityModuleSignatureP secm_sig;
   4637     struct GNUNET_TIME_Timestamp start_time;
   4638     struct GNUNET_TIME_Timestamp sign_end;
   4639     struct GNUNET_TIME_Timestamp legal_end;
   4640     struct GNUNET_TIME_Relative duration;
   4641     struct GNUNET_JSON_Specification spec[] = {
   4642       GNUNET_JSON_spec_timestamp ("stamp_start",
   4643                                   &start_time),
   4644       GNUNET_JSON_spec_timestamp ("stamp_expire",
   4645                                   &sign_end),
   4646       GNUNET_JSON_spec_timestamp ("stamp_end",
   4647                                   &legal_end),
   4648       GNUNET_JSON_spec_fixed_auto ("key",
   4649                                    &exchange_pub),
   4650       GNUNET_JSON_spec_fixed_auto ("signkey_secmod_sig",
   4651                                    &secm_sig),
   4652       GNUNET_JSON_spec_end ()
   4653     };
   4654 
   4655     if (GNUNET_OK !=
   4656         GNUNET_JSON_parse (value,
   4657                            spec,
   4658                            &err_name,
   4659                            &err_line))
   4660     {
   4661       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4662                   "Invalid input for signing key to 'show': %s #%u at %u (skipping)\n",
   4663                   err_name,
   4664                   err_line,
   4665                   (unsigned int) index);
   4666       json_dumpf (value,
   4667                   stderr,
   4668                   JSON_INDENT (2));
   4669       global_ret = EXIT_FAILURE;
   4670       GNUNET_SCHEDULER_shutdown ();
   4671       return GNUNET_SYSERR;
   4672     }
   4673 
   4674     duration = GNUNET_TIME_absolute_get_difference (start_time.abs_time,
   4675                                                     sign_end.abs_time);
   4676     if (GNUNET_OK !=
   4677         TALER_exchange_secmod_eddsa_verify (&exchange_pub,
   4678                                             start_time,
   4679                                             duration,
   4680                                             secm_pub,
   4681                                             &secm_sig))
   4682     {
   4683       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4684                   "Invalid security module signature for signing key %s (aborting)\n",
   4685                   TALER_B2S (&exchange_pub));
   4686       global_ret = EXIT_FAILURE;
   4687       GNUNET_SCHEDULER_shutdown ();
   4688       GNUNET_JSON_parse_free (spec);
   4689       return GNUNET_SYSERR;
   4690     }
   4691     {
   4692       struct TALER_MasterSignatureP master_sig;
   4693 
   4694       TALER_exchange_offline_signkey_validity_sign (&exchange_pub,
   4695                                                     start_time,
   4696                                                     sign_end,
   4697                                                     legal_end,
   4698                                                     &master_priv,
   4699                                                     &master_sig);
   4700       GNUNET_assert (0 ==
   4701                      json_array_append_new (
   4702                        result,
   4703                        GNUNET_JSON_PACK (
   4704                          GNUNET_JSON_pack_data_auto ("exchange_pub",
   4705                                                      &exchange_pub),
   4706                          GNUNET_JSON_pack_data_auto ("master_sig",
   4707                                                      &master_sig))));
   4708     }
   4709     GNUNET_JSON_parse_free (spec);
   4710   }
   4711   return GNUNET_OK;
   4712 }
   4713 
   4714 
   4715 /**
   4716  * Looks up the AGE_RESTRICTED setting for a denomination in the config and
   4717  * returns the age restriction (mask) accordingly.
   4718  *
   4719  * @param section_name Section in the configuration for the particular
   4720  *    denomination.
   4721  */
   4722 static struct TALER_AgeMask
   4723 load_age_mask (const char*section_name)
   4724 {
   4725   static const struct TALER_AgeMask null_mask = {0};
   4726   enum GNUNET_GenericReturnValue ret;
   4727 
   4728   if (GNUNET_YES !=
   4729       GNUNET_CONFIGURATION_get_value_yesno (kcfg,
   4730                                             "exchange",
   4731                                             "AGE_RESTRICTION_ENABLED"))
   4732     return null_mask;
   4733 
   4734   if (GNUNET_OK != (GNUNET_CONFIGURATION_have_value (
   4735                       kcfg,
   4736                       section_name,
   4737                       "AGE_RESTRICTED")))
   4738     return null_mask;
   4739 
   4740   ret = GNUNET_CONFIGURATION_get_value_yesno (kcfg,
   4741                                               section_name,
   4742                                               "AGE_RESTRICTED");
   4743   if (GNUNET_SYSERR == ret)
   4744     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
   4745                                section_name,
   4746                                "AGE_RESTRICTED",
   4747                                "Value must be YES or NO\n");
   4748   if (GNUNET_YES == ret)
   4749   {
   4750     char *groups = NULL;
   4751     struct TALER_AgeMask mask = {0};
   4752 
   4753     if (GNUNET_OK !=
   4754         GNUNET_CONFIGURATION_get_value_string (kcfg,
   4755                                                "exchange",
   4756                                                "AGE_GROUPS",
   4757                                                &groups))
   4758       groups = GNUNET_strdup ("8:10:12:14:16:18:21");
   4759     if (GNUNET_OK ==
   4760         TALER_parse_age_group_string (groups, &mask))
   4761     {
   4762       GNUNET_free (groups);
   4763       return mask;
   4764     }
   4765     GNUNET_free (groups);
   4766   }
   4767 
   4768   return null_mask;
   4769 }
   4770 
   4771 
   4772 /**
   4773  * Sign @a denomkeys with offline key.
   4774  *
   4775  * @param secm_pub_rsa security module public key used to sign the RSA denominations
   4776  * @param secm_pub_cs security module public key used to sign the CS denominations
   4777  * @param denomkeys keys to output
   4778  * @param[in,out] result array where to output the signatures
   4779  * @return #GNUNET_OK on success
   4780  */
   4781 static enum GNUNET_GenericReturnValue
   4782 sign_denomkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub_rsa,
   4783                 const struct TALER_SecurityModulePublicKeyP *secm_pub_cs,
   4784                 const json_t *denomkeys,
   4785                 json_t *result)
   4786 {
   4787   size_t index;
   4788   json_t *value;
   4789 
   4790   json_array_foreach (denomkeys, index, value) {
   4791     const char *err_name;
   4792     unsigned int err_line;
   4793     const char *section_name;
   4794     struct TALER_DenominationPublicKey denom_pub;
   4795     struct GNUNET_TIME_Timestamp stamp_start;
   4796     struct GNUNET_TIME_Timestamp stamp_expire_withdraw;
   4797     struct GNUNET_TIME_Timestamp stamp_expire_deposit;
   4798     struct GNUNET_TIME_Timestamp stamp_expire_legal;
   4799     struct TALER_Amount coin_value;
   4800     struct TALER_DenomFeeSet fees;
   4801     struct TALER_SecurityModuleSignatureP secm_sig;
   4802     struct GNUNET_JSON_Specification spec[] = {
   4803       GNUNET_JSON_spec_string ("section_name",
   4804                                &section_name),
   4805       TALER_JSON_spec_denom_pub ("denom_pub",
   4806                                  &denom_pub),
   4807       TALER_JSON_spec_amount ("value",
   4808                               currency,
   4809                               &coin_value),
   4810       TALER_JSON_SPEC_DENOM_FEES ("fee",
   4811                                   currency,
   4812                                   &fees),
   4813       GNUNET_JSON_spec_timestamp ("stamp_start",
   4814                                   &stamp_start),
   4815       GNUNET_JSON_spec_timestamp ("stamp_expire_withdraw",
   4816                                   &stamp_expire_withdraw),
   4817       GNUNET_JSON_spec_timestamp ("stamp_expire_deposit",
   4818                                   &stamp_expire_deposit),
   4819       GNUNET_JSON_spec_timestamp ("stamp_expire_legal",
   4820                                   &stamp_expire_legal),
   4821       GNUNET_JSON_spec_fixed_auto ("denom_secmod_sig",
   4822                                    &secm_sig),
   4823       GNUNET_JSON_spec_end ()
   4824     };
   4825     struct GNUNET_TIME_Relative duration;
   4826     struct TALER_DenominationHashP h_denom_pub;
   4827 
   4828     if (GNUNET_OK !=
   4829         GNUNET_JSON_parse (value,
   4830                            spec,
   4831                            &err_name,
   4832                            &err_line))
   4833     {
   4834       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4835                   "Invalid input for denomination key to 'sign': %s #%u at %u (skipping)\n",
   4836                   err_name,
   4837                   err_line,
   4838                   (unsigned int) index);
   4839       json_dumpf (value,
   4840                   stderr,
   4841                   JSON_INDENT (2));
   4842       GNUNET_JSON_parse_free (spec);
   4843       global_ret = EXIT_FAILURE;
   4844       GNUNET_SCHEDULER_shutdown ();
   4845       return GNUNET_SYSERR;
   4846     }
   4847     duration = GNUNET_TIME_absolute_get_difference (
   4848       stamp_start.abs_time,
   4849       stamp_expire_withdraw.abs_time);
   4850 
   4851     /* Load the age mask, if applicable to this denomination */
   4852     denom_pub.age_mask = load_age_mask (section_name);
   4853 
   4854     TALER_denom_pub_hash (&denom_pub,
   4855                           &h_denom_pub);
   4856 
   4857     switch (denom_pub.bsign_pub_key->cipher)
   4858     {
   4859     case GNUNET_CRYPTO_BSA_RSA:
   4860       {
   4861         struct TALER_RsaPubHashP h_rsa;
   4862 
   4863         TALER_rsa_pub_hash (denom_pub.bsign_pub_key->details.rsa_public_key,
   4864                             &h_rsa);
   4865         if (GNUNET_OK !=
   4866             TALER_exchange_secmod_rsa_verify (&h_rsa,
   4867                                               section_name,
   4868                                               stamp_start,
   4869                                               duration,
   4870                                               secm_pub_rsa,
   4871                                               &secm_sig))
   4872         {
   4873           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4874                       "Invalid security module signature for denomination key %s (aborting)\n",
   4875                       GNUNET_h2s (&h_denom_pub.hash));
   4876           global_ret = EXIT_FAILURE;
   4877           GNUNET_SCHEDULER_shutdown ();
   4878           GNUNET_JSON_parse_free (spec);
   4879           return GNUNET_SYSERR;
   4880         }
   4881       }
   4882       break;
   4883     case GNUNET_CRYPTO_BSA_CS:
   4884       {
   4885         struct TALER_CsPubHashP h_cs;
   4886 
   4887         TALER_cs_pub_hash (&denom_pub.bsign_pub_key->details.cs_public_key,
   4888                            &h_cs);
   4889         if (GNUNET_OK !=
   4890             TALER_exchange_secmod_cs_verify (&h_cs,
   4891                                              section_name,
   4892                                              stamp_start,
   4893                                              duration,
   4894                                              secm_pub_cs,
   4895                                              &secm_sig))
   4896         {
   4897           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4898                       "Invalid security module signature for denomination key %s (aborting)\n",
   4899                       GNUNET_h2s (&h_denom_pub.hash));
   4900           global_ret = EXIT_FAILURE;
   4901           GNUNET_SCHEDULER_shutdown ();
   4902           GNUNET_JSON_parse_free (spec);
   4903           return GNUNET_SYSERR;
   4904         }
   4905       }
   4906       break;
   4907     default:
   4908       global_ret = EXIT_FAILURE;
   4909       GNUNET_SCHEDULER_shutdown ();
   4910       GNUNET_JSON_parse_free (spec);
   4911       return GNUNET_SYSERR;
   4912     }
   4913 
   4914     {
   4915       struct TALER_MasterSignatureP master_sig;
   4916 
   4917       TALER_exchange_offline_denom_validity_sign (&h_denom_pub,
   4918                                                   stamp_start,
   4919                                                   stamp_expire_withdraw,
   4920                                                   stamp_expire_deposit,
   4921                                                   stamp_expire_legal,
   4922                                                   &coin_value,
   4923                                                   &fees,
   4924                                                   &master_priv,
   4925                                                   &master_sig);
   4926       GNUNET_assert (0 ==
   4927                      json_array_append_new (
   4928                        result,
   4929                        GNUNET_JSON_PACK (
   4930                          GNUNET_JSON_pack_data_auto ("h_denom_pub",
   4931                                                      &h_denom_pub),
   4932                          GNUNET_JSON_pack_data_auto ("master_sig",
   4933                                                      &master_sig))));
   4934     }
   4935     GNUNET_JSON_parse_free (spec);
   4936   }
   4937   return GNUNET_OK;
   4938 }
   4939 
   4940 
   4941 /**
   4942  * Sign future keys.
   4943  *
   4944  * @param args the array of command-line arguments to process next
   4945  */
   4946 static void
   4947 do_sign (char *const *args)
   4948 {
   4949   json_t *keys;
   4950   const char *err_name;
   4951   unsigned int err_line;
   4952   const json_t *denomkeys;
   4953   const json_t *signkeys;
   4954   struct TALER_MasterPublicKeyP mpub;
   4955   struct TALER_SecurityModulePublicKeySetP secmset;
   4956   struct GNUNET_JSON_Specification spec[] = {
   4957     GNUNET_JSON_spec_array_const ("future_denoms",
   4958                                   &denomkeys),
   4959     GNUNET_JSON_spec_array_const ("future_signkeys",
   4960                                   &signkeys),
   4961     GNUNET_JSON_spec_fixed_auto ("master_pub",
   4962                                  &mpub),
   4963     GNUNET_JSON_spec_fixed_auto ("denom_secmod_public_key",
   4964                                  &secmset.rsa),
   4965     GNUNET_JSON_spec_fixed_auto ("denom_secmod_cs_public_key",
   4966                                  &secmset.cs),
   4967     GNUNET_JSON_spec_fixed_auto ("signkey_secmod_public_key",
   4968                                  &secmset.eddsa),
   4969     GNUNET_JSON_spec_end ()
   4970   };
   4971 
   4972   keys = parse_keys_input ("sign");
   4973   if (NULL == keys)
   4974     return;
   4975   if (GNUNET_OK !=
   4976       load_offline_key (GNUNET_NO))
   4977   {
   4978     json_decref (keys);
   4979     return;
   4980   }
   4981   if (GNUNET_OK !=
   4982       GNUNET_JSON_parse (keys,
   4983                          spec,
   4984                          &err_name,
   4985                          &err_line))
   4986   {
   4987     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4988                 "Invalid input to 'sign' : %s #%u (skipping)\n",
   4989                 err_name,
   4990                 err_line);
   4991     json_dumpf (in,
   4992                 stderr,
   4993                 JSON_INDENT (2));
   4994     global_ret = EXIT_FAILURE;
   4995     GNUNET_SCHEDULER_shutdown ();
   4996     json_decref (keys);
   4997     return;
   4998   }
   4999   if (0 !=
   5000       GNUNET_memcmp (&master_pub,
   5001                      &mpub))
   5002   {
   5003     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   5004                 "Fatal: exchange uses different master key!\n");
   5005     global_ret = EXIT_FAILURE;
   5006     GNUNET_SCHEDULER_shutdown ();
   5007     json_decref (keys);
   5008     return;
   5009   }
   5010   if (GNUNET_SYSERR ==
   5011       tofu_check (&secmset))
   5012   {
   5013     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   5014                 "Fatal: security module keys changed!\n");
   5015     global_ret = EXIT_FAILURE;
   5016     GNUNET_SCHEDULER_shutdown ();
   5017     json_decref (keys);
   5018     return;
   5019   }
   5020   {
   5021     json_t *signkey_sig_array = json_array ();
   5022     json_t *denomkey_sig_array = json_array ();
   5023 
   5024     GNUNET_assert (NULL != signkey_sig_array);
   5025     GNUNET_assert (NULL != denomkey_sig_array);
   5026     if ( (GNUNET_OK !=
   5027           sign_signkeys (&secmset.eddsa,
   5028                          signkeys,
   5029                          signkey_sig_array)) ||
   5030          (GNUNET_OK !=
   5031           sign_denomkeys (&secmset.rsa,
   5032                           &secmset.cs,
   5033                           denomkeys,
   5034                           denomkey_sig_array)) )
   5035     {
   5036       global_ret = EXIT_FAILURE;
   5037       GNUNET_SCHEDULER_shutdown ();
   5038       json_decref (signkey_sig_array);
   5039       json_decref (denomkey_sig_array);
   5040       json_decref (keys);
   5041       return;
   5042     }
   5043 
   5044     output_operation (OP_UPLOAD_SIGS,
   5045                       GNUNET_JSON_PACK (
   5046                         GNUNET_JSON_pack_array_steal ("denom_sigs",
   5047                                                       denomkey_sig_array),
   5048                         GNUNET_JSON_pack_array_steal ("signkey_sigs",
   5049                                                       signkey_sig_array)));
   5050   }
   5051   json_decref (keys);
   5052   next (args);
   5053 }
   5054 
   5055 
   5056 /**
   5057  * Setup and output offline signing key.
   5058  *
   5059  * @param args the array of command-line arguments to process next
   5060  */
   5061 static void
   5062 do_setup (char *const *args)
   5063 {
   5064   if (GNUNET_OK !=
   5065       load_offline_key (GNUNET_YES))
   5066   {
   5067     global_ret = EXIT_NOPERMISSION;
   5068     return;
   5069   }
   5070   if (NULL != *args)
   5071   {
   5072     output_operation (OP_SETUP,
   5073                       GNUNET_JSON_PACK (
   5074                         GNUNET_JSON_pack_data_auto ("exchange_offline_pub",
   5075                                                     &master_pub)));
   5076   }
   5077 
   5078   else
   5079   {
   5080     char *pub_s;
   5081 
   5082     pub_s = GNUNET_STRINGS_data_to_string_alloc (&master_pub,
   5083                                                  sizeof (master_pub));
   5084     fprintf (stdout,
   5085              "%s\n",
   5086              pub_s);
   5087     GNUNET_free (pub_s);
   5088   }
   5089   if ( (NULL != *args) &&
   5090        (0 == strcmp (*args,
   5091                      "-")) )
   5092     args++;
   5093   next (args);
   5094 }
   5095 
   5096 
   5097 
   5098 /**
   5099  * Dispatch @a args in the @a cmds array.
   5100  *
   5101  * @param args arguments with subcommand to dispatch
   5102  * @param cmds array of possible subcommands to call
   5103  */
   5104 static void
   5105 cmd_handler (char *const *args,
   5106              const struct SubCommand *cmds)
   5107 {
   5108   nxt = NULL;
   5109   for (unsigned int i = 0; NULL != cmds[i].name; i++)
   5110   {
   5111     if (0 == strcasecmp (cmds[i].name,
   5112                          args[0]))
   5113     {
   5114       cmds[i].cb (&args[1]);
   5115       return;
   5116     }
   5117   }
   5118 
   5119   if (0 != strcasecmp ("help",
   5120                        args[0]))
   5121   {
   5122     GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
   5123                 "Unexpected command `%s'\n",
   5124                 args[0]);
   5125     global_ret = EXIT_INVALIDARGUMENT;
   5126   }
   5127 
   5128   GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
   5129               "Supported subcommands:\n");
   5130   for (unsigned int i = 0; NULL != cmds[i].name; i++)
   5131   {
   5132     GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
   5133                 "- %s: %s\n",
   5134                 cmds[i].name,
   5135                 cmds[i].help);
   5136   }
   5137   json_decref (out);
   5138   out = NULL;
   5139   GNUNET_SCHEDULER_shutdown ();
   5140 }
   5141 
   5142 
   5143 
   5144 static void
   5145 work (void *cls)
   5146 {
   5147   char *const *args = cls;
   5148   struct SubCommand cmds[] = {
   5149     {
   5150       .name = "setup",
   5151       .help =
   5152         "initialize offline key signing material and display public offline key",
   5153       .cb = &do_setup
   5154     },
   5155     {
   5156       .name = "download",
   5157       .help =
   5158         "obtain future public keys from exchange (to be performed online!)",
   5159       .cb = &do_download
   5160     },
   5161     {
   5162       .name = "show",
   5163       .help =
   5164         "display future public keys from exchange for human review (pass '-' as argument to disable consuming input)",
   5165       .cb = &do_show
   5166     },
   5167     {
   5168       .name = "sign",
   5169       .help = "sign all future public keys from the input",
   5170       .cb = &do_sign
   5171     },
   5172     {
   5173       .name = "revoke-denomination",
   5174       .help =
   5175         "revoke denomination key (hash of public key must be given as argument)",
   5176       .cb = &do_revoke_denomination_key
   5177     },
   5178     {
   5179       .name = "revoke-signkey",
   5180       .help =
   5181         "revoke exchange online signing key (public key must be given as argument)",
   5182       .cb = &do_revoke_signkey
   5183     },
   5184     {
   5185       .name = "enable-auditor",
   5186       .help =
   5187         "enable auditor for the exchange (auditor-public key, auditor-URI and auditor-name must be given as arguments)",
   5188       .cb = &do_add_auditor
   5189     },
   5190     {
   5191       .name = "disable-auditor",
   5192       .help =
   5193         "disable auditor at the exchange (auditor-public key must be given as argument)",
   5194       .cb = &do_del_auditor
   5195     },
   5196     {
   5197       .name = "enable-account",
   5198       .help =
   5199         "enable wire account of the exchange (payto-URI must be given as argument; for optional arguments see man page)",
   5200       .cb = &do_add_wire
   5201     },
   5202     {
   5203       .name = "disable-account",
   5204       .help =
   5205         "disable wire account of the exchange (payto-URI must be given as argument)",
   5206       .cb = &do_del_wire
   5207     },
   5208     {
   5209       .name = "wire-fee",
   5210       .help =
   5211         "sign wire fees for the given year (year, wire method, wire fee, and closing fee must be given as arguments)",
   5212       .cb = &do_set_wire_fee
   5213     },
   5214     {
   5215       .name = "global-fee",
   5216       .help =
   5217         "sign global fees for the given year (year, history fee, account fee, purse fee, purse timeout, history expiration and the maximum number of free purses per account must be given as arguments)",
   5218       .cb = &do_set_global_fee
   5219     },
   5220     {
   5221       .name = "drain",
   5222       .help =
   5223         "drain profits from exchange escrow account to regular exchange operator account (amount, debit account configuration section and credit account payto://-URI must be given as arguments)",
   5224       .cb = &do_drain
   5225     },
   5226     {
   5227       .name = "add-partner",
   5228       .help =
   5229         "add partner exchange for P2P wad transfers (partner master public key, partner base URL, wad fee, wad frequency and validity year must be given as arguments)",
   5230       .cb = &do_add_partner
   5231     },
   5232     {
   5233       .name = "aml-enable",
   5234       .help =
   5235         "enable AML staff member (staff member public key, legal name and rw (read write) or ro (read only) must be given as arguments)",
   5236       .cb = &enable_aml_staff
   5237     },
   5238     {
   5239       .name = "aml-disable",
   5240       .help =
   5241         "disable AML staff member (staff member public key and legal name must be given as arguments)",
   5242       .cb = &disable_aml_staff
   5243     },
   5244     {
   5245       .name = "upload",
   5246       .help =
   5247         "upload operation result to exchange (to be performed online!)",
   5248       .cb = &do_upload
   5249     },
   5250     /* list terminator */
   5251     {
   5252       .name = NULL,
   5253     }
   5254   };
   5255   (void) cls;
   5256 
   5257   cmd_handler (args, cmds);
   5258 }
   5259 
   5260 
   5261 /**
   5262  * Main function that will be run.
   5263  *
   5264  * @param cls closure
   5265  * @param args remaining command-line arguments
   5266  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
   5267  * @param cfg configuration
   5268  */
   5269 static void
   5270 run (void *cls,
   5271      char *const *args,
   5272      const char *cfgfile,
   5273      const struct GNUNET_CONFIGURATION_Handle *cfg)
   5274 {
   5275   (void) cls;
   5276   (void) cfgfile;
   5277   kcfg = cfg;
   5278 
   5279   /* age restriction is read per-denomination in load_age_mask() from [exchange] */
   5280 
   5281 
   5282   if (GNUNET_OK !=
   5283       TALER_config_get_currency (kcfg,
   5284                                  "exchange",
   5285                                  &currency))
   5286   {
   5287     global_ret = EXIT_NOTCONFIGURED;
   5288     return;
   5289   }
   5290 
   5291   ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
   5292                           &rc);
   5293   rc = GNUNET_CURL_gnunet_rc_create (ctx);
   5294   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
   5295                                  NULL);
   5296   next (args);
   5297 }
   5298 
   5299 
   5300 /**
   5301  * The main function of the taler-exchange-offline tool.  This tool is used to
   5302  * create the signing and denomination keys for the exchange.  It uses the
   5303  * long-term offline private key and generates signatures with it. It also
   5304  * supports online operations with the exchange to download its input data and
   5305  * to upload its results. Those online operations should be performed on
   5306  * another machine in production!
   5307  *
   5308  * @param argc number of arguments from the command line
   5309  * @param argv command line arguments
   5310  * @return 0 ok, 1 on error
   5311  */
   5312 int
   5313 main (int argc,
   5314       char *const *argv)
   5315 {
   5316   struct GNUNET_GETOPT_CommandLineOption options[] = {
   5317     GNUNET_GETOPT_OPTION_END
   5318   };
   5319   enum GNUNET_GenericReturnValue ret;
   5320 
   5321   ret = GNUNET_PROGRAM_run (
   5322     TALER_EXCHANGE_project_data (),
   5323     argc, argv,
   5324     "taler-exchange-offline",
   5325     gettext_noop ("Operations for offline signing for a Taler exchange"),
   5326     options,
   5327     &run, NULL);
   5328   if (GNUNET_SYSERR == ret)
   5329     return EXIT_INVALIDARGUMENT;
   5330   if (GNUNET_NO == ret)
   5331     return EXIT_SUCCESS;
   5332   return global_ret;
   5333 }
   5334 
   5335 
   5336 /* end of taler-exchange-offline.c */