Line data Source code
1 : /*
2 : This file is part of TALER
3 : (C) 2020-2023 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-instances-ID.c
22 : * @brief implementing PATCH /instances/$ID request handling
23 : * @author Christian Grothoff
24 : */
25 : #include "platform.h"
26 : #include "taler-merchant-httpd_private-patch-instances-ID.h"
27 : #include "taler-merchant-httpd_helper.h"
28 : #include <taler/taler_json_lib.h>
29 : #include <taler/taler_dbevents.h>
30 :
31 :
32 : /**
33 : * How often do we retry the simple INSERT database transaction?
34 : */
35 : #define MAX_RETRIES 3
36 :
37 :
38 : /**
39 : * Free memory used by @a wm
40 : *
41 : * @param wm wire method to free
42 : */
43 : static void
44 0 : free_wm (struct TMH_WireMethod *wm)
45 : {
46 0 : GNUNET_free (wm->payto_uri.full_payto);
47 0 : GNUNET_free (wm->wire_method);
48 0 : GNUNET_free (wm);
49 0 : }
50 :
51 :
52 : /**
53 : * PATCH configuration of an existing instance, given its configuration.
54 : *
55 : * @param mi instance to patch
56 : * @param connection the MHD connection to handle
57 : * @param[in,out] hc context with further information about the request
58 : * @return MHD result code
59 : */
60 : static MHD_RESULT
61 4 : patch_instances_ID (struct TMH_MerchantInstance *mi,
62 : struct MHD_Connection *connection,
63 : struct TMH_HandlerContext *hc)
64 : {
65 : struct TALER_MERCHANTDB_InstanceSettings is;
66 : const char *name;
67 4 : struct TMH_WireMethod *wm_head = NULL;
68 4 : struct TMH_WireMethod *wm_tail = NULL;
69 : struct GNUNET_JSON_Specification spec[] = {
70 4 : GNUNET_JSON_spec_string ("name",
71 : &name),
72 4 : GNUNET_JSON_spec_mark_optional (
73 : GNUNET_JSON_spec_string ("website",
74 : (const char **) &is.website),
75 : NULL),
76 4 : GNUNET_JSON_spec_mark_optional (
77 : GNUNET_JSON_spec_string ("email",
78 : (const char **) &is.email),
79 : NULL),
80 4 : GNUNET_JSON_spec_mark_optional (
81 : GNUNET_JSON_spec_string ("phone_number",
82 : (const char **) &is.phone),
83 : NULL),
84 4 : GNUNET_JSON_spec_mark_optional (
85 : GNUNET_JSON_spec_string ("logo",
86 : (const char **) &is.logo),
87 : NULL),
88 4 : GNUNET_JSON_spec_json ("address",
89 : &is.address),
90 4 : GNUNET_JSON_spec_json ("jurisdiction",
91 : &is.jurisdiction),
92 4 : GNUNET_JSON_spec_bool ("use_stefan",
93 : &is.use_stefan),
94 4 : GNUNET_JSON_spec_relative_time ("default_wire_transfer_delay",
95 : &is.default_wire_transfer_delay),
96 4 : GNUNET_JSON_spec_relative_time ("default_pay_delay",
97 : &is.default_pay_delay),
98 4 : GNUNET_JSON_spec_end ()
99 : };
100 : enum GNUNET_DB_QueryStatus qs;
101 :
102 4 : GNUNET_assert (NULL != mi);
103 4 : memset (&is,
104 : 0,
105 : sizeof (is));
106 : {
107 : enum GNUNET_GenericReturnValue res;
108 :
109 4 : res = TALER_MHD_parse_json_data (connection,
110 4 : hc->request_body,
111 : spec);
112 4 : if (GNUNET_OK != res)
113 : return (GNUNET_NO == res)
114 : ? MHD_YES
115 0 : : MHD_NO;
116 : }
117 4 : if (! TMH_location_object_valid (is.address))
118 : {
119 0 : GNUNET_break_op (0);
120 0 : GNUNET_JSON_parse_free (spec);
121 0 : return TALER_MHD_reply_with_error (connection,
122 : MHD_HTTP_BAD_REQUEST,
123 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
124 : "address");
125 : }
126 4 : if ( (NULL != is.logo) &&
127 0 : (! TMH_image_data_url_valid (is.logo)) )
128 : {
129 0 : GNUNET_break_op (0);
130 0 : GNUNET_JSON_parse_free (spec);
131 0 : return TALER_MHD_reply_with_error (connection,
132 : MHD_HTTP_BAD_REQUEST,
133 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
134 : "logo");
135 : }
136 :
137 4 : if (! TMH_location_object_valid (is.jurisdiction))
138 : {
139 0 : GNUNET_break_op (0);
140 0 : GNUNET_JSON_parse_free (spec);
141 0 : return TALER_MHD_reply_with_error (connection,
142 : MHD_HTTP_BAD_REQUEST,
143 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
144 : "jurisdiction");
145 : }
146 :
147 4 : for (unsigned int retry = 0; retry<MAX_RETRIES; retry++)
148 : {
149 : /* Cleanup after earlier loops */
150 : {
151 : struct TMH_WireMethod *wm;
152 :
153 4 : while (NULL != (wm = wm_head))
154 : {
155 0 : GNUNET_CONTAINER_DLL_remove (wm_head,
156 : wm_tail,
157 : wm);
158 0 : free_wm (wm);
159 : }
160 : }
161 4 : if (GNUNET_OK !=
162 4 : TMH_db->start (TMH_db->cls,
163 : "PATCH /instances"))
164 : {
165 0 : GNUNET_JSON_parse_free (spec);
166 0 : return TALER_MHD_reply_with_error (connection,
167 : MHD_HTTP_INTERNAL_SERVER_ERROR,
168 : TALER_EC_GENERIC_DB_START_FAILED,
169 : NULL);
170 : }
171 : /* Check for equality of settings */
172 4 : if (! ( (0 == strcmp (mi->settings.name,
173 2 : name)) &&
174 2 : ((mi->settings.email == is.email) ||
175 0 : (NULL != is.email && NULL != mi->settings.email &&
176 0 : 0 == strcmp (mi->settings.email,
177 0 : is.email))) &&
178 2 : ((mi->settings.website == is.website) ||
179 0 : (NULL != is.website && NULL != mi->settings.website &&
180 0 : 0 == strcmp (mi->settings.website,
181 0 : is.website))) &&
182 2 : ((mi->settings.logo == is.logo) ||
183 0 : (NULL != is.logo && NULL != mi->settings.logo &&
184 0 : 0 == strcmp (mi->settings.logo,
185 2 : is.logo))) &&
186 2 : (1 == json_equal (mi->settings.address,
187 2 : is.address)) &&
188 0 : (1 == json_equal (mi->settings.jurisdiction,
189 0 : is.jurisdiction)) &&
190 0 : (mi->settings.use_stefan == is.use_stefan) &&
191 0 : (GNUNET_TIME_relative_cmp (mi->settings.default_wire_transfer_delay,
192 : ==,
193 : is.default_wire_transfer_delay)) &&
194 0 : (GNUNET_TIME_relative_cmp (mi->settings.default_pay_delay,
195 : ==,
196 : is.default_pay_delay)) ) )
197 : {
198 4 : is.id = mi->settings.id;
199 4 : is.name = GNUNET_strdup (name);
200 4 : qs = TMH_db->update_instance (TMH_db->cls,
201 : &is);
202 4 : GNUNET_free (is.name);
203 4 : if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
204 : {
205 0 : TMH_db->rollback (TMH_db->cls);
206 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
207 0 : goto retry;
208 : else
209 0 : goto giveup;
210 : }
211 : }
212 4 : qs = TMH_db->commit (TMH_db->cls);
213 4 : retry:
214 4 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
215 0 : continue;
216 4 : break;
217 : } /* for(... MAX_RETRIES) */
218 0 : giveup:
219 : /* Update our 'settings' */
220 4 : GNUNET_free (mi->settings.name);
221 4 : GNUNET_free (mi->settings.email);
222 4 : GNUNET_free (mi->settings.phone);
223 4 : GNUNET_free (mi->settings.website);
224 4 : GNUNET_free (mi->settings.logo);
225 4 : json_decref (mi->settings.address);
226 4 : json_decref (mi->settings.jurisdiction);
227 4 : is.id = mi->settings.id;
228 4 : mi->settings = is;
229 4 : mi->settings.address = json_incref (mi->settings.address);
230 4 : mi->settings.jurisdiction = json_incref (mi->settings.jurisdiction);
231 4 : mi->settings.name = GNUNET_strdup (name);
232 4 : if (NULL != is.email)
233 0 : mi->settings.email = GNUNET_strdup (is.email);
234 4 : if (NULL != is.phone)
235 0 : mi->settings.phone = GNUNET_strdup (is.phone);
236 4 : if (NULL != is.website)
237 0 : mi->settings.website = GNUNET_strdup (is.website);
238 4 : if (NULL != is.logo)
239 0 : mi->settings.logo = GNUNET_strdup (is.logo);
240 :
241 4 : GNUNET_JSON_parse_free (spec);
242 4 : TMH_reload_instances (mi->settings.id);
243 4 : return TALER_MHD_reply_static (connection,
244 : MHD_HTTP_NO_CONTENT,
245 : NULL,
246 : NULL,
247 : 0);
248 : }
249 :
250 :
251 : MHD_RESULT
252 0 : TMH_private_patch_instances_ID (const struct TMH_RequestHandler *rh,
253 : struct MHD_Connection *connection,
254 : struct TMH_HandlerContext *hc)
255 : {
256 0 : struct TMH_MerchantInstance *mi = hc->instance;
257 :
258 0 : return patch_instances_ID (mi,
259 : connection,
260 : hc);
261 : }
262 :
263 :
264 : MHD_RESULT
265 4 : TMH_private_patch_instances_default_ID (const struct TMH_RequestHandler *rh,
266 : struct MHD_Connection *connection,
267 : struct TMH_HandlerContext *hc)
268 : {
269 : struct TMH_MerchantInstance *mi;
270 :
271 4 : mi = TMH_lookup_instance (hc->infix);
272 4 : if (NULL == mi)
273 : {
274 0 : return TALER_MHD_reply_with_error (connection,
275 : MHD_HTTP_NOT_FOUND,
276 : TALER_EC_MERCHANT_GENERIC_INSTANCE_UNKNOWN,
277 0 : hc->infix);
278 : }
279 4 : if (mi->deleted)
280 : {
281 0 : return TALER_MHD_reply_with_error (connection,
282 : MHD_HTTP_CONFLICT,
283 : TALER_EC_MERCHANT_PRIVATE_PATCH_INSTANCES_PURGE_REQUIRED,
284 0 : hc->infix);
285 : }
286 4 : return patch_instances_ID (mi,
287 : connection,
288 : hc);
289 : }
290 :
291 :
292 : /* end of taler-merchant-httpd_private-patch-instances-ID.c */
|