/*
This file is part of TALER
Copyright (C) 2020 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 2.1, 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along with
TALER; see the file COPYING.LGPL. If not, see
*/
/**
* @file lib/merchant_api_common.c
* @brief Implementation of common logic for libtalermerchant
* @author Christian Grothoff
*/
#include "platform.h"
#include
#include
#include /* just for HTTP status codes */
#include
#include
#include "taler_merchant_service.h"
#include
/**
* Take a @a response from the merchant API that (presumably) contains
* error details and setup the corresponding @a hr structure. Internally
* used to convert merchant's responses in to @a hr.
*
* @param response, if NULL we will report #TALER_EC_INVALID_RESPONSE in `ec
* @param http_status http status to use
* @param[out] hr response object to initialize, fields will
* only be valid as long as @a response is valid as well
*/
void
TALER_MERCHANT_parse_error_details_ (const json_t *response,
unsigned int http_status,
struct TALER_MERCHANT_HttpResponse *hr)
{
const json_t *jc;
memset (hr, 0, sizeof (*hr));
hr->reply = response;
hr->http_status = http_status;
if (NULL == response)
{
hr->ec = TALER_EC_INVALID_RESPONSE;
return;
}
hr->ec = TALER_JSON_get_error_code (response);
hr->hint = TALER_JSON_get_error_hint (response);
/* handle 'exchange_http_status' */
jc = json_object_get (response,
"exchange_http_status");
/* The caller already knows that the JSON represents an error,
so we are dealing with a missing error code here. */
if (NULL == jc)
return; /* no need to bother with exchange_code/hint if we had no status */
if (! json_is_integer (jc))
{
GNUNET_break_op (0);
return;
}
hr->exchange_http_status = (unsigned int) json_integer_value (jc);
/* handle 'exchange_reply' */
jc = json_object_get (response,
"exchange_reply");
if (! json_is_object (jc))
{
GNUNET_break_op (0);
}
else
{
hr->exchange_reply = jc;
}
/* handle 'exchange_code' */
jc = json_object_get (response,
"exchange_code");
/* The caller already knows that the JSON represents an error,
so we are dealing with a missing error code here. */
if (NULL == jc)
return; /* no need to bother with exchange-hint if we had no code */
if (! json_is_integer (jc))
{
GNUNET_break_op (0);
hr->exchange_code = TALER_EC_INVALID;
}
else
{
hr->exchange_code = (enum TALER_ErrorCode) json_integer_value (jc);
}
/* handle 'exchange-hint' */
jc = json_object_get (response,
"exchange-hint");
/* The caller already knows that the JSON represents an error,
so we are dealing with a missing error code here. */
if (NULL == jc)
return;
if (! json_is_string (jc))
{
GNUNET_break_op (0);
}
else
{
hr->exchange_hint = json_string_value (jc);
}
}
/**
* Construct a new base URL using the existing @a base_url
* and the given @a instance_id. The result WILL end with
* '/'.
*
* @param base_url a merchant base URL without "/instances/" in it,
* must not be the empty string; MAY end with '/'.
* @param instance_id ID of an instance
* @return "${base_url}/instances/${instance_id}/"
*/
char *
TALER_MERCHANT_baseurl_add_instance (const char *base_url,
const char *instance_id)
{
char *ret;
bool end_sl;
if ('\0' == *base_url)
{
GNUNET_break (0);
return NULL;
}
end_sl = '/' == base_url[strlen (base_url) - 1];
GNUNET_asprintf (&ret,
(end_sl)
? "%sinstances/%s/"
: "%s/instances/%s/",
base_url,
instance_id);
return ret;
}
/**
* Parses the URI scheme and action of a URI. Ensures that the scheme is either
* 'taler' or 'taler+http'.
*
* @param uri the uri to parse.
* @param[out] action the action the URI is indicating.
* @param[out] rest the substring of the URI following the action.
* @return #GNUNET_SYSERR if the URI is malformed, #GNUNET_OK otherwise.
*/
static int
parse_taler_uri_scheme_action (const char *uri,
char **action,
char **rest)
{
char *scheme = GNUNET_strdup (uri);
/* Check that the uri starts with "taler://pay" or "taler+http://pay" and
then remove it */
char *path = strchr (scheme, ':');
if ( (NULL == path) ||
(strlen (path) < 3) )
{
GNUNET_free (scheme);
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
path += 3; /* make path point to 'pay' */
{
char path_begin = *path;
*path = '\0';
if ((0 != strcmp ("taler://",
scheme)) &&
(0 != strcmp ("taler+http://",
scheme)))
{
GNUNET_free (scheme);
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
*path = path_begin;
}
{
char *pqf = strchr (path, '/');
if (NULL == pqf)
{
GNUNET_free (scheme);
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
*pqf = '\0';
++pqf;
*rest = GNUNET_strdup (pqf);
}
*action = GNUNET_strdup (path);
GNUNET_free (scheme);
return GNUNET_OK;
}
/**
* Extracts information from a taler://pay URI.
*
* @param pay_uri the URI to parse.
* @param[out] parse_data data extracted from the URI. Must be free'd.
* @return #GNUNET_SYSERR if @e pay_uri is malformed, #GNUNET_OK otherwise.
*/
int
TALER_MERCHANT_parse_pay_uri (const char *pay_uri,
struct TALER_MERCHANT_PayUriData *parse_data)
{
char *path = NULL;
{
char *action = NULL;
if ((GNUNET_OK !=
parse_taler_uri_scheme_action (pay_uri,
&action,
&path)) ||
(0 != strcmp ("pay",
action)))
{
GNUNET_break_op (0);
GNUNET_free (action);
GNUNET_free (path);
return GNUNET_SYSERR;
}
GNUNET_free (action);
}
{
char *mpp;
char *order_id;
char *session_id = strrchr (path,
'/');
struct TALER_ClaimTokenP *claim_token = NULL;
char *ssid;
if (NULL == session_id)
{
GNUNET_break_op (0);
GNUNET_free (path);
return GNUNET_SYSERR;
}
*session_id = '\0';
++session_id;
order_id = strrchr (path,
'/');
if (NULL == order_id)
{
GNUNET_break_op (0);
GNUNET_free (path);
return GNUNET_SYSERR;
}
*order_id = '\0';
++order_id;
{
char *ct_str = strchr (session_id,
'?');
char *ct_data;
if (NULL != ct_str)
{
*ct_str = '\0';
++ct_str;
}
ssid = strchr (session_id,
'#');
if (NULL != ssid)
{
*ssid = '\0';
++ssid;
}
if (NULL != ct_str)
{
ct_data = strchr (ct_str,
'=');
if (NULL == ct_data)
{
GNUNET_break_op (0);
GNUNET_free (path);
return GNUNET_SYSERR;
}
*ct_data = '\0';
++ct_data;
claim_token = GNUNET_new (struct TALER_ClaimTokenP);
if ((0 != strcmp ("c",
ct_str)) ||
(GNUNET_OK !=
GNUNET_STRINGS_string_to_data (ct_data,
strlen (ct_data),
claim_token,
sizeof (*claim_token))))
{
GNUNET_break_op (0);
GNUNET_free (path);
GNUNET_free (claim_token);
return GNUNET_SYSERR;
}
}
}
mpp = strchr (path,
'/');
if (NULL != mpp)
{
*mpp = '\0';
++mpp;
}
parse_data->merchant_host = GNUNET_strdup (path);
parse_data->merchant_prefix_path =
(NULL == mpp) ? NULL : GNUNET_strdup (mpp);
parse_data->order_id = GNUNET_strdup (order_id);
parse_data->session_id =
(0 < strlen (session_id)) ? GNUNET_strdup (session_id) : NULL;
parse_data->claim_token = claim_token;
parse_data->ssid =
(NULL == ssid) ? NULL : GNUNET_strdup (ssid);
}
GNUNET_free (path);
return GNUNET_OK;
}
/**
* Frees data contained in the result of parsing a taler://pay URI.
*
* @param parse_data the data to free.
*/
void
TALER_MERCHANT_parse_pay_uri_free (
struct TALER_MERCHANT_PayUriData *parse_data)
{
GNUNET_free (parse_data->merchant_host);
GNUNET_free (parse_data->merchant_prefix_path);
GNUNET_free (parse_data->order_id);
GNUNET_free (parse_data->session_id);
GNUNET_free (parse_data->claim_token);
GNUNET_free (parse_data->ssid);
}
/**
* Extracts information from a taler://refund URI.
*
* @param refund_uri the URI to parse.
* @param[out] parse_data data extracted from the URI. Must be free'd.
* @return #GNUNET_SYSERR if @e refund_uri is malformed, #GNUNET_OK otherwise.
*/
int
TALER_MERCHANT_parse_refund_uri (
const char *refund_uri,
struct TALER_MERCHANT_RefundUriData *parse_data)
{
char *path = NULL;
{
char *action = NULL;
if ((GNUNET_OK !=
parse_taler_uri_scheme_action (refund_uri,
&action,
&path)) ||
(0 != strcmp ("refund",
action)))
{
GNUNET_break_op (0);
GNUNET_free (action);
GNUNET_free (path);
return GNUNET_SYSERR;
}
GNUNET_free (action);
}
{
char *mpp;
char *order_id;
char *last_seg = strrchr (path,
'/');
char *ssid;
if (NULL == last_seg)
{
GNUNET_break_op (0);
GNUNET_free (path);
return GNUNET_SYSERR;
}
*last_seg = '\0';
++last_seg;
order_id = strrchr (path,
'/');
if (NULL == order_id)
{
GNUNET_break_op (0);
GNUNET_free (path);
return GNUNET_SYSERR;
}
*order_id = '\0';
++order_id;
ssid = strchr (last_seg,
'#');
if (NULL != ssid)
{
*ssid = '\0';
++ssid;
}
if (0 != strlen (last_seg))
{
GNUNET_break_op (0);
GNUNET_free (path);
return GNUNET_SYSERR;
}
mpp = strchr (path,
'/');
if (NULL != mpp)
{
*mpp = '\0';
++mpp;
}
parse_data->merchant_host = GNUNET_strdup (path);
parse_data->merchant_prefix_path =
(NULL == mpp) ? NULL : GNUNET_strdup (mpp);
parse_data->order_id = GNUNET_strdup (order_id);
parse_data->ssid =
(NULL == ssid) ? NULL : GNUNET_strdup (ssid);
}
GNUNET_free (path);
return GNUNET_OK;
}
/**
* Frees data contained in the result of parsing a taler://refund URI.
*
* @param parse_data the data to free.
*/
void
TALER_MERCHANT_parse_refund_uri_free (
struct TALER_MERCHANT_RefundUriData *parse_data)
{
GNUNET_free (parse_data->merchant_host);
GNUNET_free (parse_data->merchant_prefix_path);
GNUNET_free (parse_data->order_id);
GNUNET_free (parse_data->ssid);
}