merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

commit 399592ff65e78f56c2b51084d8f8eb3da8d00c58
parent 2498ff7f8548df50ac8a2ff24e113595da8978f9
Author: Christian Grothoff <christian@grothoff.org>
Date:   Sun,  2 Feb 2025 16:42:03 +0100

mostly dead code elimination

Diffstat:
Msrc/backend/taler-merchant-httpd_contract.c | 387-------------------------------------------------------------------------------
Msrc/backend/taler-merchant-httpd_contract.h | 79-------------------------------------------------------------------------------
Msrc/backend/taler-merchant-httpd_private-post-orders.c | 241+++++++------------------------------------------------------------------------
Msrc/include/taler_merchant_util.h | 26++++++++++++++++++++++++++
Msrc/testing/test_merchant_api.c | 2+-
Msrc/util/Makefile.am | 3++-
Msrc/util/contract_parse.c | 52++++++----------------------------------------------
Asrc/util/json.c | 171+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 225 insertions(+), 736 deletions(-)

diff --git a/src/backend/taler-merchant-httpd_contract.c b/src/backend/taler-merchant-httpd_contract.c @@ -26,393 +26,6 @@ #include "taler-merchant-httpd_contract.h" -enum TALER_MERCHANT_ContractInputType -TMH_contract_input_type_from_string (const char *str) -{ - /* For now, only 'token' is the only supported option. */ - if (0 == strcmp ("token", str)) - { - return TALER_MERCHANT_CONTRACT_INPUT_TYPE_TOKEN; - } - - return TALER_MERCHANT_CONTRACT_INPUT_TYPE_INVALID; -} - - -enum TALER_MERCHANT_ContractOutputType -TMH_contract_output_type_from_string (const char *str) -{ - /* For now, only 'token' is the only supported option. */ - if (0 == strcmp ("token", str)) - { - return TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN; - } - return TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID; -} - - -const char * -TMH_string_from_contract_input_type (enum TALER_MERCHANT_ContractInputType t) -{ - switch (t) - { - case TALER_MERCHANT_CONTRACT_INPUT_TYPE_TOKEN: - return "token"; -#if FUTURE - case TALER_MERCHANT_CONTRACT_INPUT_TYPE_COIN: - return "coin"; -#endif - default: - return "invalid"; - } -} - - -const char * -TMH_string_from_contract_output_type (enum TALER_MERCHANT_ContractOutputType t) -{ - switch (t) - { - case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN: - return "token"; -#if FUTURE - case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_COIN: - return "coin"; -#endif - case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT: - return "donation_receipt"; - default: - return "invalid"; - } -} - - -/** - * Parse given JSON object to choices array. - * - * @param cls closure, pointer to array length - * @param root the json array representing the choices - * @param[out] ospec where to write the data - * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error - */ -static enum GNUNET_GenericReturnValue -parse_choices (void *cls, - json_t *root, - struct GNUNET_JSON_Specification *ospec) -{ - struct TALER_MERCHANT_ContractChoice **choices = ospec->ptr; - unsigned int *choices_len = cls; - - if (! json_is_array (root)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - - GNUNET_array_grow (*choices, - *choices_len, - json_array_size (root)); - - for (unsigned int i = 0; i<*choices_len; i++) - { - struct TALER_MERCHANT_ContractChoice *choice = &(*choices)[i]; - const json_t *jinputs; - const json_t *joutputs; - struct GNUNET_JSON_Specification spec[] = { - TALER_JSON_spec_amount_any ("amount", - &choice->amount), - GNUNET_JSON_spec_mark_optional ( - TALER_JSON_spec_amount_any ("max_fee", - &choice->max_fee), - NULL), - GNUNET_JSON_spec_array_const ("inputs", - &jinputs), - GNUNET_JSON_spec_array_const ("outputs", - &joutputs), - GNUNET_JSON_spec_end () - }; - const char *error_name; - unsigned int error_line; - - if (GNUNET_OK != - GNUNET_JSON_parse (json_array_get (root, i), - spec, - &error_name, - &error_line)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Failed to parse %s at %u: %s\n", - spec[error_line].field, - error_line, - error_name); - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - - { - const json_t *jinput; - size_t idx; - json_array_foreach ((json_t *) jinputs, idx, jinput) - { - struct TALER_MERCHANT_ContractInput input = { - .details.token.count = 1 - }; - const char *kind; - struct GNUNET_JSON_Specification ispec[] = { - GNUNET_JSON_spec_string ("kind", - &kind), - GNUNET_JSON_spec_string ("token_family_slug", - &input.details.token.token_family_slug), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_uint32 ("count", - &input.details.token.count), - NULL), - GNUNET_JSON_spec_end () - }; - const char *ierror_name; - unsigned int ierror_line; - - if (GNUNET_OK != - GNUNET_JSON_parse (jinput, - ispec, - &ierror_name, - &ierror_line)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Failed to parse %s at %u: %s\n", - ispec[ierror_line].field, - ierror_line, - ierror_name); - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - - input.type = TMH_contract_input_type_from_string (kind); - - if (TALER_MERCHANT_CONTRACT_INPUT_TYPE_INVALID == input.type) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Field 'kind' invalid in input #%u\n", - (unsigned int) idx); - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - - if (0 == input.details.token.count) - { - /* Ignore inputs with 'number' field set to 0 */ - continue; - } - - GNUNET_array_append (choice->inputs, - choice->inputs_len, - input); - } - } - - { - const json_t *joutput; - size_t idx; - json_array_foreach ((json_t *) joutputs, idx, joutput) - { - struct TALER_MERCHANT_ContractOutput output = { - .details.token.count = 1 - }; - const char *kind; - uint32_t ki; - struct GNUNET_JSON_Specification ispec[] = { - GNUNET_JSON_spec_string ("kind", - &kind), - GNUNET_JSON_spec_string ("token_family_slug", - // FIXME... - (const char **) &output.details.token. - token_family_slug), - GNUNET_JSON_spec_uint32 ("key_index", - &ki), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_uint32 ("count", - &output.details.token.count), - NULL), - GNUNET_JSON_spec_end () - }; - const char *ierror_name; - unsigned int ierror_line; - - if (GNUNET_OK != - GNUNET_JSON_parse (joutput, - ispec, - &ierror_name, - &ierror_line)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Failed to parse %s at %u: %s\n", - ispec[ierror_line].field, - ierror_line, - ierror_name); - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - output.details.token.key_index = ki; - output.type = TMH_contract_output_type_from_string (kind); - - if (TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID == output.type) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Field 'kind' invalid in output #%u\n", - (unsigned int) idx); - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - - if (0 == output.details.token.count) - { - /* Ignore outputs with 'number' field set to 0 */ - continue; - } - - GNUNET_array_append (choice->outputs, - choice->outputs_len, - output); - } - } - } - return GNUNET_OK; -} - - -struct GNUNET_JSON_Specification -TALER_JSON_spec_choices (const char *name, - struct TALER_MERCHANT_ContractChoice **choices, - unsigned int *choices_len) -{ - struct GNUNET_JSON_Specification ret = { - .cls = (void *) choices_len, - .parser = &parse_choices, - .field = name, - .ptr = choices, - }; - - return ret; -} - - -/** - * Parse given JSON object to token families array. - * - * @param cls closure, pointer to array length - * @param root the json object representing the token families. The keys are - * the token family slugs. - * @param[out] ospec where to write the data - * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error - */ -static enum GNUNET_GenericReturnValue -parse_token_families (void *cls, - json_t *root, - struct GNUNET_JSON_Specification *ospec) -{ - struct TALER_MERCHANT_ContractTokenFamily **families = ospec->ptr; - unsigned int *families_len = cls; - json_t *jfamily; - const char *slug; - - if (! json_is_object (root)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - json_object_foreach (root, slug, jfamily) - { - const json_t *keys; - struct TALER_MERCHANT_ContractTokenFamily family = { - .slug = GNUNET_strdup (slug) - }; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_array_const ("keys", - &keys), - GNUNET_JSON_spec_bool ("critical", - &family.critical), - GNUNET_JSON_spec_end () - }; - const char *error_name; - unsigned int error_line; - - if (GNUNET_OK != - GNUNET_JSON_parse (jfamily, - spec, - &error_name, - &error_line)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Failed to parse %s at %u: %s\n", - spec[error_line].field, - error_line, - error_name); - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - GNUNET_array_grow (family.keys, - family.keys_len, - json_array_size (keys)); - - for (unsigned int i = 0; i<family.keys_len; i++) - { - struct TALER_MERCHANT_ContractTokenFamilyKey *key = &family.keys[i]; - struct GNUNET_JSON_Specification key_spec[] = { - TALER_JSON_spec_token_pub ( - "public_key", - &key->pub), - GNUNET_JSON_spec_timestamp ( - "valid_after", - &key->valid_after), - GNUNET_JSON_spec_timestamp ( - "valid_before", - &key->valid_before), - GNUNET_JSON_spec_end () - }; - const char *ierror_name; - unsigned int ierror_line; - - if (GNUNET_OK != - GNUNET_JSON_parse (json_array_get (keys, - i), - key_spec, - &ierror_name, - &ierror_line)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Failed to parse %s at %u: %s\n", - key_spec[ierror_line].field, - ierror_line, - ierror_name); - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - } - GNUNET_array_append (*families, - *families_len, - family); - } - return GNUNET_OK; -} - - -struct GNUNET_JSON_Specification -TALER_JSON_spec_token_families ( - const char *name, - struct TALER_MERCHANT_ContractTokenFamily **families, - unsigned int *families_len) -{ - struct GNUNET_JSON_Specification ret = { - .cls = (void *) families_len, - .parser = &parse_token_families, - .field = name, - .ptr = families, - }; - - return ret; -} - - enum GNUNET_GenericReturnValue TMH_find_token_family_key ( const char *slug, diff --git a/src/backend/taler-merchant-httpd_contract.h b/src/backend/taler-merchant-httpd_contract.h @@ -28,85 +28,6 @@ #include <jansson.h> -enum TALER_MERCHANT_ContractInputType -TMH_contract_input_type_from_string (const char *str); - - -enum TALER_MERCHANT_ContractOutputType -TMH_contract_output_type_from_string (const char *str); - - -const char * -TMH_string_from_contract_input_type (enum TALER_MERCHANT_ContractInputType t); - - -const char * -TMH_string_from_contract_output_type (enum TALER_MERCHANT_ContractOutputType t); - -/** - * Serialize @a contract to a JSON object, ready to be stored in the database. - * The @a contract can be of v0 or v1. - * - * @param[in] contract contract struct to serialize - * @param[in] instance merchant instance for this contract - * @param[in] exchanges JSON array of exchanges - * @param[out] out serialized contract as JSON object - * @return #GNUNET_OK on success - * #GNUNET_NO if @a contract was not valid - * #GNUNET_SYSERR on failure - */ -enum GNUNET_GenericReturnValue -TMH_serialize_contract (const struct TALER_MERCHANT_Contract *contract, - const struct TMH_MerchantInstance *instance, - json_t *exchanges, - json_t **out); - - -enum GNUNET_GenericReturnValue -TMH_serialize_contract_v0 (const struct TALER_MERCHANT_Contract *contract, - const struct TMH_MerchantInstance *instance, - json_t *exchanges, - json_t **out); - - -enum GNUNET_GenericReturnValue -TMH_serialize_contract_v1 (const struct TALER_MERCHANT_Contract *contract, - const struct TMH_MerchantInstance *instance, - json_t *exchanges, - json_t **out); - -/** - * Provide specification to parse given JSON array to an array - * of contract choices. - * - * @param name name of the choices field in the JSON - * @param[out] choices set to the first element of the array - * @param[out] choices_len set to the length of the @a choices array - * @return spec for parsing a choices array - */ -struct GNUNET_JSON_Specification -TALER_JSON_spec_choices ( - const char *name, - struct TALER_MERCHANT_ContractChoice **choices, - unsigned int *choices_len); - - -/** - * Provide specification to parse given JSON object to an array - * of token families. - * - * @param name name of the token families field in the JSON - * @param[out] families set to the first element of the array - * @param[out] families_len set to the length of the @a families - * @return spec for parsing a token families object - */ -struct GNUNET_JSON_Specification -TALER_JSON_spec_token_families ( - const char *name, - struct TALER_MERCHANT_ContractTokenFamily **families, - unsigned int *families_len); - - /** * Find matching token family in @a families based on @a slug. Then use * @a valid_after to find the matching public key within it. diff --git a/src/backend/taler-merchant-httpd_private-post-orders.c b/src/backend/taler-merchant-httpd_private-post-orders.c @@ -818,6 +818,9 @@ clean_order (void *cls) json_decref (mctf->description_i18n); switch (mctf->kind) { + case TALER_MERCHANT_CONTRACT_TOKEN_KIND_INVALID: + GNUNET_break (0); + break; case TALER_MERCHANT_CONTRACT_TOKEN_KIND_SUBSCRIPTION: for (size_t j = 0; j<mctf->details.subscription.trusted_domains_len; j++) GNUNET_free (mctf->details.subscription.trusted_domains[j]); @@ -1696,12 +1699,14 @@ add_output_token_family (struct OrderContext *oc, "lookup_token_family_key"); return GNUNET_SYSERR; case GNUNET_DB_STATUS_SOFT_ERROR: + /* Single-statement transaction shouldn't possibly cause serialization errors. + Thus treating like a hard error. */ GNUNET_break (0); reply_with_error (oc, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_GENERIC_DB_SOFT_FAILURE, "lookup_token_family_key"); - return GNUNET_SYSERR; /* FIXME: retry instead? */ + return GNUNET_SYSERR; case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Output token family slug %s unknown\n", @@ -1861,12 +1866,14 @@ add_output_token_family (struct OrderContext *oc, NULL); return GNUNET_SYSERR; case GNUNET_DB_STATUS_SOFT_ERROR: + /* Single-statement transaction shouldn't possibly cause serialization errors. + Thus treating like a hard error. */ GNUNET_break (0); reply_with_error (oc, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_GENERIC_DB_SOFT_FAILURE, NULL); - return GNUNET_SYSERR; // FIXME: or retry? + return GNUNET_SYSERR; case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: GNUNET_break (0); reply_with_error (oc, @@ -3003,11 +3010,10 @@ parse_order (struct OrderContext *oc) { GNUNET_break_op (0); GNUNET_JSON_parse_free (spec); - // FIXME: use CONFLICT and a different EC! reply_with_error (oc, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_CURRENCY_MISMATCH, - "no trusted exchange for this currency"); + MHD_HTTP_CONFLICT, + TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_NO_EXCHANGE_FOR_CURRENCY, + oc->parse_order.details.v0.brutto.currency); return; } if (NULL != choices) @@ -3337,224 +3343,13 @@ parse_order (struct OrderContext *oc) static void parse_donau_instances (struct OrderContext *oc) { + /* FIXME-#9059: not yet implemented! */ return; } #endif -/** - * Parse the inputs for a particular choice. - * - * @param[in,out] oc order context - * @param[out] choice to parse inputs for - * @param jinputs array of inputs to parse - * @return #GNUNET_OK on success, #GNUNET_SYSERR - * if an error was encountered (and already handled) - */ -static enum GNUNET_GenericReturnValue -parse_order_inputs (struct OrderContext *oc, - struct TALER_MERCHANT_ContractChoice *choice, - const json_t *jinputs) -{ - const json_t *jinput; - size_t idx; - - json_array_foreach ((json_t *) jinputs, idx, jinput) - { - struct TALER_MERCHANT_ContractInput input = { - .details.token.count = 1 - }; - const char *kind; - const char *ierror_name; - unsigned int ierror_line; - struct GNUNET_JSON_Specification ispec[] = { - // FIXME: define spec parser for 'kind'... - GNUNET_JSON_spec_string ("kind", - &kind), - GNUNET_JSON_spec_string ("token_family_slug", - &input.details.token.token_family_slug), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_uint32 ("count", - &input.details.token.count), - NULL), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (jinput, - ispec, - &ierror_name, - &ierror_line)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Invalid input #%u for field %s\n", - (unsigned int) idx, - ierror_name); - reply_with_error (oc, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - ierror_name); - return GNUNET_SYSERR; - } - - input.type = TMH_contract_input_type_from_string (kind); - if (TALER_MERCHANT_CONTRACT_INPUT_TYPE_INVALID == input.type) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Field 'kind' invalid in input #%u\n", - (unsigned int) idx); - reply_with_error (oc, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "kind"); - return GNUNET_SYSERR; - } - - if (0 == input.details.token.count) - { - /* Ignore inputs with 'number' field set to 0 */ - continue; - } - - if (GNUNET_OK != - add_input_token_family (oc, - input.details.token.token_family_slug)) - { - /* error is already scheduled, return. */ - return GNUNET_SYSERR; - } - - GNUNET_array_append (choice->inputs, - choice->inputs_len, - input); - } - return GNUNET_OK; -} - - -/** - * Parse the outputs for a particular choice. - * - * @param[in,out] oc order context - * @param[out] choice to parse inputs for - * @param joutputs array of outputs to parse - * @return #GNUNET_OK on success, #GNUNET_SYSERR - * if an error was encountered (and already handled) - */ -static enum GNUNET_GenericReturnValue -parse_order_outputs (struct OrderContext *oc, - struct TALER_MERCHANT_ContractChoice *choice, - const json_t *joutputs) -{ - const json_t *joutput; - size_t idx; - - json_array_foreach ((json_t *) joutputs, idx, joutput) - { - struct TALER_MERCHANT_ContractOutput output = { - .details.token.count = 1 - }; - const char *kind; - const char *ierror_name; - unsigned int ierror_line; - bool nots; - struct GNUNET_TIME_Timestamp valid_at; - struct GNUNET_JSON_Specification ispec[] = { - // FIXME: define spec parser for 'kind'... - GNUNET_JSON_spec_string ("kind", - &kind), - GNUNET_JSON_spec_string ("token_family_slug", - // FIXME... - (const char **) &output.details.token. - token_family_slug), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_uint32 ("count", - &output.details.token.count), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_timestamp ("valid_at", - &valid_at), - &nots), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (joutput, - ispec, - &ierror_name, - &ierror_line)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Invalid output #%u for field %s\n", - (unsigned int) idx, - ierror_name); - reply_with_error (oc, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - ierror_name); - return GNUNET_SYSERR; - } - if (nots) - { - valid_at = oc->parse_order.pay_deadline; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Looking for output token valid at pay deadline %s\n", - GNUNET_TIME_timestamp2s (valid_at)); - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Looking for output token valid at %s\n", - GNUNET_TIME_timestamp2s (valid_at)); - } - if (GNUNET_TIME_timestamp_cmp (valid_at, - <, - oc->parse_order.pay_deadline)) - { - reply_with_error (oc, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "valid_at before pay_deadline"); - return GNUNET_SYSERR; - } - - output.type = TMH_contract_output_type_from_string (kind); - if (TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID == output.type) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Field 'kind' invalid in output #%u\n", - (unsigned int) idx); - reply_with_error (oc, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "kind"); - return GNUNET_SYSERR; - } - - if (0 == output.details.token.count) - { - /* Ignore outputs with 'number' field set to 0. */ - continue; - } - - if (GNUNET_OK != - add_output_token_family (oc, - output.details.token.token_family_slug, - valid_at, - &output.details.token.key_index)) - { - /* Error is already scheduled, return. */ - return GNUNET_SYSERR; - } - - GNUNET_array_append (choice->outputs, - choice->outputs_len, - output); - } - return GNUNET_OK; -} - /** * Parse contract choices. Upon success, continue @@ -3652,11 +3447,10 @@ parse_choices (struct OrderContext *oc) { GNUNET_break_op (0); GNUNET_JSON_parse_free (spec); - // FIXME: use CONFLICT and a different EC! reply_with_error (oc, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_CURRENCY_MISMATCH, - "no trusted exchange for this currency"); + MHD_HTTP_CONFLICT, + TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_NO_EXCHANGE_FOR_CURRENCY, + choice->amount.currency); return; } @@ -3744,6 +3538,9 @@ parse_choices (struct OrderContext *oc) case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID: GNUNET_assert (0); break; + case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT: + GNUNET_break (0); /* FIXME-#9059: not yet implemented! */ + break; case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN: /* Ignore inputs tokens with 'count' field set to 0 */ if (0 == output.details.token.count) diff --git a/src/include/taler_merchant_util.h b/src/include/taler_merchant_util.h @@ -665,6 +665,19 @@ TALER_MERCHANT_contract_parse (json_t *input, /** + * Provide specification to parse an JSON contract input type. + * The value is provided as a descriptive string. + * + * @param name name of the JSON member with the contract type + * @param[out] cit where to store the contract input type + * @return spec for parsing a contract input type + */ +struct GNUNET_JSON_Specification +TALER_MERCHANT_json_spec_cit (const char *name, + enum TALER_MERCHANT_ContractInputType *cit); + + +/** * Parse JSON contract terms choice input. * * @param[in] root JSON object containing choice input @@ -682,6 +695,19 @@ TALER_MERCHANT_parse_choice_input ( /** + * Provide specification to parse an JSON contract output type. + * The value is provided as a descriptive string. + * + * @param name name of the JSON member with the contract type + * @param[out] cot where to store the contract output type + * @return spec for parsing a contract output type + */ +struct GNUNET_JSON_Specification +TALER_MERCHANT_json_spec_cot (const char *name, + enum TALER_MERCHANT_ContractOutputType *cot); + + +/** * Parse JSON contract terms choice output. * * @param[in] root JSON object containing choice output diff --git a/src/testing/test_merchant_api.c b/src/testing/test_merchant_api.c @@ -229,7 +229,7 @@ run (void *cls, TALER_TESTING_cmd_merchant_post_orders_no_claim ( "create-proposal-bad-currency", merchant_url, - MHD_HTTP_BAD_REQUEST, + MHD_HTTP_CONFLICT, "4", GNUNET_TIME_UNIT_ZERO_TS, GNUNET_TIME_UNIT_FOREVER_TS, diff --git a/src/util/Makefile.am b/src/util/Makefile.am @@ -41,6 +41,7 @@ lib_LTLIBRARIES = \ libtalermerchantutil_la_SOURCES = \ contract_parse.c \ contract_serialize.c \ + json.c \ os_installation.c libtalermerchantutil_la_LIBADD = \ -lgnunetjson \ @@ -49,7 +50,7 @@ libtalermerchantutil_la_LIBADD = \ -ltalerutil \ $(XLIB) libtalermerchantutil_la_LDFLAGS = \ - -version-info 0:0:0 \ + -version-info 1:0:1 \ -export-dynamic -no-undefined test_contract_SOURCES = \ diff --git a/src/util/contract_parse.c b/src/util/contract_parse.c @@ -112,42 +112,6 @@ spec_merchant_details (const char *name, /** - * Get enum value from contract input type string. - * - * @param str contract input type string - * @return enum value of input type - */ -static enum TALER_MERCHANT_ContractInputType -contract_input_type_from_string (const char *str) -{ - /* For now, only 'token' is the only supported option. */ - if (0 == strcmp ("token", - str)) - return TALER_MERCHANT_CONTRACT_INPUT_TYPE_TOKEN; - return TALER_MERCHANT_CONTRACT_INPUT_TYPE_INVALID; -} - - -/** - * Get enum value from contract output type string. - * - * @param str contract output type string - * @return enum value of output type - */ -static enum TALER_MERCHANT_ContractOutputType -contract_output_type_from_string (const char *str) -{ - if (0 == strcmp ("token", - str)) - return TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN; - if (0 == strcmp ("tax-receipt", - str)) - return TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT; - return TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID; -} - - -/** * Get enum value from contract token type string. * * @param str contract token type string @@ -173,12 +137,11 @@ TALER_MERCHANT_parse_choice_input ( size_t index, bool order) { - const char *type; const char *ename; unsigned int eline; struct GNUNET_JSON_Specification ispec[] = { - GNUNET_JSON_spec_string ("type", - &type), + TALER_MERCHANT_json_spec_cit ("type", + &input->type), GNUNET_JSON_spec_end () }; @@ -197,11 +160,10 @@ TALER_MERCHANT_parse_choice_input ( return GNUNET_SYSERR; } - input->type = contract_input_type_from_string (type); - switch (input->type) { case TALER_MERCHANT_CONTRACT_INPUT_TYPE_INVALID: + GNUNET_break (0); break; case TALER_MERCHANT_CONTRACT_INPUT_TYPE_TOKEN: { @@ -249,12 +211,11 @@ TALER_MERCHANT_parse_choice_output ( size_t index, bool order) { - const char *type; const char *ename; unsigned int eline; struct GNUNET_JSON_Specification ispec[] = { - GNUNET_JSON_spec_string ("type", - &type), + TALER_MERCHANT_json_spec_cot ("type", + &output->type), GNUNET_JSON_spec_end () }; @@ -273,11 +234,10 @@ TALER_MERCHANT_parse_choice_output ( return GNUNET_SYSERR; } - output->type = contract_output_type_from_string (type); - switch (output->type) { case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID: + GNUNET_break (0); break; case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN: { diff --git a/src/util/json.c b/src/util/json.c @@ -0,0 +1,171 @@ +/* + This file is part of TALER + (C) 2025 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file util/json.c + * @brief helper functions to parse JSON + * @author Christian Grothoff + */ +#include "platform.h" +#include <gnunet/gnunet_common.h> +#include <gnunet/gnunet_json_lib.h> +#include <jansson.h> +#include <taler/taler_json_lib.h> +#include <taler/taler_util.h> +#include "taler_merchant_util.h" + + +/** + * Parse given JSON object to `enum TALER_MERCHANT_ContractInputType` + * + * @param cls closure, NULL + * @param root the json object representing data + * @param[out] spec where to write the data + * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error + */ +static enum GNUNET_GenericReturnValue +parse_contract_input_type (void *cls, + json_t *root, + struct GNUNET_JSON_Specification *spec) +{ + static const struct Entry + { + const char *name; + enum TALER_MERCHANT_ContractInputType val; + } lt [] = { +#if FUTURE + { .name = "coin", + .val = TALER_MERCHANT_CONTRACT_INPUT_TYPE_COIN }, +#endif + { .name = "token", + .val = TALER_MERCHANT_CONTRACT_INPUT_TYPE_TOKEN }, + { .name = NULL, + .val = TALER_MERCHANT_CONTRACT_INPUT_TYPE_INVALID } + }; + enum TALER_MERCHANT_ContractInputType *res + = (enum TALER_MERCHANT_ContractInputType *) spec->ptr; + + (void) cls; + if (json_is_string (root)) + { + const char *str; + + str = json_string_value (root); + if (NULL == str) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + for (unsigned int i = 0; NULL != lt[i].name; i++) + { + if (0 == strcasecmp (str, + lt[i].name)) + { + *res = lt[i].val; + return GNUNET_OK; + } + } + } + GNUNET_break_op (0); + return GNUNET_SYSERR; +} + + +struct GNUNET_JSON_Specification +TALER_MERCHANT_json_spec_cit (const char *name, + enum TALER_MERCHANT_ContractInputType *cit) +{ + struct GNUNET_JSON_Specification ret = { + .parser = &parse_contract_input_type, + .field = name, + .ptr = cit + }; + + *cit = TALER_MERCHANT_CONTRACT_INPUT_TYPE_INVALID; + return ret; +} + + +/** + * Parse given JSON object to `enum TALER_MERCHANT_ContractOutputType` + * + * @param cls closure, NULL + * @param root the json object representing data + * @param[out] spec where to write the data + * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error + */ +static enum GNUNET_GenericReturnValue +parse_contract_output_type (void *cls, + json_t *root, + struct GNUNET_JSON_Specification *spec) +{ + static const struct Entry + { + const char *name; + enum TALER_MERCHANT_ContractOutputType val; + } lt [] = { + { .name = "token", + .val = TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN }, + { .name = "tax-receipt", + .val = TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT }, +#if FUTURE + { .name = "coin", + .val = TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_COIN }, +#endif + { .name = NULL, + .val = TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID } + }; + enum TALER_MERCHANT_ContractOutputType *res + = (enum TALER_MERCHANT_ContractOutputType *) spec->ptr; + + (void) cls; + if (json_is_string (root)) + { + const char *str; + + str = json_string_value (root); + if (NULL == str) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + for (unsigned int i = 0; NULL != lt[i].name; i++) + { + if (0 == strcasecmp (str, + lt[i].name)) + { + *res = lt[i].val; + return GNUNET_OK; + } + } + } + GNUNET_break_op (0); + return GNUNET_SYSERR; +} + + +struct GNUNET_JSON_Specification +TALER_MERCHANT_json_spec_cot (const char *name, + enum TALER_MERCHANT_ContractOutputType *cot) +{ + struct GNUNET_JSON_Specification ret = { + .parser = &parse_contract_output_type, + .field = name, + .ptr = cot + }; + + *cot = TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID; + return ret; +}