taler-merchant-httpd_private-patch-orders-ID-forget.c (8282B)
1 /* 2 This file is part of TALER 3 (C) 2020 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify 6 it under the terms of the GNU Affero General Public License as 7 published by the Free Software Foundation; either version 3, 8 or (at your option) any later version. 9 10 TALER is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public 16 License along with TALER; see the file COPYING. If not, 17 see <http://www.gnu.org/licenses/> 18 */ 19 /** 20 * @file taler-merchant-httpd_private-patch-orders-ID-forget.c 21 * @brief implementing PATCH /orders/$ORDER_ID/forget request handling 22 * @author Jonathan Buchanan 23 */ 24 #include "platform.h" 25 #include "taler-merchant-httpd_private-patch-orders-ID-forget.h" 26 #include <taler/taler_json_lib.h> 27 28 29 /** 30 * How often do we retry the UPDATE database transaction? 31 */ 32 #define MAX_RETRIES 3 33 34 35 /** 36 * Forget part of the contract terms. 37 * 38 * @param cls pointer to the result of the forget operation. 39 * @param object_id name of the object to forget. 40 * @param parent parent of the object at @e object_id. 41 */ 42 static void 43 forget (void *cls, 44 const char *object_id, 45 json_t *parent) 46 { 47 int *res = cls; 48 int ret; 49 50 ret = TALER_JSON_contract_part_forget (parent, 51 object_id); 52 if (GNUNET_SYSERR == ret) 53 { 54 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 55 "Matching path `%s' not forgettable!\n", 56 object_id); 57 *res = GNUNET_SYSERR; 58 } 59 if (GNUNET_NO == ret) 60 { 61 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 62 "Matching path `%s' already forgotten!\n", 63 object_id); 64 } 65 else 66 { 67 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 68 "Forgot `%s'\n", 69 object_id); 70 if (GNUNET_NO == *res) 71 *res = GNUNET_OK; 72 } 73 } 74 75 76 /** 77 * Forget fields of an order's contract terms. 78 * 79 * @param rh context of the handler 80 * @param connection the MHD connection to handle 81 * @param[in,out] hc context with further information about the request 82 * @return MHD result code 83 */ 84 MHD_RESULT 85 TMH_private_patch_orders_ID_forget (const struct TMH_RequestHandler *rh, 86 struct MHD_Connection *connection, 87 struct TMH_HandlerContext *hc) 88 { 89 const char *order_id = hc->infix; 90 enum GNUNET_DB_QueryStatus qs; 91 uint64_t order_serial; 92 93 for (unsigned int i = 0; i<MAX_RETRIES; i++) 94 { 95 const json_t *fields; 96 json_t *contract_terms; 97 bool changed = false; 98 99 if (GNUNET_OK != 100 TMH_db->start (TMH_db->cls, 101 "forget order")) 102 { 103 return TALER_MHD_reply_with_error (connection, 104 MHD_HTTP_INTERNAL_SERVER_ERROR, 105 TALER_EC_GENERIC_DB_START_FAILED, 106 NULL); 107 } 108 qs = TMH_db->lookup_contract_terms (TMH_db->cls, 109 hc->instance->settings.id, 110 order_id, 111 &contract_terms, 112 &order_serial, 113 NULL); 114 switch (qs) 115 { 116 case GNUNET_DB_STATUS_HARD_ERROR: 117 TMH_db->rollback (TMH_db->cls); 118 return TALER_MHD_reply_with_error (connection, 119 MHD_HTTP_INTERNAL_SERVER_ERROR, 120 TALER_EC_GENERIC_DB_FETCH_FAILED, 121 "contract terms"); 122 case GNUNET_DB_STATUS_SOFT_ERROR: 123 TMH_db->rollback (TMH_db->cls); 124 continue; 125 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 126 TMH_db->rollback (TMH_db->cls); 127 return TALER_MHD_reply_with_error (connection, 128 MHD_HTTP_NOT_FOUND, 129 TALER_EC_MERCHANT_GENERIC_ORDER_UNKNOWN, 130 order_id); 131 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 132 GNUNET_assert (NULL != contract_terms); 133 break; 134 } 135 136 { 137 struct GNUNET_JSON_Specification spec[] = { 138 GNUNET_JSON_spec_array_const ("fields", 139 &fields), 140 GNUNET_JSON_spec_end () 141 }; 142 enum GNUNET_GenericReturnValue res; 143 144 res = TALER_MHD_parse_json_data (connection, 145 hc->request_body, 146 spec); 147 if (GNUNET_OK != res) 148 { 149 TMH_db->rollback (TMH_db->cls); 150 json_decref (contract_terms); 151 return (GNUNET_NO == res) 152 ? MHD_YES 153 : MHD_NO; 154 } 155 } 156 { 157 size_t index; 158 json_t *value; 159 160 json_array_foreach (fields, index, value) { 161 int forget_status = GNUNET_NO; 162 int expand_status; 163 164 if (! (json_is_string (value))) 165 { 166 TMH_db->rollback (TMH_db->cls); 167 json_decref (contract_terms); 168 return TALER_MHD_reply_with_error (connection, 169 MHD_HTTP_BAD_REQUEST, 170 TALER_EC_MERCHANT_PRIVATE_PATCH_ORDERS_ID_FORGET_PATH_SYNTAX_INCORRECT, 171 "field is not a string"); 172 } 173 expand_status = TALER_JSON_expand_path (contract_terms, 174 json_string_value (value), 175 &forget, 176 &forget_status); 177 if (GNUNET_SYSERR == forget_status) 178 { 179 /* We tried to forget a field that isn't forgettable */ 180 TMH_db->rollback (TMH_db->cls); 181 json_decref (contract_terms); 182 return TALER_MHD_reply_with_error (connection, 183 MHD_HTTP_CONFLICT, 184 TALER_EC_MERCHANT_PRIVATE_PATCH_ORDERS_ID_FORGET_PATH_NOT_FORGETTABLE, 185 json_string_value (value)); 186 } 187 if (GNUNET_OK == forget_status) 188 changed = true; 189 if (GNUNET_SYSERR == expand_status) 190 { 191 /* One of the paths was malformed and couldn't be expanded */ 192 TMH_db->rollback (TMH_db->cls); 193 json_decref (contract_terms); 194 return TALER_MHD_reply_with_error (connection, 195 MHD_HTTP_BAD_REQUEST, 196 TALER_EC_MERCHANT_PRIVATE_PATCH_ORDERS_ID_FORGET_PATH_SYNTAX_INCORRECT, 197 json_string_value (value)); 198 } 199 } 200 } 201 202 if (! changed) 203 { 204 TMH_db->rollback (TMH_db->cls); 205 json_decref (contract_terms); 206 return TALER_MHD_reply_static (connection, 207 MHD_HTTP_NO_CONTENT, 208 NULL, 209 NULL, 210 0); 211 } 212 qs = TMH_db->update_contract_terms (TMH_db->cls, 213 hc->instance->settings.id, 214 order_id, 215 contract_terms); 216 json_decref (contract_terms); 217 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) 218 { 219 TMH_db->rollback (TMH_db->cls); 220 if (GNUNET_DB_STATUS_SOFT_ERROR != qs) 221 break; 222 } 223 else 224 { 225 qs = TMH_db->commit (TMH_db->cls); 226 if (GNUNET_DB_STATUS_SOFT_ERROR != qs) 227 break; 228 } 229 } 230 if (0 > qs) 231 { 232 return TALER_MHD_reply_with_error (connection, 233 MHD_HTTP_INTERNAL_SERVER_ERROR, 234 TALER_EC_GENERIC_DB_COMMIT_FAILED, 235 NULL); 236 } 237 238 return TALER_MHD_reply_static (connection, 239 MHD_HTTP_OK, 240 NULL, 241 NULL, 242 0); 243 }