Line data Source code
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 src/backend/taler-merchant-httpd_patch-private-orders-ORDER_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_patch-private-orders-ORDER_ID-forget.h"
26 : #include <taler/taler_json_lib.h>
27 : #include "merchant-database/get_contract_terms.h"
28 : #include "merchant-database/update_contract_terms.h"
29 : #include "merchant-database/start.h"
30 :
31 :
32 : /**
33 : * How often do we retry the UPDATE database transaction?
34 : */
35 : #define MAX_RETRIES 3
36 :
37 :
38 : /**
39 : * Forget part of the contract terms.
40 : *
41 : * @param cls pointer to the result of the forget operation.
42 : * @param object_id name of the object to forget.
43 : * @param parent parent of the object at @e object_id.
44 : */
45 : static void
46 2 : forget (void *cls,
47 : const char *object_id,
48 : json_t *parent)
49 : {
50 2 : int *res = cls;
51 : int ret;
52 :
53 2 : ret = TALER_JSON_contract_part_forget (parent,
54 : object_id);
55 2 : if (GNUNET_SYSERR == ret)
56 : {
57 2 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
58 : "Matching path `%s' not forgettable!\n",
59 : object_id);
60 2 : *res = GNUNET_SYSERR;
61 : }
62 2 : if (GNUNET_NO == ret)
63 : {
64 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
65 : "Matching path `%s' already forgotten!\n",
66 : object_id);
67 : }
68 : else
69 : {
70 2 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
71 : "Forgot `%s'\n",
72 : object_id);
73 2 : if (GNUNET_NO == *res)
74 0 : *res = GNUNET_OK;
75 : }
76 2 : }
77 :
78 :
79 : /**
80 : * Forget fields of an order's contract terms.
81 : *
82 : * @param rh context of the handler
83 : * @param connection the MHD connection to handle
84 : * @param[in,out] hc context with further information about the request
85 : * @return MHD result code
86 : */
87 : enum MHD_Result
88 12 : TMH_private_patch_orders_ID_forget (const struct TMH_RequestHandler *rh,
89 : struct MHD_Connection *connection,
90 : struct TMH_HandlerContext *hc)
91 : {
92 12 : const char *order_id = hc->infix;
93 : enum GNUNET_DB_QueryStatus qs;
94 : uint64_t order_serial;
95 :
96 12 : for (unsigned int i = 0; i<MAX_RETRIES; i++)
97 : {
98 : const json_t *fields;
99 : json_t *contract_terms;
100 12 : bool changed = false;
101 :
102 12 : if (GNUNET_OK !=
103 12 : TALER_MERCHANTDB_start (TMH_db,
104 : "forget order"))
105 : {
106 12 : return TALER_MHD_reply_with_error (connection,
107 : MHD_HTTP_INTERNAL_SERVER_ERROR,
108 : TALER_EC_GENERIC_DB_START_FAILED,
109 : NULL);
110 : }
111 12 : qs = TALER_MERCHANTDB_get_contract_terms (TMH_db,
112 12 : hc->instance->settings.id,
113 : order_id,
114 : &contract_terms,
115 : &order_serial,
116 : NULL);
117 12 : switch (qs)
118 : {
119 0 : case GNUNET_DB_STATUS_HARD_ERROR:
120 0 : TALER_MERCHANTDB_rollback (TMH_db);
121 0 : return TALER_MHD_reply_with_error (connection,
122 : MHD_HTTP_INTERNAL_SERVER_ERROR,
123 : TALER_EC_GENERIC_DB_FETCH_FAILED,
124 : "contract terms");
125 0 : case GNUNET_DB_STATUS_SOFT_ERROR:
126 0 : TALER_MERCHANTDB_rollback (TMH_db);
127 0 : continue;
128 2 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
129 2 : TALER_MERCHANTDB_rollback (TMH_db);
130 2 : return TALER_MHD_reply_with_error (connection,
131 : MHD_HTTP_NOT_FOUND,
132 : TALER_EC_MERCHANT_GENERIC_ORDER_UNKNOWN,
133 : order_id);
134 10 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
135 10 : GNUNET_assert (NULL != contract_terms);
136 10 : break;
137 : }
138 :
139 : {
140 : struct GNUNET_JSON_Specification spec[] = {
141 10 : GNUNET_JSON_spec_array_const ("fields",
142 : &fields),
143 10 : GNUNET_JSON_spec_end ()
144 : };
145 : enum GNUNET_GenericReturnValue res;
146 :
147 10 : res = TALER_MHD_parse_json_data (connection,
148 10 : hc->request_body,
149 : spec);
150 10 : if (GNUNET_OK != res)
151 : {
152 0 : TALER_MERCHANTDB_rollback (TMH_db);
153 0 : json_decref (contract_terms);
154 : return (GNUNET_NO == res)
155 : ? MHD_YES
156 0 : : MHD_NO;
157 : }
158 : }
159 : {
160 : size_t index;
161 : json_t *value;
162 :
163 16 : json_array_foreach (fields, index, value) {
164 10 : int forget_status = GNUNET_NO;
165 : int expand_status;
166 :
167 10 : if (! (json_is_string (value)))
168 : {
169 0 : TALER_MERCHANTDB_rollback (TMH_db);
170 0 : json_decref (contract_terms);
171 4 : return TALER_MHD_reply_with_error (connection,
172 : MHD_HTTP_BAD_REQUEST,
173 : TALER_EC_MERCHANT_PRIVATE_PATCH_ORDERS_ID_FORGET_PATH_SYNTAX_INCORRECT,
174 : "field is not a string");
175 : }
176 10 : expand_status = TALER_JSON_expand_path (contract_terms,
177 : json_string_value (value),
178 : &forget,
179 : &forget_status);
180 10 : if (GNUNET_SYSERR == forget_status)
181 : {
182 : /* We tried to forget a field that isn't forgettable */
183 2 : TALER_MERCHANTDB_rollback (TMH_db);
184 2 : json_decref (contract_terms);
185 2 : return TALER_MHD_reply_with_error (connection,
186 : MHD_HTTP_CONFLICT,
187 : TALER_EC_MERCHANT_PRIVATE_PATCH_ORDERS_ID_FORGET_PATH_NOT_FORGETTABLE,
188 : json_string_value (value));
189 : }
190 8 : if (GNUNET_OK == forget_status)
191 0 : changed = true;
192 8 : if (GNUNET_SYSERR == expand_status)
193 : {
194 : /* One of the paths was malformed and couldn't be expanded */
195 2 : TALER_MERCHANTDB_rollback (TMH_db);
196 2 : json_decref (contract_terms);
197 2 : return TALER_MHD_reply_with_error (connection,
198 : MHD_HTTP_BAD_REQUEST,
199 : TALER_EC_MERCHANT_PRIVATE_PATCH_ORDERS_ID_FORGET_PATH_SYNTAX_INCORRECT,
200 : json_string_value (value));
201 : }
202 : }
203 : }
204 :
205 6 : if (! changed)
206 : {
207 6 : TALER_MERCHANTDB_rollback (TMH_db);
208 6 : json_decref (contract_terms);
209 6 : return TALER_MHD_reply_static (connection,
210 : MHD_HTTP_NO_CONTENT,
211 : NULL,
212 : NULL,
213 : 0);
214 : }
215 0 : qs = TALER_MERCHANTDB_update_contract_terms (TMH_db,
216 0 : hc->instance->settings.id,
217 : order_id,
218 : contract_terms);
219 0 : json_decref (contract_terms);
220 0 : switch (qs)
221 : {
222 0 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
223 0 : qs = TALER_MERCHANTDB_commit (TMH_db);
224 0 : break;
225 0 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
226 0 : TALER_MERCHANTDB_rollback (TMH_db);
227 0 : return TALER_MHD_reply_with_error (
228 : connection,
229 : MHD_HTTP_NOT_FOUND,
230 : TALER_EC_MERCHANT_GENERIC_ORDER_UNKNOWN,
231 : order_id);
232 0 : case GNUNET_DB_STATUS_SOFT_ERROR:
233 0 : TALER_MERCHANTDB_rollback (TMH_db);
234 0 : continue;
235 0 : case GNUNET_DB_STATUS_HARD_ERROR:
236 0 : GNUNET_break (0);
237 0 : TALER_MERCHANTDB_rollback (TMH_db);
238 0 : return TALER_MHD_reply_with_error (
239 : connection,
240 : MHD_HTTP_INTERNAL_SERVER_ERROR,
241 : TALER_EC_GENERIC_DB_STORE_FAILED,
242 : NULL);
243 : }
244 0 : if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
245 0 : break;
246 : }
247 0 : if (0 > qs)
248 : {
249 0 : GNUNET_break (0);
250 0 : return TALER_MHD_reply_with_error (
251 : connection,
252 : MHD_HTTP_INTERNAL_SERVER_ERROR,
253 : TALER_EC_GENERIC_DB_COMMIT_FAILED,
254 : NULL);
255 : }
256 :
257 0 : return TALER_MHD_reply_static (connection,
258 : MHD_HTTP_OK,
259 : NULL,
260 : NULL,
261 : 0);
262 : }
|