Line data Source code
1 : /*
2 : This file is part of TALER
3 : (C) 2025 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify it under the
6 : terms of the GNU Lesser General Public License as published by the Free Software
7 : Foundation; either version 3, or (at your option) any later version.
8 :
9 : TALER is distributed in the hope that it will be useful, but WITHOUT ANY
10 : WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 : A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 :
13 : You should have received a copy of the GNU General Public License along with
14 : TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
15 : */
16 : /**
17 : * @file util/template_parse.c
18 : * @brief shared logic for template contract parsing
19 : * @author Bohdan Potuzhnyi
20 : */
21 : #include "taler/platform.h"
22 : #include <gnunet/gnunet_common.h>
23 : #include <gnunet/gnunet_json_lib.h>
24 : #include <jansson.h>
25 : #include <string.h>
26 : #include <taler/taler_json_lib.h>
27 : #include <taler/taler_util.h>
28 : #include "taler/taler_merchant_util.h"
29 : #include <regex.h>
30 :
31 :
32 : enum TALER_MERCHANT_TemplateType
33 0 : TALER_MERCHANT_template_type_from_contract (const json_t *template_contract)
34 : {
35 : const json_t *type_val;
36 :
37 0 : if (NULL == template_contract)
38 0 : return TALER_MERCHANT_TEMPLATE_TYPE_INVALID;
39 :
40 0 : type_val = json_object_get (template_contract,
41 : "template_type");
42 :
43 0 : if (! json_is_string (type_val))
44 0 : return TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER;
45 :
46 0 : return TALER_MERCHANT_template_type_from_string (
47 : json_string_value (type_val));
48 : }
49 :
50 :
51 : enum TALER_MERCHANT_TemplateType
52 0 : TALER_MERCHANT_template_type_from_string (const char *template_type)
53 : {
54 0 : if (NULL == template_type)
55 0 : return TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER;
56 0 : if (0 == strcmp (template_type,
57 : "fixed-order"))
58 0 : return TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER;
59 0 : if (0 == strcmp (template_type,
60 : "inventory-cart"))
61 0 : return TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART;
62 0 : if (0 == strcmp (template_type,
63 : "paivana"))
64 0 : return TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA;
65 0 : return TALER_MERCHANT_TEMPLATE_TYPE_INVALID;
66 : }
67 :
68 :
69 : const char *
70 0 : TALER_MERCHANT_template_type_to_string (
71 : enum TALER_MERCHANT_TemplateType template_type)
72 : {
73 0 : switch (template_type)
74 : {
75 0 : case TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER:
76 0 : return "fixed-order";
77 0 : case TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART:
78 0 : return "inventory-cart";
79 0 : case TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA:
80 0 : return "paivana";
81 0 : case TALER_MERCHANT_TEMPLATE_TYPE_INVALID:
82 0 : break;
83 : }
84 0 : return NULL;
85 : }
86 :
87 :
88 : /**
89 : * Parse inventory-specific fields from a template contract.
90 : *
91 : * @param template_contract json
92 : * @param[out] out where to write parsed fields
93 : * @param[out] error_name error description
94 : * @return #GNUNET_OK on success, #GNUNET_SYSERR on parse/validation failure
95 : */
96 : static enum GNUNET_GenericReturnValue
97 0 : parse_template_inventory (const json_t *template_contract,
98 : struct TALER_MERCHANT_TemplateContract *out,
99 : const char **error_name)
100 : {
101 : struct GNUNET_JSON_Specification spec[] = {
102 0 : GNUNET_JSON_spec_mark_optional (
103 : GNUNET_JSON_spec_bool ("selected_all",
104 : &out->details.inventory.selected_all),
105 : NULL),
106 0 : GNUNET_JSON_spec_mark_optional (
107 : GNUNET_JSON_spec_array_const ("selected_categories",
108 : &out->details.inventory.selected_categories)
109 : ,
110 : NULL),
111 0 : GNUNET_JSON_spec_mark_optional (
112 : GNUNET_JSON_spec_array_const ("selected_products",
113 : &out->details.inventory.selected_products),
114 : NULL),
115 0 : GNUNET_JSON_spec_mark_optional (
116 : GNUNET_JSON_spec_bool ("choose_one",
117 : &out->details.inventory.choose_one),
118 : NULL),
119 0 : GNUNET_JSON_spec_end ()
120 : };
121 : const char *en;
122 :
123 0 : if (GNUNET_OK !=
124 0 : GNUNET_JSON_parse ((json_t *) template_contract,
125 : spec,
126 : &en,
127 : NULL))
128 : {
129 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
130 : "Invalid inventory template_contract for field %s\n",
131 : en);
132 0 : if (NULL != error_name)
133 0 : *error_name = en;
134 0 : return GNUNET_SYSERR;
135 : }
136 :
137 0 : if (NULL != out->details.inventory.selected_categories)
138 : {
139 : const json_t *entry;
140 : size_t idx;
141 :
142 0 : json_array_foreach ((json_t *) out->details.inventory.selected_categories,
143 : idx,
144 : entry)
145 : {
146 0 : if ( (! json_is_integer (entry)) ||
147 0 : (0 > json_integer_value (entry)) )
148 : {
149 0 : GNUNET_break_op (0);
150 0 : if (NULL != error_name)
151 0 : *error_name = "selected_categories";
152 0 : return GNUNET_SYSERR;
153 : }
154 : }
155 : }
156 :
157 0 : if (NULL != out->details.inventory.selected_products)
158 : {
159 : const json_t *entry;
160 : size_t idx;
161 :
162 0 : json_array_foreach ((json_t *) out->details.inventory.selected_products,
163 : idx,
164 : entry)
165 : {
166 0 : if (! json_is_string (entry))
167 : {
168 0 : GNUNET_break_op (0);
169 0 : if (NULL != error_name)
170 0 : *error_name = "selected_products";
171 0 : return GNUNET_SYSERR;
172 : }
173 : }
174 : }
175 0 : return GNUNET_OK;
176 : }
177 :
178 :
179 : /**
180 : * Parse paivana-specific fields from a template contract.
181 : *
182 : * @param template_contract json
183 : * @param[out] out where to write parsed fields
184 : * @param[out] error_name error description
185 : * @return #GNUNET_OK on success, #GNUNET_SYSERR on parse/validation failure
186 : */
187 : static enum GNUNET_GenericReturnValue
188 0 : parse_template_paivana (const json_t *template_contract,
189 : struct TALER_MERCHANT_TemplateContract *out,
190 : const char **error_name)
191 : {
192 : struct GNUNET_JSON_Specification spec[] = {
193 0 : GNUNET_JSON_spec_mark_optional (
194 : GNUNET_JSON_spec_string ("website_regex",
195 : &out->details.paivana.website_regex),
196 : NULL),
197 0 : TALER_MERCHANT_spec_choices ("choices",
198 : &out->details.paivana.choices,
199 : &out->details.paivana.choices_len),
200 0 : GNUNET_JSON_spec_end ()
201 : };
202 : const char *en;
203 :
204 0 : if (GNUNET_OK !=
205 0 : GNUNET_JSON_parse ((json_t *) template_contract,
206 : spec,
207 : &en,
208 : NULL))
209 : {
210 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
211 : "Invalid paivana template_contract for field %s\n",
212 : en);
213 0 : if (NULL != error_name)
214 0 : *error_name = en;
215 0 : return GNUNET_SYSERR;
216 : }
217 0 : if (NULL != out->details.paivana.website_regex)
218 : {
219 : regex_t ex;
220 :
221 0 : if (0 != regcomp (&ex,
222 : out->details.paivana.website_regex,
223 : REG_NOSUB | REG_EXTENDED))
224 : {
225 0 : GNUNET_break_op (0);
226 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
227 : "Invalid paivana website_regex given\n");
228 0 : if (NULL != error_name)
229 0 : *error_name = "Invalid website_regex given";
230 0 : return GNUNET_SYSERR;
231 : }
232 0 : regfree (&ex);
233 : }
234 0 : return GNUNET_OK;
235 : }
236 :
237 :
238 : enum GNUNET_GenericReturnValue
239 0 : TALER_MERCHANT_template_contract_parse (
240 : const json_t *template_contract,
241 : struct TALER_MERCHANT_TemplateContract *out,
242 : const char **error_name)
243 : {
244 0 : const char *template_type_str = NULL;
245 : struct GNUNET_JSON_Specification spec[] = {
246 0 : GNUNET_JSON_spec_mark_optional (
247 : GNUNET_JSON_spec_string ("template_type",
248 : &template_type_str),
249 : NULL),
250 0 : GNUNET_JSON_spec_mark_optional (
251 : GNUNET_JSON_spec_string ("summary",
252 : &out->summary),
253 : NULL),
254 0 : GNUNET_JSON_spec_mark_optional (
255 : GNUNET_JSON_spec_string ("currency",
256 : &out->currency),
257 : NULL),
258 0 : GNUNET_JSON_spec_mark_optional (
259 : TALER_JSON_spec_amount_any ("amount",
260 : &out->amount),
261 : &out->no_amount),
262 0 : GNUNET_JSON_spec_mark_optional (
263 : GNUNET_JSON_spec_uint32 ("minimum_age",
264 : &out->minimum_age),
265 : NULL),
266 0 : GNUNET_JSON_spec_mark_optional (
267 : GNUNET_JSON_spec_relative_time ("pay_duration",
268 : &out->pay_duration),
269 : NULL),
270 0 : GNUNET_JSON_spec_mark_optional (
271 : GNUNET_JSON_spec_bool ("request_tip",
272 : &out->request_tip),
273 : NULL),
274 0 : GNUNET_JSON_spec_end ()
275 : };
276 : const char *en;
277 :
278 0 : if (NULL == template_contract)
279 : {
280 0 : if (NULL != error_name)
281 0 : *error_name = "template_contract is NULL";
282 0 : return GNUNET_SYSERR;
283 : }
284 :
285 0 : if (GNUNET_OK !=
286 0 : GNUNET_JSON_parse ((json_t *) template_contract,
287 : spec,
288 : &en,
289 : NULL))
290 : {
291 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
292 : "Invalid input for field %s\n",
293 : en);
294 0 : if (NULL != error_name)
295 0 : *error_name = en;
296 0 : return GNUNET_SYSERR;
297 : }
298 :
299 0 : out->type = TALER_MERCHANT_template_type_from_string (template_type_str);
300 0 : if (TALER_MERCHANT_TEMPLATE_TYPE_INVALID == out->type)
301 : {
302 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
303 : "Invalid template_type used '%s'\n",
304 : template_type_str);
305 0 : if (NULL != error_name)
306 0 : *error_name = "Invalid template_type used";
307 0 : return GNUNET_SYSERR;
308 : }
309 :
310 : /* Parse additional fields for each specific type */
311 0 : switch (out->type)
312 : {
313 0 : case TALER_MERCHANT_TEMPLATE_TYPE_FIXED_ORDER:
314 0 : return GNUNET_OK;
315 0 : case TALER_MERCHANT_TEMPLATE_TYPE_INVENTORY_CART:
316 0 : return parse_template_inventory (template_contract,
317 : out,
318 : error_name);
319 0 : case TALER_MERCHANT_TEMPLATE_TYPE_PAIVANA:
320 0 : return parse_template_paivana (template_contract,
321 : out,
322 : error_name);
323 0 : case TALER_MERCHANT_TEMPLATE_TYPE_INVALID:
324 0 : break;
325 : }
326 :
327 : /* I think we are never supposed to reach it */
328 0 : GNUNET_break_op (0);
329 0 : if (NULL != error_name)
330 0 : *error_name = "template_type";
331 0 : return GNUNET_SYSERR;
332 : }
333 :
334 :
335 : bool
336 0 : TALER_MERCHANT_template_contract_valid (const json_t *template_contract)
337 : {
338 : struct TALER_MERCHANT_TemplateContract tmp;
339 :
340 0 : return (GNUNET_OK ==
341 0 : TALER_MERCHANT_template_contract_parse (template_contract,
342 : &tmp,
343 : NULL));
344 : }
|