diff options
Diffstat (limited to 'src/backend/taler-merchant-httpd_private-patch-orders-ID-forget.c')
-rw-r--r-- | src/backend/taler-merchant-httpd_private-patch-orders-ID-forget.c | 248 |
1 files changed, 248 insertions, 0 deletions
diff --git a/src/backend/taler-merchant-httpd_private-patch-orders-ID-forget.c b/src/backend/taler-merchant-httpd_private-patch-orders-ID-forget.c new file mode 100644 index 00000000..314e66a5 --- /dev/null +++ b/src/backend/taler-merchant-httpd_private-patch-orders-ID-forget.c @@ -0,0 +1,248 @@ +/* + This file is part of TALER + (C) 2020 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 <http://www.gnu.org/licenses/> +*/ + +/** + * @file backend/taler-merchant-httpd_private-patch-orders-ID-forget.c + * @brief implementing PATCH /orders/$ORDER_ID/forget request handling + * @author Jonathan Buchanan + */ +#include "platform.h" +#include "taler-merchant-httpd_private-patch-instances-ID.h" +#include <taler/taler_json_lib.h> + + +/** + * Parse a json path, using the syntax defined in the spec for this method. + * @param obj the root object the path applies to. + * @param path the path to find. + * + * @return the object pointed to by @e path if it exists, NULL otherwise. + */ +static int +forget_field (json_t *obj, + json_t *prev, + const char *field) +{ + /* FIXME: Handle nonexistent paths */ + /* FIXME: If prev is NULL, check that the string starts with $ */ + char *id = GNUNET_strdup (field); + char *next_id = strchr (id, + '.'); + json_t *next_obj = NULL; + int ret; + + if (NULL != next_id) + { + *next_id = '\0'; + next_id++; + } + else + { + return TALER_JSON_contract_part_forget (prev, + id); + } + + // Check for bracketed indices + char *bracket = strchr (id, + '['); + if (NULL != bracket) + { + char *end_bracket = strchr (bracket, + ']'); + if (NULL == end_bracket) + return GNUNET_SYSERR; + *end_bracket = '\0'; + + *bracket = '\0'; + bracket++; + + json_t *arr = json_object_get (obj, + id); + if (0 == strcmp (bracket, + "*")) + { + /* FIXME: Handle wildcard expansion */ + } + else + { + unsigned int index; + if (1 != sscanf (bracket, + "%u", + &index)) + return GNUNET_SYSERR; + + next_obj = json_array_get (arr, + index); + } + } + else + { + // No brackets, so just fetch the object by name + next_obj = json_object_get (obj, + next_id); + } + + ret = forget_field (next_obj, + obj, + next_id); + + GNUNET_free (id); + + return ret; +} + + +/** + * Forget fields of an order's contract terms. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] hc context with further information about the request + * @return MHD result code + */ +MHD_RESULT +TMH_private_patch_orders_ID_forget (const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc) +{ + const char *order_id = hc->infix; + enum GNUNET_DB_QueryStatus qs; + json_t *fields; + json_t *contract_terms; + uint64_t order_serial; + + qs = TMH_db->lookup_contract_terms (TMH_db->cls, + hc->instance->settings.id, + order_id, + &contract_terms, + &order_serial); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_ORDERS_CLAIM_HARD_DB_ERROR, + "Failed to run DB transaction to lookup order"); + case GNUNET_DB_STATUS_SOFT_ERROR: + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_ORDERS_CLAIM_SOFT_DB_ERROR, + "Failed to serialize DB transaction to lookup order"); + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_FORGET_ORDER_NOT_FOUND, + "unknown order id"); + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + GNUNET_assert (NULL != contract_terms); + break; + } + + { + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_json ("fields", + &fields), + GNUNET_JSON_spec_end () + }; + enum GNUNET_GenericReturnValue res; + + res = TALER_MHD_parse_json_data (connection, + hc->request_body, + spec); + if (GNUNET_OK != res) + return (GNUNET_NO == res) + ? MHD_YES + : MHD_NO; + } + if ( !(json_is_array (fields))) + { + json_decref (contract_terms); + json_decref (fields); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_PARAMETER_MALFORMED, + "paths"); + } + + { + size_t index; + json_t *value; + json_array_foreach (fields, index, value) { + int forget_status; + if ( !(json_is_string (value))) + { + json_decref (contract_terms); + json_decref (fields); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_FORGET_PATH_SYNTAX_INCORRECT, + "field isn't a string"); + } + // Check that the field starts with "$." + forget_status = forget_field (contract_terms, + NULL, + json_string_value (value)); + if (GNUNET_SYSERR == forget_status) + { + /* We tried to forget a field that isn't forgettable */ + json_decref (contract_terms); + json_decref (fields); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_CONFLICT, + TALER_EC_FORGET_PATH_NOT_FORGETTABLE, + "field isn't forgettable"); + } + } + } + + qs = TMH_db->update_contract_terms (TMH_db->cls, + hc->instance->settings.id, + order_id, + contract_terms); + + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_ORDERS_CLAIM_HARD_DB_ERROR, + "Failed to run DB transaction to update contract terms"); + case GNUNET_DB_STATUS_SOFT_ERROR: + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_ORDERS_CLAIM_SOFT_DB_ERROR, + "Failed to serialize DB transaction to update contract terms"); + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_FORGET_ORDER_NOT_FOUND, + "unknown order id"); + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + break; + } + + json_decref (contract_terms); + json_decref (fields); + + return TALER_MHD_reply_static (connection, + MHD_HTTP_OK, + NULL, + NULL, + 0); +} |