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


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