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 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 2 : forget (void *cls,
44 : const char *object_id,
45 : json_t *parent)
46 : {
47 2 : int *res = cls;
48 : int ret;
49 :
50 2 : ret = TALER_JSON_contract_part_forget (parent,
51 : object_id);
52 2 : if (GNUNET_SYSERR == ret)
53 : {
54 2 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
55 : "Matching path `%s' not forgettable!\n",
56 : object_id);
57 2 : *res = GNUNET_SYSERR;
58 : }
59 2 : if (GNUNET_NO == ret)
60 : {
61 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
62 : "Matching path `%s' already forgotten!\n",
63 : object_id);
64 : }
65 : else
66 : {
67 2 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
68 : "Forgot `%s'\n",
69 : object_id);
70 2 : if (GNUNET_NO == *res)
71 0 : *res = GNUNET_OK;
72 : }
73 2 : }
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 12 : TMH_private_patch_orders_ID_forget (const struct TMH_RequestHandler *rh,
86 : struct MHD_Connection *connection,
87 : struct TMH_HandlerContext *hc)
88 : {
89 12 : const char *order_id = hc->infix;
90 : enum GNUNET_DB_QueryStatus qs;
91 : uint64_t order_serial;
92 :
93 12 : for (unsigned int i = 0; i<MAX_RETRIES; i++)
94 : {
95 : const json_t *fields;
96 : json_t *contract_terms;
97 12 : bool changed = false;
98 :
99 12 : if (GNUNET_OK !=
100 12 : TMH_db->start (TMH_db->cls,
101 : "forget order"))
102 : {
103 12 : return TALER_MHD_reply_with_error (connection,
104 : MHD_HTTP_INTERNAL_SERVER_ERROR,
105 : TALER_EC_GENERIC_DB_START_FAILED,
106 : NULL);
107 : }
108 12 : qs = TMH_db->lookup_contract_terms (TMH_db->cls,
109 12 : hc->instance->settings.id,
110 : order_id,
111 : &contract_terms,
112 : &order_serial,
113 : NULL);
114 12 : switch (qs)
115 : {
116 0 : case GNUNET_DB_STATUS_HARD_ERROR:
117 0 : TMH_db->rollback (TMH_db->cls);
118 0 : return TALER_MHD_reply_with_error (connection,
119 : MHD_HTTP_INTERNAL_SERVER_ERROR,
120 : TALER_EC_GENERIC_DB_FETCH_FAILED,
121 : "contract terms");
122 0 : case GNUNET_DB_STATUS_SOFT_ERROR:
123 0 : TMH_db->rollback (TMH_db->cls);
124 0 : continue;
125 2 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
126 2 : TMH_db->rollback (TMH_db->cls);
127 2 : return TALER_MHD_reply_with_error (connection,
128 : MHD_HTTP_NOT_FOUND,
129 : TALER_EC_MERCHANT_GENERIC_ORDER_UNKNOWN,
130 : order_id);
131 10 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
132 10 : GNUNET_assert (NULL != contract_terms);
133 10 : break;
134 : }
135 :
136 : {
137 : struct GNUNET_JSON_Specification spec[] = {
138 10 : GNUNET_JSON_spec_array_const ("fields",
139 : &fields),
140 10 : GNUNET_JSON_spec_end ()
141 : };
142 : enum GNUNET_GenericReturnValue res;
143 :
144 10 : res = TALER_MHD_parse_json_data (connection,
145 10 : hc->request_body,
146 : spec);
147 10 : if (GNUNET_OK != res)
148 : {
149 0 : TMH_db->rollback (TMH_db->cls);
150 0 : json_decref (contract_terms);
151 : return (GNUNET_NO == res)
152 : ? MHD_YES
153 0 : : MHD_NO;
154 : }
155 : }
156 : {
157 : size_t index;
158 : json_t *value;
159 :
160 16 : json_array_foreach (fields, index, value) {
161 10 : int forget_status = GNUNET_NO;
162 : int expand_status;
163 :
164 10 : if (! (json_is_string (value)))
165 : {
166 0 : TMH_db->rollback (TMH_db->cls);
167 0 : json_decref (contract_terms);
168 4 : 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 10 : expand_status = TALER_JSON_expand_path (contract_terms,
174 : json_string_value (value),
175 : &forget,
176 : &forget_status);
177 10 : if (GNUNET_SYSERR == forget_status)
178 : {
179 : /* We tried to forget a field that isn't forgettable */
180 2 : TMH_db->rollback (TMH_db->cls);
181 2 : json_decref (contract_terms);
182 2 : 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 8 : if (GNUNET_OK == forget_status)
188 0 : changed = true;
189 8 : if (GNUNET_SYSERR == expand_status)
190 : {
191 : /* One of the paths was malformed and couldn't be expanded */
192 2 : TMH_db->rollback (TMH_db->cls);
193 2 : json_decref (contract_terms);
194 2 : 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 6 : if (! changed)
203 : {
204 6 : TMH_db->rollback (TMH_db->cls);
205 6 : json_decref (contract_terms);
206 6 : return TALER_MHD_reply_static (connection,
207 : MHD_HTTP_NO_CONTENT,
208 : NULL,
209 : NULL,
210 : 0);
211 : }
212 0 : qs = TMH_db->update_contract_terms (TMH_db->cls,
213 0 : hc->instance->settings.id,
214 : order_id,
215 : contract_terms);
216 0 : json_decref (contract_terms);
217 0 : if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
218 : {
219 0 : TMH_db->rollback (TMH_db->cls);
220 0 : if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
221 0 : break;
222 : }
223 : else
224 : {
225 0 : qs = TMH_db->commit (TMH_db->cls);
226 0 : if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
227 0 : break;
228 : }
229 : }
230 0 : if (0 > qs)
231 : {
232 0 : 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 0 : return TALER_MHD_reply_static (connection,
239 : MHD_HTTP_OK,
240 : NULL,
241 : NULL,
242 : 0);
243 : }
|