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