Line data Source code
1 : /*
2 : This file is part of TALER
3 : (C) 2022-2024 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-post-templates.c
22 : * @brief implementing POST /templates request handling
23 : * @author Priscilla HUANG
24 : */
25 : #include "platform.h"
26 : #include "taler-merchant-httpd_private-post-templates.h"
27 : #include "taler-merchant-httpd_helper.h"
28 : #include <taler/taler_json_lib.h>
29 :
30 :
31 : /**
32 : * Check if the two templates are identical.
33 : *
34 : * @param t1 template to compare
35 : * @param t2 other template to compare
36 : * @return true if they are 'equal', false if not or of payto_uris is not an array
37 : */
38 : static bool
39 4 : templates_equal (const struct TALER_MERCHANTDB_TemplateDetails *t1,
40 : const struct TALER_MERCHANTDB_TemplateDetails *t2)
41 : {
42 4 : return ( (0 == strcmp (t1->template_description,
43 6 : t2->template_description)) &&
44 2 : ( ( (NULL == t1->otp_id) &&
45 2 : (NULL == t2->otp_id) ) ||
46 0 : ( (NULL != t1->otp_id) &&
47 0 : (NULL != t2->otp_id) &&
48 0 : (0 == strcmp (t1->otp_id,
49 0 : t2->otp_id))) ) &&
50 2 : ( ( (NULL == t1->editable_defaults) &&
51 2 : (NULL == t2->editable_defaults) ) ||
52 0 : ( (NULL != t1->editable_defaults) &&
53 0 : (NULL != t2->editable_defaults) &&
54 0 : (1 == json_equal (t1->editable_defaults,
55 6 : t2->editable_defaults))) ) &&
56 2 : (1 == json_equal (t1->template_contract,
57 2 : t2->template_contract)) );
58 : }
59 :
60 :
61 : MHD_RESULT
62 12 : TMH_private_post_templates (const struct TMH_RequestHandler *rh,
63 : struct MHD_Connection *connection,
64 : struct TMH_HandlerContext *hc)
65 : {
66 12 : struct TMH_MerchantInstance *mi = hc->instance;
67 12 : struct TALER_MERCHANTDB_TemplateDetails tp = { 0 };
68 : const char *template_id;
69 : enum GNUNET_DB_QueryStatus qs;
70 : struct GNUNET_JSON_Specification spec[] = {
71 12 : GNUNET_JSON_spec_string ("template_id",
72 : &template_id),
73 12 : GNUNET_JSON_spec_string ("template_description",
74 : (const char **) &tp.template_description),
75 12 : GNUNET_JSON_spec_mark_optional (
76 : GNUNET_JSON_spec_string ("otp_id",
77 : (const char **) &tp.otp_id),
78 : NULL),
79 12 : GNUNET_JSON_spec_json ("template_contract",
80 : &tp.template_contract),
81 12 : GNUNET_JSON_spec_mark_optional (
82 : GNUNET_JSON_spec_json ("editable_defaults",
83 : &tp.editable_defaults),
84 : NULL),
85 12 : GNUNET_JSON_spec_end ()
86 : };
87 12 : uint64_t otp_serial = 0;
88 :
89 12 : GNUNET_assert (NULL != mi);
90 : {
91 : enum GNUNET_GenericReturnValue res;
92 :
93 12 : res = TALER_MHD_parse_json_data (connection,
94 12 : hc->request_body,
95 : spec);
96 12 : if (GNUNET_OK != res)
97 : {
98 0 : GNUNET_break_op (0);
99 : return (GNUNET_NO == res)
100 : ? MHD_YES
101 0 : : MHD_NO;
102 : }
103 : }
104 12 : if (! TMH_template_contract_valid (tp.template_contract))
105 : {
106 0 : GNUNET_break_op (0);
107 0 : json_dumpf (tp.template_contract,
108 : stderr,
109 : JSON_INDENT (2));
110 0 : GNUNET_JSON_parse_free (spec);
111 0 : return TALER_MHD_reply_with_error (connection,
112 : MHD_HTTP_BAD_REQUEST,
113 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
114 : "template_contract");
115 : }
116 :
117 12 : if (NULL != tp.editable_defaults)
118 : {
119 : const char *key;
120 : json_t *val;
121 :
122 0 : json_object_foreach (tp.editable_defaults, key, val)
123 : {
124 0 : if (NULL !=
125 0 : json_object_get (tp.template_contract,
126 : key))
127 : {
128 : char *msg;
129 : MHD_RESULT ret;
130 :
131 0 : GNUNET_break_op (0);
132 0 : GNUNET_asprintf (&msg,
133 : "editable_defaults::%s conflicts with template_contract",
134 : key);
135 0 : GNUNET_JSON_parse_free (spec);
136 0 : ret = TALER_MHD_reply_with_error (connection,
137 : MHD_HTTP_BAD_REQUEST,
138 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
139 : msg);
140 0 : GNUNET_free (msg);
141 0 : return ret;
142 : }
143 : }
144 : }
145 :
146 12 : if (NULL != tp.otp_id)
147 : {
148 2 : qs = TMH_db->select_otp_serial (TMH_db->cls,
149 2 : mi->settings.id,
150 2 : tp.otp_id,
151 : &otp_serial);
152 2 : switch (qs)
153 : {
154 0 : case GNUNET_DB_STATUS_HARD_ERROR:
155 : case GNUNET_DB_STATUS_SOFT_ERROR:
156 0 : GNUNET_break (0);
157 0 : GNUNET_JSON_parse_free (spec);
158 0 : return TALER_MHD_reply_with_error (connection,
159 : MHD_HTTP_INTERNAL_SERVER_ERROR,
160 : TALER_EC_GENERIC_DB_STORE_FAILED,
161 : "select_otp_serial");
162 0 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
163 0 : GNUNET_JSON_parse_free (spec);
164 0 : return TALER_MHD_reply_with_error (connection,
165 : MHD_HTTP_NOT_FOUND,
166 : TALER_EC_MERCHANT_GENERIC_OTP_DEVICE_UNKNOWN,
167 : NULL);
168 2 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
169 2 : break;
170 : }
171 : }
172 :
173 12 : qs = TMH_db->insert_template (TMH_db->cls,
174 12 : mi->settings.id,
175 : template_id,
176 : otp_serial,
177 : &tp);
178 12 : switch (qs)
179 : {
180 0 : case GNUNET_DB_STATUS_HARD_ERROR:
181 : case GNUNET_DB_STATUS_SOFT_ERROR:
182 0 : GNUNET_break (0);
183 0 : GNUNET_JSON_parse_free (spec);
184 0 : return TALER_MHD_reply_with_error (connection,
185 : MHD_HTTP_INTERNAL_SERVER_ERROR,
186 : TALER_EC_GENERIC_DB_STORE_FAILED,
187 : NULL);
188 8 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
189 8 : GNUNET_JSON_parse_free (spec);
190 8 : return TALER_MHD_reply_static (connection,
191 : MHD_HTTP_NO_CONTENT,
192 : NULL,
193 : NULL,
194 : 0);
195 4 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
196 4 : break;
197 : }
198 :
199 : {
200 : /* Test if a template of this id is known */
201 : struct TALER_MERCHANTDB_TemplateDetails etp;
202 :
203 4 : qs = TMH_db->lookup_template (TMH_db->cls,
204 4 : mi->settings.id,
205 : template_id,
206 : &etp);
207 4 : switch (qs)
208 : {
209 0 : case GNUNET_DB_STATUS_HARD_ERROR:
210 : case GNUNET_DB_STATUS_SOFT_ERROR:
211 : /* Clean up and fail hard */
212 0 : GNUNET_break (0);
213 0 : GNUNET_JSON_parse_free (spec);
214 0 : return TALER_MHD_reply_with_error (connection,
215 : MHD_HTTP_INTERNAL_SERVER_ERROR,
216 : TALER_EC_GENERIC_DB_FETCH_FAILED,
217 : NULL);
218 0 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
219 0 : GNUNET_break (0);
220 0 : GNUNET_JSON_parse_free (spec);
221 0 : return TALER_MHD_reply_with_error (connection,
222 : MHD_HTTP_INTERNAL_SERVER_ERROR,
223 : TALER_EC_GENERIC_DB_FETCH_FAILED,
224 : "logic error");
225 4 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
226 4 : break;
227 : }
228 : /* idempotency check: is etp == tp? */
229 : {
230 : bool eq;
231 :
232 4 : eq = templates_equal (&tp,
233 : &etp);
234 4 : TALER_MERCHANTDB_template_details_free (&etp);
235 4 : TMH_db->rollback (TMH_db->cls);
236 4 : GNUNET_JSON_parse_free (spec);
237 : return eq
238 2 : ? TALER_MHD_reply_static (connection,
239 : MHD_HTTP_NO_CONTENT,
240 : NULL,
241 : NULL,
242 : 0)
243 6 : : TALER_MHD_reply_with_error (connection,
244 : MHD_HTTP_CONFLICT,
245 : TALER_EC_MERCHANT_PRIVATE_POST_TEMPLATES_CONFLICT_TEMPLATE_EXISTS,
246 : template_id);
247 : }
248 : }
249 : }
250 :
251 :
252 : /* end of taler-merchant-httpd_private-post-templates.c */
|