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 (161997B)


      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  * Function called with information about the add partner operation.
   2268  *
   2269  * @param par the request
   2270  * @param apr response data
   2271  */
   2272 static void
   2273 add_partner_cb (
   2274   struct PartnerAddRequest *par,
   2275   const struct TALER_EXCHANGE_PostManagementPartnersResponse *apr)
   2276 {
   2277   const struct TALER_EXCHANGE_HttpResponse *hr = &apr->hr;
   2278 
   2279   if (MHD_HTTP_NO_CONTENT != hr->http_status)
   2280   {
   2281     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2282                 "Upload failed for command %u with status %u: %s (%s)\n",
   2283                 (unsigned int) par->idx,
   2284                 hr->http_status,
   2285                 TALER_ErrorCode_get_hint (hr->ec),
   2286                 hr->hint);
   2287     global_ret = EXIT_FAILURE;
   2288   }
   2289   GNUNET_CONTAINER_DLL_remove (par_head,
   2290                                par_tail,
   2291                                par);
   2292   GNUNET_free (par);
   2293   test_shutdown ();
   2294 }
   2295 
   2296 
   2297 /**
   2298  * Add partner action.
   2299  *
   2300  * @param exchange_url base URL of the exchange
   2301  * @param idx index of the operation we are performing (for logging)
   2302  * @param value arguments for add partner
   2303  */
   2304 static void
   2305 add_partner (const char *exchange_url,
   2306              size_t idx,
   2307              const json_t *value)
   2308 {
   2309   struct TALER_MasterPublicKeyP partner_pub;
   2310   struct GNUNET_TIME_Timestamp start_date;
   2311   struct GNUNET_TIME_Timestamp end_date;
   2312   struct GNUNET_TIME_Relative wad_frequency;
   2313   struct TALER_Amount wad_fee;
   2314   const char *partner_base_url;
   2315   struct TALER_MasterSignatureP master_sig;
   2316   struct PartnerAddRequest *par;
   2317   struct GNUNET_JSON_Specification spec[] = {
   2318     GNUNET_JSON_spec_fixed_auto ("partner_pub",
   2319                                  &partner_pub),
   2320     TALER_JSON_spec_amount ("wad_fee",
   2321                             currency,
   2322                             &wad_fee),
   2323     GNUNET_JSON_spec_relative_time ("wad_frequency",
   2324                                     &wad_frequency),
   2325     GNUNET_JSON_spec_timestamp ("start_date",
   2326                                 &start_date),
   2327     GNUNET_JSON_spec_timestamp ("end_date",
   2328                                 &end_date),
   2329     TALER_JSON_spec_web_url ("partner_base_url",
   2330                              &partner_base_url),
   2331     GNUNET_JSON_spec_fixed_auto ("master_sig",
   2332                                  &master_sig),
   2333     GNUNET_JSON_spec_end ()
   2334   };
   2335   const char *err_name;
   2336   unsigned int err_line;
   2337 
   2338   if (GNUNET_OK !=
   2339       GNUNET_JSON_parse (value,
   2340                          spec,
   2341                          &err_name,
   2342                          &err_line))
   2343   {
   2344     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2345                 "Invalid input to add partner: %s#%u at %u (skipping)\n",
   2346                 err_name,
   2347                 err_line,
   2348                 (unsigned int) idx);
   2349     json_dumpf (value,
   2350                 stderr,
   2351                 JSON_INDENT (2));
   2352     global_ret = EXIT_FAILURE;
   2353     GNUNET_SCHEDULER_shutdown ();
   2354     return;
   2355   }
   2356   par = GNUNET_new (struct PartnerAddRequest);
   2357   par->idx = idx;
   2358   par->h =
   2359     TALER_EXCHANGE_post_management_partners_create (ctx,
   2360                                                     exchange_url,
   2361                                                     &partner_pub,
   2362                                                     start_date,
   2363                                                     end_date,
   2364                                                     wad_frequency,
   2365                                                     &wad_fee,
   2366                                                     partner_base_url,
   2367                                                     &master_sig);
   2368   TALER_EXCHANGE_post_management_partners_start (par->h,
   2369                                                  &add_partner_cb,
   2370                                                  par);
   2371   GNUNET_CONTAINER_DLL_insert (par_head,
   2372                                par_tail,
   2373                                par);
   2374 }
   2375 
   2376 
   2377 /**
   2378  * Function called with information about the AML officer update operation.
   2379  *
   2380  * @param asr the request
   2381  * @param ar response data
   2382  */
   2383 static void
   2384 update_aml_officer_cb (
   2385   TALER_EXCHANGE_POST_MANAGEMENT_AML_OFFICERS_RESULT_CLOSURE *asr,
   2386   const struct TALER_EXCHANGE_PostManagementAmlOfficersResponse *ar)
   2387 {
   2388   const struct TALER_EXCHANGE_HttpResponse *hr = &ar->hr;
   2389 
   2390   if (MHD_HTTP_NO_CONTENT != hr->http_status)
   2391   {
   2392     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2393                 "Upload failed for command %u with status %u: %s (%s)\n",
   2394                 (unsigned int) asr->idx,
   2395                 hr->http_status,
   2396                 TALER_ErrorCode_get_hint (hr->ec),
   2397                 hr->hint);
   2398     global_ret = EXIT_FAILURE;
   2399   }
   2400   GNUNET_CONTAINER_DLL_remove (asr_head,
   2401                                asr_tail,
   2402                                asr);
   2403   GNUNET_free (asr);
   2404   test_shutdown ();
   2405 }
   2406 
   2407 
   2408 /**
   2409  * Upload AML staff action.
   2410  *
   2411  * @param exchange_url base URL of the exchange
   2412  * @param idx index of the operation we are performing (for logging)
   2413  * @param value arguments for AML staff change
   2414  */
   2415 static void
   2416 update_aml_staff (const char *exchange_url,
   2417                   size_t idx,
   2418                   const json_t *value)
   2419 {
   2420   struct TALER_AmlOfficerPublicKeyP officer_pub;
   2421   const char *officer_name;
   2422   struct GNUNET_TIME_Timestamp change_date;
   2423   bool is_active;
   2424   bool read_only;
   2425   struct TALER_MasterSignatureP master_sig;
   2426   struct AmlStaffRequest *asr;
   2427   struct GNUNET_JSON_Specification spec[] = {
   2428     GNUNET_JSON_spec_fixed_auto ("officer_pub",
   2429                                  &officer_pub),
   2430     GNUNET_JSON_spec_timestamp ("change_date",
   2431                                 &change_date),
   2432     GNUNET_JSON_spec_bool ("is_active",
   2433                            &is_active),
   2434     GNUNET_JSON_spec_bool ("read_only",
   2435                            &read_only),
   2436     GNUNET_JSON_spec_string ("officer_name",
   2437                              &officer_name),
   2438     GNUNET_JSON_spec_fixed_auto ("master_sig",
   2439                                  &master_sig),
   2440     GNUNET_JSON_spec_end ()
   2441   };
   2442   const char *err_name;
   2443   unsigned int err_line;
   2444 
   2445   if (GNUNET_OK !=
   2446       GNUNET_JSON_parse (value,
   2447                          spec,
   2448                          &err_name,
   2449                          &err_line))
   2450   {
   2451     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2452                 "Invalid input to AML staff update: %s#%u at %u (skipping)\n",
   2453                 err_name,
   2454                 err_line,
   2455                 (unsigned int) idx);
   2456     json_dumpf (value,
   2457                 stderr,
   2458                 JSON_INDENT (2));
   2459     global_ret = EXIT_FAILURE;
   2460     GNUNET_SCHEDULER_shutdown ();
   2461     return;
   2462   }
   2463   asr = GNUNET_new (struct AmlStaffRequest);
   2464   asr->idx = idx;
   2465   asr->h = TALER_EXCHANGE_post_management_aml_officers_create (
   2466     ctx,
   2467     exchange_url,
   2468     &officer_pub,
   2469     officer_name,
   2470     change_date,
   2471     is_active,
   2472     read_only,
   2473     &master_sig);
   2474   GNUNET_CONTAINER_DLL_insert (asr_head,
   2475                                asr_tail,
   2476                                asr);
   2477   GNUNET_assert (TALER_EC_NONE ==
   2478                  TALER_EXCHANGE_post_management_aml_officers_start (
   2479                    asr->h,
   2480                    &update_aml_officer_cb,
   2481                    asr));
   2482 }
   2483 
   2484 
   2485 /**
   2486  * Perform uploads based on the JSON in #out.
   2487  *
   2488  * @param exchange_url base URL of the exchange to use
   2489  */
   2490 static void
   2491 trigger_upload (const char *exchange_url)
   2492 {
   2493   struct UploadHandler uhs[] = {
   2494     {
   2495       .key = OP_REVOKE_DENOMINATION,
   2496       .cb = &upload_denom_revocation
   2497     },
   2498     {
   2499       .key = OP_REVOKE_SIGNKEY,
   2500       .cb = &upload_signkey_revocation
   2501     },
   2502     {
   2503       .key = OP_ENABLE_AUDITOR,
   2504       .cb = &upload_auditor_add
   2505     },
   2506     {
   2507       .key = OP_DISABLE_AUDITOR,
   2508       .cb = &upload_auditor_del
   2509     },
   2510     {
   2511       .key = OP_ENABLE_WIRE,
   2512       .cb = &upload_wire_add
   2513     },
   2514     {
   2515       .key = OP_DISABLE_WIRE,
   2516       .cb = &upload_wire_del
   2517     },
   2518     {
   2519       .key = OP_SET_WIRE_FEE,
   2520       .cb = &upload_wire_fee
   2521     },
   2522     {
   2523       .key = OP_SET_GLOBAL_FEE,
   2524       .cb = &upload_global_fee
   2525     },
   2526     {
   2527       .key = OP_UPLOAD_SIGS,
   2528       .cb = &upload_keys
   2529     },
   2530     {
   2531       .key = OP_DRAIN_PROFITS,
   2532       .cb = &upload_drain
   2533     },
   2534     {
   2535       .key = OP_UPDATE_AML_STAFF,
   2536       .cb = &update_aml_staff
   2537     },
   2538     {
   2539       .key = OP_ADD_PARTNER,
   2540       .cb = &add_partner
   2541     },
   2542     /* array termination */
   2543     {
   2544       .key = NULL
   2545     }
   2546   };
   2547   size_t index;
   2548   json_t *obj;
   2549 
   2550   json_array_foreach (out, index, obj) {
   2551     bool found = false;
   2552     const char *key;
   2553     const json_t *value;
   2554 
   2555     key = json_string_value (json_object_get (obj, "operation"));
   2556     value = json_object_get (obj, "arguments");
   2557     if (NULL == key)
   2558     {
   2559       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2560                   "Malformed JSON input\n");
   2561       global_ret = EXIT_FAILURE;
   2562       GNUNET_SCHEDULER_shutdown ();
   2563       return;
   2564     }
   2565     /* block of code that uses key and value */
   2566     for (unsigned int i = 0; NULL != uhs[i].key; i++)
   2567     {
   2568       if (0 == strcasecmp (key,
   2569                            uhs[i].key))
   2570       {
   2571 
   2572         found = true;
   2573         uhs[i].cb (exchange_url,
   2574                    index,
   2575                    value);
   2576         break;
   2577       }
   2578     }
   2579     if (! found)
   2580     {
   2581       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2582                   "Upload does not know how to handle `%s'\n",
   2583                   key);
   2584       global_ret = EXIT_FAILURE;
   2585       GNUNET_SCHEDULER_shutdown ();
   2586       return;
   2587     }
   2588   }
   2589 }
   2590 
   2591 
   2592 /**
   2593  * Upload operation result (signatures) to exchange.
   2594  *
   2595  * @param args the array of command-line arguments to process next
   2596  */
   2597 static void
   2598 do_upload (char *const *args)
   2599 {
   2600   (void) args;
   2601   if (NULL != in)
   2602   {
   2603     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2604                 "Downloaded data was not consumed, refusing upload\n");
   2605     GNUNET_SCHEDULER_shutdown ();
   2606     global_ret = EXIT_FAILURE;
   2607     return;
   2608   }
   2609   if (NULL == out)
   2610   {
   2611     json_error_t err;
   2612 
   2613     out = json_loadf (stdin,
   2614                       JSON_REJECT_DUPLICATES,
   2615                       &err);
   2616     if (NULL == out)
   2617     {
   2618       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2619                   "Failed to read JSON input: %s at %d:%s (offset: %d)\n",
   2620                   err.text,
   2621                   err.line,
   2622                   err.source,
   2623                   err.position);
   2624       GNUNET_SCHEDULER_shutdown ();
   2625       global_ret = EXIT_FAILURE;
   2626       return;
   2627     }
   2628   }
   2629   if (! json_is_array (out))
   2630   {
   2631     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2632                 "Error: expected JSON array for `upload` command\n");
   2633     GNUNET_SCHEDULER_shutdown ();
   2634     global_ret = EXIT_FAILURE;
   2635     return;
   2636   }
   2637   if ( (NULL == CFG_exchange_url) &&
   2638        (GNUNET_OK !=
   2639         GNUNET_CONFIGURATION_get_value_string (kcfg,
   2640                                                "exchange",
   2641                                                "BASE_URL",
   2642                                                &CFG_exchange_url)) )
   2643   {
   2644     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   2645                                "exchange",
   2646                                "BASE_URL");
   2647     global_ret = EXIT_NOTCONFIGURED;
   2648     GNUNET_SCHEDULER_shutdown ();
   2649     return;
   2650   }
   2651   trigger_upload (CFG_exchange_url);
   2652   json_decref (out);
   2653   out = NULL;
   2654 }
   2655 
   2656 
   2657 /**
   2658  * Revoke denomination key.
   2659  *
   2660  * @param args the array of command-line arguments to process next;
   2661  *        args[0] must be the hash of the denomination key to revoke
   2662  */
   2663 static void
   2664 do_revoke_denomination_key (char *const *args)
   2665 {
   2666   struct TALER_DenominationHashP h_denom_pub;
   2667   struct TALER_MasterSignatureP master_sig;
   2668 
   2669   if (NULL != in)
   2670   {
   2671     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2672                 "Downloaded data was not consumed, refusing revocation\n");
   2673     GNUNET_SCHEDULER_shutdown ();
   2674     global_ret = EXIT_FAILURE;
   2675     return;
   2676   }
   2677   if ( (NULL == args[0]) ||
   2678        (GNUNET_OK !=
   2679         GNUNET_STRINGS_string_to_data (args[0],
   2680                                        strlen (args[0]),
   2681                                        &h_denom_pub,
   2682                                        sizeof (h_denom_pub))) )
   2683   {
   2684     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2685                 "You must specify a denomination key with this subcommand\n");
   2686     GNUNET_SCHEDULER_shutdown ();
   2687     global_ret = EXIT_INVALIDARGUMENT;
   2688     return;
   2689   }
   2690   if (GNUNET_OK !=
   2691       load_offline_key (GNUNET_NO))
   2692     return;
   2693   TALER_exchange_offline_denomination_revoke_sign (&h_denom_pub,
   2694                                                    &master_priv,
   2695                                                    &master_sig);
   2696   output_operation (OP_REVOKE_DENOMINATION,
   2697                     GNUNET_JSON_PACK (
   2698                       GNUNET_JSON_pack_data_auto ("h_denom_pub",
   2699                                                   &h_denom_pub),
   2700                       GNUNET_JSON_pack_data_auto ("master_sig",
   2701                                                   &master_sig)));
   2702   next (args + 1);
   2703 }
   2704 
   2705 
   2706 /**
   2707  * Revoke signkey.
   2708  *
   2709  * @param args the array of command-line arguments to process next;
   2710  *        args[0] must be the hash of the denomination key to revoke
   2711  */
   2712 static void
   2713 do_revoke_signkey (char *const *args)
   2714 {
   2715   struct TALER_ExchangePublicKeyP exchange_pub;
   2716   struct TALER_MasterSignatureP master_sig;
   2717 
   2718   if (NULL != in)
   2719   {
   2720     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2721                 "Downloaded data was not consumed, refusing revocation\n");
   2722     GNUNET_SCHEDULER_shutdown ();
   2723     global_ret = EXIT_FAILURE;
   2724     return;
   2725   }
   2726   if ( (NULL == args[0]) ||
   2727        (GNUNET_OK !=
   2728         GNUNET_STRINGS_string_to_data (args[0],
   2729                                        strlen (args[0]),
   2730                                        &exchange_pub,
   2731                                        sizeof (exchange_pub))) )
   2732   {
   2733     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2734                 "You must specify an exchange signing key with this subcommand\n");
   2735     GNUNET_SCHEDULER_shutdown ();
   2736     global_ret = EXIT_INVALIDARGUMENT;
   2737     return;
   2738   }
   2739   if (GNUNET_OK !=
   2740       load_offline_key (GNUNET_NO))
   2741     return;
   2742   TALER_exchange_offline_signkey_revoke_sign (&exchange_pub,
   2743                                               &master_priv,
   2744                                               &master_sig);
   2745   output_operation (OP_REVOKE_SIGNKEY,
   2746                     GNUNET_JSON_PACK (
   2747                       GNUNET_JSON_pack_data_auto ("exchange_pub",
   2748                                                   &exchange_pub),
   2749                       GNUNET_JSON_pack_data_auto ("master_sig",
   2750                                                   &master_sig)));
   2751   next (args + 1);
   2752 }
   2753 
   2754 
   2755 /**
   2756  * Add auditor.
   2757  *
   2758  * @param args the array of command-line arguments to process next;
   2759  *        args[0] must be the auditor's public key, args[1] the auditor's
   2760  *        API base URL, and args[2] the auditor's name.
   2761  */
   2762 static void
   2763 do_add_auditor (char *const *args)
   2764 {
   2765   struct TALER_MasterSignatureP master_sig;
   2766   struct TALER_AuditorPublicKeyP auditor_pub;
   2767   struct GNUNET_TIME_Timestamp now;
   2768 
   2769   if (NULL != in)
   2770   {
   2771     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2772                 "Downloaded data was not consumed, not adding auditor\n");
   2773     GNUNET_SCHEDULER_shutdown ();
   2774     global_ret = EXIT_FAILURE;
   2775     return;
   2776   }
   2777   if ( (NULL == args[0]) ||
   2778        (GNUNET_OK !=
   2779         GNUNET_STRINGS_string_to_data (args[0],
   2780                                        strlen (args[0]),
   2781                                        &auditor_pub,
   2782                                        sizeof (auditor_pub))) )
   2783   {
   2784     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2785                 "You must specify an auditor public key as first argument for this subcommand\n");
   2786     GNUNET_SCHEDULER_shutdown ();
   2787     global_ret = EXIT_INVALIDARGUMENT;
   2788     return;
   2789   }
   2790 
   2791   if ( (NULL == args[1]) ||
   2792        (0 != strncmp ("http",
   2793                       args[1],
   2794                       strlen ("http"))) ||
   2795        (NULL == args[2]) )
   2796   {
   2797     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2798                 "You must specify an auditor URI and auditor name as 2nd and 3rd arguments to this subcommand\n");
   2799     GNUNET_SCHEDULER_shutdown ();
   2800     global_ret = EXIT_INVALIDARGUMENT;
   2801     return;
   2802   }
   2803   if (GNUNET_OK !=
   2804       load_offline_key (GNUNET_NO))
   2805     return;
   2806   now = GNUNET_TIME_timestamp_get ();
   2807   TALER_exchange_offline_auditor_add_sign (&auditor_pub,
   2808                                            args[1],
   2809                                            now,
   2810                                            &master_priv,
   2811                                            &master_sig);
   2812   output_operation (OP_ENABLE_AUDITOR,
   2813                     GNUNET_JSON_PACK (
   2814                       GNUNET_JSON_pack_string ("auditor_url",
   2815                                                args[1]),
   2816                       GNUNET_JSON_pack_string ("auditor_name",
   2817                                                args[2]),
   2818                       GNUNET_JSON_pack_timestamp ("validity_start",
   2819                                                   now),
   2820                       GNUNET_JSON_pack_data_auto ("auditor_pub",
   2821                                                   &auditor_pub),
   2822                       GNUNET_JSON_pack_data_auto ("master_sig",
   2823                                                   &master_sig)));
   2824   next (args + 3);
   2825 }
   2826 
   2827 
   2828 /**
   2829  * Disable auditor account.
   2830  *
   2831  * @param args the array of command-line arguments to process next;
   2832  *        args[0] must be the hash of the denomination key to revoke
   2833  */
   2834 static void
   2835 do_del_auditor (char *const *args)
   2836 {
   2837   struct TALER_MasterSignatureP master_sig;
   2838   struct TALER_AuditorPublicKeyP auditor_pub;
   2839   struct GNUNET_TIME_Timestamp now;
   2840 
   2841   if (NULL != in)
   2842   {
   2843     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2844                 "Downloaded data was not consumed, not deleting auditor account\n");
   2845     GNUNET_SCHEDULER_shutdown ();
   2846     global_ret = EXIT_FAILURE;
   2847     return;
   2848   }
   2849   if ( (NULL == args[0]) ||
   2850        (GNUNET_OK !=
   2851         GNUNET_STRINGS_string_to_data (args[0],
   2852                                        strlen (args[0]),
   2853                                        &auditor_pub,
   2854                                        sizeof (auditor_pub))) )
   2855   {
   2856     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2857                 "You must specify an auditor public key as first argument for this subcommand\n");
   2858     GNUNET_SCHEDULER_shutdown ();
   2859     global_ret = EXIT_INVALIDARGUMENT;
   2860     return;
   2861   }
   2862   if (GNUNET_OK !=
   2863       load_offline_key (GNUNET_NO))
   2864     return;
   2865   now = GNUNET_TIME_timestamp_get ();
   2866   TALER_exchange_offline_auditor_del_sign (&auditor_pub,
   2867                                            now,
   2868                                            &master_priv,
   2869                                            &master_sig);
   2870   output_operation (OP_DISABLE_AUDITOR,
   2871                     GNUNET_JSON_PACK (
   2872                       GNUNET_JSON_pack_data_auto ("auditor_pub",
   2873                                                   &auditor_pub),
   2874                       GNUNET_JSON_pack_timestamp ("validity_end",
   2875                                                   now),
   2876                       GNUNET_JSON_pack_data_auto ("master_sig",
   2877                                                   &master_sig)));
   2878   next (args + 1);
   2879 }
   2880 
   2881 
   2882 /**
   2883  * Parse account restriction.
   2884  *
   2885  * @param args the array of command-line arguments to process next
   2886  * @param[in,out] restrictions JSON array to update
   2887  * @return -1 on error, otherwise number of arguments from @a args that were used
   2888  */
   2889 static int
   2890 parse_restriction (char *const *args,
   2891                    json_t *restrictions)
   2892 {
   2893   if (NULL == args[0])
   2894   {
   2895     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2896                 "Restriction TYPE argument missing\n");
   2897     return -1;
   2898   }
   2899   if (0 == strcmp (args[0],
   2900                    "deny"))
   2901   {
   2902     GNUNET_assert (0 ==
   2903                    json_array_append_new (
   2904                      restrictions,
   2905                      GNUNET_JSON_PACK (
   2906                        GNUNET_JSON_pack_string ("type",
   2907                                                 "deny"))));
   2908     return 1;
   2909   }
   2910   if (0 == strcmp (args[0],
   2911                    "regex"))
   2912   {
   2913     json_t *i18n;
   2914     json_error_t err;
   2915 
   2916     if ( (NULL == args[1]) ||
   2917          (NULL == args[2]) ||
   2918          (NULL == args[3]) )
   2919     {
   2920       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2921                   "Mandatory arguments for restriction of type `regex' missing (REGEX, HINT, HINT-I18 required)\n");
   2922       return -1;
   2923     }
   2924     {
   2925       regex_t ex;
   2926 
   2927       if (0 != regcomp (&ex,
   2928                         args[1],
   2929                         REG_NOSUB | REG_EXTENDED))
   2930       {
   2931         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2932                     "Invalid regular expression `%s'\n",
   2933                     args[1]);
   2934         return -1;
   2935       }
   2936       regfree (&ex);
   2937     }
   2938 
   2939     i18n = json_loads (args[3],
   2940                        JSON_REJECT_DUPLICATES,
   2941                        &err);
   2942     if (NULL == i18n)
   2943     {
   2944       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2945                   "Invalid JSON for restriction of type `regex': `%s` at %d\n",
   2946                   args[3],
   2947                   err.position);
   2948       return -1;
   2949     }
   2950     GNUNET_assert (0 ==
   2951                    json_array_append_new (
   2952                      restrictions,
   2953                      GNUNET_JSON_PACK (
   2954                        GNUNET_JSON_pack_string ("type",
   2955                                                 "regex"),
   2956                        GNUNET_JSON_pack_string ("payto_regex",
   2957                                                 args[1]),
   2958                        GNUNET_JSON_pack_string ("human_hint",
   2959                                                 args[2]),
   2960                        GNUNET_JSON_pack_object_steal ("human_hint_i18n",
   2961                                                       i18n)
   2962                        )));
   2963     return 4;
   2964   }
   2965   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2966               "Restriction TYPE `%s' unsupported\n",
   2967               args[0]);
   2968   return -1;
   2969 }
   2970 
   2971 
   2972 /**
   2973  * Add wire account.
   2974  *
   2975  * @param args the array of command-line arguments to process next;
   2976  *        args[0] must be the hash of the denomination key to revoke
   2977  */
   2978 static void
   2979 do_add_wire (char *const *args)
   2980 {
   2981   struct TALER_MasterSignatureP master_sig_add;
   2982   struct TALER_MasterSignatureP master_sig_wire;
   2983   struct GNUNET_TIME_Timestamp now;
   2984   const char *conversion_url = NULL;
   2985   const char *open_banking_gateway = NULL;
   2986   const char *prepared_transfer_url = NULL;
   2987   const char *bank_label = NULL;
   2988   int64_t priority = 0;
   2989   json_t *debit_restrictions;
   2990   json_t *credit_restrictions;
   2991   unsigned int num_args = 1;
   2992   struct TALER_FullPayto payto_uri = {
   2993     .full_payto = args[0]
   2994   };
   2995 
   2996   if (NULL != in)
   2997   {
   2998     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2999                 "Downloaded data was not consumed, not adding wire account\n");
   3000     GNUNET_SCHEDULER_shutdown ();
   3001     global_ret = EXIT_FAILURE;
   3002     return;
   3003   }
   3004   if (NULL == args[0])
   3005   {
   3006     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3007                 "You must specify a payto://-URI with this subcommand\n");
   3008     GNUNET_SCHEDULER_shutdown ();
   3009     global_ret = EXIT_INVALIDARGUMENT;
   3010     return;
   3011   }
   3012   if (GNUNET_OK !=
   3013       load_offline_key (GNUNET_NO))
   3014     return;
   3015   {
   3016     char *msg = TALER_payto_validate (payto_uri);
   3017 
   3018     if (NULL != msg)
   3019     {
   3020       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3021                   "payto URI `%s' is malformed: %s\n",
   3022                   payto_uri.full_payto,
   3023                   msg);
   3024       GNUNET_free (msg);
   3025       GNUNET_SCHEDULER_shutdown ();
   3026       global_ret = EXIT_INVALIDARGUMENT;
   3027       return;
   3028     }
   3029   }
   3030   now = GNUNET_TIME_timestamp_get ();
   3031   {
   3032     char *wire_method;
   3033 
   3034     wire_method = TALER_payto_get_method (payto_uri.full_payto);
   3035     if (NULL == wire_method)
   3036     {
   3037       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3038                   "payto:// URI `%s' is malformed\n",
   3039                   payto_uri.full_payto);
   3040       global_ret = EXIT_INVALIDARGUMENT;
   3041       GNUNET_SCHEDULER_shutdown ();
   3042       return;
   3043     }
   3044     GNUNET_free (wire_method);
   3045   }
   3046   debit_restrictions = json_array ();
   3047   GNUNET_assert (NULL != debit_restrictions);
   3048   credit_restrictions = json_array ();
   3049   GNUNET_assert (NULL != credit_restrictions);
   3050   while (NULL != args[num_args])
   3051   {
   3052     if (0 == strcmp (args[num_args],
   3053                      "conversion-url"))
   3054     {
   3055       num_args++;
   3056       conversion_url = args[num_args];
   3057       if (NULL == conversion_url)
   3058       {
   3059         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3060                     "'conversion-url' requires an argument\n");
   3061         global_ret = EXIT_INVALIDARGUMENT;
   3062         GNUNET_SCHEDULER_shutdown ();
   3063         json_decref (debit_restrictions);
   3064         json_decref (credit_restrictions);
   3065         return;
   3066       }
   3067       if (! TALER_is_web_url (conversion_url))
   3068       {
   3069         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3070                     "'conversion-url' must refer to HTTP(S) endpoint, `%s' is invalid\n",
   3071                     conversion_url);
   3072         global_ret = EXIT_INVALIDARGUMENT;
   3073         GNUNET_SCHEDULER_shutdown ();
   3074         json_decref (debit_restrictions);
   3075         json_decref (credit_restrictions);
   3076         return;
   3077       }
   3078       num_args++;
   3079       continue;
   3080     }
   3081 
   3082     if (0 == strcmp (args[num_args],
   3083                      "open-banking-gateway"))
   3084     {
   3085       num_args++;
   3086       open_banking_gateway = args[num_args];
   3087       if (NULL == open_banking_gateway)
   3088       {
   3089         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3090                     "'open-banking-gateway' requires an argument\n");
   3091         global_ret = EXIT_INVALIDARGUMENT;
   3092         GNUNET_SCHEDULER_shutdown ();
   3093         json_decref (debit_restrictions);
   3094         json_decref (credit_restrictions);
   3095         return;
   3096       }
   3097       if (! TALER_is_web_url (open_banking_gateway))
   3098       {
   3099         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3100                     "'open-banking-gateway' must refer to HTTP(S) endpoint, `%s' is invalid\n",
   3101                     open_banking_gateway);
   3102         global_ret = EXIT_INVALIDARGUMENT;
   3103         GNUNET_SCHEDULER_shutdown ();
   3104         json_decref (debit_restrictions);
   3105         json_decref (credit_restrictions);
   3106         return;
   3107       }
   3108       num_args++;
   3109       continue;
   3110     }
   3111 
   3112     if (0 == strcmp (args[num_args],
   3113                      "prepared-transfer-url"))
   3114     {
   3115       num_args++;
   3116       prepared_transfer_url = args[num_args];
   3117       if (NULL == prepared_transfer_url)
   3118       {
   3119         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3120                     "'prepared-transfer-url' requires an argument\n");
   3121         global_ret = EXIT_INVALIDARGUMENT;
   3122         GNUNET_SCHEDULER_shutdown ();
   3123         json_decref (debit_restrictions);
   3124         json_decref (credit_restrictions);
   3125         return;
   3126       }
   3127       if (! TALER_is_web_url (prepared_transfer_url))
   3128       {
   3129         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3130                     "'prepared-transfer-url' must refer to HTTP(S) endpoint, `%s' is invalid\n",
   3131                     prepared_transfer_url);
   3132         global_ret = EXIT_INVALIDARGUMENT;
   3133         GNUNET_SCHEDULER_shutdown ();
   3134         json_decref (debit_restrictions);
   3135         json_decref (credit_restrictions);
   3136         return;
   3137       }
   3138       num_args++;
   3139       continue;
   3140     }
   3141 
   3142     if (0 == strcmp (args[num_args],
   3143                      "credit-restriction"))
   3144     {
   3145       int iret;
   3146 
   3147       num_args++;
   3148       iret = parse_restriction (&args[num_args],
   3149                                 credit_restrictions);
   3150       if (iret <= 0)
   3151       {
   3152         global_ret = EXIT_INVALIDARGUMENT;
   3153         GNUNET_SCHEDULER_shutdown ();
   3154         json_decref (debit_restrictions);
   3155         json_decref (credit_restrictions);
   3156         return;
   3157       }
   3158       num_args += iret;
   3159       continue;
   3160     }
   3161     if (0 == strcmp (args[num_args],
   3162                      "debit-restriction"))
   3163     {
   3164       int iret;
   3165 
   3166       num_args++;
   3167       iret = parse_restriction (&args[num_args],
   3168                                 debit_restrictions);
   3169       if (iret <= 0)
   3170       {
   3171         global_ret = EXIT_INVALIDARGUMENT;
   3172         GNUNET_SCHEDULER_shutdown ();
   3173         json_decref (debit_restrictions);
   3174         json_decref (credit_restrictions);
   3175         return;
   3176       }
   3177       num_args += iret;
   3178       continue;
   3179     }
   3180     if (0 == strcmp (args[num_args],
   3181                      "display-hint"))
   3182     {
   3183       long long p;
   3184       char dummy;
   3185 
   3186       num_args++;
   3187       if ( (NULL == args[num_args]) ||
   3188            (NULL == args[num_args + 1]) )
   3189       {
   3190         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3191                     "'display-hint' requires at least two arguments (priority and label)\n");
   3192         global_ret = EXIT_INVALIDARGUMENT;
   3193         GNUNET_SCHEDULER_shutdown ();
   3194         json_decref (debit_restrictions);
   3195         json_decref (credit_restrictions);
   3196         return;
   3197       }
   3198       if (1 != sscanf (args[num_args],
   3199                        "%lld%c",
   3200                        &p,
   3201                        &dummy))
   3202       {
   3203         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3204                     "Priority argument `%s' is not a number\n",
   3205                     args[num_args]);
   3206         global_ret = EXIT_INVALIDARGUMENT;
   3207         GNUNET_SCHEDULER_shutdown ();
   3208         json_decref (debit_restrictions);
   3209         json_decref (credit_restrictions);
   3210         return;
   3211       }
   3212       priority = (int64_t) p;
   3213       num_args++;
   3214       bank_label = args[num_args];
   3215       num_args++;
   3216       continue;
   3217     }
   3218     break;
   3219   }
   3220   TALER_exchange_offline_wire_add_sign (payto_uri,
   3221                                         conversion_url,
   3222                                         open_banking_gateway,
   3223                                         prepared_transfer_url,
   3224                                         debit_restrictions,
   3225                                         credit_restrictions,
   3226                                         now,
   3227                                         &master_priv,
   3228                                         &master_sig_add);
   3229   TALER_exchange_wire_signature_make (payto_uri,
   3230                                       conversion_url,
   3231                                       open_banking_gateway,
   3232                                       prepared_transfer_url,
   3233                                       debit_restrictions,
   3234                                       credit_restrictions,
   3235                                       &master_priv,
   3236                                       &master_sig_wire);
   3237   output_operation (OP_ENABLE_WIRE,
   3238                     GNUNET_JSON_PACK (
   3239                       TALER_JSON_pack_full_payto ("payto_uri",
   3240                                                   payto_uri),
   3241                       GNUNET_JSON_pack_array_steal ("debit_restrictions",
   3242                                                     debit_restrictions),
   3243                       GNUNET_JSON_pack_array_steal ("credit_restrictions",
   3244                                                     credit_restrictions),
   3245                       GNUNET_JSON_pack_allow_null (
   3246                         GNUNET_JSON_pack_string ("conversion_url",
   3247                                                  conversion_url)),
   3248                       GNUNET_JSON_pack_allow_null (
   3249                         GNUNET_JSON_pack_string ("open_banking_gateway",
   3250                                                  open_banking_gateway)),
   3251                       GNUNET_JSON_pack_allow_null (
   3252                         GNUNET_JSON_pack_string ("prepared_transfer_url",
   3253                                                  prepared_transfer_url)),
   3254                       GNUNET_JSON_pack_allow_null (
   3255                         GNUNET_JSON_pack_string ("bank_label",
   3256                                                  bank_label)),
   3257                       GNUNET_JSON_pack_int64 ("priority",
   3258                                               priority),
   3259                       GNUNET_JSON_pack_timestamp ("validity_start",
   3260                                                   now),
   3261                       GNUNET_JSON_pack_data_auto ("master_sig_add",
   3262                                                   &master_sig_add),
   3263                       GNUNET_JSON_pack_data_auto ("master_sig_wire",
   3264                                                   &master_sig_wire)));
   3265   next (args + num_args);
   3266 }
   3267 
   3268 
   3269 /**
   3270  * Disable wire account.
   3271  *
   3272  * @param args the array of command-line arguments to process next;
   3273  *        args[0] must be the payto URI of the account to disable
   3274  */
   3275 static void
   3276 do_del_wire (char *const *args)
   3277 {
   3278   struct TALER_MasterSignatureP master_sig;
   3279   struct GNUNET_TIME_Timestamp now;
   3280   struct TALER_FullPayto payto_uri = {
   3281     .full_payto = args[0]
   3282   };
   3283 
   3284   if (NULL != in)
   3285   {
   3286     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3287                 "Downloaded data was not consumed, not deleting wire account\n")
   3288     ;
   3289     GNUNET_SCHEDULER_shutdown ();
   3290     global_ret = EXIT_FAILURE;
   3291     return;
   3292   }
   3293   if (NULL == args[0])
   3294   {
   3295     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3296                 "You must specify a payto://-URI with this subcommand\n");
   3297     GNUNET_SCHEDULER_shutdown ();
   3298     global_ret = EXIT_INVALIDARGUMENT;
   3299     return;
   3300   }
   3301   if (GNUNET_OK !=
   3302       load_offline_key (GNUNET_NO))
   3303     return;
   3304   now = GNUNET_TIME_timestamp_get ();
   3305   TALER_exchange_offline_wire_del_sign (payto_uri,
   3306                                         now,
   3307                                         &master_priv,
   3308                                         &master_sig);
   3309   output_operation (OP_DISABLE_WIRE,
   3310                     GNUNET_JSON_PACK (
   3311                       TALER_JSON_pack_full_payto ("payto_uri",
   3312                                                   payto_uri),
   3313                       GNUNET_JSON_pack_timestamp ("validity_end",
   3314                                                   now),
   3315                       GNUNET_JSON_pack_data_auto ("master_sig",
   3316                                                   &master_sig)));
   3317   next (args + 1);
   3318 }
   3319 
   3320 
   3321 /**
   3322  * Set wire fees for the given year.
   3323  *
   3324  * @param args the array of command-line arguments to process next;
   3325  *        args[0] must be the year, args[1] the wire method, args[2] the wire fee and args[3]
   3326  *        the closing fee.
   3327  */
   3328 static void
   3329 do_set_wire_fee (char *const *args)
   3330 {
   3331   struct TALER_MasterSignatureP master_sig;
   3332   char dummy;
   3333   unsigned int year;
   3334   struct TALER_WireFeeSet fees;
   3335   struct GNUNET_TIME_Timestamp start_time;
   3336   struct GNUNET_TIME_Timestamp end_time;
   3337 
   3338   if (NULL != in)
   3339   {
   3340     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3341                 "Downloaded data was not consumed, not setting wire fee\n");
   3342     GNUNET_SCHEDULER_shutdown ();
   3343     global_ret = EXIT_FAILURE;
   3344     return;
   3345   }
   3346   if ( (NULL == args[0]) ||
   3347        (NULL == args[1]) ||
   3348        (NULL == args[2]) ||
   3349        (NULL == args[3]) ||
   3350        ( (1 != sscanf (args[0],
   3351                        "%u%c",
   3352                        &year,
   3353                        &dummy)) &&
   3354          (0 != strcasecmp ("now",
   3355                            args[0])) ) ||
   3356        (GNUNET_OK !=
   3357         TALER_string_to_amount (args[2],
   3358                                 &fees.wire)) ||
   3359        (GNUNET_OK !=
   3360         TALER_string_to_amount (args[3],
   3361                                 &fees.closing)) )
   3362   {
   3363     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3364                 "You must use YEAR, METHOD, WIRE-FEE, and CLOSING-FEE as arguments for this subcommand\n");
   3365     GNUNET_SCHEDULER_shutdown ();
   3366     global_ret = EXIT_INVALIDARGUMENT;
   3367     return;
   3368   }
   3369   if (0 == strcasecmp ("now",
   3370                        args[0]))
   3371     year = GNUNET_TIME_get_current_year ();
   3372   if (GNUNET_OK !=
   3373       load_offline_key (GNUNET_NO))
   3374     return;
   3375   start_time = GNUNET_TIME_absolute_to_timestamp (
   3376     GNUNET_TIME_year_to_time (year));
   3377   end_time = GNUNET_TIME_absolute_to_timestamp (
   3378     GNUNET_TIME_year_to_time (year + 1));
   3379 
   3380   TALER_exchange_offline_wire_fee_sign (args[1],
   3381                                         start_time,
   3382                                         end_time,
   3383                                         &fees,
   3384                                         &master_priv,
   3385                                         &master_sig);
   3386   output_operation (OP_SET_WIRE_FEE,
   3387                     GNUNET_JSON_PACK (
   3388                       GNUNET_JSON_pack_string ("wire_method",
   3389                                                args[1]),
   3390                       GNUNET_JSON_pack_timestamp ("start_time",
   3391                                                   start_time),
   3392                       GNUNET_JSON_pack_timestamp ("end_time",
   3393                                                   end_time),
   3394                       TALER_JSON_pack_amount ("wire_fee",
   3395                                               &fees.wire),
   3396                       TALER_JSON_pack_amount ("closing_fee",
   3397                                               &fees.closing),
   3398                       GNUNET_JSON_pack_data_auto ("master_sig",
   3399                                                   &master_sig)));
   3400   next (args + 4);
   3401 }
   3402 
   3403 
   3404 /**
   3405  * Set global fees for the given year.
   3406  *
   3407  * @param args the array of command-line arguments to process next;
   3408  *        args[0] must be the year, args[1] the history fee, args[2]
   3409  *        the account fee and args[3] the purse fee. These are followed by args[4] purse timeout,
   3410  *        args[5] history expiration. Last is args[6] the (free) purse account limit.
   3411  */
   3412 static void
   3413 do_set_global_fee (char *const *args)
   3414 {
   3415   struct TALER_MasterSignatureP master_sig;
   3416   char dummy;
   3417   unsigned int year;
   3418   struct TALER_GlobalFeeSet fees;
   3419   struct GNUNET_TIME_Relative purse_timeout;
   3420   struct GNUNET_TIME_Relative history_expiration;
   3421   unsigned int purse_account_limit;
   3422   struct GNUNET_TIME_Timestamp start_time;
   3423   struct GNUNET_TIME_Timestamp end_time;
   3424 
   3425   if (NULL != in)
   3426   {
   3427     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3428                 "Downloaded data was not consumed, not setting global fee\n");
   3429     GNUNET_SCHEDULER_shutdown ();
   3430     global_ret = EXIT_FAILURE;
   3431     return;
   3432   }
   3433   if ( (NULL == args[0]) ||
   3434        (NULL == args[1]) ||
   3435        (NULL == args[2]) ||
   3436        (NULL == args[3]) ||
   3437        (NULL == args[4]) ||
   3438        (NULL == args[5]) ||
   3439        (NULL == args[6]) )
   3440   {
   3441     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3442                 "You must use YEAR, HISTORY-FEE, ACCOUNT-FEE, PURSE-FEE, PURSE-TIMEOUT, HISTORY-EXPIRATION and PURSE-ACCOUNT-LIMIT as arguments for this subcommand\n");
   3443     GNUNET_SCHEDULER_shutdown ();
   3444     global_ret = EXIT_INVALIDARGUMENT;
   3445     return;
   3446   }
   3447   if ( (1 != sscanf (args[0],
   3448                      "%u%c",
   3449                      &year,
   3450                      &dummy)) &&
   3451        (0 != strcasecmp ("now",
   3452                          args[0])) )
   3453   {
   3454     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3455                 "Invalid YEAR given for 'global-fee' subcommand\n");
   3456     GNUNET_SCHEDULER_shutdown ();
   3457     global_ret = EXIT_INVALIDARGUMENT;
   3458     return;
   3459   }
   3460   if ( (GNUNET_OK !=
   3461         TALER_string_to_amount (args[1],
   3462                                 &fees.history)) ||
   3463        (GNUNET_OK !=
   3464         TALER_string_to_amount (args[2],
   3465                                 &fees.account)) ||
   3466        (GNUNET_OK !=
   3467         TALER_string_to_amount (args[3],
   3468                                 &fees.purse)) )
   3469   {
   3470     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3471                 "Invalid amount given for 'global-fee' subcommand\n");
   3472     GNUNET_SCHEDULER_shutdown ();
   3473     global_ret = EXIT_INVALIDARGUMENT;
   3474     return;
   3475   }
   3476   if (GNUNET_OK !=
   3477       GNUNET_STRINGS_fancy_time_to_relative (args[4],
   3478                                              &purse_timeout))
   3479   {
   3480     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3481                 "Invalid purse timeout `%s' given for 'global-fee' subcommand\n",
   3482                 args[4]);
   3483     GNUNET_SCHEDULER_shutdown ();
   3484     global_ret = EXIT_INVALIDARGUMENT;
   3485     return;
   3486   }
   3487   if (GNUNET_OK !=
   3488       GNUNET_STRINGS_fancy_time_to_relative (args[5],
   3489                                              &history_expiration))
   3490   {
   3491     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3492                 "Invalid history expiratoin `%s' given for 'global-fee' subcommand\n",
   3493                 args[5]);
   3494     GNUNET_SCHEDULER_shutdown ();
   3495     global_ret = EXIT_INVALIDARGUMENT;
   3496     return;
   3497   }
   3498   if (1 != sscanf (args[6],
   3499                    "%u%c",
   3500                    &purse_account_limit,
   3501                    &dummy))
   3502   {
   3503     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3504                 "Invalid purse account limit given for 'global-fee' subcommand\n");
   3505     GNUNET_SCHEDULER_shutdown ();
   3506     global_ret = EXIT_INVALIDARGUMENT;
   3507     return;
   3508   }
   3509   if (0 == strcasecmp ("now",
   3510                        args[0]))
   3511     year = GNUNET_TIME_get_current_year ();
   3512   if (GNUNET_OK !=
   3513       load_offline_key (GNUNET_NO))
   3514     return;
   3515   start_time = GNUNET_TIME_absolute_to_timestamp (
   3516     GNUNET_TIME_year_to_time (year));
   3517   end_time = GNUNET_TIME_absolute_to_timestamp (
   3518     GNUNET_TIME_year_to_time (year + 1));
   3519 
   3520   TALER_exchange_offline_global_fee_sign (start_time,
   3521                                           end_time,
   3522                                           &fees,
   3523                                           purse_timeout,
   3524                                           history_expiration,
   3525                                           (uint32_t) purse_account_limit,
   3526                                           &master_priv,
   3527                                           &master_sig);
   3528   output_operation (OP_SET_GLOBAL_FEE,
   3529                     GNUNET_JSON_PACK (
   3530                       GNUNET_JSON_pack_timestamp ("start_time",
   3531                                                   start_time),
   3532                       GNUNET_JSON_pack_timestamp ("end_time",
   3533                                                   end_time),
   3534                       TALER_JSON_pack_amount ("history_fee",
   3535                                               &fees.history),
   3536                       TALER_JSON_pack_amount ("account_fee",
   3537                                               &fees.account),
   3538                       TALER_JSON_pack_amount ("purse_fee",
   3539                                               &fees.purse),
   3540                       GNUNET_JSON_pack_time_rel ("purse_timeout",
   3541                                                  purse_timeout),
   3542                       GNUNET_JSON_pack_time_rel ("history_expiration",
   3543                                                  history_expiration),
   3544                       GNUNET_JSON_pack_uint64 ("purse_account_limit",
   3545                                                (uint32_t) purse_account_limit),
   3546                       GNUNET_JSON_pack_data_auto ("master_sig",
   3547                                                   &master_sig)));
   3548   next (args + 7);
   3549 }
   3550 
   3551 
   3552 /**
   3553  * Drain profits from exchange's escrow account to
   3554  * regular exchange account.
   3555  *
   3556  * @param args the array of command-line arguments to process next;
   3557  *        args[0] must be the amount,
   3558  *        args[1] must be the section of the escrow account to drain
   3559  *        args[2] must be the payto://-URI of the target account
   3560  */
   3561 static void
   3562 do_drain (char *const *args)
   3563 {
   3564   struct TALER_WireTransferIdentifierRawP wtid;
   3565   struct GNUNET_TIME_Timestamp date;
   3566   struct TALER_Amount amount;
   3567   const char *account_section;
   3568   struct TALER_FullPayto payto_uri;
   3569   struct TALER_MasterSignatureP master_sig;
   3570   char *err;
   3571 
   3572   if (NULL != in)
   3573   {
   3574     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3575                 "Downloaded data was not consumed, refusing drain\n");
   3576     GNUNET_SCHEDULER_shutdown ();
   3577     global_ret = EXIT_FAILURE;
   3578     return;
   3579   }
   3580   if ( (NULL == args[0]) ||
   3581        (NULL == args[1]) ||
   3582        (NULL == args[2]) )
   3583   {
   3584     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3585                 "Drain requires an amount, section name and target payto://-URI as arguments\n");
   3586     GNUNET_SCHEDULER_shutdown ();
   3587     global_ret = EXIT_INVALIDARGUMENT;
   3588     return;
   3589   }
   3590   if (GNUNET_OK !=
   3591       TALER_string_to_amount (args[0],
   3592                               &amount))
   3593   {
   3594     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3595                 "Invalid amount `%s' specified for drain\n",
   3596                 args[0]);
   3597     GNUNET_SCHEDULER_shutdown ();
   3598     global_ret = EXIT_INVALIDARGUMENT;
   3599     return;
   3600   }
   3601   account_section = args[1];
   3602   payto_uri.full_payto = args[2];
   3603   err = TALER_payto_validate (payto_uri);
   3604   if (NULL != err)
   3605   {
   3606     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3607                 "Invalid payto://-URI `%s' specified for drain: %s\n",
   3608                 payto_uri.full_payto,
   3609                 err);
   3610     GNUNET_free (err);
   3611     GNUNET_SCHEDULER_shutdown ();
   3612     global_ret = EXIT_INVALIDARGUMENT;
   3613     return;
   3614   }
   3615   if (GNUNET_OK !=
   3616       load_offline_key (GNUNET_NO))
   3617     return;
   3618   GNUNET_CRYPTO_random_block (&wtid,
   3619                               sizeof (wtid));
   3620   date = GNUNET_TIME_timestamp_get ();
   3621   TALER_exchange_offline_profit_drain_sign (&wtid,
   3622                                             date,
   3623                                             &amount,
   3624                                             account_section,
   3625                                             payto_uri,
   3626                                             &master_priv,
   3627                                             &master_sig);
   3628   output_operation (OP_DRAIN_PROFITS,
   3629                     GNUNET_JSON_PACK (
   3630                       GNUNET_JSON_pack_data_auto ("wtid",
   3631                                                   &wtid),
   3632                       GNUNET_JSON_pack_string ("account_section",
   3633                                                account_section),
   3634                       TALER_JSON_pack_full_payto ("payto_uri",
   3635                                                   payto_uri),
   3636                       TALER_JSON_pack_amount ("amount",
   3637                                               &amount),
   3638                       GNUNET_JSON_pack_timestamp ("date",
   3639                                                   date),
   3640                       GNUNET_JSON_pack_data_auto ("master_sig",
   3641                                                   &master_sig)));
   3642   next (args + 3);
   3643 }
   3644 
   3645 
   3646 /**
   3647  * Add partner.
   3648  *
   3649  * @param args the array of command-line arguments to process next;
   3650  *        args[0] must be the partner's master public key, args[1] the partner's
   3651  *        API base URL, args[2] the wad fee, args[3] the wad frequency, and
   3652  *        args[4] the year (including possibly 'now')
   3653  */
   3654 static void
   3655 do_add_partner (char *const *args)
   3656 {
   3657   struct TALER_MasterPublicKeyP partner_pub;
   3658   struct GNUNET_TIME_Timestamp start_date;
   3659   struct GNUNET_TIME_Timestamp end_date;
   3660   struct GNUNET_TIME_Relative wad_frequency;
   3661   struct TALER_Amount wad_fee;
   3662   const char *partner_base_url;
   3663   struct TALER_MasterSignatureP master_sig;
   3664   char dummy;
   3665   unsigned int year;
   3666 
   3667   if (NULL != in)
   3668   {
   3669     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3670                 "Downloaded data was not consumed, not adding partner\n");
   3671     GNUNET_SCHEDULER_shutdown ();
   3672     global_ret = EXIT_FAILURE;
   3673     return;
   3674   }
   3675   if ( (NULL == args[0]) ||
   3676        (GNUNET_OK !=
   3677         GNUNET_STRINGS_string_to_data (args[0],
   3678                                        strlen (args[0]),
   3679                                        &partner_pub,
   3680                                        sizeof (partner_pub))) )
   3681   {
   3682     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3683                 "You must specify the partner master public key as first argument for this subcommand\n");
   3684     GNUNET_SCHEDULER_shutdown ();
   3685     global_ret = EXIT_INVALIDARGUMENT;
   3686     return;
   3687   }
   3688   if ( (NULL == args[1]) ||
   3689        (0 != strncmp ("http",
   3690                       args[1],
   3691                       strlen ("http"))) )
   3692   {
   3693     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3694                 "You must specify the partner's base URL as the 2nd argument to this subcommand\n");
   3695     GNUNET_SCHEDULER_shutdown ();
   3696     global_ret = EXIT_INVALIDARGUMENT;
   3697     return;
   3698   }
   3699   partner_base_url = args[1];
   3700   if ( (NULL == args[2]) ||
   3701        (GNUNET_OK !=
   3702         TALER_string_to_amount (args[2],
   3703                                 &wad_fee)) )
   3704   {
   3705     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3706                 "Invalid amount `%s' specified for wad fee of partner\n",
   3707                 args[2]);
   3708     GNUNET_SCHEDULER_shutdown ();
   3709     global_ret = EXIT_INVALIDARGUMENT;
   3710     return;
   3711   }
   3712   if ( (NULL == args[3]) ||
   3713        (GNUNET_OK !=
   3714         GNUNET_STRINGS_fancy_time_to_relative (args[3],
   3715                                                &wad_frequency)) )
   3716   {
   3717     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3718                 "Invalid wad frequency `%s' specified for add partner\n",
   3719                 args[3]);
   3720     GNUNET_SCHEDULER_shutdown ();
   3721     global_ret = EXIT_INVALIDARGUMENT;
   3722     return;
   3723   }
   3724   if ( (NULL == args[4]) ||
   3725        ( (1 != sscanf (args[4],
   3726                        "%u%c",
   3727                        &year,
   3728                        &dummy)) &&
   3729          (0 != strcasecmp ("now",
   3730                            args[4])) ) )
   3731   {
   3732     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3733                 "Invalid year `%s' specified for add partner\n",
   3734                 args[4]);
   3735     GNUNET_SCHEDULER_shutdown ();
   3736     global_ret = EXIT_INVALIDARGUMENT;
   3737     return;
   3738   }
   3739   if (0 == strcasecmp ("now",
   3740                        args[4]))
   3741     year = GNUNET_TIME_get_current_year ();
   3742   start_date = GNUNET_TIME_absolute_to_timestamp (
   3743     GNUNET_TIME_year_to_time (year));
   3744   end_date = GNUNET_TIME_absolute_to_timestamp (
   3745     GNUNET_TIME_year_to_time (year + 1));
   3746 
   3747   if (GNUNET_OK !=
   3748       load_offline_key (GNUNET_NO))
   3749     return;
   3750   TALER_exchange_offline_partner_details_sign (&partner_pub,
   3751                                                start_date,
   3752                                                end_date,
   3753                                                wad_frequency,
   3754                                                &wad_fee,
   3755                                                partner_base_url,
   3756                                                &master_priv,
   3757                                                &master_sig);
   3758   output_operation (OP_ADD_PARTNER,
   3759                     GNUNET_JSON_PACK (
   3760                       GNUNET_JSON_pack_string ("partner_base_url",
   3761                                                partner_base_url),
   3762                       GNUNET_JSON_pack_time_rel ("wad_frequency",
   3763                                                  wad_frequency),
   3764                       GNUNET_JSON_pack_timestamp ("start_date",
   3765                                                   start_date),
   3766                       GNUNET_JSON_pack_timestamp ("end_date",
   3767                                                   end_date),
   3768                       GNUNET_JSON_pack_data_auto ("partner_pub",
   3769                                                   &partner_pub),
   3770                       GNUNET_JSON_pack_data_auto ("master_sig",
   3771                                                   &master_sig)));
   3772   next (args + 5);
   3773 }
   3774 
   3775 
   3776 /**
   3777  * Enable or disable AML staff.
   3778  *
   3779  * @param is_active true to enable, false to disable
   3780  * @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)
   3781  */
   3782 static void
   3783 do_set_aml_staff (bool is_active,
   3784                   char *const *args)
   3785 {
   3786   struct TALER_AmlOfficerPublicKeyP officer_pub;
   3787   const char *officer_name;
   3788   bool read_only;
   3789   struct TALER_MasterSignatureP master_sig;
   3790   struct GNUNET_TIME_Timestamp now
   3791     = GNUNET_TIME_timestamp_get ();
   3792 
   3793   if (NULL != in)
   3794   {
   3795     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3796                 "Downloaded data was not consumed, not updating AML staff status\n");
   3797     GNUNET_SCHEDULER_shutdown ();
   3798     global_ret = EXIT_FAILURE;
   3799     return;
   3800   }
   3801   if ( (NULL == args[0]) ||
   3802        (GNUNET_OK !=
   3803         GNUNET_STRINGS_string_to_data (args[0],
   3804                                        strlen (args[0]),
   3805                                        &officer_pub,
   3806                                        sizeof (officer_pub))) )
   3807   {
   3808     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3809                 "You must specify the AML officer's public key as first argument for this subcommand\n");
   3810     GNUNET_SCHEDULER_shutdown ();
   3811     global_ret = EXIT_INVALIDARGUMENT;
   3812     return;
   3813   }
   3814   if (NULL == args[1])
   3815   {
   3816     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3817                 "You must specify the officer's legal name as the 2nd argument to this subcommand\n");
   3818     GNUNET_SCHEDULER_shutdown ();
   3819     global_ret = EXIT_INVALIDARGUMENT;
   3820     return;
   3821   }
   3822   officer_name = args[1];
   3823   if (is_active)
   3824   {
   3825     if ( (NULL == args[2]) ||
   3826          ( (0 != strcmp (args[2],
   3827                          "ro")) &&
   3828            (0 != strcmp (args[2],
   3829                          "rw")) ) )
   3830     {
   3831       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3832                   "You must specify 'ro' or 'rw' (and not `%s') for the access level\n",
   3833                   args[2]);
   3834       GNUNET_SCHEDULER_shutdown ();
   3835       global_ret = EXIT_INVALIDARGUMENT;
   3836       return;
   3837     }
   3838     read_only = (0 == strcmp (args[2],
   3839                               "ro"));
   3840   }
   3841   else
   3842   {
   3843     read_only = true;
   3844   }
   3845   if (GNUNET_OK !=
   3846       load_offline_key (GNUNET_NO))
   3847     return;
   3848   TALER_exchange_offline_aml_officer_status_sign (&officer_pub,
   3849                                                   officer_name,
   3850                                                   now,
   3851                                                   is_active,
   3852                                                   read_only,
   3853                                                   &master_priv,
   3854                                                   &master_sig);
   3855   output_operation (OP_UPDATE_AML_STAFF,
   3856                     GNUNET_JSON_PACK (
   3857                       GNUNET_JSON_pack_string ("officer_name",
   3858                                                officer_name),
   3859                       GNUNET_JSON_pack_timestamp ("change_date",
   3860                                                   now),
   3861                       GNUNET_JSON_pack_bool ("is_active",
   3862                                              is_active),
   3863                       GNUNET_JSON_pack_bool ("read_only",
   3864                                              read_only),
   3865                       GNUNET_JSON_pack_data_auto ("officer_pub",
   3866                                                   &officer_pub),
   3867                       GNUNET_JSON_pack_data_auto ("master_sig",
   3868                                                   &master_sig)));
   3869   next (args + (is_active ? 3 : 2));
   3870 }
   3871 
   3872 
   3873 /**
   3874  * Disable AML staff.
   3875  *
   3876  * @param args the array of command-line arguments to process next;
   3877  *        args[0] must be the AML staff's public key, args[1] the AML staff's legal name
   3878  */
   3879 static void
   3880 disable_aml_staff (char *const *args)
   3881 {
   3882   do_set_aml_staff (false,
   3883                     args);
   3884 }
   3885 
   3886 
   3887 /**
   3888  * Enable AML staff.
   3889  *
   3890  * @param args the array of command-line arguments to process next;
   3891  *        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)
   3892  */
   3893 static void
   3894 enable_aml_staff (char *const *args)
   3895 {
   3896   do_set_aml_staff (true,
   3897                     args);
   3898 }
   3899 
   3900 
   3901 /**
   3902  * Function called with information about future keys.  Dumps the JSON output
   3903  * (on success), either into an internal buffer or to stdout (depending on
   3904  * whether there are subsequent commands).
   3905  *
   3906  * @param args the remaining args
   3907  * @param mgr response data
   3908  */
   3909 static void
   3910 download_cb (char *const *args,
   3911              const struct TALER_EXCHANGE_GetManagementKeysResponse *mgr)
   3912 {
   3913   const struct TALER_EXCHANGE_HttpResponse *hr = &mgr->hr;
   3914 
   3915   mgkh = NULL;
   3916   switch (hr->http_status)
   3917   {
   3918   case MHD_HTTP_OK:
   3919     break;
   3920   default:
   3921     if (0 != hr->http_status)
   3922       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3923                   "Failed to download keys from `%s': %s (HTTP status: %u/%u)\n",
   3924                   CFG_exchange_url,
   3925                   hr->hint,
   3926                   hr->http_status,
   3927                   (unsigned int) hr->ec);
   3928     else
   3929       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3930                   "Failed to download keys from `%s' (no HTTP response)\n",
   3931                   CFG_exchange_url);
   3932     GNUNET_SCHEDULER_shutdown ();
   3933     global_ret = EXIT_FAILURE;
   3934     return;
   3935   }
   3936   in = GNUNET_JSON_PACK (
   3937     GNUNET_JSON_pack_string ("operation",
   3938                              OP_INPUT_KEYS),
   3939     GNUNET_JSON_pack_object_incref ("arguments",
   3940                                     (json_t *) hr->reply));
   3941   if (NULL == args[0])
   3942   {
   3943     json_dumpf (in,
   3944                 stdout,
   3945                 JSON_INDENT (2));
   3946     json_decref (in);
   3947     in = NULL;
   3948   }
   3949   next (args);
   3950 }
   3951 
   3952 
   3953 /**
   3954  * Download future keys.
   3955  *
   3956  * @param args the array of command-line arguments to process next
   3957  */
   3958 static void
   3959 do_download (char *const *args)
   3960 {
   3961   if ( (NULL == CFG_exchange_url) &&
   3962        (GNUNET_OK !=
   3963         GNUNET_CONFIGURATION_get_value_string (kcfg,
   3964                                                "exchange",
   3965                                                "BASE_URL",
   3966                                                &CFG_exchange_url)) )
   3967   {
   3968     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   3969                                "exchange",
   3970                                "BASE_URL");
   3971     GNUNET_SCHEDULER_shutdown ();
   3972     global_ret = EXIT_NOTCONFIGURED;
   3973     return;
   3974   }
   3975   mgkh = TALER_EXCHANGE_get_management_keys_create (ctx,
   3976                                                     CFG_exchange_url);
   3977   TALER_EXCHANGE_get_management_keys_start (mgkh,
   3978                                             &download_cb,
   3979                                             (void *) args);
   3980 }
   3981 
   3982 
   3983 /**
   3984  * Check that the security module keys are the same as before.  If we had no
   3985  * keys in store before, remember them (Trust On First Use).
   3986  *
   3987  * @param secmset security module keys
   3988  * @return #GNUNET_OK if keys match with what we have in store
   3989  *         #GNUNET_NO if we had nothing in store but now do
   3990  *         #GNUNET_SYSERR if keys changed from what we remember or other error
   3991  */
   3992 static enum GNUNET_GenericReturnValue
   3993 tofu_check (const struct TALER_SecurityModulePublicKeySetP *secmset)
   3994 {
   3995   char *fn;
   3996   struct TALER_SecurityModulePublicKeySetP oldset;
   3997   ssize_t ret;
   3998 
   3999   if (GNUNET_OK !=
   4000       GNUNET_CONFIGURATION_get_value_filename (kcfg,
   4001                                                "exchange-offline",
   4002                                                "SECM_TOFU_FILE",
   4003                                                &fn))
   4004   {
   4005     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   4006                                "exchange-offline",
   4007                                "SECM_TOFU_FILE");
   4008     return GNUNET_SYSERR;
   4009   }
   4010   if (GNUNET_OK ==
   4011       GNUNET_DISK_file_test (fn))
   4012   {
   4013     ret = GNUNET_DISK_fn_read (fn,
   4014                                &oldset,
   4015                                sizeof (oldset));
   4016     if (GNUNET_SYSERR != ret)
   4017     {
   4018       if (ret != sizeof (oldset))
   4019       {
   4020         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4021                     "File `%s' corrupt\n",
   4022                     fn);
   4023         GNUNET_free (fn);
   4024         return GNUNET_SYSERR;
   4025       }
   4026       /* TOFU check */
   4027       if (0 != memcmp (&oldset,
   4028                        secmset,
   4029                        sizeof (*secmset)))
   4030       {
   4031         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4032                     "Fatal: security module keys changed (file `%s')!\n",
   4033                     fn);
   4034         GNUNET_free (fn);
   4035         return GNUNET_SYSERR;
   4036       }
   4037       GNUNET_free (fn);
   4038       return GNUNET_OK;
   4039     }
   4040   }
   4041 
   4042   {
   4043     char *key;
   4044 
   4045     /* check against SECMOD-keys pinned in configuration */
   4046     if (GNUNET_OK ==
   4047         GNUNET_CONFIGURATION_get_value_string (kcfg,
   4048                                                "exchange-offline",
   4049                                                "SECM_ESIGN_PUBKEY",
   4050                                                &key))
   4051     {
   4052       struct TALER_SecurityModulePublicKeyP k;
   4053 
   4054       if (GNUNET_OK !=
   4055           GNUNET_STRINGS_string_to_data (key,
   4056                                          strlen (key),
   4057                                          &k,
   4058                                          sizeof (k)))
   4059       {
   4060         GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
   4061                                    "exchange-offline",
   4062                                    "SECM_ESIGN_PUBKEY",
   4063                                    "key malformed");
   4064         GNUNET_free (key);
   4065         GNUNET_free (fn);
   4066         return GNUNET_SYSERR;
   4067       }
   4068       GNUNET_free (key);
   4069       if (0 !=
   4070           GNUNET_memcmp (&k,
   4071                          &secmset->eddsa))
   4072       {
   4073         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4074                     "ESIGN security module key does not match SECM_ESIGN_PUBKEY in configuration\n")
   4075         ;
   4076         GNUNET_free (fn);
   4077         return GNUNET_SYSERR;
   4078       }
   4079     }
   4080     if (GNUNET_OK ==
   4081         GNUNET_CONFIGURATION_get_value_string (kcfg,
   4082                                                "exchange-offline",
   4083                                                "SECM_DENOM_PUBKEY",
   4084                                                &key))
   4085     {
   4086       struct TALER_SecurityModulePublicKeyP k;
   4087 
   4088       if (GNUNET_OK !=
   4089           GNUNET_STRINGS_string_to_data (key,
   4090                                          strlen (key),
   4091                                          &k,
   4092                                          sizeof (k)))
   4093       {
   4094         GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
   4095                                    "exchange-offline",
   4096                                    "SECM_DENOM_PUBKEY",
   4097                                    "key malformed");
   4098         GNUNET_free (key);
   4099         GNUNET_free (fn);
   4100         return GNUNET_SYSERR;
   4101       }
   4102       GNUNET_free (key);
   4103       if (0 !=
   4104           GNUNET_memcmp (&k,
   4105                          &secmset->rsa))
   4106       {
   4107         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4108                     "DENOM security module key does not match SECM_DENOM_PUBKEY in configuration\n")
   4109         ;
   4110         GNUNET_free (fn);
   4111         return GNUNET_SYSERR;
   4112       }
   4113     }
   4114     if (GNUNET_OK ==
   4115         GNUNET_CONFIGURATION_get_value_string (kcfg,
   4116                                                "exchange-offline",
   4117                                                "SECM_DENOM_CS_PUBKEY",
   4118                                                &key))
   4119     {
   4120       struct TALER_SecurityModulePublicKeyP k;
   4121 
   4122       if (GNUNET_OK !=
   4123           GNUNET_STRINGS_string_to_data (key,
   4124                                          strlen (key),
   4125                                          &k,
   4126                                          sizeof (k)))
   4127       {
   4128         GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
   4129                                    "exchange-offline",
   4130                                    "SECM_DENOM_CS_PUBKEY",
   4131                                    "key malformed");
   4132         GNUNET_free (key);
   4133         GNUNET_free (fn);
   4134         return GNUNET_SYSERR;
   4135       }
   4136       GNUNET_free (key);
   4137       if (0 !=
   4138           GNUNET_memcmp (&k,
   4139                          &secmset->cs))
   4140       {
   4141         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4142                     "DENOM security module key does not match SECM_DENOM_CS_PUBKEY in configuration\n");
   4143         GNUNET_free (fn);
   4144         return GNUNET_SYSERR;
   4145       }
   4146     }
   4147   }
   4148   if (GNUNET_OK !=
   4149       GNUNET_DISK_directory_create_for_file (fn))
   4150   {
   4151     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4152                 "Failed create directory to store key material in file `%s'\n",
   4153                 fn);
   4154     GNUNET_free (fn);
   4155     return GNUNET_SYSERR;
   4156   }
   4157   /* persist keys for future runs */
   4158   if (GNUNET_OK !=
   4159       GNUNET_DISK_fn_write (fn,
   4160                             secmset,
   4161                             sizeof (oldset),
   4162                             GNUNET_DISK_PERM_USER_READ))
   4163   {
   4164     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4165                 "Failed to store key material in file `%s'\n",
   4166                 fn);
   4167     GNUNET_free (fn);
   4168     return GNUNET_SYSERR;
   4169   }
   4170   GNUNET_free (fn);
   4171   return GNUNET_NO;
   4172 }
   4173 
   4174 
   4175 /**
   4176  * Output @a signkeys for human consumption.
   4177  *
   4178  * @param secm_pub security module public key used to sign the denominations
   4179  * @param signkeys keys to output
   4180  * @return #GNUNET_OK on success
   4181  */
   4182 static enum GNUNET_GenericReturnValue
   4183 show_signkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub,
   4184                const json_t *signkeys)
   4185 {
   4186   size_t index;
   4187   json_t *value;
   4188 
   4189   json_array_foreach (signkeys, index, value) {
   4190     const char *err_name;
   4191     unsigned int err_line;
   4192     struct TALER_ExchangePublicKeyP exchange_pub;
   4193     struct TALER_SecurityModuleSignatureP secm_sig;
   4194     struct GNUNET_TIME_Timestamp start_time;
   4195     struct GNUNET_TIME_Timestamp sign_end;
   4196     struct GNUNET_TIME_Timestamp legal_end;
   4197     struct GNUNET_TIME_Relative duration;
   4198     struct GNUNET_JSON_Specification spec[] = {
   4199       GNUNET_JSON_spec_timestamp ("stamp_start",
   4200                                   &start_time),
   4201       GNUNET_JSON_spec_timestamp ("stamp_expire",
   4202                                   &sign_end),
   4203       GNUNET_JSON_spec_timestamp ("stamp_end",
   4204                                   &legal_end),
   4205       GNUNET_JSON_spec_fixed_auto ("key",
   4206                                    &exchange_pub),
   4207       GNUNET_JSON_spec_fixed_auto ("signkey_secmod_sig",
   4208                                    &secm_sig),
   4209       GNUNET_JSON_spec_end ()
   4210     };
   4211 
   4212     if (GNUNET_OK !=
   4213         GNUNET_JSON_parse (value,
   4214                            spec,
   4215                            &err_name,
   4216                            &err_line))
   4217     {
   4218       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4219                   "Invalid input for signing key to 'show': %s#%u at %u (skipping)\n",
   4220                   err_name,
   4221                   err_line,
   4222                   (unsigned int) index);
   4223       json_dumpf (value,
   4224                   stderr,
   4225                   JSON_INDENT (2));
   4226       global_ret = EXIT_FAILURE;
   4227       GNUNET_SCHEDULER_shutdown ();
   4228       return GNUNET_SYSERR;
   4229     }
   4230     duration = GNUNET_TIME_absolute_get_difference (start_time.abs_time,
   4231                                                     sign_end.abs_time);
   4232     if (GNUNET_OK !=
   4233         TALER_exchange_secmod_eddsa_verify (&exchange_pub,
   4234                                             start_time,
   4235                                             duration,
   4236                                             secm_pub,
   4237                                             &secm_sig))
   4238     {
   4239       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4240                   "Invalid security module signature for signing key %s (aborting)\n",
   4241                   TALER_B2S (&exchange_pub));
   4242       global_ret = EXIT_FAILURE;
   4243       GNUNET_SCHEDULER_shutdown ();
   4244       return GNUNET_SYSERR;
   4245     }
   4246     {
   4247       char *legal_end_s;
   4248 
   4249       legal_end_s = GNUNET_strdup (
   4250         GNUNET_TIME_timestamp2s (legal_end));
   4251       printf ("EXCHANGE-KEY %s starting at %s (used for: %s, legal end: %s)\n",
   4252               TALER_B2S (&exchange_pub),
   4253               GNUNET_TIME_timestamp2s (start_time),
   4254               GNUNET_TIME_relative2s (duration,
   4255                                       false),
   4256               legal_end_s);
   4257       GNUNET_free (legal_end_s);
   4258     }
   4259   }
   4260   return GNUNET_OK;
   4261 }
   4262 
   4263 
   4264 /**
   4265  * Looks up the AGE_RESTRICTED setting for a denomination in the config and
   4266  * returns the age restriction (mask) accordingly.
   4267  *
   4268  * @param section_name Section in the configuration for the particular
   4269  *    denomination.
   4270  */
   4271 static struct TALER_AgeMask
   4272 load_age_mask (const char *section_name)
   4273 {
   4274   static const struct TALER_AgeMask null_mask = {0};
   4275   enum GNUNET_GenericReturnValue ret;
   4276 
   4277   if (GNUNET_YES !=
   4278       GNUNET_CONFIGURATION_get_value_yesno (kcfg,
   4279                                             "exchange",
   4280                                             "AGE_RESTRICTION_ENABLED"))
   4281     return null_mask;
   4282 
   4283   if (GNUNET_OK != (GNUNET_CONFIGURATION_have_value (
   4284                       kcfg,
   4285                       section_name,
   4286                       "AGE_RESTRICTED")))
   4287     return null_mask;
   4288 
   4289   ret = GNUNET_CONFIGURATION_get_value_yesno (kcfg,
   4290                                               section_name,
   4291                                               "AGE_RESTRICTED");
   4292   if (GNUNET_SYSERR == ret)
   4293     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
   4294                                section_name,
   4295                                "AGE_RESTRICTED",
   4296                                "Value must be YES or NO\n");
   4297   if (GNUNET_YES == ret)
   4298   {
   4299     char *groups = NULL;
   4300     struct TALER_AgeMask mask = {0};
   4301 
   4302     if (GNUNET_OK !=
   4303         GNUNET_CONFIGURATION_get_value_string (kcfg,
   4304                                                "exchange",
   4305                                                "AGE_GROUPS",
   4306                                                &groups))
   4307       groups = GNUNET_strdup ("8:10:12:14:16:18:21");
   4308     if (GNUNET_OK ==
   4309         TALER_parse_age_group_string (groups, &mask))
   4310     {
   4311       GNUNET_free (groups);
   4312       return mask;
   4313     }
   4314     GNUNET_free (groups);
   4315   }
   4316 
   4317   return null_mask;
   4318 }
   4319 
   4320 
   4321 /**
   4322  * Output @a denomkeys for human consumption.
   4323  *
   4324  * @param secm_pub_rsa security module public key used to sign the RSA denominations
   4325  * @param secm_pub_cs security module public key used to sign the CS denominations
   4326  * @param denomkeys keys to output
   4327  * @return #GNUNET_OK on success
   4328  */
   4329 static enum GNUNET_GenericReturnValue
   4330 show_denomkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub_rsa,
   4331                 const struct TALER_SecurityModulePublicKeyP *secm_pub_cs,
   4332                 const json_t *denomkeys)
   4333 {
   4334   size_t index;
   4335   json_t *value;
   4336 
   4337   json_array_foreach (denomkeys, index, value) {
   4338     const char *err_name;
   4339     unsigned int err_line;
   4340     const char *section_name;
   4341     struct TALER_DenominationPublicKey denom_pub;
   4342     struct GNUNET_TIME_Timestamp stamp_start;
   4343     struct GNUNET_TIME_Timestamp stamp_expire_withdraw;
   4344     struct GNUNET_TIME_Timestamp stamp_expire_deposit;
   4345     struct GNUNET_TIME_Timestamp stamp_expire_legal;
   4346     struct TALER_Amount coin_value;
   4347     struct TALER_DenomFeeSet fees;
   4348     struct TALER_SecurityModuleSignatureP secm_sig;
   4349     struct GNUNET_JSON_Specification spec[] = {
   4350       GNUNET_JSON_spec_string ("section_name",
   4351                                &section_name),
   4352       TALER_JSON_spec_denom_pub ("denom_pub",
   4353                                  &denom_pub),
   4354       TALER_JSON_spec_amount ("value",
   4355                               currency,
   4356                               &coin_value),
   4357       TALER_JSON_SPEC_DENOM_FEES ("fee",
   4358                                   currency,
   4359                                   &fees),
   4360       GNUNET_JSON_spec_timestamp ("stamp_start",
   4361                                   &stamp_start),
   4362       GNUNET_JSON_spec_timestamp ("stamp_expire_withdraw",
   4363                                   &stamp_expire_withdraw),
   4364       GNUNET_JSON_spec_timestamp ("stamp_expire_deposit",
   4365                                   &stamp_expire_deposit),
   4366       GNUNET_JSON_spec_timestamp ("stamp_expire_legal",
   4367                                   &stamp_expire_legal),
   4368       GNUNET_JSON_spec_fixed_auto ("denom_secmod_sig",
   4369                                    &secm_sig),
   4370       GNUNET_JSON_spec_end ()
   4371     };
   4372     struct GNUNET_TIME_Relative duration;
   4373     struct TALER_DenominationHashP h_denom_pub;
   4374     enum GNUNET_GenericReturnValue ok;
   4375 
   4376     if (GNUNET_OK !=
   4377         GNUNET_JSON_parse (value,
   4378                            spec,
   4379                            &err_name,
   4380                            &err_line))
   4381     {
   4382       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4383                   "Invalid input for denomination key to 'show': %s#%u at %u (skipping)\n",
   4384                   err_name,
   4385                   err_line,
   4386                   (unsigned int) index);
   4387       json_dumpf (value,
   4388                   stderr,
   4389                   JSON_INDENT (2));
   4390       GNUNET_JSON_parse_free (spec);
   4391       global_ret = EXIT_FAILURE;
   4392       GNUNET_SCHEDULER_shutdown ();
   4393       return GNUNET_SYSERR;
   4394     }
   4395     duration = GNUNET_TIME_absolute_get_difference (
   4396       stamp_start.abs_time,
   4397       stamp_expire_withdraw.abs_time);
   4398     denom_pub.age_mask = load_age_mask (section_name);
   4399     TALER_denom_pub_hash (&denom_pub,
   4400                           &h_denom_pub);
   4401     switch (denom_pub.bsign_pub_key->cipher)
   4402     {
   4403     case GNUNET_CRYPTO_BSA_RSA:
   4404       {
   4405         struct TALER_RsaPubHashP h_rsa;
   4406 
   4407         TALER_rsa_pub_hash (denom_pub.bsign_pub_key->details.rsa_public_key,
   4408                             &h_rsa);
   4409         ok = TALER_exchange_secmod_rsa_verify (&h_rsa,
   4410                                                section_name,
   4411                                                stamp_start,
   4412                                                duration,
   4413                                                secm_pub_rsa,
   4414                                                &secm_sig);
   4415       }
   4416       break;
   4417     case GNUNET_CRYPTO_BSA_CS:
   4418       {
   4419         struct TALER_CsPubHashP h_cs;
   4420 
   4421         TALER_cs_pub_hash (&denom_pub.bsign_pub_key->details.cs_public_key,
   4422                            &h_cs);
   4423         ok = TALER_exchange_secmod_cs_verify (&h_cs,
   4424                                               section_name,
   4425                                               stamp_start,
   4426                                               duration,
   4427                                               secm_pub_cs,
   4428                                               &secm_sig);
   4429       }
   4430       break;
   4431     default:
   4432       GNUNET_break (0);
   4433       ok = GNUNET_SYSERR;
   4434       break;
   4435     }
   4436     if (GNUNET_OK != ok)
   4437     {
   4438       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4439                   "Invalid security module signature for denomination key %s (aborting)\n",
   4440                   GNUNET_h2s (&h_denom_pub.hash));
   4441       global_ret = EXIT_FAILURE;
   4442       GNUNET_JSON_parse_free (spec);
   4443       GNUNET_SCHEDULER_shutdown ();
   4444       return GNUNET_SYSERR;
   4445     }
   4446 
   4447     {
   4448       char *withdraw_fee_s;
   4449       char *deposit_fee_s;
   4450       char *refresh_fee_s;
   4451       char *refund_fee_s;
   4452       char *deposit_s;
   4453       char *legal_s;
   4454 
   4455       withdraw_fee_s = TALER_amount_to_string (&fees.withdraw);
   4456       deposit_fee_s = TALER_amount_to_string (&fees.deposit);
   4457       refresh_fee_s = TALER_amount_to_string (&fees.refresh);
   4458       refund_fee_s = TALER_amount_to_string (&fees.refund);
   4459       deposit_s = GNUNET_strdup (
   4460         GNUNET_TIME_timestamp2s (stamp_expire_deposit));
   4461       legal_s = GNUNET_strdup (
   4462         GNUNET_TIME_timestamp2s (stamp_expire_legal));
   4463 
   4464       printf (
   4465         "DENOMINATION-KEY(%s) %s of value %s starting at %s "
   4466         "(used for: %s, deposit until: %s legal end: %s) with fees %s/%s/%s/%s\n",
   4467         section_name,
   4468         TALER_B2S (&h_denom_pub),
   4469         TALER_amount2s (&coin_value),
   4470         GNUNET_TIME_timestamp2s (stamp_start),
   4471         GNUNET_TIME_relative2s (duration,
   4472                                 false),
   4473         deposit_s,
   4474         legal_s,
   4475         withdraw_fee_s,
   4476         deposit_fee_s,
   4477         refresh_fee_s,
   4478         refund_fee_s);
   4479       GNUNET_free (withdraw_fee_s);
   4480       GNUNET_free (deposit_fee_s);
   4481       GNUNET_free (refresh_fee_s);
   4482       GNUNET_free (refund_fee_s);
   4483       GNUNET_free (deposit_s);
   4484       GNUNET_free (legal_s);
   4485     }
   4486 
   4487     GNUNET_JSON_parse_free (spec);
   4488   }
   4489   return GNUNET_OK;
   4490 }
   4491 
   4492 
   4493 /**
   4494  * Parse the input of exchange keys for the 'show' and 'sign' commands.
   4495  *
   4496  * @param command_name name of the command, for logging
   4497  * @return NULL on error, otherwise the keys details to be free'd by caller
   4498  */
   4499 static json_t *
   4500 parse_keys_input (const char *command_name)
   4501 {
   4502   const char *op_str;
   4503   json_t *keys;
   4504   struct GNUNET_JSON_Specification spec[] = {
   4505     GNUNET_JSON_spec_json ("arguments",
   4506                            &keys),
   4507     GNUNET_JSON_spec_string ("operation",
   4508                              &op_str),
   4509     GNUNET_JSON_spec_end ()
   4510   };
   4511   const char *err_name;
   4512   unsigned int err_line;
   4513 
   4514   if (NULL == in)
   4515   {
   4516     json_error_t err;
   4517 
   4518     in = json_loadf (stdin,
   4519                      JSON_REJECT_DUPLICATES,
   4520                      &err);
   4521     if (NULL == in)
   4522     {
   4523       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4524                   "Failed to read JSON input: %s at %d:%s (offset: %d)\n",
   4525                   err.text,
   4526                   err.line,
   4527                   err.source,
   4528                   err.position);
   4529       global_ret = EXIT_FAILURE;
   4530       GNUNET_SCHEDULER_shutdown ();
   4531       return NULL;
   4532     }
   4533   }
   4534   if (GNUNET_OK !=
   4535       GNUNET_JSON_parse (in,
   4536                          spec,
   4537                          &err_name,
   4538                          &err_line))
   4539   {
   4540     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4541                 "Invalid input to '%s': %s#%u (skipping)\n",
   4542                 command_name,
   4543                 err_name,
   4544                 err_line);
   4545     json_dumpf (in,
   4546                 stderr,
   4547                 JSON_INDENT (2));
   4548     global_ret = EXIT_FAILURE;
   4549     GNUNET_SCHEDULER_shutdown ();
   4550     return NULL;
   4551   }
   4552   if (0 != strcmp (op_str,
   4553                    OP_INPUT_KEYS))
   4554   {
   4555     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4556                 "Invalid input to '%s' : operation is `%s', expected `%s'\n",
   4557                 command_name,
   4558                 op_str,
   4559                 OP_INPUT_KEYS);
   4560     GNUNET_JSON_parse_free (spec);
   4561     return NULL;
   4562   }
   4563   json_decref (in);
   4564   in = NULL;
   4565   return keys;
   4566 }
   4567 
   4568 
   4569 /**
   4570  * Show future keys.
   4571  *
   4572  * @param args the array of command-line arguments to process next
   4573  */
   4574 static void
   4575 do_show (char *const *args)
   4576 {
   4577   json_t *keys;
   4578   const char *err_name;
   4579   unsigned int err_line;
   4580   const json_t *denomkeys;
   4581   const json_t *signkeys;
   4582   struct TALER_MasterPublicKeyP mpub;
   4583   struct TALER_SecurityModulePublicKeySetP secmset;
   4584   struct GNUNET_JSON_Specification spec[] = {
   4585     GNUNET_JSON_spec_array_const ("future_denoms",
   4586                                   &denomkeys),
   4587     GNUNET_JSON_spec_array_const ("future_signkeys",
   4588                                   &signkeys),
   4589     GNUNET_JSON_spec_fixed_auto ("master_pub",
   4590                                  &mpub),
   4591     GNUNET_JSON_spec_fixed_auto ("denom_secmod_public_key",
   4592                                  &secmset.rsa),
   4593     GNUNET_JSON_spec_fixed_auto ("denom_secmod_cs_public_key",
   4594                                  &secmset.cs),
   4595     GNUNET_JSON_spec_fixed_auto ("signkey_secmod_public_key",
   4596                                  &secmset.eddsa),
   4597     GNUNET_JSON_spec_end ()
   4598   };
   4599 
   4600   keys = parse_keys_input ("show");
   4601   if (NULL == keys)
   4602     return;
   4603 
   4604   if (GNUNET_OK !=
   4605       load_offline_key (GNUNET_NO))
   4606   {
   4607     json_decref (keys);
   4608     return;
   4609   }
   4610   if (GNUNET_OK !=
   4611       GNUNET_JSON_parse (keys,
   4612                          spec,
   4613                          &err_name,
   4614                          &err_line))
   4615   {
   4616     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4617                 "Invalid input to 'show': %s #%u (skipping)\n",
   4618                 err_name,
   4619                 err_line);
   4620     json_dumpf (keys,
   4621                 stderr,
   4622                 JSON_INDENT (2));
   4623     global_ret = EXIT_FAILURE;
   4624     GNUNET_SCHEDULER_shutdown ();
   4625     json_decref (keys);
   4626     return;
   4627   }
   4628   if (0 !=
   4629       GNUNET_memcmp (&master_pub,
   4630                      &mpub))
   4631   {
   4632     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4633                 "Fatal: exchange uses different master key!\n");
   4634     global_ret = EXIT_FAILURE;
   4635     GNUNET_SCHEDULER_shutdown ();
   4636     json_decref (keys);
   4637     return;
   4638   }
   4639   if (GNUNET_SYSERR ==
   4640       tofu_check (&secmset))
   4641   {
   4642     global_ret = EXIT_FAILURE;
   4643     GNUNET_SCHEDULER_shutdown ();
   4644     json_decref (keys);
   4645     return;
   4646   }
   4647   if ( (GNUNET_OK !=
   4648         show_signkeys (&secmset.eddsa,
   4649                        signkeys)) ||
   4650        (GNUNET_OK !=
   4651         show_denomkeys (&secmset.rsa,
   4652                         &secmset.cs,
   4653                         denomkeys)) )
   4654   {
   4655     global_ret = EXIT_FAILURE;
   4656     GNUNET_SCHEDULER_shutdown ();
   4657     json_decref (keys);
   4658     return;
   4659   }
   4660   json_decref (keys);
   4661   next (args);
   4662 }
   4663 
   4664 
   4665 /**
   4666  * Sign @a signkeys with offline key.
   4667  *
   4668  * @param secm_pub security module public key used to sign the denominations
   4669  * @param signkeys keys to output
   4670  * @param[in,out] result array where to output the signatures
   4671  * @return #GNUNET_OK on success
   4672  */
   4673 static enum GNUNET_GenericReturnValue
   4674 sign_signkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub,
   4675                const json_t *signkeys,
   4676                json_t *result)
   4677 {
   4678   size_t index;
   4679   json_t *value;
   4680 
   4681   json_array_foreach (signkeys, index, value) {
   4682     const char *err_name;
   4683     unsigned int err_line;
   4684     struct TALER_ExchangePublicKeyP exchange_pub;
   4685     struct TALER_SecurityModuleSignatureP secm_sig;
   4686     struct GNUNET_TIME_Timestamp start_time;
   4687     struct GNUNET_TIME_Timestamp sign_end;
   4688     struct GNUNET_TIME_Timestamp legal_end;
   4689     struct GNUNET_TIME_Relative duration;
   4690     struct GNUNET_JSON_Specification spec[] = {
   4691       GNUNET_JSON_spec_timestamp ("stamp_start",
   4692                                   &start_time),
   4693       GNUNET_JSON_spec_timestamp ("stamp_expire",
   4694                                   &sign_end),
   4695       GNUNET_JSON_spec_timestamp ("stamp_end",
   4696                                   &legal_end),
   4697       GNUNET_JSON_spec_fixed_auto ("key",
   4698                                    &exchange_pub),
   4699       GNUNET_JSON_spec_fixed_auto ("signkey_secmod_sig",
   4700                                    &secm_sig),
   4701       GNUNET_JSON_spec_end ()
   4702     };
   4703 
   4704     if (GNUNET_OK !=
   4705         GNUNET_JSON_parse (value,
   4706                            spec,
   4707                            &err_name,
   4708                            &err_line))
   4709     {
   4710       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4711                   "Invalid input for signing key to 'show': %s #%u at %u (skipping)\n",
   4712                   err_name,
   4713                   err_line,
   4714                   (unsigned int) index);
   4715       json_dumpf (value,
   4716                   stderr,
   4717                   JSON_INDENT (2));
   4718       global_ret = EXIT_FAILURE;
   4719       GNUNET_SCHEDULER_shutdown ();
   4720       return GNUNET_SYSERR;
   4721     }
   4722 
   4723     duration = GNUNET_TIME_absolute_get_difference (start_time.abs_time,
   4724                                                     sign_end.abs_time);
   4725     if (GNUNET_OK !=
   4726         TALER_exchange_secmod_eddsa_verify (&exchange_pub,
   4727                                             start_time,
   4728                                             duration,
   4729                                             secm_pub,
   4730                                             &secm_sig))
   4731     {
   4732       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4733                   "Invalid security module signature for signing key %s (aborting)\n",
   4734                   TALER_B2S (&exchange_pub));
   4735       global_ret = EXIT_FAILURE;
   4736       GNUNET_SCHEDULER_shutdown ();
   4737       GNUNET_JSON_parse_free (spec);
   4738       return GNUNET_SYSERR;
   4739     }
   4740     {
   4741       struct TALER_MasterSignatureP master_sig;
   4742 
   4743       TALER_exchange_offline_signkey_validity_sign (&exchange_pub,
   4744                                                     start_time,
   4745                                                     sign_end,
   4746                                                     legal_end,
   4747                                                     &master_priv,
   4748                                                     &master_sig);
   4749       GNUNET_assert (0 ==
   4750                      json_array_append_new (
   4751                        result,
   4752                        GNUNET_JSON_PACK (
   4753                          GNUNET_JSON_pack_data_auto ("exchange_pub",
   4754                                                      &exchange_pub),
   4755                          GNUNET_JSON_pack_data_auto ("master_sig",
   4756                                                      &master_sig))));
   4757     }
   4758     GNUNET_JSON_parse_free (spec);
   4759   }
   4760   return GNUNET_OK;
   4761 }
   4762 
   4763 
   4764 /**
   4765  * Sign @a denomkeys with offline key.
   4766  *
   4767  * @param secm_pub_rsa security module public key used to sign the RSA denominations
   4768  * @param secm_pub_cs security module public key used to sign the CS denominations
   4769  * @param denomkeys keys to output
   4770  * @param[in,out] result array where to output the signatures
   4771  * @return #GNUNET_OK on success
   4772  */
   4773 static enum GNUNET_GenericReturnValue
   4774 sign_denomkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub_rsa,
   4775                 const struct TALER_SecurityModulePublicKeyP *secm_pub_cs,
   4776                 const json_t *denomkeys,
   4777                 json_t *result)
   4778 {
   4779   size_t index;
   4780   json_t *value;
   4781 
   4782   json_array_foreach (denomkeys, index, value) {
   4783     const char *err_name;
   4784     unsigned int err_line;
   4785     const char *section_name;
   4786     struct TALER_DenominationPublicKey denom_pub;
   4787     struct GNUNET_TIME_Timestamp stamp_start;
   4788     struct GNUNET_TIME_Timestamp stamp_expire_withdraw;
   4789     struct GNUNET_TIME_Timestamp stamp_expire_deposit;
   4790     struct GNUNET_TIME_Timestamp stamp_expire_legal;
   4791     struct TALER_Amount coin_value;
   4792     struct TALER_DenomFeeSet fees;
   4793     struct TALER_SecurityModuleSignatureP secm_sig;
   4794     struct GNUNET_JSON_Specification spec[] = {
   4795       GNUNET_JSON_spec_string ("section_name",
   4796                                &section_name),
   4797       TALER_JSON_spec_denom_pub ("denom_pub",
   4798                                  &denom_pub),
   4799       TALER_JSON_spec_amount ("value",
   4800                               currency,
   4801                               &coin_value),
   4802       TALER_JSON_SPEC_DENOM_FEES ("fee",
   4803                                   currency,
   4804                                   &fees),
   4805       GNUNET_JSON_spec_timestamp ("stamp_start",
   4806                                   &stamp_start),
   4807       GNUNET_JSON_spec_timestamp ("stamp_expire_withdraw",
   4808                                   &stamp_expire_withdraw),
   4809       GNUNET_JSON_spec_timestamp ("stamp_expire_deposit",
   4810                                   &stamp_expire_deposit),
   4811       GNUNET_JSON_spec_timestamp ("stamp_expire_legal",
   4812                                   &stamp_expire_legal),
   4813       GNUNET_JSON_spec_fixed_auto ("denom_secmod_sig",
   4814                                    &secm_sig),
   4815       GNUNET_JSON_spec_end ()
   4816     };
   4817     struct GNUNET_TIME_Relative duration;
   4818     struct TALER_DenominationHashP h_denom_pub;
   4819 
   4820     if (GNUNET_OK !=
   4821         GNUNET_JSON_parse (value,
   4822                            spec,
   4823                            &err_name,
   4824                            &err_line))
   4825     {
   4826       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4827                   "Invalid input for denomination key to 'sign': %s #%u at %u (skipping)\n",
   4828                   err_name,
   4829                   err_line,
   4830                   (unsigned int) index);
   4831       json_dumpf (value,
   4832                   stderr,
   4833                   JSON_INDENT (2));
   4834       GNUNET_JSON_parse_free (spec);
   4835       global_ret = EXIT_FAILURE;
   4836       GNUNET_SCHEDULER_shutdown ();
   4837       return GNUNET_SYSERR;
   4838     }
   4839     duration = GNUNET_TIME_absolute_get_difference (
   4840       stamp_start.abs_time,
   4841       stamp_expire_withdraw.abs_time);
   4842 
   4843     /* Load the age mask, if applicable to this denomination */
   4844     denom_pub.age_mask = load_age_mask (section_name);
   4845 
   4846     TALER_denom_pub_hash (&denom_pub,
   4847                           &h_denom_pub);
   4848 
   4849     switch (denom_pub.bsign_pub_key->cipher)
   4850     {
   4851     case GNUNET_CRYPTO_BSA_RSA:
   4852       {
   4853         struct TALER_RsaPubHashP h_rsa;
   4854 
   4855         TALER_rsa_pub_hash (denom_pub.bsign_pub_key->details.rsa_public_key,
   4856                             &h_rsa);
   4857         if (GNUNET_OK !=
   4858             TALER_exchange_secmod_rsa_verify (&h_rsa,
   4859                                               section_name,
   4860                                               stamp_start,
   4861                                               duration,
   4862                                               secm_pub_rsa,
   4863                                               &secm_sig))
   4864         {
   4865           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4866                       "Invalid security module signature for denomination key %s (aborting)\n",
   4867                       GNUNET_h2s (&h_denom_pub.hash));
   4868           global_ret = EXIT_FAILURE;
   4869           GNUNET_SCHEDULER_shutdown ();
   4870           GNUNET_JSON_parse_free (spec);
   4871           return GNUNET_SYSERR;
   4872         }
   4873       }
   4874       break;
   4875     case GNUNET_CRYPTO_BSA_CS:
   4876       {
   4877         struct TALER_CsPubHashP h_cs;
   4878 
   4879         TALER_cs_pub_hash (&denom_pub.bsign_pub_key->details.cs_public_key,
   4880                            &h_cs);
   4881         if (GNUNET_OK !=
   4882             TALER_exchange_secmod_cs_verify (&h_cs,
   4883                                              section_name,
   4884                                              stamp_start,
   4885                                              duration,
   4886                                              secm_pub_cs,
   4887                                              &secm_sig))
   4888         {
   4889           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4890                       "Invalid security module signature for denomination key %s (aborting)\n",
   4891                       GNUNET_h2s (&h_denom_pub.hash));
   4892           global_ret = EXIT_FAILURE;
   4893           GNUNET_SCHEDULER_shutdown ();
   4894           GNUNET_JSON_parse_free (spec);
   4895           return GNUNET_SYSERR;
   4896         }
   4897       }
   4898       break;
   4899     default:
   4900       global_ret = EXIT_FAILURE;
   4901       GNUNET_SCHEDULER_shutdown ();
   4902       GNUNET_JSON_parse_free (spec);
   4903       return GNUNET_SYSERR;
   4904     }
   4905 
   4906     {
   4907       struct TALER_MasterSignatureP master_sig;
   4908 
   4909       TALER_exchange_offline_denom_validity_sign (&h_denom_pub,
   4910                                                   stamp_start,
   4911                                                   stamp_expire_withdraw,
   4912                                                   stamp_expire_deposit,
   4913                                                   stamp_expire_legal,
   4914                                                   &coin_value,
   4915                                                   &fees,
   4916                                                   &master_priv,
   4917                                                   &master_sig);
   4918       GNUNET_assert (0 ==
   4919                      json_array_append_new (
   4920                        result,
   4921                        GNUNET_JSON_PACK (
   4922                          GNUNET_JSON_pack_data_auto ("h_denom_pub",
   4923                                                      &h_denom_pub),
   4924                          GNUNET_JSON_pack_data_auto ("master_sig",
   4925                                                      &master_sig))));
   4926     }
   4927     GNUNET_JSON_parse_free (spec);
   4928   }
   4929   return GNUNET_OK;
   4930 }
   4931 
   4932 
   4933 /**
   4934  * Sign future keys.
   4935  *
   4936  * @param args the array of command-line arguments to process next
   4937  */
   4938 static void
   4939 do_sign (char *const *args)
   4940 {
   4941   json_t *keys;
   4942   const char *err_name;
   4943   unsigned int err_line;
   4944   const json_t *denomkeys;
   4945   const json_t *signkeys;
   4946   struct TALER_MasterPublicKeyP mpub;
   4947   struct TALER_SecurityModulePublicKeySetP secmset;
   4948   struct GNUNET_JSON_Specification spec[] = {
   4949     GNUNET_JSON_spec_array_const ("future_denoms",
   4950                                   &denomkeys),
   4951     GNUNET_JSON_spec_array_const ("future_signkeys",
   4952                                   &signkeys),
   4953     GNUNET_JSON_spec_fixed_auto ("master_pub",
   4954                                  &mpub),
   4955     GNUNET_JSON_spec_fixed_auto ("denom_secmod_public_key",
   4956                                  &secmset.rsa),
   4957     GNUNET_JSON_spec_fixed_auto ("denom_secmod_cs_public_key",
   4958                                  &secmset.cs),
   4959     GNUNET_JSON_spec_fixed_auto ("signkey_secmod_public_key",
   4960                                  &secmset.eddsa),
   4961     GNUNET_JSON_spec_end ()
   4962   };
   4963 
   4964   keys = parse_keys_input ("sign");
   4965   if (NULL == keys)
   4966     return;
   4967   if (GNUNET_OK !=
   4968       load_offline_key (GNUNET_NO))
   4969   {
   4970     json_decref (keys);
   4971     return;
   4972   }
   4973   if (GNUNET_OK !=
   4974       GNUNET_JSON_parse (keys,
   4975                          spec,
   4976                          &err_name,
   4977                          &err_line))
   4978   {
   4979     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4980                 "Invalid input to 'sign' : %s #%u (skipping)\n",
   4981                 err_name,
   4982                 err_line);
   4983     json_dumpf (keys,
   4984                 stderr,
   4985                 JSON_INDENT (2));
   4986     global_ret = EXIT_FAILURE;
   4987     GNUNET_SCHEDULER_shutdown ();
   4988     json_decref (keys);
   4989     return;
   4990   }
   4991   if (0 !=
   4992       GNUNET_memcmp (&master_pub,
   4993                      &mpub))
   4994   {
   4995     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4996                 "Fatal: exchange uses different master key!\n");
   4997     global_ret = EXIT_FAILURE;
   4998     GNUNET_SCHEDULER_shutdown ();
   4999     json_decref (keys);
   5000     return;
   5001   }
   5002   if (GNUNET_SYSERR ==
   5003       tofu_check (&secmset))
   5004   {
   5005     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   5006                 "Fatal: security module keys changed!\n");
   5007     global_ret = EXIT_FAILURE;
   5008     GNUNET_SCHEDULER_shutdown ();
   5009     json_decref (keys);
   5010     return;
   5011   }
   5012   {
   5013     json_t *signkey_sig_array = json_array ();
   5014     json_t *denomkey_sig_array = json_array ();
   5015 
   5016     GNUNET_assert (NULL != signkey_sig_array);
   5017     GNUNET_assert (NULL != denomkey_sig_array);
   5018     if ( (GNUNET_OK !=
   5019           sign_signkeys (&secmset.eddsa,
   5020                          signkeys,
   5021                          signkey_sig_array)) ||
   5022          (GNUNET_OK !=
   5023           sign_denomkeys (&secmset.rsa,
   5024                           &secmset.cs,
   5025                           denomkeys,
   5026                           denomkey_sig_array)) )
   5027     {
   5028       global_ret = EXIT_FAILURE;
   5029       GNUNET_SCHEDULER_shutdown ();
   5030       json_decref (signkey_sig_array);
   5031       json_decref (denomkey_sig_array);
   5032       json_decref (keys);
   5033       return;
   5034     }
   5035 
   5036     output_operation (OP_UPLOAD_SIGS,
   5037                       GNUNET_JSON_PACK (
   5038                         GNUNET_JSON_pack_array_steal ("denom_sigs",
   5039                                                       denomkey_sig_array),
   5040                         GNUNET_JSON_pack_array_steal ("signkey_sigs",
   5041                                                       signkey_sig_array)));
   5042   }
   5043   json_decref (keys);
   5044   next (args);
   5045 }
   5046 
   5047 
   5048 /**
   5049  * Setup and output offline signing key.
   5050  *
   5051  * @param args the array of command-line arguments to process next
   5052  */
   5053 static void
   5054 do_setup (char *const *args)
   5055 {
   5056   if (GNUNET_OK !=
   5057       load_offline_key (GNUNET_YES))
   5058   {
   5059     global_ret = EXIT_NOPERMISSION;
   5060     return;
   5061   }
   5062   if (NULL != *args)
   5063   {
   5064     output_operation (OP_SETUP,
   5065                       GNUNET_JSON_PACK (
   5066                         GNUNET_JSON_pack_data_auto ("exchange_offline_pub",
   5067                                                     &master_pub)));
   5068   }
   5069 
   5070   else
   5071   {
   5072     char *pub_s;
   5073 
   5074     pub_s = GNUNET_STRINGS_data_to_string_alloc (&master_pub,
   5075                                                  sizeof (master_pub));
   5076     fprintf (stdout,
   5077              "%s\n",
   5078              pub_s);
   5079     GNUNET_free (pub_s);
   5080   }
   5081   if ( (NULL != *args) &&
   5082        (0 == strcmp (*args,
   5083                      "-")) )
   5084     args++;
   5085   next (args);
   5086 }
   5087 
   5088 
   5089 /**
   5090  * Dispatch @a args in the @a cmds array.
   5091  *
   5092  * @param args arguments with subcommand to dispatch
   5093  * @param cmds array of possible subcommands to call
   5094  */
   5095 static void
   5096 cmd_handler (char *const *args,
   5097              const struct SubCommand *cmds)
   5098 {
   5099   nxt = NULL;
   5100   for (unsigned int i = 0; NULL != cmds[i].name; i++)
   5101   {
   5102     if (0 == strcasecmp (cmds[i].name,
   5103                          args[0]))
   5104     {
   5105       cmds[i].cb (&args[1]);
   5106       return;
   5107     }
   5108   }
   5109 
   5110   if (0 != strcasecmp ("help",
   5111                        args[0]))
   5112   {
   5113     GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
   5114                 "Unexpected command `%s'\n",
   5115                 args[0]);
   5116     global_ret = EXIT_INVALIDARGUMENT;
   5117   }
   5118 
   5119   GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
   5120               "Supported subcommands:\n");
   5121   for (unsigned int i = 0; NULL != cmds[i].name; i++)
   5122   {
   5123     GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
   5124                 "- %s: %s\n",
   5125                 cmds[i].name,
   5126                 cmds[i].help);
   5127   }
   5128   json_decref (out);
   5129   out = NULL;
   5130   GNUNET_SCHEDULER_shutdown ();
   5131 }
   5132 
   5133 
   5134 static void
   5135 work (void *cls)
   5136 {
   5137   char *const *args = cls;
   5138   struct SubCommand cmds[] = {
   5139     {
   5140       .name = "setup",
   5141       .help =
   5142         "initialize offline key signing material and display public offline key",
   5143       .cb = &do_setup
   5144     },
   5145     {
   5146       .name = "download",
   5147       .help =
   5148         "obtain future public keys from exchange (to be performed online!)",
   5149       .cb = &do_download
   5150     },
   5151     {
   5152       .name = "show",
   5153       .help =
   5154         "display future public keys from exchange for human review (pass '-' as argument to disable consuming input)",
   5155       .cb = &do_show
   5156     },
   5157     {
   5158       .name = "sign",
   5159       .help = "sign all future public keys from the input",
   5160       .cb = &do_sign
   5161     },
   5162     {
   5163       .name = "revoke-denomination",
   5164       .help =
   5165         "revoke denomination key (hash of public key must be given as argument)",
   5166       .cb = &do_revoke_denomination_key
   5167     },
   5168     {
   5169       .name = "revoke-signkey",
   5170       .help =
   5171         "revoke exchange online signing key (public key must be given as argument)",
   5172       .cb = &do_revoke_signkey
   5173     },
   5174     {
   5175       .name = "enable-auditor",
   5176       .help =
   5177         "enable auditor for the exchange (auditor-public key, auditor-URI and auditor-name must be given as arguments)",
   5178       .cb = &do_add_auditor
   5179     },
   5180     {
   5181       .name = "disable-auditor",
   5182       .help =
   5183         "disable auditor at the exchange (auditor-public key must be given as argument)",
   5184       .cb = &do_del_auditor
   5185     },
   5186     {
   5187       .name = "enable-account",
   5188       .help =
   5189         "enable wire account of the exchange (payto-URI must be given as argument; for optional arguments see man page)",
   5190       .cb = &do_add_wire
   5191     },
   5192     {
   5193       .name = "disable-account",
   5194       .help =
   5195         "disable wire account of the exchange (payto-URI must be given as argument)",
   5196       .cb = &do_del_wire
   5197     },
   5198     {
   5199       .name = "wire-fee",
   5200       .help =
   5201         "sign wire fees for the given year (year, wire method, wire fee, and closing fee must be given as arguments)",
   5202       .cb = &do_set_wire_fee
   5203     },
   5204     {
   5205       .name = "global-fee",
   5206       .help =
   5207         "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)",
   5208       .cb = &do_set_global_fee
   5209     },
   5210     {
   5211       .name = "drain",
   5212       .help =
   5213         "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)",
   5214       .cb = &do_drain
   5215     },
   5216     {
   5217       .name = "add-partner",
   5218       .help =
   5219         "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)",
   5220       .cb = &do_add_partner
   5221     },
   5222     {
   5223       .name = "aml-enable",
   5224       .help =
   5225         "enable AML staff member (staff member public key, legal name and rw (read write) or ro (read only) must be given as arguments)",
   5226       .cb = &enable_aml_staff
   5227     },
   5228     {
   5229       .name = "aml-disable",
   5230       .help =
   5231         "disable AML staff member (staff member public key and legal name must be given as arguments)",
   5232       .cb = &disable_aml_staff
   5233     },
   5234     {
   5235       .name = "upload",
   5236       .help =
   5237         "upload operation result to exchange (to be performed online!)",
   5238       .cb = &do_upload
   5239     },
   5240     /* list terminator */
   5241     {
   5242       .name = NULL,
   5243     }
   5244   };
   5245   (void) cls;
   5246 
   5247   cmd_handler (args, cmds);
   5248 }
   5249 
   5250 
   5251 /**
   5252  * Main function that will be run.
   5253  *
   5254  * @param cls closure
   5255  * @param args remaining command-line arguments
   5256  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
   5257  * @param cfg configuration
   5258  */
   5259 static void
   5260 run (void *cls,
   5261      char *const *args,
   5262      const char *cfgfile,
   5263      const struct GNUNET_CONFIGURATION_Handle *cfg)
   5264 {
   5265   (void) cls;
   5266   (void) cfgfile;
   5267   kcfg = cfg;
   5268 
   5269   /* age restriction is read per-denomination in load_age_mask() from [exchange] */
   5270 
   5271 
   5272   if (GNUNET_OK !=
   5273       TALER_config_get_currency (kcfg,
   5274                                  "exchange",
   5275                                  &currency))
   5276   {
   5277     global_ret = EXIT_NOTCONFIGURED;
   5278     return;
   5279   }
   5280 
   5281   ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
   5282                           &rc);
   5283   rc = GNUNET_CURL_gnunet_rc_create (ctx);
   5284   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
   5285                                  NULL);
   5286   next (args);
   5287 }
   5288 
   5289 
   5290 /**
   5291  * The main function of the taler-exchange-offline tool.  This tool is used to
   5292  * create the signing and denomination keys for the exchange.  It uses the
   5293  * long-term offline private key and generates signatures with it. It also
   5294  * supports online operations with the exchange to download its input data and
   5295  * to upload its results. Those online operations should be performed on
   5296  * another machine in production!
   5297  *
   5298  * @param argc number of arguments from the command line
   5299  * @param argv command line arguments
   5300  * @return 0 ok, 1 on error
   5301  */
   5302 int
   5303 main (int argc,
   5304       char *const *argv)
   5305 {
   5306   struct GNUNET_GETOPT_CommandLineOption options[] = {
   5307     GNUNET_GETOPT_OPTION_END
   5308   };
   5309   enum GNUNET_GenericReturnValue ret;
   5310 
   5311   ret = GNUNET_PROGRAM_run (
   5312     TALER_EXCHANGE_project_data (),
   5313     argc, argv,
   5314     "taler-exchange-offline",
   5315     gettext_noop ("Operations for offline signing for a Taler exchange"),
   5316     options,
   5317     &run, NULL);
   5318   if (GNUNET_SYSERR == ret)
   5319     return EXIT_INVALIDARGUMENT;
   5320   if (GNUNET_NO == ret)
   5321     return EXIT_SUCCESS;
   5322   return global_ret;
   5323 }
   5324 
   5325 
   5326 /* end of taler-exchange-offline.c */