/* 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 */ /** * @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 /** * 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); }