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


      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        (GNUNET_OK !=
   3584         TALER_string_to_amount (args[0],
   3585                                 &amount)) )
   3586   {
   3587     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3588                 "Drain requires an amount, section name and target payto://-URI as arguments\n");
   3589     GNUNET_SCHEDULER_shutdown ();
   3590     global_ret = EXIT_INVALIDARGUMENT;
   3591     return;
   3592   }
   3593   if ( (NULL == args[0]) ||
   3594        (NULL == args[1]) ||
   3595        (NULL == args[2]) )
   3596   {
   3597     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3598                 "Drain requires an amount, section name and target payto://-URI as arguments\n");
   3599     GNUNET_SCHEDULER_shutdown ();
   3600     global_ret = EXIT_INVALIDARGUMENT;
   3601     return;
   3602   }
   3603   if (GNUNET_OK !=
   3604       TALER_string_to_amount (args[0],
   3605                               &amount))
   3606   {
   3607     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3608                 "Invalid amount `%s' specified for drain\n",
   3609                 args[0]);
   3610     GNUNET_SCHEDULER_shutdown ();
   3611     global_ret = EXIT_INVALIDARGUMENT;
   3612     return;
   3613   }
   3614   account_section = args[1];
   3615   payto_uri.full_payto = args[2];
   3616   err = TALER_payto_validate (payto_uri);
   3617   if (NULL != err)
   3618   {
   3619     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3620                 "Invalid payto://-URI `%s' specified for drain: %s\n",
   3621                 payto_uri.full_payto,
   3622                 err);
   3623     GNUNET_free (err);
   3624     GNUNET_SCHEDULER_shutdown ();
   3625     global_ret = EXIT_INVALIDARGUMENT;
   3626     return;
   3627   }
   3628   if (GNUNET_OK !=
   3629       load_offline_key (GNUNET_NO))
   3630     return;
   3631   GNUNET_CRYPTO_random_block (&wtid,
   3632                               sizeof (wtid));
   3633   date = GNUNET_TIME_timestamp_get ();
   3634   TALER_exchange_offline_profit_drain_sign (&wtid,
   3635                                             date,
   3636                                             &amount,
   3637                                             account_section,
   3638                                             payto_uri,
   3639                                             &master_priv,
   3640                                             &master_sig);
   3641   output_operation (OP_DRAIN_PROFITS,
   3642                     GNUNET_JSON_PACK (
   3643                       GNUNET_JSON_pack_data_auto ("wtid",
   3644                                                   &wtid),
   3645                       GNUNET_JSON_pack_string ("account_section",
   3646                                                account_section),
   3647                       TALER_JSON_pack_full_payto ("payto_uri",
   3648                                                   payto_uri),
   3649                       TALER_JSON_pack_amount ("amount",
   3650                                               &amount),
   3651                       GNUNET_JSON_pack_timestamp ("date",
   3652                                                   date),
   3653                       GNUNET_JSON_pack_data_auto ("master_sig",
   3654                                                   &master_sig)));
   3655   next (args + 3);
   3656 }
   3657 
   3658 
   3659 /**
   3660  * Add partner.
   3661  *
   3662  * @param args the array of command-line arguments to process next;
   3663  *        args[0] must be the partner's master public key, args[1] the partner's
   3664  *        API base URL, args[2] the wad fee, args[3] the wad frequency, and
   3665  *        args[4] the year (including possibly 'now')
   3666  */
   3667 static void
   3668 do_add_partner (char *const *args)
   3669 {
   3670   struct TALER_MasterPublicKeyP partner_pub;
   3671   struct GNUNET_TIME_Timestamp start_date;
   3672   struct GNUNET_TIME_Timestamp end_date;
   3673   struct GNUNET_TIME_Relative wad_frequency;
   3674   struct TALER_Amount wad_fee;
   3675   const char *partner_base_url;
   3676   struct TALER_MasterSignatureP master_sig;
   3677   char dummy;
   3678   unsigned int year;
   3679 
   3680   if (NULL != in)
   3681   {
   3682     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3683                 "Downloaded data was not consumed, not adding partner\n");
   3684     GNUNET_SCHEDULER_shutdown ();
   3685     global_ret = EXIT_FAILURE;
   3686     return;
   3687   }
   3688   if ( (NULL == args[0]) ||
   3689        (GNUNET_OK !=
   3690         GNUNET_STRINGS_string_to_data (args[0],
   3691                                        strlen (args[0]),
   3692                                        &partner_pub,
   3693                                        sizeof (partner_pub))) )
   3694   {
   3695     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3696                 "You must specify the partner master public key as first argument for this subcommand\n");
   3697     GNUNET_SCHEDULER_shutdown ();
   3698     global_ret = EXIT_INVALIDARGUMENT;
   3699     return;
   3700   }
   3701   if ( (NULL == args[1]) ||
   3702        (0 != strncmp ("http",
   3703                       args[1],
   3704                       strlen ("http"))) )
   3705   {
   3706     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3707                 "You must specify the partner's base URL as the 2nd argument to this subcommand\n");
   3708     GNUNET_SCHEDULER_shutdown ();
   3709     global_ret = EXIT_INVALIDARGUMENT;
   3710     return;
   3711   }
   3712   partner_base_url = args[1];
   3713   if ( (NULL == args[2]) ||
   3714        (GNUNET_OK !=
   3715         TALER_string_to_amount (args[2],
   3716                                 &wad_fee)) )
   3717   {
   3718     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3719                 "Invalid amount `%s' specified for wad fee of partner\n",
   3720                 args[2]);
   3721     GNUNET_SCHEDULER_shutdown ();
   3722     global_ret = EXIT_INVALIDARGUMENT;
   3723     return;
   3724   }
   3725   if ( (NULL == args[3]) ||
   3726        (GNUNET_OK !=
   3727         GNUNET_STRINGS_fancy_time_to_relative (args[3],
   3728                                                &wad_frequency)) )
   3729   {
   3730     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3731                 "Invalid wad frequency `%s' specified for add partner\n",
   3732                 args[3]);
   3733     GNUNET_SCHEDULER_shutdown ();
   3734     global_ret = EXIT_INVALIDARGUMENT;
   3735     return;
   3736   }
   3737   if ( (NULL == args[4]) ||
   3738        ( (1 != sscanf (args[4],
   3739                        "%u%c",
   3740                        &year,
   3741                        &dummy)) &&
   3742          (0 != strcasecmp ("now",
   3743                            args[4])) ) )
   3744   {
   3745     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3746                 "Invalid year `%s' specified for add partner\n",
   3747                 args[4]);
   3748     GNUNET_SCHEDULER_shutdown ();
   3749     global_ret = EXIT_INVALIDARGUMENT;
   3750     return;
   3751   }
   3752   if (0 == strcasecmp ("now",
   3753                        args[4]))
   3754     year = GNUNET_TIME_get_current_year ();
   3755   start_date = GNUNET_TIME_absolute_to_timestamp (
   3756     GNUNET_TIME_year_to_time (year));
   3757   end_date = GNUNET_TIME_absolute_to_timestamp (
   3758     GNUNET_TIME_year_to_time (year + 1));
   3759 
   3760   if (GNUNET_OK !=
   3761       load_offline_key (GNUNET_NO))
   3762     return;
   3763   TALER_exchange_offline_partner_details_sign (&partner_pub,
   3764                                                start_date,
   3765                                                end_date,
   3766                                                wad_frequency,
   3767                                                &wad_fee,
   3768                                                partner_base_url,
   3769                                                &master_priv,
   3770                                                &master_sig);
   3771   output_operation (OP_ADD_PARTNER,
   3772                     GNUNET_JSON_PACK (
   3773                       GNUNET_JSON_pack_string ("partner_base_url",
   3774                                                partner_base_url),
   3775                       GNUNET_JSON_pack_time_rel ("wad_frequency",
   3776                                                  wad_frequency),
   3777                       GNUNET_JSON_pack_timestamp ("start_date",
   3778                                                   start_date),
   3779                       GNUNET_JSON_pack_timestamp ("end_date",
   3780                                                   end_date),
   3781                       GNUNET_JSON_pack_data_auto ("partner_pub",
   3782                                                   &partner_pub),
   3783                       GNUNET_JSON_pack_data_auto ("master_sig",
   3784                                                   &master_sig)));
   3785   next (args + 5);
   3786 }
   3787 
   3788 
   3789 /**
   3790  * Enable or disable AML staff.
   3791  *
   3792  * @param is_active true to enable, false to disable
   3793  * @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)
   3794  */
   3795 static void
   3796 do_set_aml_staff (bool is_active,
   3797                   char *const *args)
   3798 {
   3799   struct TALER_AmlOfficerPublicKeyP officer_pub;
   3800   const char *officer_name;
   3801   bool read_only;
   3802   struct TALER_MasterSignatureP master_sig;
   3803   struct GNUNET_TIME_Timestamp now
   3804     = GNUNET_TIME_timestamp_get ();
   3805 
   3806   if (NULL != in)
   3807   {
   3808     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3809                 "Downloaded data was not consumed, not updating AML staff status\n");
   3810     GNUNET_SCHEDULER_shutdown ();
   3811     global_ret = EXIT_FAILURE;
   3812     return;
   3813   }
   3814   if ( (NULL == args[0]) ||
   3815        (GNUNET_OK !=
   3816         GNUNET_STRINGS_string_to_data (args[0],
   3817                                        strlen (args[0]),
   3818                                        &officer_pub,
   3819                                        sizeof (officer_pub))) )
   3820   {
   3821     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3822                 "You must specify the AML officer's public key as first argument for this subcommand\n");
   3823     GNUNET_SCHEDULER_shutdown ();
   3824     global_ret = EXIT_INVALIDARGUMENT;
   3825     return;
   3826   }
   3827   if (NULL == args[1])
   3828   {
   3829     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3830                 "You must specify the officer's legal name as the 2nd argument to this subcommand\n");
   3831     GNUNET_SCHEDULER_shutdown ();
   3832     global_ret = EXIT_INVALIDARGUMENT;
   3833     return;
   3834   }
   3835   officer_name = args[1];
   3836   if (is_active)
   3837   {
   3838     if ( (NULL == args[2]) ||
   3839          ( (0 != strcmp (args[2],
   3840                          "ro")) &&
   3841            (0 != strcmp (args[2],
   3842                          "rw")) ) )
   3843     {
   3844       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3845                   "You must specify 'ro' or 'rw' (and not `%s') for the access level\n",
   3846                   args[2]);
   3847       GNUNET_SCHEDULER_shutdown ();
   3848       global_ret = EXIT_INVALIDARGUMENT;
   3849       return;
   3850     }
   3851     read_only = (0 == strcmp (args[2],
   3852                               "ro"));
   3853   }
   3854   else
   3855   {
   3856     read_only = true;
   3857   }
   3858   if (GNUNET_OK !=
   3859       load_offline_key (GNUNET_NO))
   3860     return;
   3861   TALER_exchange_offline_aml_officer_status_sign (&officer_pub,
   3862                                                   officer_name,
   3863                                                   now,
   3864                                                   is_active,
   3865                                                   read_only,
   3866                                                   &master_priv,
   3867                                                   &master_sig);
   3868   output_operation (OP_UPDATE_AML_STAFF,
   3869                     GNUNET_JSON_PACK (
   3870                       GNUNET_JSON_pack_string ("officer_name",
   3871                                                officer_name),
   3872                       GNUNET_JSON_pack_timestamp ("change_date",
   3873                                                   now),
   3874                       GNUNET_JSON_pack_bool ("is_active",
   3875                                              is_active),
   3876                       GNUNET_JSON_pack_bool ("read_only",
   3877                                              read_only),
   3878                       GNUNET_JSON_pack_data_auto ("officer_pub",
   3879                                                   &officer_pub),
   3880                       GNUNET_JSON_pack_data_auto ("master_sig",
   3881                                                   &master_sig)));
   3882   next (args + (is_active ? 3 : 2));
   3883 }
   3884 
   3885 
   3886 /**
   3887  * Disable AML staff.
   3888  *
   3889  * @param args the array of command-line arguments to process next;
   3890  *        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)
   3891  */
   3892 static void
   3893 disable_aml_staff (char *const *args)
   3894 {
   3895   do_set_aml_staff (false,
   3896                     args);
   3897 }
   3898 
   3899 
   3900 /**
   3901  * Enable AML staff.
   3902  *
   3903  * @param args the array of command-line arguments to process next;
   3904  *        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)
   3905  */
   3906 static void
   3907 enable_aml_staff (char *const *args)
   3908 {
   3909   do_set_aml_staff (true,
   3910                     args);
   3911 }
   3912 
   3913 
   3914 /**
   3915  * Function called with information about future keys.  Dumps the JSON output
   3916  * (on success), either into an internal buffer or to stdout (depending on
   3917  * whether there are subsequent commands).
   3918  *
   3919  * @param args the remaining args
   3920  * @param mgr response data
   3921  */
   3922 static void
   3923 download_cb (char *const *args,
   3924              const struct TALER_EXCHANGE_GetManagementKeysResponse *mgr)
   3925 {
   3926   const struct TALER_EXCHANGE_HttpResponse *hr = &mgr->hr;
   3927 
   3928   mgkh = NULL;
   3929   switch (hr->http_status)
   3930   {
   3931   case MHD_HTTP_OK:
   3932     break;
   3933   default:
   3934     if (0 != hr->http_status)
   3935       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3936                   "Failed to download keys from `%s': %s (HTTP status: %u/%u)\n",
   3937                   CFG_exchange_url,
   3938                   hr->hint,
   3939                   hr->http_status,
   3940                   (unsigned int) hr->ec);
   3941     else
   3942       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3943                   "Failed to download keys from `%s' (no HTTP response)\n",
   3944                   CFG_exchange_url);
   3945     GNUNET_SCHEDULER_shutdown ();
   3946     global_ret = EXIT_FAILURE;
   3947     return;
   3948   }
   3949   in = GNUNET_JSON_PACK (
   3950     GNUNET_JSON_pack_string ("operation",
   3951                              OP_INPUT_KEYS),
   3952     GNUNET_JSON_pack_object_incref ("arguments",
   3953                                     (json_t *) hr->reply));
   3954   if (NULL == args[0])
   3955   {
   3956     json_dumpf (in,
   3957                 stdout,
   3958                 JSON_INDENT (2));
   3959     json_decref (in);
   3960     in = NULL;
   3961   }
   3962   next (args);
   3963 }
   3964 
   3965 
   3966 /**
   3967  * Download future keys.
   3968  *
   3969  * @param args the array of command-line arguments to process next
   3970  */
   3971 static void
   3972 do_download (char *const *args)
   3973 {
   3974   if ( (NULL == CFG_exchange_url) &&
   3975        (GNUNET_OK !=
   3976         GNUNET_CONFIGURATION_get_value_string (kcfg,
   3977                                                "exchange",
   3978                                                "BASE_URL",
   3979                                                &CFG_exchange_url)) )
   3980   {
   3981     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   3982                                "exchange",
   3983                                "BASE_URL");
   3984     GNUNET_SCHEDULER_shutdown ();
   3985     global_ret = EXIT_NOTCONFIGURED;
   3986     return;
   3987   }
   3988   mgkh = TALER_EXCHANGE_get_management_keys_create (ctx,
   3989                                                     CFG_exchange_url);
   3990   TALER_EXCHANGE_get_management_keys_start (mgkh,
   3991                                             &download_cb,
   3992                                             (void *) args);
   3993 }
   3994 
   3995 
   3996 /**
   3997  * Check that the security module keys are the same as before.  If we had no
   3998  * keys in store before, remember them (Trust On First Use).
   3999  *
   4000  * @param secmset security module keys
   4001  * @return #GNUNET_OK if keys match with what we have in store
   4002  *         #GNUNET_NO if we had nothing in store but now do
   4003  *         #GNUNET_SYSERR if keys changed from what we remember or other error
   4004  */
   4005 static enum GNUNET_GenericReturnValue
   4006 tofu_check (const struct TALER_SecurityModulePublicKeySetP *secmset)
   4007 {
   4008   char *fn;
   4009   struct TALER_SecurityModulePublicKeySetP oldset;
   4010   ssize_t ret;
   4011 
   4012   if (GNUNET_OK !=
   4013       GNUNET_CONFIGURATION_get_value_filename (kcfg,
   4014                                                "exchange-offline",
   4015                                                "SECM_TOFU_FILE",
   4016                                                &fn))
   4017   {
   4018     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   4019                                "exchange-offline",
   4020                                "SECM_TOFU_FILE");
   4021     return GNUNET_SYSERR;
   4022   }
   4023   if (GNUNET_OK ==
   4024       GNUNET_DISK_file_test (fn))
   4025   {
   4026     ret = GNUNET_DISK_fn_read (fn,
   4027                                &oldset,
   4028                                sizeof (oldset));
   4029     if (GNUNET_SYSERR != ret)
   4030     {
   4031       if (ret != sizeof (oldset))
   4032       {
   4033         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4034                     "File `%s' corrupt\n",
   4035                     fn);
   4036         GNUNET_free (fn);
   4037         return GNUNET_SYSERR;
   4038       }
   4039       /* TOFU check */
   4040       if (0 != memcmp (&oldset,
   4041                        secmset,
   4042                        sizeof (*secmset)))
   4043       {
   4044         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4045                     "Fatal: security module keys changed (file `%s')!\n",
   4046                     fn);
   4047         GNUNET_free (fn);
   4048         return GNUNET_SYSERR;
   4049       }
   4050       GNUNET_free (fn);
   4051       return GNUNET_OK;
   4052     }
   4053   }
   4054 
   4055   {
   4056     char *key;
   4057 
   4058     /* check against SECMOD-keys pinned in configuration */
   4059     if (GNUNET_OK ==
   4060         GNUNET_CONFIGURATION_get_value_string (kcfg,
   4061                                                "exchange-offline",
   4062                                                "SECM_ESIGN_PUBKEY",
   4063                                                &key))
   4064     {
   4065       struct TALER_SecurityModulePublicKeyP k;
   4066 
   4067       if (GNUNET_OK !=
   4068           GNUNET_STRINGS_string_to_data (key,
   4069                                          strlen (key),
   4070                                          &k,
   4071                                          sizeof (k)))
   4072       {
   4073         GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
   4074                                    "exchange-offline",
   4075                                    "SECM_ESIGN_PUBKEY",
   4076                                    "key malformed");
   4077         GNUNET_free (key);
   4078         GNUNET_free (fn);
   4079         return GNUNET_SYSERR;
   4080       }
   4081       GNUNET_free (key);
   4082       if (0 !=
   4083           GNUNET_memcmp (&k,
   4084                          &secmset->eddsa))
   4085       {
   4086         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4087                     "ESIGN security module key does not match SECM_ESIGN_PUBKEY in configuration\n");
   4088         GNUNET_free (fn);
   4089         return GNUNET_SYSERR;
   4090       }
   4091     }
   4092     if (GNUNET_OK ==
   4093         GNUNET_CONFIGURATION_get_value_string (kcfg,
   4094                                                "exchange-offline",
   4095                                                "SECM_DENOM_PUBKEY",
   4096                                                &key))
   4097     {
   4098       struct TALER_SecurityModulePublicKeyP k;
   4099 
   4100       if (GNUNET_OK !=
   4101           GNUNET_STRINGS_string_to_data (key,
   4102                                          strlen (key),
   4103                                          &k,
   4104                                          sizeof (k)))
   4105       {
   4106         GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
   4107                                    "exchange-offline",
   4108                                    "SECM_DENOM_PUBKEY",
   4109                                    "key malformed");
   4110         GNUNET_free (key);
   4111         GNUNET_free (fn);
   4112         return GNUNET_SYSERR;
   4113       }
   4114       GNUNET_free (key);
   4115       if (0 !=
   4116           GNUNET_memcmp (&k,
   4117                          &secmset->rsa))
   4118       {
   4119         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4120                     "DENOM security module key does not match SECM_DENOM_PUBKEY in configuration\n");
   4121         GNUNET_free (fn);
   4122         return GNUNET_SYSERR;
   4123       }
   4124     }
   4125     if (GNUNET_OK ==
   4126         GNUNET_CONFIGURATION_get_value_string (kcfg,
   4127                                                "exchange-offline",
   4128                                                "SECM_DENOM_CS_PUBKEY",
   4129                                                &key))
   4130     {
   4131       struct TALER_SecurityModulePublicKeyP k;
   4132 
   4133       if (GNUNET_OK !=
   4134           GNUNET_STRINGS_string_to_data (key,
   4135                                          strlen (key),
   4136                                          &k,
   4137                                          sizeof (k)))
   4138       {
   4139         GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
   4140                                    "exchange-offline",
   4141                                    "SECM_DENOM_CS_PUBKEY",
   4142                                    "key malformed");
   4143         GNUNET_free (key);
   4144         GNUNET_free (fn);
   4145         return GNUNET_SYSERR;
   4146       }
   4147       GNUNET_free (key);
   4148       if (0 !=
   4149           GNUNET_memcmp (&k,
   4150                          &secmset->cs))
   4151       {
   4152         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4153                     "DENOM security module key does not match SECM_DENOM_CS_PUBKEY in configuration\n");
   4154         GNUNET_free (fn);
   4155         return GNUNET_SYSERR;
   4156       }
   4157     }
   4158   }
   4159   if (GNUNET_OK !=
   4160       GNUNET_DISK_directory_create_for_file (fn))
   4161   {
   4162     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4163                 "Failed create directory to store key material in file `%s'\n",
   4164                 fn);
   4165     GNUNET_free (fn);
   4166     return GNUNET_SYSERR;
   4167   }
   4168   /* persist keys for future runs */
   4169   if (GNUNET_OK !=
   4170       GNUNET_DISK_fn_write (fn,
   4171                             secmset,
   4172                             sizeof (oldset),
   4173                             GNUNET_DISK_PERM_USER_READ))
   4174   {
   4175     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4176                 "Failed to store key material in file `%s'\n",
   4177                 fn);
   4178     GNUNET_free (fn);
   4179     return GNUNET_SYSERR;
   4180   }
   4181   GNUNET_free (fn);
   4182   return GNUNET_NO;
   4183 }
   4184 
   4185 
   4186 /**
   4187  * Output @a signkeys for human consumption.
   4188  *
   4189  * @param secm_pub security module public key used to sign the denominations
   4190  * @param signkeys keys to output
   4191  * @return #GNUNET_OK on success
   4192  */
   4193 static enum GNUNET_GenericReturnValue
   4194 show_signkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub,
   4195                const json_t *signkeys)
   4196 {
   4197   size_t index;
   4198   json_t *value;
   4199 
   4200   json_array_foreach (signkeys, index, value) {
   4201     const char *err_name;
   4202     unsigned int err_line;
   4203     struct TALER_ExchangePublicKeyP exchange_pub;
   4204     struct TALER_SecurityModuleSignatureP secm_sig;
   4205     struct GNUNET_TIME_Timestamp start_time;
   4206     struct GNUNET_TIME_Timestamp sign_end;
   4207     struct GNUNET_TIME_Timestamp legal_end;
   4208     struct GNUNET_TIME_Relative duration;
   4209     struct GNUNET_JSON_Specification spec[] = {
   4210       GNUNET_JSON_spec_timestamp ("stamp_start",
   4211                                   &start_time),
   4212       GNUNET_JSON_spec_timestamp ("stamp_expire",
   4213                                   &sign_end),
   4214       GNUNET_JSON_spec_timestamp ("stamp_end",
   4215                                   &legal_end),
   4216       GNUNET_JSON_spec_fixed_auto ("key",
   4217                                    &exchange_pub),
   4218       GNUNET_JSON_spec_fixed_auto ("signkey_secmod_sig",
   4219                                    &secm_sig),
   4220       GNUNET_JSON_spec_end ()
   4221     };
   4222 
   4223     if (GNUNET_OK !=
   4224         GNUNET_JSON_parse (value,
   4225                            spec,
   4226                            &err_name,
   4227                            &err_line))
   4228     {
   4229       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4230                   "Invalid input for signing key to 'show': %s#%u at %u (skipping)\n",
   4231                   err_name,
   4232                   err_line,
   4233                   (unsigned int) index);
   4234       json_dumpf (value,
   4235                   stderr,
   4236                   JSON_INDENT (2));
   4237       global_ret = EXIT_FAILURE;
   4238       GNUNET_SCHEDULER_shutdown ();
   4239       return GNUNET_SYSERR;
   4240     }
   4241     duration = GNUNET_TIME_absolute_get_difference (start_time.abs_time,
   4242                                                     sign_end.abs_time);
   4243     if (GNUNET_OK !=
   4244         TALER_exchange_secmod_eddsa_verify (&exchange_pub,
   4245                                             start_time,
   4246                                             duration,
   4247                                             secm_pub,
   4248                                             &secm_sig))
   4249     {
   4250       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4251                   "Invalid security module signature for signing key %s (aborting)\n",
   4252                   TALER_B2S (&exchange_pub));
   4253       global_ret = EXIT_FAILURE;
   4254       GNUNET_SCHEDULER_shutdown ();
   4255       return GNUNET_SYSERR;
   4256     }
   4257     {
   4258       char *legal_end_s;
   4259 
   4260       legal_end_s = GNUNET_strdup (
   4261         GNUNET_TIME_timestamp2s (legal_end));
   4262       printf ("EXCHANGE-KEY %s starting at %s (used for: %s, legal end: %s)\n",
   4263               TALER_B2S (&exchange_pub),
   4264               GNUNET_TIME_timestamp2s (start_time),
   4265               GNUNET_TIME_relative2s (duration,
   4266                                       false),
   4267               legal_end_s);
   4268       GNUNET_free (legal_end_s);
   4269     }
   4270   }
   4271   return GNUNET_OK;
   4272 }
   4273 
   4274 
   4275 /**
   4276  * Output @a denomkeys for human consumption.
   4277  *
   4278  * @param secm_pub_rsa security module public key used to sign the RSA denominations
   4279  * @param secm_pub_cs security module public key used to sign the CS denominations
   4280  * @param denomkeys keys to output
   4281  * @return #GNUNET_OK on success
   4282  */
   4283 static enum GNUNET_GenericReturnValue
   4284 show_denomkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub_rsa,
   4285                 const struct TALER_SecurityModulePublicKeyP *secm_pub_cs,
   4286                 const json_t *denomkeys)
   4287 {
   4288   size_t index;
   4289   json_t *value;
   4290 
   4291   json_array_foreach (denomkeys, index, value) {
   4292     const char *err_name;
   4293     unsigned int err_line;
   4294     const char *section_name;
   4295     struct TALER_DenominationPublicKey denom_pub;
   4296     struct GNUNET_TIME_Timestamp stamp_start;
   4297     struct GNUNET_TIME_Timestamp stamp_expire_withdraw;
   4298     struct GNUNET_TIME_Timestamp stamp_expire_deposit;
   4299     struct GNUNET_TIME_Timestamp stamp_expire_legal;
   4300     struct TALER_Amount coin_value;
   4301     struct TALER_DenomFeeSet fees;
   4302     struct TALER_SecurityModuleSignatureP secm_sig;
   4303     struct GNUNET_JSON_Specification spec[] = {
   4304       GNUNET_JSON_spec_string ("section_name",
   4305                                &section_name),
   4306       TALER_JSON_spec_denom_pub ("denom_pub",
   4307                                  &denom_pub),
   4308       TALER_JSON_spec_amount ("value",
   4309                               currency,
   4310                               &coin_value),
   4311       TALER_JSON_SPEC_DENOM_FEES ("fee",
   4312                                   currency,
   4313                                   &fees),
   4314       GNUNET_JSON_spec_timestamp ("stamp_start",
   4315                                   &stamp_start),
   4316       GNUNET_JSON_spec_timestamp ("stamp_expire_withdraw",
   4317                                   &stamp_expire_withdraw),
   4318       GNUNET_JSON_spec_timestamp ("stamp_expire_deposit",
   4319                                   &stamp_expire_deposit),
   4320       GNUNET_JSON_spec_timestamp ("stamp_expire_legal",
   4321                                   &stamp_expire_legal),
   4322       GNUNET_JSON_spec_fixed_auto ("denom_secmod_sig",
   4323                                    &secm_sig),
   4324       GNUNET_JSON_spec_end ()
   4325     };
   4326     struct GNUNET_TIME_Relative duration;
   4327     struct TALER_DenominationHashP h_denom_pub;
   4328     enum GNUNET_GenericReturnValue ok;
   4329 
   4330     if (GNUNET_OK !=
   4331         GNUNET_JSON_parse (value,
   4332                            spec,
   4333                            &err_name,
   4334                            &err_line))
   4335     {
   4336       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4337                   "Invalid input for denomination key to 'show': %s#%u at %u (skipping)\n",
   4338                   err_name,
   4339                   err_line,
   4340                   (unsigned int) index);
   4341       json_dumpf (value,
   4342                   stderr,
   4343                   JSON_INDENT (2));
   4344       GNUNET_JSON_parse_free (spec);
   4345       global_ret = EXIT_FAILURE;
   4346       GNUNET_SCHEDULER_shutdown ();
   4347       return GNUNET_SYSERR;
   4348     }
   4349     duration = GNUNET_TIME_absolute_get_difference (
   4350       stamp_start.abs_time,
   4351       stamp_expire_withdraw.abs_time);
   4352     TALER_denom_pub_hash (&denom_pub,
   4353                           &h_denom_pub);
   4354     switch (denom_pub.bsign_pub_key->cipher)
   4355     {
   4356     case GNUNET_CRYPTO_BSA_RSA:
   4357       {
   4358         struct TALER_RsaPubHashP h_rsa;
   4359 
   4360         TALER_rsa_pub_hash (denom_pub.bsign_pub_key->details.rsa_public_key,
   4361                             &h_rsa);
   4362         ok = TALER_exchange_secmod_rsa_verify (&h_rsa,
   4363                                                section_name,
   4364                                                stamp_start,
   4365                                                duration,
   4366                                                secm_pub_rsa,
   4367                                                &secm_sig);
   4368       }
   4369       break;
   4370     case GNUNET_CRYPTO_BSA_CS:
   4371       {
   4372         struct TALER_CsPubHashP h_cs;
   4373 
   4374         TALER_cs_pub_hash (&denom_pub.bsign_pub_key->details.cs_public_key,
   4375                            &h_cs);
   4376         ok = TALER_exchange_secmod_cs_verify (&h_cs,
   4377                                               section_name,
   4378                                               stamp_start,
   4379                                               duration,
   4380                                               secm_pub_cs,
   4381                                               &secm_sig);
   4382       }
   4383       break;
   4384     default:
   4385       GNUNET_break (0);
   4386       ok = GNUNET_SYSERR;
   4387       break;
   4388     }
   4389     if (GNUNET_OK != ok)
   4390     {
   4391       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4392                   "Invalid security module signature for denomination key %s (aborting)\n",
   4393                   GNUNET_h2s (&h_denom_pub.hash));
   4394       global_ret = EXIT_FAILURE;
   4395       GNUNET_SCHEDULER_shutdown ();
   4396       return GNUNET_SYSERR;
   4397     }
   4398 
   4399     {
   4400       char *withdraw_fee_s;
   4401       char *deposit_fee_s;
   4402       char *refresh_fee_s;
   4403       char *refund_fee_s;
   4404       char *deposit_s;
   4405       char *legal_s;
   4406 
   4407       withdraw_fee_s = TALER_amount_to_string (&fees.withdraw);
   4408       deposit_fee_s = TALER_amount_to_string (&fees.deposit);
   4409       refresh_fee_s = TALER_amount_to_string (&fees.refresh);
   4410       refund_fee_s = TALER_amount_to_string (&fees.refund);
   4411       deposit_s = GNUNET_strdup (
   4412         GNUNET_TIME_timestamp2s (stamp_expire_deposit));
   4413       legal_s = GNUNET_strdup (
   4414         GNUNET_TIME_timestamp2s (stamp_expire_legal));
   4415 
   4416       printf (
   4417         "DENOMINATION-KEY(%s) %s of value %s starting at %s "
   4418         "(used for: %s, deposit until: %s legal end: %s) with fees %s/%s/%s/%s\n",
   4419         section_name,
   4420         TALER_B2S (&h_denom_pub),
   4421         TALER_amount2s (&coin_value),
   4422         GNUNET_TIME_timestamp2s (stamp_start),
   4423         GNUNET_TIME_relative2s (duration,
   4424                                 false),
   4425         deposit_s,
   4426         legal_s,
   4427         withdraw_fee_s,
   4428         deposit_fee_s,
   4429         refresh_fee_s,
   4430         refund_fee_s);
   4431       GNUNET_free (withdraw_fee_s);
   4432       GNUNET_free (deposit_fee_s);
   4433       GNUNET_free (refresh_fee_s);
   4434       GNUNET_free (refund_fee_s);
   4435       GNUNET_free (deposit_s);
   4436       GNUNET_free (legal_s);
   4437     }
   4438 
   4439     GNUNET_JSON_parse_free (spec);
   4440   }
   4441   return GNUNET_OK;
   4442 }
   4443 
   4444 
   4445 /**
   4446  * Parse the input of exchange keys for the 'show' and 'sign' commands.
   4447  *
   4448  * @param command_name name of the command, for logging
   4449  * @return NULL on error, otherwise the keys details to be free'd by caller
   4450  */
   4451 static json_t *
   4452 parse_keys_input (const char *command_name)
   4453 {
   4454   const char *op_str;
   4455   json_t *keys;
   4456   struct GNUNET_JSON_Specification spec[] = {
   4457     GNUNET_JSON_spec_json ("arguments",
   4458                            &keys),
   4459     GNUNET_JSON_spec_string ("operation",
   4460                              &op_str),
   4461     GNUNET_JSON_spec_end ()
   4462   };
   4463   const char *err_name;
   4464   unsigned int err_line;
   4465 
   4466   if (NULL == in)
   4467   {
   4468     json_error_t err;
   4469 
   4470     in = json_loadf (stdin,
   4471                      JSON_REJECT_DUPLICATES,
   4472                      &err);
   4473     if (NULL == in)
   4474     {
   4475       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4476                   "Failed to read JSON input: %s at %d:%s (offset: %d)\n",
   4477                   err.text,
   4478                   err.line,
   4479                   err.source,
   4480                   err.position);
   4481       global_ret = EXIT_FAILURE;
   4482       GNUNET_SCHEDULER_shutdown ();
   4483       return NULL;
   4484     }
   4485   }
   4486   if (GNUNET_OK !=
   4487       GNUNET_JSON_parse (in,
   4488                          spec,
   4489                          &err_name,
   4490                          &err_line))
   4491   {
   4492     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4493                 "Invalid input to '%s': %s#%u (skipping)\n",
   4494                 command_name,
   4495                 err_name,
   4496                 err_line);
   4497     json_dumpf (in,
   4498                 stderr,
   4499                 JSON_INDENT (2));
   4500     global_ret = EXIT_FAILURE;
   4501     GNUNET_SCHEDULER_shutdown ();
   4502     return NULL;
   4503   }
   4504   if (0 != strcmp (op_str,
   4505                    OP_INPUT_KEYS))
   4506   {
   4507     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4508                 "Invalid input to '%s' : operation is `%s', expected `%s'\n",
   4509                 command_name,
   4510                 op_str,
   4511                 OP_INPUT_KEYS);
   4512     GNUNET_JSON_parse_free (spec);
   4513     return NULL;
   4514   }
   4515   json_decref (in);
   4516   in = NULL;
   4517   return keys;
   4518 }
   4519 
   4520 
   4521 /**
   4522  * Show future keys.
   4523  *
   4524  * @param args the array of command-line arguments to process next
   4525  */
   4526 static void
   4527 do_show (char *const *args)
   4528 {
   4529   json_t *keys;
   4530   const char *err_name;
   4531   unsigned int err_line;
   4532   const json_t *denomkeys;
   4533   const json_t *signkeys;
   4534   struct TALER_MasterPublicKeyP mpub;
   4535   struct TALER_SecurityModulePublicKeySetP secmset;
   4536   struct GNUNET_JSON_Specification spec[] = {
   4537     GNUNET_JSON_spec_array_const ("future_denoms",
   4538                                   &denomkeys),
   4539     GNUNET_JSON_spec_array_const ("future_signkeys",
   4540                                   &signkeys),
   4541     GNUNET_JSON_spec_fixed_auto ("master_pub",
   4542                                  &mpub),
   4543     GNUNET_JSON_spec_fixed_auto ("denom_secmod_public_key",
   4544                                  &secmset.rsa),
   4545     GNUNET_JSON_spec_fixed_auto ("denom_secmod_cs_public_key",
   4546                                  &secmset.cs),
   4547     GNUNET_JSON_spec_fixed_auto ("signkey_secmod_public_key",
   4548                                  &secmset.eddsa),
   4549     GNUNET_JSON_spec_end ()
   4550   };
   4551 
   4552   keys = parse_keys_input ("show");
   4553   if (NULL == keys)
   4554     return;
   4555 
   4556   if (GNUNET_OK !=
   4557       load_offline_key (GNUNET_NO))
   4558     return;
   4559   if (GNUNET_OK !=
   4560       GNUNET_JSON_parse (keys,
   4561                          spec,
   4562                          &err_name,
   4563                          &err_line))
   4564   {
   4565     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4566                 "Invalid input to 'show': %s #%u (skipping)\n",
   4567                 err_name,
   4568                 err_line);
   4569     json_dumpf (in,
   4570                 stderr,
   4571                 JSON_INDENT (2));
   4572     global_ret = EXIT_FAILURE;
   4573     GNUNET_SCHEDULER_shutdown ();
   4574     json_decref (keys);
   4575     return;
   4576   }
   4577   if (0 !=
   4578       GNUNET_memcmp (&master_pub,
   4579                      &mpub))
   4580   {
   4581     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4582                 "Fatal: exchange uses different master key!\n");
   4583     global_ret = EXIT_FAILURE;
   4584     GNUNET_SCHEDULER_shutdown ();
   4585     json_decref (keys);
   4586     return;
   4587   }
   4588   if (GNUNET_SYSERR ==
   4589       tofu_check (&secmset))
   4590   {
   4591     global_ret = EXIT_FAILURE;
   4592     GNUNET_SCHEDULER_shutdown ();
   4593     json_decref (keys);
   4594     return;
   4595   }
   4596   if ( (GNUNET_OK !=
   4597         show_signkeys (&secmset.eddsa,
   4598                        signkeys)) ||
   4599        (GNUNET_OK !=
   4600         show_denomkeys (&secmset.rsa,
   4601                         &secmset.cs,
   4602                         denomkeys)) )
   4603   {
   4604     global_ret = EXIT_FAILURE;
   4605     GNUNET_SCHEDULER_shutdown ();
   4606     json_decref (keys);
   4607     return;
   4608   }
   4609   json_decref (keys);
   4610   next (args);
   4611 }
   4612 
   4613 
   4614 /**
   4615  * Sign @a signkeys with offline key.
   4616  *
   4617  * @param secm_pub security module public key used to sign the denominations
   4618  * @param signkeys keys to output
   4619  * @param[in,out] result array where to output the signatures
   4620  * @return #GNUNET_OK on success
   4621  */
   4622 static enum GNUNET_GenericReturnValue
   4623 sign_signkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub,
   4624                const json_t *signkeys,
   4625                json_t *result)
   4626 {
   4627   size_t index;
   4628   json_t *value;
   4629 
   4630   json_array_foreach (signkeys, index, value) {
   4631     const char *err_name;
   4632     unsigned int err_line;
   4633     struct TALER_ExchangePublicKeyP exchange_pub;
   4634     struct TALER_SecurityModuleSignatureP secm_sig;
   4635     struct GNUNET_TIME_Timestamp start_time;
   4636     struct GNUNET_TIME_Timestamp sign_end;
   4637     struct GNUNET_TIME_Timestamp legal_end;
   4638     struct GNUNET_TIME_Relative duration;
   4639     struct GNUNET_JSON_Specification spec[] = {
   4640       GNUNET_JSON_spec_timestamp ("stamp_start",
   4641                                   &start_time),
   4642       GNUNET_JSON_spec_timestamp ("stamp_expire",
   4643                                   &sign_end),
   4644       GNUNET_JSON_spec_timestamp ("stamp_end",
   4645                                   &legal_end),
   4646       GNUNET_JSON_spec_fixed_auto ("key",
   4647                                    &exchange_pub),
   4648       GNUNET_JSON_spec_fixed_auto ("signkey_secmod_sig",
   4649                                    &secm_sig),
   4650       GNUNET_JSON_spec_end ()
   4651     };
   4652 
   4653     if (GNUNET_OK !=
   4654         GNUNET_JSON_parse (value,
   4655                            spec,
   4656                            &err_name,
   4657                            &err_line))
   4658     {
   4659       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4660                   "Invalid input for signing key to 'show': %s #%u at %u (skipping)\n",
   4661                   err_name,
   4662                   err_line,
   4663                   (unsigned int) index);
   4664       json_dumpf (value,
   4665                   stderr,
   4666                   JSON_INDENT (2));
   4667       global_ret = EXIT_FAILURE;
   4668       GNUNET_SCHEDULER_shutdown ();
   4669       return GNUNET_SYSERR;
   4670     }
   4671 
   4672     duration = GNUNET_TIME_absolute_get_difference (start_time.abs_time,
   4673                                                     sign_end.abs_time);
   4674     if (GNUNET_OK !=
   4675         TALER_exchange_secmod_eddsa_verify (&exchange_pub,
   4676                                             start_time,
   4677                                             duration,
   4678                                             secm_pub,
   4679                                             &secm_sig))
   4680     {
   4681       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4682                   "Invalid security module signature for signing key %s (aborting)\n",
   4683                   TALER_B2S (&exchange_pub));
   4684       global_ret = EXIT_FAILURE;
   4685       GNUNET_SCHEDULER_shutdown ();
   4686       GNUNET_JSON_parse_free (spec);
   4687       return GNUNET_SYSERR;
   4688     }
   4689     {
   4690       struct TALER_MasterSignatureP master_sig;
   4691 
   4692       TALER_exchange_offline_signkey_validity_sign (&exchange_pub,
   4693                                                     start_time,
   4694                                                     sign_end,
   4695                                                     legal_end,
   4696                                                     &master_priv,
   4697                                                     &master_sig);
   4698       GNUNET_assert (0 ==
   4699                      json_array_append_new (
   4700                        result,
   4701                        GNUNET_JSON_PACK (
   4702                          GNUNET_JSON_pack_data_auto ("exchange_pub",
   4703                                                      &exchange_pub),
   4704                          GNUNET_JSON_pack_data_auto ("master_sig",
   4705                                                      &master_sig))));
   4706     }
   4707     GNUNET_JSON_parse_free (spec);
   4708   }
   4709   return GNUNET_OK;
   4710 }
   4711 
   4712 
   4713 /**
   4714  * Looks up the AGE_RESTRICTED setting for a denomination in the config and
   4715  * returns the age restriction (mask) accordingly.
   4716  *
   4717  * @param section_name Section in the configuration for the particular
   4718  *    denomination.
   4719  */
   4720 static struct TALER_AgeMask
   4721 load_age_mask (const char*section_name)
   4722 {
   4723   static const struct TALER_AgeMask null_mask = {0};
   4724   enum GNUNET_GenericReturnValue ret;
   4725 
   4726   if (GNUNET_YES !=
   4727       GNUNET_CONFIGURATION_get_value_yesno (kcfg,
   4728                                             "exchange",
   4729                                             "AGE_RESTRICTION_ENABLED"))
   4730     return null_mask;
   4731 
   4732   if (GNUNET_OK != (GNUNET_CONFIGURATION_have_value (
   4733                       kcfg,
   4734                       section_name,
   4735                       "AGE_RESTRICTED")))
   4736     return null_mask;
   4737 
   4738   ret = GNUNET_CONFIGURATION_get_value_yesno (kcfg,
   4739                                               section_name,
   4740                                               "AGE_RESTRICTED");
   4741   if (GNUNET_SYSERR == ret)
   4742     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
   4743                                section_name,
   4744                                "AGE_RESTRICTED",
   4745                                "Value must be YES or NO\n");
   4746   if (GNUNET_YES == ret)
   4747   {
   4748     char *groups = NULL;
   4749     struct TALER_AgeMask mask = {0};
   4750 
   4751     if (GNUNET_OK !=
   4752         GNUNET_CONFIGURATION_get_value_string (kcfg,
   4753                                                "exchange",
   4754                                                "AGE_GROUPS",
   4755                                                &groups))
   4756       groups = GNUNET_strdup ("8:10:12:14:16:18:21");
   4757     if (GNUNET_OK ==
   4758         TALER_parse_age_group_string (groups, &mask))
   4759     {
   4760       GNUNET_free (groups);
   4761       return mask;
   4762     }
   4763     GNUNET_free (groups);
   4764   }
   4765 
   4766   return null_mask;
   4767 }
   4768 
   4769 
   4770 /**
   4771  * Sign @a denomkeys with offline key.
   4772  *
   4773  * @param secm_pub_rsa security module public key used to sign the RSA denominations
   4774  * @param secm_pub_cs security module public key used to sign the CS denominations
   4775  * @param denomkeys keys to output
   4776  * @param[in,out] result array where to output the signatures
   4777  * @return #GNUNET_OK on success
   4778  */
   4779 static enum GNUNET_GenericReturnValue
   4780 sign_denomkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub_rsa,
   4781                 const struct TALER_SecurityModulePublicKeyP *secm_pub_cs,
   4782                 const json_t *denomkeys,
   4783                 json_t *result)
   4784 {
   4785   size_t index;
   4786   json_t *value;
   4787 
   4788   json_array_foreach (denomkeys, index, value) {
   4789     const char *err_name;
   4790     unsigned int err_line;
   4791     const char *section_name;
   4792     struct TALER_DenominationPublicKey denom_pub;
   4793     struct GNUNET_TIME_Timestamp stamp_start;
   4794     struct GNUNET_TIME_Timestamp stamp_expire_withdraw;
   4795     struct GNUNET_TIME_Timestamp stamp_expire_deposit;
   4796     struct GNUNET_TIME_Timestamp stamp_expire_legal;
   4797     struct TALER_Amount coin_value;
   4798     struct TALER_DenomFeeSet fees;
   4799     struct TALER_SecurityModuleSignatureP secm_sig;
   4800     struct GNUNET_JSON_Specification spec[] = {
   4801       GNUNET_JSON_spec_string ("section_name",
   4802                                &section_name),
   4803       TALER_JSON_spec_denom_pub ("denom_pub",
   4804                                  &denom_pub),
   4805       TALER_JSON_spec_amount ("value",
   4806                               currency,
   4807                               &coin_value),
   4808       TALER_JSON_SPEC_DENOM_FEES ("fee",
   4809                                   currency,
   4810                                   &fees),
   4811       GNUNET_JSON_spec_timestamp ("stamp_start",
   4812                                   &stamp_start),
   4813       GNUNET_JSON_spec_timestamp ("stamp_expire_withdraw",
   4814                                   &stamp_expire_withdraw),
   4815       GNUNET_JSON_spec_timestamp ("stamp_expire_deposit",
   4816                                   &stamp_expire_deposit),
   4817       GNUNET_JSON_spec_timestamp ("stamp_expire_legal",
   4818                                   &stamp_expire_legal),
   4819       GNUNET_JSON_spec_fixed_auto ("denom_secmod_sig",
   4820                                    &secm_sig),
   4821       GNUNET_JSON_spec_end ()
   4822     };
   4823     struct GNUNET_TIME_Relative duration;
   4824     struct TALER_DenominationHashP h_denom_pub;
   4825 
   4826     if (GNUNET_OK !=
   4827         GNUNET_JSON_parse (value,
   4828                            spec,
   4829                            &err_name,
   4830                            &err_line))
   4831     {
   4832       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4833                   "Invalid input for denomination key to 'sign': %s #%u at %u (skipping)\n",
   4834                   err_name,
   4835                   err_line,
   4836                   (unsigned int) index);
   4837       json_dumpf (value,
   4838                   stderr,
   4839                   JSON_INDENT (2));
   4840       GNUNET_JSON_parse_free (spec);
   4841       global_ret = EXIT_FAILURE;
   4842       GNUNET_SCHEDULER_shutdown ();
   4843       return GNUNET_SYSERR;
   4844     }
   4845     duration = GNUNET_TIME_absolute_get_difference (
   4846       stamp_start.abs_time,
   4847       stamp_expire_withdraw.abs_time);
   4848 
   4849     /* Load the age mask, if applicable to this denomination */
   4850     denom_pub.age_mask = load_age_mask (section_name);
   4851 
   4852     TALER_denom_pub_hash (&denom_pub,
   4853                           &h_denom_pub);
   4854 
   4855     switch (denom_pub.bsign_pub_key->cipher)
   4856     {
   4857     case GNUNET_CRYPTO_BSA_RSA:
   4858       {
   4859         struct TALER_RsaPubHashP h_rsa;
   4860 
   4861         TALER_rsa_pub_hash (denom_pub.bsign_pub_key->details.rsa_public_key,
   4862                             &h_rsa);
   4863         if (GNUNET_OK !=
   4864             TALER_exchange_secmod_rsa_verify (&h_rsa,
   4865                                               section_name,
   4866                                               stamp_start,
   4867                                               duration,
   4868                                               secm_pub_rsa,
   4869                                               &secm_sig))
   4870         {
   4871           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4872                       "Invalid security module signature for denomination key %s (aborting)\n",
   4873                       GNUNET_h2s (&h_denom_pub.hash));
   4874           global_ret = EXIT_FAILURE;
   4875           GNUNET_SCHEDULER_shutdown ();
   4876           GNUNET_JSON_parse_free (spec);
   4877           return GNUNET_SYSERR;
   4878         }
   4879       }
   4880       break;
   4881     case GNUNET_CRYPTO_BSA_CS:
   4882       {
   4883         struct TALER_CsPubHashP h_cs;
   4884 
   4885         TALER_cs_pub_hash (&denom_pub.bsign_pub_key->details.cs_public_key,
   4886                            &h_cs);
   4887         if (GNUNET_OK !=
   4888             TALER_exchange_secmod_cs_verify (&h_cs,
   4889                                              section_name,
   4890                                              stamp_start,
   4891                                              duration,
   4892                                              secm_pub_cs,
   4893                                              &secm_sig))
   4894         {
   4895           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4896                       "Invalid security module signature for denomination key %s (aborting)\n",
   4897                       GNUNET_h2s (&h_denom_pub.hash));
   4898           global_ret = EXIT_FAILURE;
   4899           GNUNET_SCHEDULER_shutdown ();
   4900           GNUNET_JSON_parse_free (spec);
   4901           return GNUNET_SYSERR;
   4902         }
   4903       }
   4904       break;
   4905     default:
   4906       global_ret = EXIT_FAILURE;
   4907       GNUNET_SCHEDULER_shutdown ();
   4908       GNUNET_JSON_parse_free (spec);
   4909       return GNUNET_SYSERR;
   4910     }
   4911 
   4912     {
   4913       struct TALER_MasterSignatureP master_sig;
   4914 
   4915       TALER_exchange_offline_denom_validity_sign (&h_denom_pub,
   4916                                                   stamp_start,
   4917                                                   stamp_expire_withdraw,
   4918                                                   stamp_expire_deposit,
   4919                                                   stamp_expire_legal,
   4920                                                   &coin_value,
   4921                                                   &fees,
   4922                                                   &master_priv,
   4923                                                   &master_sig);
   4924       GNUNET_assert (0 ==
   4925                      json_array_append_new (
   4926                        result,
   4927                        GNUNET_JSON_PACK (
   4928                          GNUNET_JSON_pack_data_auto ("h_denom_pub",
   4929                                                      &h_denom_pub),
   4930                          GNUNET_JSON_pack_data_auto ("master_sig",
   4931                                                      &master_sig))));
   4932     }
   4933     GNUNET_JSON_parse_free (spec);
   4934   }
   4935   return GNUNET_OK;
   4936 }
   4937 
   4938 
   4939 /**
   4940  * Sign future keys.
   4941  *
   4942  * @param args the array of command-line arguments to process next
   4943  */
   4944 static void
   4945 do_sign (char *const *args)
   4946 {
   4947   json_t *keys;
   4948   const char *err_name;
   4949   unsigned int err_line;
   4950   const json_t *denomkeys;
   4951   const json_t *signkeys;
   4952   struct TALER_MasterPublicKeyP mpub;
   4953   struct TALER_SecurityModulePublicKeySetP secmset;
   4954   struct GNUNET_JSON_Specification spec[] = {
   4955     GNUNET_JSON_spec_array_const ("future_denoms",
   4956                                   &denomkeys),
   4957     GNUNET_JSON_spec_array_const ("future_signkeys",
   4958                                   &signkeys),
   4959     GNUNET_JSON_spec_fixed_auto ("master_pub",
   4960                                  &mpub),
   4961     GNUNET_JSON_spec_fixed_auto ("denom_secmod_public_key",
   4962                                  &secmset.rsa),
   4963     GNUNET_JSON_spec_fixed_auto ("denom_secmod_cs_public_key",
   4964                                  &secmset.cs),
   4965     GNUNET_JSON_spec_fixed_auto ("signkey_secmod_public_key",
   4966                                  &secmset.eddsa),
   4967     GNUNET_JSON_spec_end ()
   4968   };
   4969 
   4970   keys = parse_keys_input ("sign");
   4971   if (NULL == keys)
   4972     return;
   4973   if (GNUNET_OK !=
   4974       load_offline_key (GNUNET_NO))
   4975   {
   4976     json_decref (keys);
   4977     return;
   4978   }
   4979   if (GNUNET_OK !=
   4980       GNUNET_JSON_parse (keys,
   4981                          spec,
   4982                          &err_name,
   4983                          &err_line))
   4984   {
   4985     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   4986                 "Invalid input to 'sign' : %s #%u (skipping)\n",
   4987                 err_name,
   4988                 err_line);
   4989     json_dumpf (in,
   4990                 stderr,
   4991                 JSON_INDENT (2));
   4992     global_ret = EXIT_FAILURE;
   4993     GNUNET_SCHEDULER_shutdown ();
   4994     json_decref (keys);
   4995     return;
   4996   }
   4997   if (0 !=
   4998       GNUNET_memcmp (&master_pub,
   4999                      &mpub))
   5000   {
   5001     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   5002                 "Fatal: exchange uses different master key!\n");
   5003     global_ret = EXIT_FAILURE;
   5004     GNUNET_SCHEDULER_shutdown ();
   5005     json_decref (keys);
   5006     return;
   5007   }
   5008   if (GNUNET_SYSERR ==
   5009       tofu_check (&secmset))
   5010   {
   5011     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   5012                 "Fatal: security module keys changed!\n");
   5013     global_ret = EXIT_FAILURE;
   5014     GNUNET_SCHEDULER_shutdown ();
   5015     json_decref (keys);
   5016     return;
   5017   }
   5018   {
   5019     json_t *signkey_sig_array = json_array ();
   5020     json_t *denomkey_sig_array = json_array ();
   5021 
   5022     GNUNET_assert (NULL != signkey_sig_array);
   5023     GNUNET_assert (NULL != denomkey_sig_array);
   5024     if ( (GNUNET_OK !=
   5025           sign_signkeys (&secmset.eddsa,
   5026                          signkeys,
   5027                          signkey_sig_array)) ||
   5028          (GNUNET_OK !=
   5029           sign_denomkeys (&secmset.rsa,
   5030                           &secmset.cs,
   5031                           denomkeys,
   5032                           denomkey_sig_array)) )
   5033     {
   5034       global_ret = EXIT_FAILURE;
   5035       GNUNET_SCHEDULER_shutdown ();
   5036       json_decref (signkey_sig_array);
   5037       json_decref (denomkey_sig_array);
   5038       json_decref (keys);
   5039       return;
   5040     }
   5041 
   5042     output_operation (OP_UPLOAD_SIGS,
   5043                       GNUNET_JSON_PACK (
   5044                         GNUNET_JSON_pack_array_steal ("denom_sigs",
   5045                                                       denomkey_sig_array),
   5046                         GNUNET_JSON_pack_array_steal ("signkey_sigs",
   5047                                                       signkey_sig_array)));
   5048   }
   5049   json_decref (keys);
   5050   next (args);
   5051 }
   5052 
   5053 
   5054 /**
   5055  * Setup and output offline signing key.
   5056  *
   5057  * @param args the array of command-line arguments to process next
   5058  */
   5059 static void
   5060 do_setup (char *const *args)
   5061 {
   5062   if (GNUNET_OK !=
   5063       load_offline_key (GNUNET_YES))
   5064   {
   5065     global_ret = EXIT_NOPERMISSION;
   5066     return;
   5067   }
   5068   if (NULL != *args)
   5069   {
   5070     output_operation (OP_SETUP,
   5071                       GNUNET_JSON_PACK (
   5072                         GNUNET_JSON_pack_data_auto ("exchange_offline_pub",
   5073                                                     &master_pub)));
   5074   }
   5075 
   5076   else
   5077   {
   5078     char *pub_s;
   5079 
   5080     pub_s = GNUNET_STRINGS_data_to_string_alloc (&master_pub,
   5081                                                  sizeof (master_pub));
   5082     fprintf (stdout,
   5083              "%s\n",
   5084              pub_s);
   5085     GNUNET_free (pub_s);
   5086   }
   5087   if ( (NULL != *args) &&
   5088        (0 == strcmp (*args,
   5089                      "-")) )
   5090     args++;
   5091   next (args);
   5092 }
   5093 
   5094 
   5095 /**
   5096  * Dispatch @a args in the @a cmds array.
   5097  *
   5098  * @param args arguments with subcommand to dispatch
   5099  * @param cmds array of possible subcommands to call
   5100  */
   5101 static void
   5102 cmd_handler (char *const *args,
   5103              const struct SubCommand *cmds)
   5104 {
   5105   nxt = NULL;
   5106   for (unsigned int i = 0; NULL != cmds[i].name; i++)
   5107   {
   5108     if (0 == strcasecmp (cmds[i].name,
   5109                          args[0]))
   5110     {
   5111       cmds[i].cb (&args[1]);
   5112       return;
   5113     }
   5114   }
   5115 
   5116   if (0 != strcasecmp ("help",
   5117                        args[0]))
   5118   {
   5119     GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
   5120                 "Unexpected command `%s'\n",
   5121                 args[0]);
   5122     global_ret = EXIT_INVALIDARGUMENT;
   5123   }
   5124 
   5125   GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
   5126               "Supported subcommands:\n");
   5127   for (unsigned int i = 0; NULL != cmds[i].name; i++)
   5128   {
   5129     GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
   5130                 "- %s: %s\n",
   5131                 cmds[i].name,
   5132                 cmds[i].help);
   5133   }
   5134   json_decref (out);
   5135   out = NULL;
   5136   GNUNET_SCHEDULER_shutdown ();
   5137 }
   5138 
   5139 
   5140 static void
   5141 work (void *cls)
   5142 {
   5143   char *const *args = cls;
   5144   struct SubCommand cmds[] = {
   5145     {
   5146       .name = "setup",
   5147       .help =
   5148         "initialize offline key signing material and display public offline key",
   5149       .cb = &do_setup
   5150     },
   5151     {
   5152       .name = "download",
   5153       .help =
   5154         "obtain future public keys from exchange (to be performed online!)",
   5155       .cb = &do_download
   5156     },
   5157     {
   5158       .name = "show",
   5159       .help =
   5160         "display future public keys from exchange for human review (pass '-' as argument to disable consuming input)",
   5161       .cb = &do_show
   5162     },
   5163     {
   5164       .name = "sign",
   5165       .help = "sign all future public keys from the input",
   5166       .cb = &do_sign
   5167     },
   5168     {
   5169       .name = "revoke-denomination",
   5170       .help =
   5171         "revoke denomination key (hash of public key must be given as argument)",
   5172       .cb = &do_revoke_denomination_key
   5173     },
   5174     {
   5175       .name = "revoke-signkey",
   5176       .help =
   5177         "revoke exchange online signing key (public key must be given as argument)",
   5178       .cb = &do_revoke_signkey
   5179     },
   5180     {
   5181       .name = "enable-auditor",
   5182       .help =
   5183         "enable auditor for the exchange (auditor-public key, auditor-URI and auditor-name must be given as arguments)",
   5184       .cb = &do_add_auditor
   5185     },
   5186     {
   5187       .name = "disable-auditor",
   5188       .help =
   5189         "disable auditor at the exchange (auditor-public key must be given as argument)",
   5190       .cb = &do_del_auditor
   5191     },
   5192     {
   5193       .name = "enable-account",
   5194       .help =
   5195         "enable wire account of the exchange (payto-URI must be given as argument; for optional arguments see man page)",
   5196       .cb = &do_add_wire
   5197     },
   5198     {
   5199       .name = "disable-account",
   5200       .help =
   5201         "disable wire account of the exchange (payto-URI must be given as argument)",
   5202       .cb = &do_del_wire
   5203     },
   5204     {
   5205       .name = "wire-fee",
   5206       .help =
   5207         "sign wire fees for the given year (year, wire method, wire fee, and closing fee must be given as arguments)",
   5208       .cb = &do_set_wire_fee
   5209     },
   5210     {
   5211       .name = "global-fee",
   5212       .help =
   5213         "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)",
   5214       .cb = &do_set_global_fee
   5215     },
   5216     {
   5217       .name = "drain",
   5218       .help =
   5219         "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)",
   5220       .cb = &do_drain
   5221     },
   5222     {
   5223       .name = "add-partner",
   5224       .help =
   5225         "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)",
   5226       .cb = &do_add_partner
   5227     },
   5228     {
   5229       .name = "aml-enable",
   5230       .help =
   5231         "enable AML staff member (staff member public key, legal name and rw (read write) or ro (read only) must be given as arguments)",
   5232       .cb = &enable_aml_staff
   5233     },
   5234     {
   5235       .name = "aml-disable",
   5236       .help =
   5237         "disable AML staff member (staff member public key and legal name must be given as arguments)",
   5238       .cb = &disable_aml_staff
   5239     },
   5240     {
   5241       .name = "upload",
   5242       .help =
   5243         "upload operation result to exchange (to be performed online!)",
   5244       .cb = &do_upload
   5245     },
   5246     /* list terminator */
   5247     {
   5248       .name = NULL,
   5249     }
   5250   };
   5251   (void) cls;
   5252 
   5253   cmd_handler (args, cmds);
   5254 }
   5255 
   5256 
   5257 /**
   5258  * Main function that will be run.
   5259  *
   5260  * @param cls closure
   5261  * @param args remaining command-line arguments
   5262  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
   5263  * @param cfg configuration
   5264  */
   5265 static void
   5266 run (void *cls,
   5267      char *const *args,
   5268      const char *cfgfile,
   5269      const struct GNUNET_CONFIGURATION_Handle *cfg)
   5270 {
   5271   (void) cls;
   5272   (void) cfgfile;
   5273   kcfg = cfg;
   5274 
   5275   /* age restriction is read per-denomination in load_age_mask() from [exchange] */
   5276 
   5277 
   5278   if (GNUNET_OK !=
   5279       TALER_config_get_currency (kcfg,
   5280                                  "exchange",
   5281                                  &currency))
   5282   {
   5283     global_ret = EXIT_NOTCONFIGURED;
   5284     return;
   5285   }
   5286 
   5287   ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
   5288                           &rc);
   5289   rc = GNUNET_CURL_gnunet_rc_create (ctx);
   5290   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
   5291                                  NULL);
   5292   next (args);
   5293 }
   5294 
   5295 
   5296 /**
   5297  * The main function of the taler-exchange-offline tool.  This tool is used to
   5298  * create the signing and denomination keys for the exchange.  It uses the
   5299  * long-term offline private key and generates signatures with it. It also
   5300  * supports online operations with the exchange to download its input data and
   5301  * to upload its results. Those online operations should be performed on
   5302  * another machine in production!
   5303  *
   5304  * @param argc number of arguments from the command line
   5305  * @param argv command line arguments
   5306  * @return 0 ok, 1 on error
   5307  */
   5308 int
   5309 main (int argc,
   5310       char *const *argv)
   5311 {
   5312   struct GNUNET_GETOPT_CommandLineOption options[] = {
   5313     GNUNET_GETOPT_OPTION_END
   5314   };
   5315   enum GNUNET_GenericReturnValue ret;
   5316 
   5317   ret = GNUNET_PROGRAM_run (
   5318     TALER_EXCHANGE_project_data (),
   5319     argc, argv,
   5320     "taler-exchange-offline",
   5321     gettext_noop ("Operations for offline signing for a Taler exchange"),
   5322     options,
   5323     &run, NULL);
   5324   if (GNUNET_SYSERR == ret)
   5325     return EXIT_INVALIDARGUMENT;
   5326   if (GNUNET_NO == ret)
   5327     return EXIT_SUCCESS;
   5328   return global_ret;
   5329 }
   5330 
   5331 
   5332 /* end of taler-exchange-offline.c */