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


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