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