/* 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 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 /** * How often do we retry the UPDATE database transaction? */ #define MAX_RETRIES 3 /** * Forget part of the contract terms. * * @param cls pointer to the result of the forget operation. * @param object_id name of the object to forget. * @param parent parent of the object at @e object_id. */ static void forget (void *cls, const char *object_id, json_t *parent) { int *res = cls; int ret; ret = TALER_JSON_contract_part_forget (parent, object_id); if (GNUNET_SYSERR == ret) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Matching path `%s' not forgettable!\n", object_id); *res = GNUNET_SYSERR; } if (GNUNET_NO == ret) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Matching path `%s' already forgotten!\n", object_id); } else { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Forgot `%s'\n", object_id); if (GNUNET_NO == *res) *res = GNUNET_OK; } } /** * 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; uint64_t order_serial; for (unsigned int i = 0; istart (TMH_db->cls, "forget order")) { return TALER_MHD_reply_with_error (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_GENERIC_DB_START_FAILED, NULL); } 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: TMH_db->rollback (TMH_db->cls); return TALER_MHD_reply_with_error (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_GENERIC_DB_FETCH_FAILED, "contract terms"); case GNUNET_DB_STATUS_SOFT_ERROR: TMH_db->rollback (TMH_db->cls); continue; case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: TMH_db->rollback (TMH_db->cls); return TALER_MHD_reply_with_error (connection, MHD_HTTP_NOT_FOUND, TALER_EC_MERCHANT_GENERIC_ORDER_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) { TMH_db->rollback (TMH_db->cls); json_decref (contract_terms); return (GNUNET_NO == res) ? MHD_YES : MHD_NO; } } if (! (json_is_array (fields))) { TMH_db->rollback (TMH_db->cls); json_decref (contract_terms); json_decref (fields); return TALER_MHD_reply_with_error (connection, MHD_HTTP_BAD_REQUEST, TALER_EC_GENERIC_PARAMETER_MALFORMED, "fields"); } { size_t index; json_t *value; json_array_foreach (fields, index, value) { int forget_status = GNUNET_NO; int expand_status; if (! (json_is_string (value))) { TMH_db->rollback (TMH_db->cls); json_decref (contract_terms); json_decref (fields); return TALER_MHD_reply_with_error (connection, MHD_HTTP_BAD_REQUEST, TALER_EC_MERCHANT_PRIVATE_PATCH_ORDERS_ID_FORGET_PATH_SYNTAX_INCORRECT, "field is not a string"); } expand_status = TALER_JSON_expand_path (contract_terms, json_string_value (value), &forget, &forget_status); if (GNUNET_SYSERR == forget_status) { /* We tried to forget a field that isn't forgettable */ TMH_db->rollback (TMH_db->cls); json_decref (contract_terms); json_decref (fields); return TALER_MHD_reply_with_error (connection, MHD_HTTP_CONFLICT, TALER_EC_MERCHANT_PRIVATE_PATCH_ORDERS_ID_FORGET_PATH_NOT_FORGETTABLE, json_string_value (value)); } if (GNUNET_OK == forget_status) changed = true; if (GNUNET_SYSERR == expand_status) { /* One of the paths was malformed and couldn't be expanded */ TMH_db->rollback (TMH_db->cls); json_decref (contract_terms); json_decref (fields); return TALER_MHD_reply_with_error (connection, MHD_HTTP_BAD_REQUEST, TALER_EC_MERCHANT_PRIVATE_PATCH_ORDERS_ID_FORGET_PATH_SYNTAX_INCORRECT, json_string_value (value)); } } } if (! changed) { TMH_db->rollback (TMH_db->cls); json_decref (contract_terms); json_decref (fields); return TALER_MHD_reply_static (connection, MHD_HTTP_NO_CONTENT, NULL, NULL, 0); } qs = TMH_db->update_contract_terms (TMH_db->cls, hc->instance->settings.id, order_id, contract_terms); json_decref (contract_terms); json_decref (fields); if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) { TMH_db->rollback (TMH_db->cls); if (GNUNET_DB_STATUS_SOFT_ERROR != qs) break; } else { qs = TMH_db->commit (TMH_db->cls); if (GNUNET_DB_STATUS_SOFT_ERROR != qs) break; } } if (0 > qs) { return TALER_MHD_reply_with_error (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_GENERIC_DB_COMMIT_FAILED, NULL); } return TALER_MHD_reply_static (connection, MHD_HTTP_OK, NULL, NULL, 0); }