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 ("logo",
82 : (const char **) &is.logo),
83 : NULL),
84 4 : GNUNET_JSON_spec_json ("address",
85 : &is.address),
86 4 : GNUNET_JSON_spec_json ("jurisdiction",
87 : &is.jurisdiction),
88 4 : GNUNET_JSON_spec_bool ("use_stefan",
89 : &is.use_stefan),
90 4 : GNUNET_JSON_spec_relative_time ("default_wire_transfer_delay",
91 : &is.default_wire_transfer_delay),
92 4 : GNUNET_JSON_spec_relative_time ("default_pay_delay",
93 : &is.default_pay_delay),
94 4 : GNUNET_JSON_spec_end ()
95 : };
96 : enum GNUNET_DB_QueryStatus qs;
97 :
98 4 : GNUNET_assert (NULL != mi);
99 4 : memset (&is,
100 : 0,
101 : sizeof (is));
102 : {
103 : enum GNUNET_GenericReturnValue res;
104 :
105 4 : res = TALER_MHD_parse_json_data (connection,
106 4 : hc->request_body,
107 : spec);
108 4 : if (GNUNET_OK != res)
109 : return (GNUNET_NO == res)
110 : ? MHD_YES
111 0 : : MHD_NO;
112 : }
113 4 : if (! TMH_location_object_valid (is.address))
114 : {
115 0 : GNUNET_break_op (0);
116 0 : GNUNET_JSON_parse_free (spec);
117 0 : return TALER_MHD_reply_with_error (connection,
118 : MHD_HTTP_BAD_REQUEST,
119 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
120 : "address");
121 : }
122 4 : if ( (NULL != is.logo) &&
123 0 : (! TMH_image_data_url_valid (is.logo)) )
124 : {
125 0 : GNUNET_break_op (0);
126 0 : GNUNET_JSON_parse_free (spec);
127 0 : return TALER_MHD_reply_with_error (connection,
128 : MHD_HTTP_BAD_REQUEST,
129 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
130 : "logo");
131 : }
132 :
133 4 : if (! TMH_location_object_valid (is.jurisdiction))
134 : {
135 0 : GNUNET_break_op (0);
136 0 : GNUNET_JSON_parse_free (spec);
137 0 : return TALER_MHD_reply_with_error (connection,
138 : MHD_HTTP_BAD_REQUEST,
139 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
140 : "jurisdiction");
141 : }
142 :
143 4 : for (unsigned int retry = 0; retry<MAX_RETRIES; retry++)
144 : {
145 : /* Cleanup after earlier loops */
146 : {
147 : struct TMH_WireMethod *wm;
148 :
149 4 : while (NULL != (wm = wm_head))
150 : {
151 0 : GNUNET_CONTAINER_DLL_remove (wm_head,
152 : wm_tail,
153 : wm);
154 0 : free_wm (wm);
155 : }
156 : }
157 4 : if (GNUNET_OK !=
158 4 : TMH_db->start (TMH_db->cls,
159 : "PATCH /instances"))
160 : {
161 0 : GNUNET_JSON_parse_free (spec);
162 0 : return TALER_MHD_reply_with_error (connection,
163 : MHD_HTTP_INTERNAL_SERVER_ERROR,
164 : TALER_EC_GENERIC_DB_START_FAILED,
165 : NULL);
166 : }
167 : /* Check for equality of settings */
168 4 : if (! ( (0 == strcmp (mi->settings.name,
169 2 : name)) &&
170 2 : ((mi->settings.email == is.email) ||
171 0 : (NULL != is.email && NULL != mi->settings.email &&
172 0 : 0 == strcmp (mi->settings.email,
173 0 : is.email))) &&
174 2 : ((mi->settings.website == is.website) ||
175 0 : (NULL != is.website && NULL != mi->settings.website &&
176 0 : 0 == strcmp (mi->settings.website,
177 0 : is.website))) &&
178 2 : ((mi->settings.logo == is.logo) ||
179 0 : (NULL != is.logo && NULL != mi->settings.logo &&
180 0 : 0 == strcmp (mi->settings.logo,
181 2 : is.logo))) &&
182 2 : (1 == json_equal (mi->settings.address,
183 2 : is.address)) &&
184 0 : (1 == json_equal (mi->settings.jurisdiction,
185 0 : is.jurisdiction)) &&
186 0 : (mi->settings.use_stefan == is.use_stefan) &&
187 0 : (GNUNET_TIME_relative_cmp (mi->settings.default_wire_transfer_delay,
188 : ==,
189 : is.default_wire_transfer_delay)) &&
190 0 : (GNUNET_TIME_relative_cmp (mi->settings.default_pay_delay,
191 : ==,
192 : is.default_pay_delay)) ) )
193 : {
194 4 : is.id = mi->settings.id;
195 4 : is.name = GNUNET_strdup (name);
196 4 : qs = TMH_db->update_instance (TMH_db->cls,
197 : &is);
198 4 : GNUNET_free (is.name);
199 4 : if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
200 : {
201 0 : TMH_db->rollback (TMH_db->cls);
202 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
203 0 : goto retry;
204 : else
205 0 : goto giveup;
206 : }
207 : }
208 4 : qs = TMH_db->commit (TMH_db->cls);
209 4 : retry:
210 4 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
211 0 : continue;
212 4 : break;
213 : } /* for(... MAX_RETRIES) */
214 0 : giveup:
215 : /* Update our 'settings' */
216 4 : GNUNET_free (mi->settings.name);
217 4 : GNUNET_free (mi->settings.email);
218 4 : GNUNET_free (mi->settings.website);
219 4 : GNUNET_free (mi->settings.logo);
220 4 : json_decref (mi->settings.address);
221 4 : json_decref (mi->settings.jurisdiction);
222 4 : is.id = mi->settings.id;
223 4 : mi->settings = is;
224 4 : mi->settings.address = json_incref (mi->settings.address);
225 4 : mi->settings.jurisdiction = json_incref (mi->settings.jurisdiction);
226 4 : mi->settings.name = GNUNET_strdup (name);
227 4 : if (NULL != is.email)
228 0 : mi->settings.email = GNUNET_strdup (is.email);
229 4 : if (NULL != is.website)
230 0 : mi->settings.website = GNUNET_strdup (is.website);
231 4 : if (NULL != is.logo)
232 0 : mi->settings.logo = GNUNET_strdup (is.logo);
233 :
234 4 : GNUNET_JSON_parse_free (spec);
235 4 : TMH_reload_instances (mi->settings.id);
236 4 : return TALER_MHD_reply_static (connection,
237 : MHD_HTTP_NO_CONTENT,
238 : NULL,
239 : NULL,
240 : 0);
241 : }
242 :
243 :
244 : MHD_RESULT
245 0 : TMH_private_patch_instances_ID (const struct TMH_RequestHandler *rh,
246 : struct MHD_Connection *connection,
247 : struct TMH_HandlerContext *hc)
248 : {
249 0 : struct TMH_MerchantInstance *mi = hc->instance;
250 :
251 0 : return patch_instances_ID (mi,
252 : connection,
253 : hc);
254 : }
255 :
256 :
257 : MHD_RESULT
258 4 : TMH_private_patch_instances_default_ID (const struct TMH_RequestHandler *rh,
259 : struct MHD_Connection *connection,
260 : struct TMH_HandlerContext *hc)
261 : {
262 : struct TMH_MerchantInstance *mi;
263 :
264 4 : mi = TMH_lookup_instance (hc->infix);
265 4 : if (NULL == mi)
266 : {
267 0 : return TALER_MHD_reply_with_error (connection,
268 : MHD_HTTP_NOT_FOUND,
269 : TALER_EC_MERCHANT_GENERIC_INSTANCE_UNKNOWN,
270 0 : hc->infix);
271 : }
272 4 : if (mi->deleted)
273 : {
274 0 : return TALER_MHD_reply_with_error (connection,
275 : MHD_HTTP_CONFLICT,
276 : TALER_EC_MERCHANT_PRIVATE_PATCH_INSTANCES_PURGE_REQUIRED,
277 0 : hc->infix);
278 : }
279 4 : return patch_instances_ID (mi,
280 : connection,
281 : hc);
282 : }
283 :
284 :
285 : /* end of taler-merchant-httpd_private-patch-instances-ID.c */
|