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


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