diff options
Diffstat (limited to 'src/exchange-tools/taler-exchange-offline.c')
-rw-r--r-- | src/exchange-tools/taler-exchange-offline.c | 1251 |
1 files changed, 1077 insertions, 174 deletions
diff --git a/src/exchange-tools/taler-exchange-offline.c b/src/exchange-tools/taler-exchange-offline.c index a8a25bd3c..1f10c55e3 100644 --- a/src/exchange-tools/taler-exchange-offline.c +++ b/src/exchange-tools/taler-exchange-offline.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2020, 2021, 2022 Taler Systems SA + Copyright (C) 2020-2024 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -24,6 +24,8 @@ #include "taler_json_lib.h" #include "taler_exchange_service.h" #include "taler_extensions.h" +#include <regex.h> + /** * Name of the input for the 'sign' and 'show' operation. @@ -113,6 +115,16 @@ #define OP_DRAIN_PROFITS "exchange-drain-profits-0" /** + * Setup AML staff. + */ +#define OP_UPDATE_AML_STAFF "exchange-add-aml-staff-0" + +/** + * Setup partner exchange for wad transfers. + */ +#define OP_ADD_PARTNER "exchange-add-partner-0" + +/** * Our private key, initialized in #load_offline_key(). */ static struct TALER_MasterPrivateKeyP master_priv; @@ -499,6 +511,62 @@ struct UploadExtensionsRequest /** + * Data structure for AML staff requests. + */ +struct AmlStaffRequest +{ + + /** + * Kept in a DLL. + */ + struct AmlStaffRequest *next; + + /** + * Kept in a DLL. + */ + struct AmlStaffRequest *prev; + + /** + * Operation handle. + */ + struct TALER_EXCHANGE_ManagementUpdateAmlOfficer *h; + + /** + * Array index of the associated command. + */ + size_t idx; +}; + + +/** + * Data structure for partner add requests. + */ +struct PartnerAddRequest +{ + + /** + * Kept in a DLL. + */ + struct PartnerAddRequest *next; + + /** + * Kept in a DLL. + */ + struct PartnerAddRequest *prev; + + /** + * Operation handle. + */ + struct TALER_EXCHANGE_ManagementAddPartner *h; + + /** + * Array index of the associated command. + */ + size_t idx; +}; + + +/** * Next work item to perform. */ static struct GNUNET_SCHEDULER_Task *nxt; @@ -508,6 +576,27 @@ static struct GNUNET_SCHEDULER_Task *nxt; */ static struct TALER_EXCHANGE_ManagementGetKeysHandle *mgkh; + +/** + * Active AML staff change requests. + */ +static struct AmlStaffRequest *asr_head; + +/** + * Active AML staff change requests. + */ +static struct AmlStaffRequest *asr_tail; + +/** + * Active partner add requests. + */ +static struct PartnerAddRequest *par_head; + +/** + * Active partner add requests. + */ +static struct PartnerAddRequest *par_tail; + /** * Active denomiantion revocation requests. */ @@ -630,6 +719,36 @@ do_shutdown (void *cls) (void) cls; { + struct AmlStaffRequest *asr; + + while (NULL != (asr = asr_head)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Aborting incomplete AML staff update #%u\n", + (unsigned int) asr->idx); + TALER_EXCHANGE_management_update_aml_officer_cancel (asr->h); + GNUNET_CONTAINER_DLL_remove (asr_head, + asr_tail, + asr); + GNUNET_free (asr); + } + } + { + struct PartnerAddRequest *par; + + while (NULL != (par = par_head)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Aborting incomplete partner add request #%u\n", + (unsigned int) par->idx); + TALER_EXCHANGE_management_add_partner_cancel (par->h); + GNUNET_CONTAINER_DLL_remove (par_head, + par_tail, + par); + GNUNET_free (par); + } + } + { struct DenomRevocationRequest *drr; while (NULL != (drr = drr_head)) @@ -799,9 +918,10 @@ do_shutdown (void *cls) if (NULL != out) { - json_dumpf (out, - stdout, - JSON_INDENT (2)); + if (EXIT_SUCCESS == global_ret) + json_dumpf (out, + stdout, + JSON_INDENT (2)); json_decref (out); out = NULL; } @@ -842,6 +962,8 @@ static void test_shutdown (void) { if ( (NULL == drr_head) && + (NULL == par_head) && + (NULL == asr_head) && (NULL == srr_head) && (NULL == aar_head) && (NULL == adr_head) && @@ -1000,14 +1122,15 @@ load_offline_key (int do_create) * Function called with information about the post revocation operation result. * * @param cls closure with a `struct DenomRevocationRequest` - * @param hr HTTP response data + * @param dr response data */ static void denom_revocation_cb ( void *cls, - const struct TALER_EXCHANGE_HttpResponse *hr) + const struct TALER_EXCHANGE_ManagementRevokeDenominationResponse *dr) { struct DenomRevocationRequest *drr = cls; + const struct TALER_EXCHANGE_HttpResponse *hr = &dr->hr; if (MHD_HTTP_NO_CONTENT != hr->http_status) { @@ -1067,7 +1190,7 @@ upload_denom_revocation (const char *exchange_url, stderr, JSON_INDENT (2)); global_ret = EXIT_FAILURE; - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); return; } drr = GNUNET_new (struct DenomRevocationRequest); @@ -1089,14 +1212,15 @@ upload_denom_revocation (const char *exchange_url, * Function called with information about the post revocation operation result. * * @param cls closure with a `struct SignkeyRevocationRequest` - * @param hr HTTP response data + * @param sr response data */ static void signkey_revocation_cb ( void *cls, - const struct TALER_EXCHANGE_HttpResponse *hr) + const struct TALER_EXCHANGE_ManagementRevokeSigningKeyResponse *sr) { struct SignkeyRevocationRequest *srr = cls; + const struct TALER_EXCHANGE_HttpResponse *hr = &sr->hr; if (MHD_HTTP_NO_CONTENT != hr->http_status) { @@ -1156,7 +1280,7 @@ upload_signkey_revocation (const char *exchange_url, stderr, JSON_INDENT (2)); global_ret = EXIT_FAILURE; - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); return; } srr = GNUNET_new (struct SignkeyRevocationRequest); @@ -1178,13 +1302,15 @@ upload_signkey_revocation (const char *exchange_url, * Function called with information about the post auditor add operation result. * * @param cls closure with a `struct AuditorAddRequest` - * @param hr HTTP response data + * @param mer response data */ static void -auditor_add_cb (void *cls, - const struct TALER_EXCHANGE_HttpResponse *hr) +auditor_add_cb ( + void *cls, + const struct TALER_EXCHANGE_ManagementAuditorEnableResponse *mer) { struct AuditorAddRequest *aar = cls; + const struct TALER_EXCHANGE_HttpResponse *hr = &mer->hr; if (MHD_HTTP_NO_CONTENT != hr->http_status) { @@ -1225,7 +1351,7 @@ upload_auditor_add (const char *exchange_url, const char *err_name; unsigned int err_line; struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("auditor_url", + TALER_JSON_spec_web_url ("auditor_url", &auditor_url), GNUNET_JSON_spec_string ("auditor_name", &auditor_name), @@ -1253,7 +1379,7 @@ upload_auditor_add (const char *exchange_url, stderr, JSON_INDENT (2)); global_ret = EXIT_FAILURE; - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); return; } aar = GNUNET_new (struct AuditorAddRequest); @@ -1278,13 +1404,15 @@ upload_auditor_add (const char *exchange_url, * Function called with information about the post auditor del operation result. * * @param cls closure with a `struct AuditorDelRequest` - * @param hr HTTP response data + * @param mdr response data */ static void auditor_del_cb (void *cls, - const struct TALER_EXCHANGE_HttpResponse *hr) + const struct + TALER_EXCHANGE_ManagementAuditorDisableResponse *mdr) { struct AuditorDelRequest *adr = cls; + const struct TALER_EXCHANGE_HttpResponse *hr = &mdr->hr; if (MHD_HTTP_NO_CONTENT != hr->http_status) { @@ -1347,7 +1475,7 @@ upload_auditor_del (const char *exchange_url, stderr, JSON_INDENT (2)); global_ret = EXIT_FAILURE; - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); return; } adr = GNUNET_new (struct AuditorDelRequest); @@ -1370,13 +1498,14 @@ upload_auditor_del (const char *exchange_url, * Function called with information about the post wire add operation result. * * @param cls closure with a `struct WireAddRequest` - * @param hr HTTP response data + * @param wer response data */ static void wire_add_cb (void *cls, - const struct TALER_EXCHANGE_HttpResponse *hr) + const struct TALER_EXCHANGE_ManagementWireEnableResponse *wer) { struct WireAddRequest *war = cls; + const struct TALER_EXCHANGE_HttpResponse *hr = &wer->hr; if (MHD_HTTP_NO_CONTENT != hr->http_status) { @@ -1414,10 +1543,29 @@ upload_wire_add (const char *exchange_url, struct GNUNET_TIME_Timestamp start_time; struct WireAddRequest *war; const char *err_name; + const char *conversion_url = NULL; + const char *bank_label = NULL; + int64_t priority = 0; + const json_t *debit_restrictions; + const json_t *credit_restrictions; unsigned int err_line; struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("payto_uri", - &payto_uri), + TALER_JSON_spec_payto_uri ("payto_uri", + &payto_uri), + GNUNET_JSON_spec_mark_optional ( + TALER_JSON_spec_web_url ("conversion_url", + &conversion_url), + NULL), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_string ("bank_label", + &bank_label), + NULL), + GNUNET_JSON_spec_int64 ("priority", + &priority), + GNUNET_JSON_spec_array_const ("debit_restrictions", + &debit_restrictions), + GNUNET_JSON_spec_array_const ("credit_restrictions", + &credit_restrictions), GNUNET_JSON_spec_timestamp ("validity_start", &start_time), GNUNET_JSON_spec_fixed_auto ("master_sig_add", @@ -1442,7 +1590,7 @@ upload_wire_add (const char *exchange_url, stderr, JSON_INDENT (2)); global_ret = EXIT_FAILURE; - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); return; } { @@ -1455,7 +1603,7 @@ upload_wire_add (const char *exchange_url, "payto:// URI `%s' is malformed\n", payto_uri); global_ret = EXIT_FAILURE; - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); return; } GNUNET_free (wire_method); @@ -1469,7 +1617,7 @@ upload_wire_add (const char *exchange_url, "payto URI is malformed: %s\n", msg); GNUNET_free (msg); - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); global_ret = EXIT_INVALIDARGUMENT; return; } @@ -1480,9 +1628,14 @@ upload_wire_add (const char *exchange_url, TALER_EXCHANGE_management_enable_wire (ctx, exchange_url, payto_uri, + conversion_url, + debit_restrictions, + credit_restrictions, start_time, &master_sig_add, &master_sig_wire, + bank_label, + priority, &wire_add_cb, war); GNUNET_CONTAINER_DLL_insert (war_head, @@ -1495,13 +1648,14 @@ upload_wire_add (const char *exchange_url, * Function called with information about the post wire del operation result. * * @param cls closure with a `struct WireDelRequest` - * @param hr HTTP response data + * @param wdres response data */ static void wire_del_cb (void *cls, - const struct TALER_EXCHANGE_HttpResponse *hr) + const struct TALER_EXCHANGE_ManagementWireDisableResponse *wdres) { struct WireDelRequest *wdr = cls; + const struct TALER_EXCHANGE_HttpResponse *hr = &wdres->hr; if (MHD_HTTP_NO_CONTENT != hr->http_status) { @@ -1540,8 +1694,8 @@ upload_wire_del (const char *exchange_url, const char *err_name; unsigned int err_line; struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("payto_uri", - &payto_uri), + TALER_JSON_spec_payto_uri ("payto_uri", + &payto_uri), GNUNET_JSON_spec_timestamp ("validity_end", &end_time), GNUNET_JSON_spec_fixed_auto ("master_sig", @@ -1564,7 +1718,7 @@ upload_wire_del (const char *exchange_url, stderr, JSON_INDENT (2)); global_ret = EXIT_FAILURE; - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); return; } wdr = GNUNET_new (struct WireDelRequest); @@ -1587,14 +1741,15 @@ upload_wire_del (const char *exchange_url, * Function called with information about the post wire fee operation result. * * @param cls closure with a `struct WireFeeRequest` - * @param hr HTTP response data + * @param swr response data */ static void wire_fee_cb ( void *cls, - const struct TALER_EXCHANGE_HttpResponse *hr) + const struct TALER_EXCHANGE_ManagementSetWireFeeResponse *swr) { struct WireFeeRequest *wfr = cls; + const struct TALER_EXCHANGE_HttpResponse *hr = &swr->hr; if (MHD_HTTP_NO_CONTENT != hr->http_status) { @@ -1667,7 +1822,7 @@ upload_wire_fee (const char *exchange_url, stderr, JSON_INDENT (2)); global_ret = EXIT_FAILURE; - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); return; } wfr = GNUNET_new (struct WireFeeRequest); @@ -1692,14 +1847,15 @@ upload_wire_fee (const char *exchange_url, * Function called with information about the post global fee operation result. * * @param cls closure with a `struct WireFeeRequest` - * @param hr HTTP response data + * @param gr response data */ static void global_fee_cb ( void *cls, - const struct TALER_EXCHANGE_HttpResponse *hr) + const struct TALER_EXCHANGE_ManagementSetGlobalFeeResponse *gr) { struct GlobalFeeRequest *gfr = cls; + const struct TALER_EXCHANGE_HttpResponse *hr = &gr->hr; if (MHD_HTTP_NO_CONTENT != hr->http_status) { @@ -1781,7 +1937,7 @@ upload_global_fee (const char *exchange_url, stderr, JSON_INDENT (2)); global_ret = EXIT_FAILURE; - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); return; } gfr = GNUNET_new (struct GlobalFeeRequest); @@ -1808,14 +1964,15 @@ upload_global_fee (const char *exchange_url, * Function called with information about the drain profits operation. * * @param cls closure with a `struct DrainProfitsRequest` - * @param hr HTTP response data + * @param mdr response data */ static void drain_profits_cb ( void *cls, - const struct TALER_EXCHANGE_HttpResponse *hr) + const struct TALER_EXCHANGE_ManagementDrainResponse *mdr) { struct DrainProfitsRequest *dpr = cls; + const struct TALER_EXCHANGE_HttpResponse *hr = &mdr->hr; if (MHD_HTTP_NO_CONTENT != hr->http_status) { @@ -1866,8 +2023,8 @@ upload_drain (const char *exchange_url, &date), GNUNET_JSON_spec_string ("account_section", &account_section), - GNUNET_JSON_spec_string ("payto_uri", - &payto_uri), + TALER_JSON_spec_payto_uri ("payto_uri", + &payto_uri), GNUNET_JSON_spec_fixed_auto ("master_sig", &master_sig), GNUNET_JSON_spec_end () @@ -1888,7 +2045,7 @@ upload_drain (const char *exchange_url, stderr, JSON_INDENT (2)); global_ret = EXIT_FAILURE; - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); return; } dpr = GNUNET_new (struct DrainProfitsRequest); @@ -1914,14 +2071,15 @@ upload_drain (const char *exchange_url, * Function called with information about the post upload keys operation result. * * @param cls closure with a `struct UploadKeysRequest` - * @param hr HTTP response data + * @param mr response data */ static void keys_cb ( void *cls, - const struct TALER_EXCHANGE_HttpResponse *hr) + const struct TALER_EXCHANGE_ManagementPostKeysResponse *mr) { struct UploadKeysRequest *ukr = cls; + const struct TALER_EXCHANGE_HttpResponse *hr = &mr->hr; if (MHD_HTTP_NO_CONTENT != hr->http_status) { @@ -1957,13 +2115,13 @@ upload_keys (const char *exchange_url, struct UploadKeysRequest *ukr; const char *err_name; unsigned int err_line; - json_t *denom_sigs; - json_t *signkey_sigs; + const json_t *denom_sigs; + const json_t *signkey_sigs; struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_json ("denom_sigs", - &denom_sigs), - GNUNET_JSON_spec_json ("signkey_sigs", - &signkey_sigs), + GNUNET_JSON_spec_array_const ("denom_sigs", + &denom_sigs), + GNUNET_JSON_spec_array_const ("signkey_sigs", + &signkey_sigs), GNUNET_JSON_spec_end () }; bool ok = true; @@ -1982,7 +2140,7 @@ upload_keys (const char *exchange_url, stderr, JSON_INDENT (2)); global_ret = EXIT_FAILURE; - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); return; } pkd.num_sign_sigs = json_array_size (signkey_sigs); @@ -2075,11 +2233,10 @@ upload_keys (const char *exchange_url, else { global_ret = EXIT_FAILURE; - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); } GNUNET_free (pkd.sign_sigs); GNUNET_free (pkd.denom_sigs); - GNUNET_JSON_parse_free (spec); } @@ -2087,14 +2244,15 @@ upload_keys (const char *exchange_url, * Function called with information about the post upload extensions operation result. * * @param cls closure with a `struct UploadExtensionsRequest` - * @param hr HTTP response data + * @param er response data */ static void extensions_cb ( void *cls, - const struct TALER_EXCHANGE_HttpResponse *hr) + const struct TALER_EXCHANGE_ManagementPostExtensionsResponse *er) { struct UploadExtensionsRequest *uer = cls; + const struct TALER_EXCHANGE_HttpResponse *hr = &er->hr; if (MHD_HTTP_NO_CONTENT != hr->http_status) { @@ -2126,13 +2284,13 @@ upload_extensions (const char *exchange_url, size_t idx, const json_t *value) { - json_t *extensions; + const json_t *extensions; struct TALER_MasterSignatureP sig; const char *err_name; unsigned int err_line; struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_json ("extensions", - &extensions), + GNUNET_JSON_spec_object_const ("extensions", + &extensions), GNUNET_JSON_spec_fixed_auto ("extensions_sig", &sig), GNUNET_JSON_spec_end () @@ -2154,7 +2312,7 @@ upload_extensions (const char *exchange_url, stderr, JSON_INDENT (2)); global_ret = EXIT_FAILURE; - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); return; } @@ -2163,13 +2321,13 @@ upload_extensions (const char *exchange_url, struct TALER_ExtensionManifestsHashP h_manifests; if (GNUNET_OK != - TALER_JSON_extensions_manifests_hash (extensions, &h_manifests)) + TALER_JSON_extensions_manifests_hash (extensions, + &h_manifests)) { - GNUNET_JSON_parse_free (spec); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "couldn't hash extensions' manifests\n"); global_ret = EXIT_FAILURE; - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); return; } @@ -2182,11 +2340,10 @@ upload_extensions (const char *exchange_url, &master_pub, &sig)) { - GNUNET_JSON_parse_free (spec); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "invalid signature for extensions\n"); global_ret = EXIT_FAILURE; - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); return; } } @@ -2197,8 +2354,9 @@ upload_extensions (const char *exchange_url, .extensions = extensions, .extensions_sig = sig, }; - struct UploadExtensionsRequest *uer = GNUNET_new (struct - UploadExtensionsRequest); + struct UploadExtensionsRequest *uer + = GNUNET_new (struct UploadExtensionsRequest); + uer->idx = idx; uer->h = TALER_EXCHANGE_management_post_extensions ( ctx, @@ -2210,7 +2368,223 @@ upload_extensions (const char *exchange_url, uer_tail, uer); } - GNUNET_JSON_parse_free (spec); +} + + +/** + * Function called with information about the add partner operation. + * + * @param cls closure with a `struct PartnerAddRequest` + * @param apr response data + */ +static void +add_partner_cb ( + void *cls, + const struct TALER_EXCHANGE_ManagementAddPartnerResponse *apr) +{ + struct PartnerAddRequest *par = cls; + const struct TALER_EXCHANGE_HttpResponse *hr = &apr->hr; + + if (MHD_HTTP_NO_CONTENT != hr->http_status) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Upload failed for command %u with status %u: %s (%s)\n", + (unsigned int) par->idx, + hr->http_status, + TALER_ErrorCode_get_hint (hr->ec), + hr->hint); + global_ret = EXIT_FAILURE; + } + GNUNET_CONTAINER_DLL_remove (par_head, + par_tail, + par); + GNUNET_free (par); + test_shutdown (); +} + + +/** + * Add partner action. + * + * @param exchange_url base URL of the exchange + * @param idx index of the operation we are performing (for logging) + * @param value arguments for add partner + */ +static void +add_partner (const char *exchange_url, + size_t idx, + const json_t *value) +{ + struct TALER_MasterPublicKeyP partner_pub; + struct GNUNET_TIME_Timestamp start_date; + struct GNUNET_TIME_Timestamp end_date; + struct GNUNET_TIME_Relative wad_frequency; + struct TALER_Amount wad_fee; + const char *partner_base_url; + struct TALER_MasterSignatureP master_sig; + struct PartnerAddRequest *par; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto ("partner_pub", + &partner_pub), + TALER_JSON_spec_amount ("wad_fee", + currency, + &wad_fee), + GNUNET_JSON_spec_relative_time ("wad_frequency", + &wad_frequency), + GNUNET_JSON_spec_timestamp ("start_date", + &start_date), + GNUNET_JSON_spec_timestamp ("end_date", + &end_date), + TALER_JSON_spec_web_url ("partner_base_url", + &partner_base_url), + GNUNET_JSON_spec_fixed_auto ("master_sig", + &master_sig), + GNUNET_JSON_spec_end () + }; + const char *err_name; + unsigned int err_line; + + if (GNUNET_OK != + GNUNET_JSON_parse (value, + spec, + &err_name, + &err_line)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Invalid input to add partner: %s#%u at %u (skipping)\n", + err_name, + err_line, + (unsigned int) idx); + json_dumpf (value, + stderr, + JSON_INDENT (2)); + global_ret = EXIT_FAILURE; + GNUNET_SCHEDULER_shutdown (); + return; + } + par = GNUNET_new (struct PartnerAddRequest); + par->idx = idx; + par->h = + TALER_EXCHANGE_management_add_partner (ctx, + exchange_url, + &partner_pub, + start_date, + end_date, + wad_frequency, + &wad_fee, + partner_base_url, + &master_sig, + &add_partner_cb, + par); + GNUNET_CONTAINER_DLL_insert (par_head, + par_tail, + par); +} + + +/** + * Function called with information about the AML officer update operation. + * + * @param cls closure with a `struct AmlStaffRequest` + * @param ar response data + */ +static void +update_aml_officer_cb ( + void *cls, + const struct TALER_EXCHANGE_ManagementUpdateAmlOfficerResponse *ar) +{ + struct AmlStaffRequest *asr = cls; + const struct TALER_EXCHANGE_HttpResponse *hr = &ar->hr; + + if (MHD_HTTP_NO_CONTENT != hr->http_status) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Upload failed for command %u with status %u: %s (%s)\n", + (unsigned int) asr->idx, + hr->http_status, + TALER_ErrorCode_get_hint (hr->ec), + hr->hint); + global_ret = EXIT_FAILURE; + } + GNUNET_CONTAINER_DLL_remove (asr_head, + asr_tail, + asr); + GNUNET_free (asr); + test_shutdown (); +} + + +/** + * Upload AML staff action. + * + * @param exchange_url base URL of the exchange + * @param idx index of the operation we are performing (for logging) + * @param value arguments for AML staff change + */ +static void +update_aml_staff (const char *exchange_url, + size_t idx, + const json_t *value) +{ + struct TALER_AmlOfficerPublicKeyP officer_pub; + const char *officer_name; + struct GNUNET_TIME_Timestamp change_date; + bool is_active; + bool read_only; + struct TALER_MasterSignatureP master_sig; + struct AmlStaffRequest *asr; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto ("officer_pub", + &officer_pub), + GNUNET_JSON_spec_timestamp ("change_date", + &change_date), + GNUNET_JSON_spec_bool ("is_active", + &is_active), + GNUNET_JSON_spec_bool ("read_only", + &read_only), + GNUNET_JSON_spec_string ("officer_name", + &officer_name), + GNUNET_JSON_spec_fixed_auto ("master_sig", + &master_sig), + GNUNET_JSON_spec_end () + }; + const char *err_name; + unsigned int err_line; + + if (GNUNET_OK != + GNUNET_JSON_parse (value, + spec, + &err_name, + &err_line)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Invalid input to AML staff update: %s#%u at %u (skipping)\n", + err_name, + err_line, + (unsigned int) idx); + json_dumpf (value, + stderr, + JSON_INDENT (2)); + global_ret = EXIT_FAILURE; + GNUNET_SCHEDULER_shutdown (); + return; + } + asr = GNUNET_new (struct AmlStaffRequest); + asr->idx = idx; + asr->h = + TALER_EXCHANGE_management_update_aml_officer (ctx, + exchange_url, + &officer_pub, + officer_name, + change_date, + is_active, + read_only, + &master_sig, + &update_aml_officer_cb, + asr); + GNUNET_CONTAINER_DLL_insert (asr_head, + asr_tail, + asr); } @@ -2267,6 +2641,14 @@ trigger_upload (const char *exchange_url) .key = OP_EXTENSIONS, .cb = &upload_extensions }, + { + .key = OP_UPDATE_AML_STAFF, + .cb = &update_aml_staff + }, + { + .key = OP_ADD_PARTNER, + .cb = &add_partner + }, /* array termination */ { .key = NULL @@ -2287,7 +2669,7 @@ trigger_upload (const char *exchange_url) GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed JSON input\n"); global_ret = EXIT_FAILURE; - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); return; } /* block of code that uses key and value */ @@ -2310,7 +2692,7 @@ trigger_upload (const char *exchange_url) "Upload does not know how to handle `%s'\n", key); global_ret = EXIT_FAILURE; - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); return; } } @@ -2330,7 +2712,7 @@ do_upload (char *const *args) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Downloaded data was not consumed, refusing upload\n"); - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); global_ret = EXIT_FAILURE; return; } @@ -2349,7 +2731,7 @@ do_upload (char *const *args) err.line, err.source, err.position); - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); global_ret = EXIT_FAILURE; return; } @@ -2358,7 +2740,7 @@ do_upload (char *const *args) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Error: expected JSON array for `upload` command\n"); - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); global_ret = EXIT_FAILURE; return; } @@ -2373,7 +2755,7 @@ do_upload (char *const *args) "exchange", "BASE_URL"); global_ret = EXIT_NOTCONFIGURED; - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); return; } trigger_upload (CFG_exchange_url); @@ -2398,7 +2780,7 @@ do_revoke_denomination_key (char *const *args) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Downloaded data was not consumed, refusing revocation\n"); - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); global_ret = EXIT_FAILURE; return; } @@ -2411,7 +2793,7 @@ do_revoke_denomination_key (char *const *args) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "You must specify a denomination key with this subcommand\n"); - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); global_ret = EXIT_INVALIDARGUMENT; return; } @@ -2447,7 +2829,7 @@ do_revoke_signkey (char *const *args) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Downloaded data was not consumed, refusing revocation\n"); - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); global_ret = EXIT_FAILURE; return; } @@ -2460,7 +2842,7 @@ do_revoke_signkey (char *const *args) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "You must specify an exchange signing key with this subcommand\n"); - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); global_ret = EXIT_INVALIDARGUMENT; return; } @@ -2498,7 +2880,7 @@ do_add_auditor (char *const *args) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Downloaded data was not consumed, not adding auditor\n"); - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); global_ret = EXIT_FAILURE; return; } @@ -2511,7 +2893,7 @@ do_add_auditor (char *const *args) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "You must specify an auditor public key as first argument for this subcommand\n"); - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); global_ret = EXIT_INVALIDARGUMENT; return; } @@ -2524,7 +2906,7 @@ do_add_auditor (char *const *args) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "You must specify an auditor URI and auditor name as 2nd and 3rd arguments to this subcommand\n"); - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); global_ret = EXIT_INVALIDARGUMENT; return; } @@ -2570,7 +2952,7 @@ do_del_auditor (char *const *args) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Downloaded data was not consumed, not deleting auditor account\n"); - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); global_ret = EXIT_FAILURE; return; } @@ -2583,7 +2965,7 @@ do_del_auditor (char *const *args) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "You must specify an auditor public key as first argument for this subcommand\n"); - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); global_ret = EXIT_INVALIDARGUMENT; return; } @@ -2608,6 +2990,96 @@ do_del_auditor (char *const *args) /** + * Parse account restriction. + * + * @param args the array of command-line arguments to process next + * @param[in,out] restrictions JSON array to update + * @return -1 on error, otherwise number of arguments from @a args that were used + */ +static int +parse_restriction (char *const *args, + json_t *restrictions) +{ + if (NULL == args[0]) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Restriction TYPE argument missing\n"); + return -1; + } + if (0 == strcmp (args[0], + "deny")) + { + GNUNET_assert (0 == + json_array_append_new ( + restrictions, + GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("type", + "deny")))); + return 1; + } + if (0 == strcmp (args[0], + "regex")) + { + json_t *i18n; + json_error_t err; + + if ( (NULL == args[1]) || + (NULL == args[2]) || + (NULL == args[3]) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Mandatory arguments for restriction of type `regex' missing (REGEX, HINT, HINT-I18 required)\n"); + return -1; + } + { + regex_t ex; + + if (0 != regcomp (&ex, + args[1], + REG_NOSUB | REG_EXTENDED)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Invalid regular expression `%s'\n", + args[1]); + return -1; + } + regfree (&ex); + } + + i18n = json_loads (args[3], + JSON_REJECT_DUPLICATES, + &err); + if (NULL == i18n) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Invalid JSON for restriction of type `regex': `%s` at %d\n", + args[3], + err.position); + return -1; + } + GNUNET_assert (0 == + json_array_append_new ( + restrictions, + GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("type", + "regex"), + GNUNET_JSON_pack_string ("payto_regex", + args[1]), + GNUNET_JSON_pack_string ("human_hint", + args[2]), + GNUNET_JSON_pack_object_steal ("human_hint_i18n", + i18n) + ))); + return 4; + } + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Restriction TYPE `%s' unsupported\n", + args[0]); + return -1; +} + + +/** * Add wire account. * * @param args the array of command-line arguments to process next; @@ -2619,12 +3091,18 @@ do_add_wire (char *const *args) struct TALER_MasterSignatureP master_sig_add; struct TALER_MasterSignatureP master_sig_wire; struct GNUNET_TIME_Timestamp now; + const char *conversion_url = NULL; + const char *bank_label = NULL; + int64_t priority = 0; + json_t *debit_restrictions; + json_t *credit_restrictions; + unsigned int num_args = 1; if (NULL != in) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Downloaded data was not consumed, not adding wire account\n"); - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); global_ret = EXIT_FAILURE; return; } @@ -2632,7 +3110,7 @@ do_add_wire (char *const *args) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "You must specify a payto://-URI with this subcommand\n"); - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); global_ret = EXIT_INVALIDARGUMENT; return; } @@ -2648,7 +3126,7 @@ do_add_wire (char *const *args) "payto URI is malformed: %s\n", msg); GNUNET_free (msg); - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); global_ret = EXIT_INVALIDARGUMENT; return; } @@ -2664,29 +3142,160 @@ do_add_wire (char *const *args) "payto:// URI `%s' is malformed\n", args[0]); global_ret = EXIT_INVALIDARGUMENT; - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); return; } GNUNET_free (wire_method); } + debit_restrictions = json_array (); + GNUNET_assert (NULL != debit_restrictions); + credit_restrictions = json_array (); + GNUNET_assert (NULL != credit_restrictions); + while (NULL != args[num_args]) + { + if (0 == strcmp (args[num_args], + "conversion-url")) + { + num_args++; + conversion_url = args[num_args]; + if (NULL == conversion_url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "'conversion-url' requires an argument\n"); + global_ret = EXIT_INVALIDARGUMENT; + GNUNET_SCHEDULER_shutdown (); + json_decref (debit_restrictions); + json_decref (credit_restrictions); + return; + } + if (! TALER_is_web_url (conversion_url)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "'conversion-url' must refer to HTTP(S) endpoint, `%s' is invalid\n", + conversion_url); + global_ret = EXIT_INVALIDARGUMENT; + GNUNET_SCHEDULER_shutdown (); + json_decref (debit_restrictions); + json_decref (credit_restrictions); + return; + } + num_args++; + continue; + } + if (0 == strcmp (args[num_args], + "credit-restriction")) + { + int iret; + + num_args++; + iret = parse_restriction (&args[num_args], + credit_restrictions); + if (iret <= 0) + { + global_ret = EXIT_INVALIDARGUMENT; + GNUNET_SCHEDULER_shutdown (); + json_decref (debit_restrictions); + json_decref (credit_restrictions); + return; + } + num_args += iret; + continue; + } + if (0 == strcmp (args[num_args], + "debit-restriction")) + { + int iret; + + num_args++; + iret = parse_restriction (&args[num_args], + debit_restrictions); + if (iret <= 0) + { + global_ret = EXIT_INVALIDARGUMENT; + GNUNET_SCHEDULER_shutdown (); + json_decref (debit_restrictions); + json_decref (credit_restrictions); + return; + } + num_args += iret; + continue; + } + if (0 == strcmp (args[num_args], + "display-hint")) + { + long long p; + char dummy; + + num_args++; + if ( (NULL == args[num_args]) || + (NULL == args[num_args + 1]) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "'display-hint' requires at least two arguments (priority and label)\n"); + global_ret = EXIT_INVALIDARGUMENT; + GNUNET_SCHEDULER_shutdown (); + json_decref (debit_restrictions); + json_decref (credit_restrictions); + return; + } + if (1 != sscanf (args[num_args], + "%lld%c", + &p, + &dummy)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Priority argument `%s' is not a number\n", + args[num_args]); + global_ret = EXIT_INVALIDARGUMENT; + GNUNET_SCHEDULER_shutdown (); + json_decref (debit_restrictions); + json_decref (credit_restrictions); + return; + } + priority = (int64_t) p; + num_args++; + bank_label = args[num_args]; + num_args++; + continue; + } + break; + } TALER_exchange_offline_wire_add_sign (args[0], + conversion_url, + debit_restrictions, + credit_restrictions, now, &master_priv, &master_sig_add); TALER_exchange_wire_signature_make (args[0], + conversion_url, + debit_restrictions, + credit_restrictions, &master_priv, &master_sig_wire); output_operation (OP_ENABLE_WIRE, GNUNET_JSON_PACK ( GNUNET_JSON_pack_string ("payto_uri", args[0]), + GNUNET_JSON_pack_array_steal ("debit_restrictions", + debit_restrictions), + GNUNET_JSON_pack_array_steal ("credit_restrictions", + credit_restrictions), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("conversion_url", + conversion_url)), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("bank_label", + bank_label)), + GNUNET_JSON_pack_int64 ("priority", + priority), GNUNET_JSON_pack_timestamp ("validity_start", now), GNUNET_JSON_pack_data_auto ("master_sig_add", &master_sig_add), GNUNET_JSON_pack_data_auto ("master_sig_wire", &master_sig_wire))); - next (args + 1); + next (args + num_args); } @@ -2706,7 +3315,7 @@ do_del_wire (char *const *args) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Downloaded data was not consumed, not deleting wire account\n"); - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); global_ret = EXIT_FAILURE; return; } @@ -2714,7 +3323,7 @@ do_del_wire (char *const *args) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "You must specify a payto://-URI with this subcommand\n"); - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); global_ret = EXIT_INVALIDARGUMENT; return; } @@ -2759,7 +3368,7 @@ do_set_wire_fee (char *const *args) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Downloaded data was not consumed, not setting wire fee\n"); - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); global_ret = EXIT_FAILURE; return; } @@ -2782,7 +3391,7 @@ do_set_wire_fee (char *const *args) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "You must use YEAR, METHOD, WIRE-FEE, and CLOSING-FEE as arguments for this subcommand\n"); - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); global_ret = EXIT_INVALIDARGUMENT; return; } @@ -2846,7 +3455,7 @@ do_set_global_fee (char *const *args) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Downloaded data was not consumed, not setting global fee\n"); - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); global_ret = EXIT_FAILURE; return; } @@ -2856,14 +3465,28 @@ do_set_global_fee (char *const *args) (NULL == args[3]) || (NULL == args[4]) || (NULL == args[5]) || - (NULL == args[6]) || - ( (1 != sscanf (args[0], - "%u%c", - &year, - &dummy)) && - (0 != strcasecmp ("now", - args[0])) ) || - (GNUNET_OK != + (NULL == args[6]) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "You must use YEAR, HISTORY-FEE, ACCOUNT-FEE, PURSE-FEE, PURSE-TIMEOUT, HISTORY-EXPIRATION and PURSE-ACCOUNT-LIMIT as arguments for this subcommand\n"); + GNUNET_SCHEDULER_shutdown (); + global_ret = EXIT_INVALIDARGUMENT; + return; + } + if ( (1 != sscanf (args[0], + "%u%c", + &year, + &dummy)) && + (0 != strcasecmp ("now", + args[0])) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Invalid YEAR given for 'global-fee' subcommand\n"); + GNUNET_SCHEDULER_shutdown (); + global_ret = EXIT_INVALIDARGUMENT; + return; + } + if ( (GNUNET_OK != TALER_string_to_amount (args[1], &fees.history)) || (GNUNET_OK != @@ -2871,21 +3494,35 @@ do_set_global_fee (char *const *args) &fees.account)) || (GNUNET_OK != TALER_string_to_amount (args[3], - &fees.purse)) || - (GNUNET_OK != + &fees.purse)) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Invalid amount given for 'global-fee' subcommand\n"); + GNUNET_SCHEDULER_shutdown (); + global_ret = EXIT_INVALIDARGUMENT; + return; + } + if ( (GNUNET_OK != GNUNET_STRINGS_fancy_time_to_relative (args[4], &purse_timeout)) || (GNUNET_OK != GNUNET_STRINGS_fancy_time_to_relative (args[5], - &history_expiration)) || - (1 != sscanf (args[6], - "%u%c", - &purse_account_limit, - &dummy)) ) + &history_expiration)) ) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "You must use YEAR, HISTORY-FEE, ACCOUNT-FEE, PURSE-FEE, PURSE-TIMEOUT, HISTORY-EXPIRATION and PURSE-ACCOUNT-LIMIT as arguments for this subcommand\n"); - test_shutdown (); + "Invalid delay given for 'global-fee' subcommand\n"); + GNUNET_SCHEDULER_shutdown (); + global_ret = EXIT_INVALIDARGUMENT; + return; + } + if (1 != sscanf (args[6], + "%u%c", + &purse_account_limit, + &dummy)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Invalid purse account limit given for 'global-fee' subcommand\n"); + GNUNET_SCHEDULER_shutdown (); global_ret = EXIT_INVALIDARGUMENT; return; } @@ -2956,7 +3593,7 @@ do_drain (char *const *args) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Downloaded data was not consumed, refusing drain\n"); - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); global_ret = EXIT_FAILURE; return; } @@ -2969,7 +3606,7 @@ do_drain (char *const *args) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Drain requires an amount, section name and target payto://-URI as arguments\n"); - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); global_ret = EXIT_INVALIDARGUMENT; return; } @@ -2979,7 +3616,7 @@ do_drain (char *const *args) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Drain requires an amount, section name and target payto://-URI as arguments\n"); - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); global_ret = EXIT_INVALIDARGUMENT; return; } @@ -2990,7 +3627,7 @@ do_drain (char *const *args) GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Invalid amount `%s' specified for drain\n", args[0]); - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); global_ret = EXIT_INVALIDARGUMENT; return; } @@ -3004,7 +3641,7 @@ do_drain (char *const *args) payto_uri, err); GNUNET_free (err); - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); global_ret = EXIT_INVALIDARGUMENT; return; } @@ -3041,23 +3678,275 @@ do_drain (char *const *args) /** + * Add partner. + * + * @param args the array of command-line arguments to process next; + * args[0] must be the partner's master public key, args[1] the partner's + * API base URL, args[2] the wad fee, args[3] the wad frequency, and + * args[4] the year (including possibly 'now') + */ +static void +do_add_partner (char *const *args) +{ + struct TALER_MasterPublicKeyP partner_pub; + struct GNUNET_TIME_Timestamp start_date; + struct GNUNET_TIME_Timestamp end_date; + struct GNUNET_TIME_Relative wad_frequency; + struct TALER_Amount wad_fee; + const char *partner_base_url; + struct TALER_MasterSignatureP master_sig; + char dummy; + unsigned int year; + + if (NULL != in) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Downloaded data was not consumed, not adding partner\n"); + GNUNET_SCHEDULER_shutdown (); + global_ret = EXIT_FAILURE; + return; + } + if ( (NULL == args[0]) || + (GNUNET_OK != + GNUNET_STRINGS_string_to_data (args[0], + strlen (args[0]), + &partner_pub, + sizeof (partner_pub))) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "You must specify the partner master public key as first argument for this subcommand\n"); + GNUNET_SCHEDULER_shutdown (); + global_ret = EXIT_INVALIDARGUMENT; + return; + } + if ( (NULL == args[1]) || + (0 != strncmp ("http", + args[1], + strlen ("http"))) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "You must specify the partner's base URL as the 2nd argument to this subcommand\n"); + GNUNET_SCHEDULER_shutdown (); + global_ret = EXIT_INVALIDARGUMENT; + return; + } + partner_base_url = args[1]; + if ( (NULL == args[2]) || + (GNUNET_OK != + TALER_string_to_amount (args[2], + &wad_fee)) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Invalid amount `%s' specified for wad fee of partner\n", + args[2]); + GNUNET_SCHEDULER_shutdown (); + global_ret = EXIT_INVALIDARGUMENT; + return; + } + if ( (NULL == args[3]) || + (GNUNET_OK != + GNUNET_STRINGS_fancy_time_to_relative (args[3], + &wad_frequency)) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Invalid wad frequency `%s' specified for add partner\n", + args[3]); + GNUNET_SCHEDULER_shutdown (); + global_ret = EXIT_INVALIDARGUMENT; + return; + } + if ( (NULL == args[4]) || + ( (1 != sscanf (args[4], + "%u%c", + &year, + &dummy)) && + (0 != strcasecmp ("now", + args[4])) ) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Invalid year `%s' specified for add partner\n", + args[4]); + GNUNET_SCHEDULER_shutdown (); + global_ret = EXIT_INVALIDARGUMENT; + return; + } + if (0 == strcasecmp ("now", + args[4])) + year = GNUNET_TIME_get_current_year (); + start_date = GNUNET_TIME_absolute_to_timestamp ( + GNUNET_TIME_year_to_time (year)); + end_date = GNUNET_TIME_absolute_to_timestamp ( + GNUNET_TIME_year_to_time (year + 1)); + + if (GNUNET_OK != + load_offline_key (GNUNET_NO)) + return; + TALER_exchange_offline_partner_details_sign (&partner_pub, + start_date, + end_date, + wad_frequency, + &wad_fee, + partner_base_url, + &master_priv, + &master_sig); + output_operation (OP_ADD_PARTNER, + GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("partner_base_url", + partner_base_url), + GNUNET_JSON_pack_time_rel ("wad_frequency", + wad_frequency), + GNUNET_JSON_pack_timestamp ("start_date", + start_date), + GNUNET_JSON_pack_timestamp ("end_date", + end_date), + GNUNET_JSON_pack_data_auto ("partner_pub", + &partner_pub), + GNUNET_JSON_pack_data_auto ("master_sig", + &master_sig))); + next (args + 5); +} + + +/** + * Enable or disable AML staff. + * + * @param is_active true to enable, false to disable + * @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) + */ +static void +do_set_aml_staff (bool is_active, + char *const *args) +{ + struct TALER_AmlOfficerPublicKeyP officer_pub; + const char *officer_name; + bool read_only; + struct TALER_MasterSignatureP master_sig; + struct GNUNET_TIME_Timestamp now + = GNUNET_TIME_timestamp_get (); + + if (NULL != in) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Downloaded data was not consumed, not updating AML staff status\n"); + GNUNET_SCHEDULER_shutdown (); + global_ret = EXIT_FAILURE; + return; + } + if ( (NULL == args[0]) || + (GNUNET_OK != + GNUNET_STRINGS_string_to_data (args[0], + strlen (args[0]), + &officer_pub, + sizeof (officer_pub))) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "You must specify the AML officer's public key as first argument for this subcommand\n"); + GNUNET_SCHEDULER_shutdown (); + global_ret = EXIT_INVALIDARGUMENT; + return; + } + if (NULL == args[1]) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "You must specify the officer's legal name as the 2nd argument to this subcommand\n"); + GNUNET_SCHEDULER_shutdown (); + global_ret = EXIT_INVALIDARGUMENT; + return; + } + officer_name = args[1]; + if (is_active) + { + if ( (NULL == args[2]) || + ( (0 != strcmp (args[2], + "ro")) && + (0 != strcmp (args[2], + "rw")) ) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "You must specify 'ro' or 'rw' (and not `%s') for the access level\n", + args[2]); + GNUNET_SCHEDULER_shutdown (); + global_ret = EXIT_INVALIDARGUMENT; + return; + } + read_only = (0 == strcmp (args[2], + "ro")); + } + else + { + read_only = true; + } + if (GNUNET_OK != + load_offline_key (GNUNET_NO)) + return; + TALER_exchange_offline_aml_officer_status_sign (&officer_pub, + officer_name, + now, + is_active, + read_only, + &master_priv, + &master_sig); + output_operation (OP_UPDATE_AML_STAFF, + GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("officer_name", + officer_name), + GNUNET_JSON_pack_timestamp ("change_date", + now), + GNUNET_JSON_pack_bool ("is_active", + is_active), + GNUNET_JSON_pack_bool ("read_only", + read_only), + GNUNET_JSON_pack_data_auto ("officer_pub", + &officer_pub), + GNUNET_JSON_pack_data_auto ("master_sig", + &master_sig))); + next (args + (is_active ? 3 : 2)); +} + + +/** + * Disable AML staff. + * + * @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, args[2] rw (read write) or ro (read only) + */ +static void +disable_aml_staff (char *const *args) +{ + do_set_aml_staff (false, + args); +} + + +/** + * Enable AML staff. + * + * @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, args[2] rw (read write) or ro (read only) + */ +static void +enable_aml_staff (char *const *args) +{ + do_set_aml_staff (true, + args); +} + + +/** * Function called with information about future keys. Dumps the JSON output * (on success), either into an internal buffer or to stdout (depending on * whether there are subsequent commands). * * @param cls closure with the `char **` remaining args - * @param hr HTTP response data - * @param keys information about the various keys used - * by the exchange, NULL if /management/keys failed + * @param mgr response data */ static void download_cb (void *cls, - const struct TALER_EXCHANGE_HttpResponse *hr, - const struct TALER_EXCHANGE_FutureKeys *keys) + const struct TALER_EXCHANGE_ManagementGetKeysResponse *mgr) { char *const *args = cls; + const struct TALER_EXCHANGE_HttpResponse *hr = &mgr->hr; - (void) keys; mgkh = NULL; switch (hr->http_status) { @@ -3075,7 +3964,7 @@ download_cb (void *cls, GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to download keys from `%s' (no HTTP response)\n", CFG_exchange_url); - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); global_ret = EXIT_FAILURE; return; } @@ -3114,7 +4003,7 @@ do_download (char *const *args) GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "exchange", "BASE_URL"); - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); global_ret = EXIT_NOTCONFIGURED; return; } @@ -3322,7 +4211,7 @@ tofu_check (const struct TALER_SecurityModulePublicKeySetP *secmset) * @param signkeys keys to output * @return #GNUNET_OK on success */ -static int +static enum GNUNET_GenericReturnValue show_signkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub, const json_t *signkeys) { @@ -3367,7 +4256,7 @@ show_signkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub, stderr, JSON_INDENT (2)); global_ret = EXIT_FAILURE; - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); return GNUNET_SYSERR; } duration = GNUNET_TIME_absolute_get_difference (start_time.abs_time, @@ -3383,7 +4272,7 @@ show_signkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub, "Invalid security module signature for signing key %s (aborting)\n", TALER_B2S (&exchange_pub)); global_ret = EXIT_FAILURE; - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); return GNUNET_SYSERR; } { @@ -3475,7 +4364,7 @@ show_denomkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub_rsa, JSON_INDENT (2)); GNUNET_JSON_parse_free (spec); global_ret = EXIT_FAILURE; - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); return GNUNET_SYSERR; } duration = GNUNET_TIME_absolute_get_difference ( @@ -3483,13 +4372,13 @@ show_denomkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub_rsa, stamp_expire_withdraw.abs_time); TALER_denom_pub_hash (&denom_pub, &h_denom_pub); - switch (denom_pub.cipher) + switch (denom_pub.bsign_pub_key->cipher) { - case TALER_DENOMINATION_RSA: + case GNUNET_CRYPTO_BSA_RSA: { struct TALER_RsaPubHashP h_rsa; - TALER_rsa_pub_hash (denom_pub.details.rsa_public_key, + TALER_rsa_pub_hash (denom_pub.bsign_pub_key->details.rsa_public_key, &h_rsa); ok = TALER_exchange_secmod_rsa_verify (&h_rsa, section_name, @@ -3499,11 +4388,11 @@ show_denomkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub_rsa, &secm_sig); } break; - case TALER_DENOMINATION_CS: + case GNUNET_CRYPTO_BSA_CS: { struct TALER_CsPubHashP h_cs; - TALER_cs_pub_hash (&denom_pub.details.cs_public_key, + TALER_cs_pub_hash (&denom_pub.bsign_pub_key->details.cs_public_key, &h_cs); ok = TALER_exchange_secmod_cs_verify (&h_cs, section_name, @@ -3524,7 +4413,7 @@ show_denomkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub_rsa, "Invalid security module signature for denomination key %s (aborting)\n", GNUNET_h2s (&h_denom_pub.hash)); global_ret = EXIT_FAILURE; - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); return GNUNET_SYSERR; } @@ -3611,7 +4500,7 @@ parse_keys_input (const char *command_name) err.source, err.position); global_ret = EXIT_FAILURE; - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); return NULL; } } @@ -3630,7 +4519,7 @@ parse_keys_input (const char *command_name) stderr, JSON_INDENT (2)); global_ret = EXIT_FAILURE; - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); return NULL; } if (0 != strcmp (op_str, @@ -3661,15 +4550,15 @@ do_show (char *const *args) json_t *keys; const char *err_name; unsigned int err_line; - json_t *denomkeys; - json_t *signkeys; + const json_t *denomkeys; + const json_t *signkeys; struct TALER_MasterPublicKeyP mpub; struct TALER_SecurityModulePublicKeySetP secmset; struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_json ("future_denoms", - &denomkeys), - GNUNET_JSON_spec_json ("future_signkeys", - &signkeys), + GNUNET_JSON_spec_array_const ("future_denoms", + &denomkeys), + GNUNET_JSON_spec_array_const ("future_signkeys", + &signkeys), GNUNET_JSON_spec_fixed_auto ("master_pub", &mpub), GNUNET_JSON_spec_fixed_auto ("denom_secmod_public_key", @@ -3702,7 +4591,7 @@ do_show (char *const *args) stderr, JSON_INDENT (2)); global_ret = EXIT_FAILURE; - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); json_decref (keys); return; } @@ -3713,8 +4602,7 @@ do_show (char *const *args) GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Fatal: exchange uses different master key!\n"); global_ret = EXIT_FAILURE; - test_shutdown (); - GNUNET_JSON_parse_free (spec); + GNUNET_SCHEDULER_shutdown (); json_decref (keys); return; } @@ -3722,8 +4610,7 @@ do_show (char *const *args) tofu_check (&secmset)) { global_ret = EXIT_FAILURE; - test_shutdown (); - GNUNET_JSON_parse_free (spec); + GNUNET_SCHEDULER_shutdown (); json_decref (keys); return; } @@ -3736,13 +4623,11 @@ do_show (char *const *args) denomkeys)) ) { global_ret = EXIT_FAILURE; - test_shutdown (); - GNUNET_JSON_parse_free (spec); + GNUNET_SCHEDULER_shutdown (); json_decref (keys); return; } json_decref (keys); - GNUNET_JSON_parse_free (spec); next (args); } @@ -3801,7 +4686,7 @@ sign_signkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub, stderr, JSON_INDENT (2)); global_ret = EXIT_FAILURE; - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); return GNUNET_SYSERR; } @@ -3818,7 +4703,7 @@ sign_signkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub, "Invalid security module signature for signing key %s (aborting)\n", TALER_B2S (&exchange_pub)); global_ret = EXIT_FAILURE; - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); GNUNET_JSON_parse_free (spec); return GNUNET_SYSERR; } @@ -3955,7 +4840,7 @@ sign_denomkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub_rsa, JSON_INDENT (2)); GNUNET_JSON_parse_free (spec); global_ret = EXIT_FAILURE; - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); return GNUNET_SYSERR; } duration = GNUNET_TIME_absolute_get_difference ( @@ -3967,13 +4852,14 @@ sign_denomkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub_rsa, TALER_denom_pub_hash (&denom_pub, &h_denom_pub); - switch (denom_pub.cipher) + + switch (denom_pub.bsign_pub_key->cipher) { - case TALER_DENOMINATION_RSA: + case GNUNET_CRYPTO_BSA_RSA: { struct TALER_RsaPubHashP h_rsa; - TALER_rsa_pub_hash (denom_pub.details.rsa_public_key, + TALER_rsa_pub_hash (denom_pub.bsign_pub_key->details.rsa_public_key, &h_rsa); if (GNUNET_OK != TALER_exchange_secmod_rsa_verify (&h_rsa, @@ -3987,17 +4873,17 @@ sign_denomkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub_rsa, "Invalid security module signature for denomination key %s (aborting)\n", GNUNET_h2s (&h_denom_pub.hash)); global_ret = EXIT_FAILURE; - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); GNUNET_JSON_parse_free (spec); return GNUNET_SYSERR; } } break; - case TALER_DENOMINATION_CS: + case GNUNET_CRYPTO_BSA_CS: { struct TALER_CsPubHashP h_cs; - TALER_cs_pub_hash (&denom_pub.details.cs_public_key, + TALER_cs_pub_hash (&denom_pub.bsign_pub_key->details.cs_public_key, &h_cs); if (GNUNET_OK != TALER_exchange_secmod_cs_verify (&h_cs, @@ -4011,7 +4897,7 @@ sign_denomkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub_rsa, "Invalid security module signature for denomination key %s (aborting)\n", GNUNET_h2s (&h_denom_pub.hash)); global_ret = EXIT_FAILURE; - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); GNUNET_JSON_parse_free (spec); return GNUNET_SYSERR; } @@ -4019,7 +4905,7 @@ sign_denomkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub_rsa, break; default: global_ret = EXIT_FAILURE; - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); GNUNET_JSON_parse_free (spec); return GNUNET_SYSERR; } @@ -4062,15 +4948,15 @@ do_sign (char *const *args) json_t *keys; const char *err_name; unsigned int err_line; - json_t *denomkeys; - json_t *signkeys; + const json_t *denomkeys; + const json_t *signkeys; struct TALER_MasterPublicKeyP mpub; struct TALER_SecurityModulePublicKeySetP secmset; struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_json ("future_denoms", - &denomkeys), - GNUNET_JSON_spec_json ("future_signkeys", - &signkeys), + GNUNET_JSON_spec_array_const ("future_denoms", + &denomkeys), + GNUNET_JSON_spec_array_const ("future_signkeys", + &signkeys), GNUNET_JSON_spec_fixed_auto ("master_pub", &mpub), GNUNET_JSON_spec_fixed_auto ("denom_secmod_public_key", @@ -4105,7 +4991,7 @@ do_sign (char *const *args) stderr, JSON_INDENT (2)); global_ret = EXIT_FAILURE; - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); json_decref (keys); return; } @@ -4116,8 +5002,7 @@ do_sign (char *const *args) GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Fatal: exchange uses different master key!\n"); global_ret = EXIT_FAILURE; - test_shutdown (); - GNUNET_JSON_parse_free (spec); + GNUNET_SCHEDULER_shutdown (); json_decref (keys); return; } @@ -4127,8 +5012,7 @@ do_sign (char *const *args) GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Fatal: security module keys changed!\n"); global_ret = EXIT_FAILURE; - test_shutdown (); - GNUNET_JSON_parse_free (spec); + GNUNET_SCHEDULER_shutdown (); json_decref (keys); return; } @@ -4149,10 +5033,9 @@ do_sign (char *const *args) denomkey_sig_array)) ) { global_ret = EXIT_FAILURE; - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); json_decref (signkey_sig_array); json_decref (denomkey_sig_array); - GNUNET_JSON_parse_free (spec); json_decref (keys); return; } @@ -4164,7 +5047,6 @@ do_sign (char *const *args) GNUNET_JSON_pack_array_steal ("signkey_sigs", signkey_sig_array))); } - GNUNET_JSON_parse_free (spec); json_decref (keys); next (args); } @@ -4353,6 +5235,9 @@ cmd_handler (char *const *args, cmds[i].name, cmds[i].help); } + json_decref (out); + out = NULL; + GNUNET_SCHEDULER_shutdown (); } @@ -4381,7 +5266,7 @@ do_work_extensions (char *const *args) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "You must provide a subcommand: `show` or `sign`.\n"); - test_shutdown (); + GNUNET_SCHEDULER_shutdown (); global_ret = EXIT_INVALIDARGUMENT; return; } @@ -4445,7 +5330,7 @@ work (void *cls) { .name = "enable-account", .help = - "enable wire account of the exchange (payto-URI must be given as argument)", + "enable wire account of the exchange (payto-URI must be given as argument; for optional arguments see man page)", .cb = &do_add_wire }, { @@ -4473,6 +5358,24 @@ work (void *cls) .cb = &do_drain }, { + .name = "add-partner", + .help = + "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)", + .cb = &do_add_partner + }, + { + .name = "aml-enable", + .help = + "enable AML staff member (staff member public key, legal name and rw (read write) or ro (read only) must be given as arguments)", + .cb = &enable_aml_staff + }, + { + .name = "aml-disable", + .help = + "disable AML staff member (staff member public key and legal name must be given as arguments)", + .cb = &disable_aml_staff + }, + { .name = "upload", .help = "upload operation result to exchange (to be performed online!)", |