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


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