/*
This file is part of TALER
(C) 2022-2024 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_private-post-templates.c
* @brief implementing POST /templates request handling
* @author Priscilla HUANG
*/
#include "platform.h"
#include "taler-merchant-httpd_private-post-templates.h"
#include "taler-merchant-httpd_helper.h"
#include
/**
* Check if the two templates are identical.
*
* @param t1 template to compare
* @param t2 other template to compare
* @return true if they are 'equal', false if not or of payto_uris is not an array
*/
static bool
templates_equal (const struct TALER_MERCHANTDB_TemplateDetails *t1,
const struct TALER_MERCHANTDB_TemplateDetails *t2)
{
return ( (0 == strcmp (t1->template_description,
t2->template_description)) &&
( ( (NULL == t1->otp_id) &&
(NULL == t2->otp_id) ) ||
( (NULL != t1->otp_id) &&
(NULL != t2->otp_id) &&
(0 == strcmp (t1->otp_id,
t2->otp_id))) ) &&
( ( (NULL == t1->editable_defaults) &&
(NULL == t2->editable_defaults) ) ||
( (NULL != t1->editable_defaults) &&
(NULL != t2->editable_defaults) &&
(1 == json_equal (t1->editable_defaults,
t2->editable_defaults))) ) &&
(1 == json_equal (t1->template_contract,
t2->template_contract)) );
}
MHD_RESULT
TMH_private_post_templates (const struct TMH_RequestHandler *rh,
struct MHD_Connection *connection,
struct TMH_HandlerContext *hc)
{
struct TMH_MerchantInstance *mi = hc->instance;
struct TALER_MERCHANTDB_TemplateDetails tp = { 0 };
const char *template_id;
enum GNUNET_DB_QueryStatus qs;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_string ("template_id",
&template_id),
GNUNET_JSON_spec_string ("template_description",
(const char **) &tp.template_description),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_string ("otp_id",
(const char **) &tp.otp_id),
NULL),
GNUNET_JSON_spec_json ("template_contract",
&tp.template_contract),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_json ("editable_defaults",
&tp.editable_defaults),
NULL),
GNUNET_JSON_spec_end ()
};
uint64_t otp_serial = 0;
GNUNET_assert (NULL != mi);
{
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;
}
}
if (! TMH_template_contract_valid (tp.template_contract))
{
GNUNET_break_op (0);
json_dumpf (tp.template_contract,
stderr,
JSON_INDENT (2));
GNUNET_JSON_parse_free (spec);
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_BAD_REQUEST,
TALER_EC_GENERIC_PARAMETER_MALFORMED,
"template_contract");
}
if (NULL != tp.editable_defaults)
{
const char *key;
json_t *val;
json_object_foreach (tp.editable_defaults, key, val)
{
if (NULL !=
json_object_get (tp.template_contract,
key))
{
char *msg;
MHD_RESULT ret;
GNUNET_break_op (0);
GNUNET_asprintf (&msg,
"editable_defaults::%s conflicts with template_contract",
key);
GNUNET_JSON_parse_free (spec);
ret = TALER_MHD_reply_with_error (connection,
MHD_HTTP_BAD_REQUEST,
TALER_EC_GENERIC_PARAMETER_MALFORMED,
msg);
GNUNET_free (msg);
return ret;
}
}
}
if (NULL != tp.otp_id)
{
qs = TMH_db->select_otp_serial (TMH_db->cls,
mi->settings.id,
tp.otp_id,
&otp_serial);
switch (qs)
{
case GNUNET_DB_STATUS_HARD_ERROR:
case GNUNET_DB_STATUS_SOFT_ERROR:
GNUNET_break (0);
GNUNET_JSON_parse_free (spec);
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_GENERIC_DB_STORE_FAILED,
"select_otp_serial");
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
GNUNET_JSON_parse_free (spec);
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_NOT_FOUND,
TALER_EC_MERCHANT_GENERIC_OTP_DEVICE_UNKNOWN,
NULL);
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
break;
}
}
qs = TMH_db->insert_template (TMH_db->cls,
mi->settings.id,
template_id,
otp_serial,
&tp);
switch (qs)
{
case GNUNET_DB_STATUS_HARD_ERROR:
case GNUNET_DB_STATUS_SOFT_ERROR:
GNUNET_break (0);
GNUNET_JSON_parse_free (spec);
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_GENERIC_DB_STORE_FAILED,
NULL);
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
GNUNET_JSON_parse_free (spec);
return TALER_MHD_reply_static (connection,
MHD_HTTP_NO_CONTENT,
NULL,
NULL,
0);
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
break;
}
{
/* Test if a template of this id is known */
struct TALER_MERCHANTDB_TemplateDetails etp;
qs = TMH_db->lookup_template (TMH_db->cls,
mi->settings.id,
template_id,
&etp);
switch (qs)
{
case GNUNET_DB_STATUS_HARD_ERROR:
case GNUNET_DB_STATUS_SOFT_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_SUCCESS_NO_RESULTS:
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,
"logic error");
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
break;
}
/* idempotency check: is etp == tp? */
{
bool eq;
eq = templates_equal (&tp,
&etp);
TALER_MERCHANTDB_template_details_free (&etp);
TMH_db->rollback (TMH_db->cls);
GNUNET_JSON_parse_free (spec);
return eq
? TALER_MHD_reply_static (connection,
MHD_HTTP_NO_CONTENT,
NULL,
NULL,
0)
: TALER_MHD_reply_with_error (connection,
MHD_HTTP_CONFLICT,
TALER_EC_MERCHANT_PRIVATE_POST_TEMPLATES_CONFLICT_TEMPLATE_EXISTS,
template_id);
}
}
}
/* end of taler-merchant-httpd_private-post-templates.c */