/* This file is part of TALER (C) 2022-2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Affero 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 */ /** * @file taler-merchant-httpd_post-using-templates.c * @brief implementing POST /using-templates request handling * @author Priscilla HUANG * @author Christian Grothoff */ #include "platform.h" #include "taler-merchant-httpd_post-using-templates.h" #include "taler-merchant-httpd_private-post-orders.h" #include "taler-merchant-httpd_helper.h" #include /** * Our context. */ struct UseContext { /** * Internal handler context we are passing into the * POST /private/orders handler. */ struct TMH_HandlerContext ihc; /** * Our template details from the DB. */ struct TALER_MERCHANTDB_TemplateDetails etp; }; /** * Clean up a `struct UseContext *` * * @param cls a `struct UseContext *` */ static void cleanup_use_context (void *cls) { struct UseContext *uc = cls; TALER_MERCHANTDB_template_details_free (&uc->etp); if (NULL != uc->ihc.cc) uc->ihc.cc (uc->ihc.ctx); json_decref (uc->ihc.request_body); GNUNET_free (uc); } MHD_RESULT TMH_post_using_templates_ID (const struct TMH_RequestHandler *rh, struct MHD_Connection *connection, struct TMH_HandlerContext *hc) { struct TMH_MerchantInstance *mi = hc->instance; const char *template_id = hc->infix; const char *summary = NULL; const char *fulfillment_url = NULL; const char *fulfillment_message = NULL; struct TALER_Amount amount; bool no_amount; json_t *fake_body; bool no_summary; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_string ("summary", &summary), NULL), GNUNET_JSON_spec_mark_optional ( TALER_JSON_spec_amount_any ("amount", &amount), &no_amount), GNUNET_JSON_spec_end () }; struct UseContext *uc = hc->ctx; if (NULL == uc) { uc = GNUNET_new (struct UseContext); hc->ctx = uc; hc->cc = &cleanup_use_context; uc->ihc.instance = hc->instance; } { enum GNUNET_GenericReturnValue res; res = TALER_MHD_parse_json_data (connection, hc->request_body, spec); if (GNUNET_OK != res) { GNUNET_break_op (0); return (GNUNET_NO == res) ? MHD_YES : MHD_NO; } } { enum GNUNET_DB_QueryStatus qs; qs = TMH_db->lookup_template (TMH_db->cls, mi->settings.id, template_id, &uc->etp); switch (qs) { case GNUNET_DB_STATUS_HARD_ERROR: /* Clean up and fail hard */ GNUNET_break (0); GNUNET_JSON_parse_free (spec); return TALER_MHD_reply_with_error ( connection, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_GENERIC_DB_FETCH_FAILED, NULL); case GNUNET_DB_STATUS_SOFT_ERROR: /* this should be impossible (single select) */ GNUNET_break (0); GNUNET_JSON_parse_free (spec); return TALER_MHD_reply_with_error ( connection, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_GENERIC_DB_FETCH_FAILED, NULL); case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: /* template not found! */ GNUNET_JSON_parse_free (spec); return TALER_MHD_reply_with_error ( connection, MHD_HTTP_NOT_FOUND, TALER_EC_MERCHANT_GENERIC_TEMPLATE_UNKNOWN, template_id); case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: /* all good */ break; } /* End of the switch */ } { /* template */ const char *tsummary = NULL; const char *tcurrency = NULL; uint32_t min_age; struct GNUNET_TIME_Relative pay_duration; struct TALER_Amount tamount; bool no_tamount; struct GNUNET_JSON_Specification tspec[] = { GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_string ("summary", &tsummary), NULL), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_string ("currency", &tcurrency), NULL), GNUNET_JSON_spec_mark_optional ( TALER_JSON_spec_amount_any ("amount", &tamount), &no_tamount), GNUNET_JSON_spec_uint32 ("minimum_age", &min_age), GNUNET_JSON_spec_relative_time ("pay_duration", &pay_duration), GNUNET_JSON_spec_end () }; { enum GNUNET_GenericReturnValue res; const char *err_name; unsigned int err_line; res = GNUNET_JSON_parse (uc->etp.template_contract, tspec, &err_name, &err_line); if (GNUNET_OK != res) { GNUNET_break (0); json_dumpf (uc->etp.template_contract, stderr, JSON_INDENT (2)); GNUNET_JSON_parse_free (spec); return TALER_MHD_reply_with_error ( connection, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_GENERIC_DB_FETCH_FAILED, err_name); } } if ( (! no_amount) && (! no_tamount) ) { GNUNET_JSON_parse_free (spec); return TALER_MHD_reply_with_error ( connection, MHD_HTTP_CONFLICT, TALER_EC_MERCHANT_POST_USING_TEMPLATES_AMOUNT_CONFLICT_TEMPLATES_CONTRACT_AMOUNT, NULL); } if ( (! no_amount) && (NULL != tcurrency) && (0 != strcmp (tcurrency, amount.currency)) ) { GNUNET_JSON_parse_free (spec); return TALER_MHD_reply_with_error ( connection, MHD_HTTP_CONFLICT, TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH, tcurrency); } if (no_amount && no_tamount) { GNUNET_JSON_parse_free (spec); return TALER_MHD_reply_with_error ( connection, MHD_HTTP_CONFLICT, TALER_EC_MERCHANT_POST_USING_TEMPLATES_NO_AMOUNT, NULL); } if ( (NULL != summary) && (NULL != tsummary) ) { GNUNET_JSON_parse_free (spec); return TALER_MHD_reply_with_error ( connection, MHD_HTTP_CONFLICT, TALER_EC_MERCHANT_POST_USING_TEMPLATES_SUMMARY_CONFLICT_TEMPLATES_CONTRACT_SUBJECT, NULL); } if ( (NULL == summary) && (NULL == tsummary) ) { GNUNET_JSON_parse_free (spec); return TALER_MHD_reply_with_error ( connection, MHD_HTTP_CONFLICT, TALER_EC_MERCHANT_POST_USING_TEMPLATES_NO_SUMMARY, NULL); } no_summary = (NULL == summary); fake_body = GNUNET_JSON_PACK ( GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_string ("otp_id", uc->etp.otp_id)), GNUNET_JSON_pack_object_steal ( "order", GNUNET_JSON_PACK ( TALER_JSON_pack_amount ("amount", no_amount ? &tamount : &amount), GNUNET_JSON_pack_string ("summary", no_summary ? tsummary : summary), GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_string ( "fulfillment_url", fulfillment_url)), GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_string ( "fulfillment_message", fulfillment_message)) )) ); } uc->ihc.request_body = fake_body; return TMH_private_post_orders ( NULL, /* not even used */ connection, &uc->ihc); }