/*
This file is part of TALER
(C) 2020-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_private-post-account.c
* @brief implementing POST /private/accounts request handling
* @author Christian Grothoff
*/
#include "platform.h"
#include "taler-merchant-httpd_private-post-account.h"
#include "taler-merchant-httpd_helper.h"
#include "taler_merchant_bank_lib.h"
#include
#include
MHD_RESULT
TMH_private_post_account (const struct TMH_RequestHandler *rh,
struct MHD_Connection *connection,
struct TMH_HandlerContext *hc)
{
struct TMH_MerchantInstance *mi = hc->instance;
const char *credit_facade_url = NULL;
const json_t *credit_facade_credentials = NULL;
const char *uri;
struct GNUNET_JSON_Specification ispec[] = {
TALER_JSON_spec_payto_uri ("payto_uri",
&uri),
GNUNET_JSON_spec_mark_optional (
TALER_JSON_spec_web_url ("credit_facade_url",
&credit_facade_url),
NULL),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_object_const ("credit_facade_credentials",
&credit_facade_credentials),
NULL),
GNUNET_JSON_spec_end ()
};
struct TMH_WireMethod *wm;
{
enum GNUNET_GenericReturnValue res;
res = TALER_MHD_parse_json_data (connection,
hc->request_body,
ispec);
if (GNUNET_OK != res)
return (GNUNET_NO == res)
? MHD_YES
: MHD_NO;
}
{
char *err;
if (NULL !=
(err = TALER_payto_validate (uri)))
{
MHD_RESULT mret;
GNUNET_break_op (0);
mret = TALER_MHD_reply_with_error (connection,
MHD_HTTP_BAD_REQUEST,
TALER_EC_GENERIC_PAYTO_URI_MALFORMED,
err);
GNUNET_free (err);
return mret;
}
}
if ( (NULL == credit_facade_url) !=
(NULL == credit_facade_credentials) )
{
GNUNET_break_op (0);
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_BAD_REQUEST,
TALER_EC_GENERIC_PARAMETER_MISSING,
(NULL == credit_facade_url)
? "credit_facade_url"
: "credit_facade_credentials");
}
if ( (NULL != credit_facade_url) ||
(NULL != credit_facade_credentials) )
{
struct TALER_MERCHANT_BANK_AuthenticationData auth;
if (GNUNET_OK !=
TALER_MERCHANT_BANK_auth_parse_json (credit_facade_credentials,
credit_facade_url,
&auth))
{
GNUNET_break_op (0);
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_BAD_REQUEST,
TALER_EC_GENERIC_PARAMETER_MALFORMED,
"credit_facade_credentials");
}
TALER_MERCHANT_BANK_auth_free (&auth);
}
/* convert provided payto URI into internal data structure with salts */
wm = TMH_setup_wire_account (uri,
credit_facade_url,
credit_facade_credentials);
GNUNET_assert (NULL != wm);
{
struct TALER_MERCHANTDB_AccountDetails ad = {
.payto_uri = wm->payto_uri,
.salt = wm->wire_salt,
.h_wire = wm->h_wire,
.credit_facade_url = wm->credit_facade_url,
.credit_facade_credentials = wm->credit_facade_credentials,
.active = wm->active
};
enum GNUNET_DB_QueryStatus qs;
qs = TMH_db->insert_account (TMH_db->cls,
mi->settings.id,
&ad);
switch (qs)
{
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
break;
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
/* conflict: account exists */
{
struct TALER_MERCHANTDB_AccountDetails adx;
qs = TMH_db->select_account_by_uri (TMH_db->cls,
mi->settings.id,
ad.payto_uri,
&adx);
switch (qs)
{
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
if ( (0 == strcmp (adx.payto_uri,
ad.payto_uri) ) &&
( (adx.credit_facade_credentials ==
ad.credit_facade_credentials) ||
( (NULL != adx.credit_facade_credentials) &&
(NULL != ad.credit_facade_credentials) &&
(1 == json_equal (adx.credit_facade_credentials,
ad.credit_facade_credentials)) ) ) &&
( (adx.credit_facade_url == ad.credit_facade_url) ||
( (NULL != adx.credit_facade_url) &&
(NULL != ad.credit_facade_url) &&
(0 == strcmp (adx.credit_facade_url,
ad.credit_facade_url)) ) ) )
{
TMH_wire_method_free (wm);
GNUNET_free (adx.payto_uri);
return TALER_MHD_REPLY_JSON_PACK (connection,
MHD_HTTP_OK,
GNUNET_JSON_pack_data_auto (
"salt",
&adx.
salt),
GNUNET_JSON_pack_data_auto (
"h_wire",
&adx.
h_wire));
}
break;
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
case GNUNET_DB_STATUS_SOFT_ERROR:
case GNUNET_DB_STATUS_HARD_ERROR:
GNUNET_break (0);
TMH_wire_method_free (wm);
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_GENERIC_DB_FETCH_FAILED,
"select_account");
}
}
TMH_wire_method_free (wm);
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_CONFLICT,
TALER_EC_MERCHANT_PRIVATE_ACCOUNT_EXISTS,
uri);
case GNUNET_DB_STATUS_SOFT_ERROR:
case GNUNET_DB_STATUS_HARD_ERROR:
GNUNET_break (0);
TMH_wire_method_free (wm);
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_GENERIC_DB_STORE_FAILED,
"insert_account");
}
}
{
struct GNUNET_DB_EventHeaderP es = {
.size = htons (sizeof (es)),
.type = htons (TALER_DBEVENT_MERCHANT_ACCOUNTS_CHANGED)
};
TMH_db->event_notify (TMH_db->cls,
&es,
NULL,
0);
}
/* Finally, also update our running process */
GNUNET_CONTAINER_DLL_insert (mi->wm_head,
mi->wm_tail,
wm);
/* Note: we may not need to do this, as we notified
about the account change above. But also hardly hurts. */
TMH_reload_instances (mi->settings.id);
return TALER_MHD_REPLY_JSON_PACK (connection,
MHD_HTTP_OK,
GNUNET_JSON_pack_data_auto ("salt",
&wm->wire_salt),
GNUNET_JSON_pack_data_auto ("h_wire",
&wm->h_wire));
}
/* end of taler-merchant-httpd_private-post-account.c */