Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2014-2026 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 2.1, 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 Lesser General Public License for more details.
12 :
13 : You should have received a copy of the GNU Lesser General Public License along with
14 : TALER; see the file COPYING.LGPL. If not, see
15 : <http://www.gnu.org/licenses/>
16 : */
17 : /**
18 : * @file merchant_api_get-private-products-PRODUCT_ID-new.c
19 : * @brief Implementation of the GET /private/products/$PRODUCT_ID request
20 : * @author Christian Grothoff
21 : */
22 : #include "taler/platform.h"
23 : #include <curl/curl.h>
24 : #include <jansson.h>
25 : #include <microhttpd.h> /* just for HTTP status codes */
26 : #include <gnunet/gnunet_util_lib.h>
27 : #include <gnunet/gnunet_curl_lib.h>
28 : #include <taler/merchant/get-private-products-PRODUCT_ID.h>
29 : #include "merchant_api_curl_defaults.h"
30 : #include <taler/taler_json_lib.h>
31 :
32 :
33 : /**
34 : * Handle for a GET /private/products/$PRODUCT_ID operation.
35 : */
36 : struct TALER_MERCHANT_GetPrivateProductHandle
37 : {
38 : /**
39 : * Base URL of the merchant backend.
40 : */
41 : char *base_url;
42 :
43 : /**
44 : * Product identifier.
45 : */
46 : char *product_id;
47 :
48 : /**
49 : * The full URL for this request.
50 : */
51 : char *url;
52 :
53 : /**
54 : * Handle for the request.
55 : */
56 : struct GNUNET_CURL_Job *job;
57 :
58 : /**
59 : * Function to call with the result.
60 : */
61 : TALER_MERCHANT_GetPrivateProductCallback cb;
62 :
63 : /**
64 : * Closure for @a cb.
65 : */
66 : TALER_MERCHANT_GET_PRIVATE_PRODUCT_RESULT_CLOSURE *cb_cls;
67 :
68 : /**
69 : * Reference to the execution context.
70 : */
71 : struct GNUNET_CURL_Context *ctx;
72 : };
73 :
74 :
75 : /**
76 : * Function called when we're done processing the
77 : * HTTP GET /private/products/$PRODUCT_ID request.
78 : *
79 : * @param cls the `struct TALER_MERCHANT_GetPrivateProductHandle`
80 : * @param response_code HTTP response code, 0 on error
81 : * @param response response body, NULL if not in JSON
82 : */
83 : static void
84 0 : handle_get_product_finished (void *cls,
85 : long response_code,
86 : const void *response)
87 : {
88 0 : struct TALER_MERCHANT_GetPrivateProductHandle *gpp = cls;
89 0 : const json_t *json = response;
90 0 : struct TALER_MERCHANT_GetPrivateProductResponse pgr = {
91 0 : .hr.http_status = (unsigned int) response_code,
92 : .hr.reply = json
93 : };
94 :
95 0 : gpp->job = NULL;
96 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
97 : "Got /private/products/$PRODUCT_ID response with status code %u\n",
98 : (unsigned int) response_code);
99 0 : switch (response_code)
100 : {
101 0 : case MHD_HTTP_OK:
102 : {
103 0 : const json_t *jcategories = NULL;
104 : struct GNUNET_JSON_Specification spec[] = {
105 0 : GNUNET_JSON_spec_string (
106 : "product_name",
107 : &pgr.details.ok.product_name),
108 0 : GNUNET_JSON_spec_string (
109 : "description",
110 : &pgr.details.ok.description),
111 0 : GNUNET_JSON_spec_object_const (
112 : "description_i18n",
113 : &pgr.details.ok.description_i18n),
114 0 : GNUNET_JSON_spec_string (
115 : "unit",
116 : &pgr.details.ok.unit),
117 0 : TALER_JSON_spec_amount_any_array (
118 : "unit_price",
119 : &pgr.details.ok.unit_price_len,
120 : (struct TALER_Amount **) &pgr.details.ok.unit_price),
121 0 : TALER_JSON_spec_amount_any (
122 : "price",
123 : &pgr.details.ok.price),
124 0 : GNUNET_JSON_spec_mark_optional (
125 : GNUNET_JSON_spec_string (
126 : "image",
127 : &pgr.details.ok.image),
128 : NULL),
129 0 : GNUNET_JSON_spec_mark_optional (
130 : GNUNET_JSON_spec_array_const (
131 : "taxes",
132 : &pgr.details.ok.taxes),
133 : NULL),
134 0 : GNUNET_JSON_spec_int64 (
135 : "total_stock",
136 : &pgr.details.ok.total_stock),
137 0 : GNUNET_JSON_spec_string (
138 : "unit_total_stock",
139 : &pgr.details.ok.unit_total_stock),
140 0 : GNUNET_JSON_spec_bool (
141 : "unit_allow_fraction",
142 : &pgr.details.ok.unit_allow_fraction),
143 0 : GNUNET_JSON_spec_uint32 (
144 : "unit_precision_level",
145 : &pgr.details.ok.unit_precision_level),
146 0 : GNUNET_JSON_spec_uint64 (
147 : "total_sold",
148 : &pgr.details.ok.total_sold),
149 0 : GNUNET_JSON_spec_uint64 (
150 : "total_lost",
151 : &pgr.details.ok.total_lost),
152 0 : GNUNET_JSON_spec_mark_optional (
153 : GNUNET_JSON_spec_object_const (
154 : "address",
155 : &pgr.details.ok.location),
156 : NULL),
157 0 : GNUNET_JSON_spec_mark_optional (
158 : GNUNET_JSON_spec_timestamp (
159 : "next_restock",
160 : &pgr.details.ok.next_restock),
161 : NULL),
162 0 : GNUNET_JSON_spec_mark_optional (
163 : GNUNET_JSON_spec_array_const (
164 : "categories",
165 : &jcategories),
166 : NULL),
167 0 : GNUNET_JSON_spec_mark_optional (
168 : GNUNET_JSON_spec_uint64 (
169 : "product_group_id",
170 : &pgr.details.ok.product_group_id),
171 : NULL),
172 0 : GNUNET_JSON_spec_mark_optional (
173 : GNUNET_JSON_spec_uint64 (
174 : "money_pot_id",
175 : &pgr.details.ok.money_pot_id),
176 : NULL),
177 0 : GNUNET_JSON_spec_mark_optional (
178 : GNUNET_JSON_spec_bool (
179 : "price_is_net",
180 : &pgr.details.ok.price_is_net),
181 : NULL),
182 0 : GNUNET_JSON_spec_mark_optional (
183 : GNUNET_JSON_spec_uint16 (
184 : "minimum_age",
185 : &pgr.details.ok.minimum_age),
186 : NULL),
187 0 : GNUNET_JSON_spec_end ()
188 : };
189 :
190 0 : if (GNUNET_OK !=
191 0 : GNUNET_JSON_parse (json,
192 : spec,
193 : NULL, NULL))
194 : {
195 0 : pgr.hr.http_status = 0;
196 0 : pgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
197 0 : break;
198 : }
199 : {
200 : unsigned int num_categories;
201 : uint64_t *categories;
202 :
203 0 : num_categories = (unsigned int) json_array_size (jcategories);
204 0 : if (json_array_size (jcategories) != (size_t) num_categories)
205 : {
206 0 : GNUNET_break (0);
207 0 : pgr.hr.http_status = 0;
208 0 : pgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
209 0 : break;
210 : }
211 0 : categories = GNUNET_new_array (num_categories,
212 : uint64_t);
213 0 : for (unsigned int i = 0; i < num_categories; i++)
214 : {
215 0 : json_t *jval = json_array_get (jcategories,
216 : i);
217 :
218 0 : if (! json_is_integer (jval))
219 : {
220 0 : GNUNET_break_op (0);
221 0 : GNUNET_free (categories);
222 0 : pgr.hr.http_status = 0;
223 0 : pgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
224 0 : goto done;
225 : }
226 0 : categories[i] = (uint64_t) json_integer_value (jval);
227 : }
228 0 : pgr.details.ok.categories_len = num_categories;
229 0 : pgr.details.ok.categories = categories;
230 0 : gpp->cb (gpp->cb_cls,
231 : &pgr);
232 0 : GNUNET_free (categories);
233 0 : TALER_MERCHANT_get_private_product_cancel (gpp);
234 0 : return;
235 : }
236 : }
237 0 : case MHD_HTTP_UNAUTHORIZED:
238 0 : pgr.hr.ec = TALER_JSON_get_error_code (json);
239 0 : pgr.hr.hint = TALER_JSON_get_error_hint (json);
240 0 : break;
241 0 : case MHD_HTTP_NOT_FOUND:
242 0 : pgr.hr.ec = TALER_JSON_get_error_code (json);
243 0 : pgr.hr.hint = TALER_JSON_get_error_hint (json);
244 0 : break;
245 0 : default:
246 0 : pgr.hr.ec = TALER_JSON_get_error_code (json);
247 0 : pgr.hr.hint = TALER_JSON_get_error_hint (json);
248 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
249 : "Unexpected response code %u/%d\n",
250 : (unsigned int) response_code,
251 : (int) pgr.hr.ec);
252 0 : break;
253 : }
254 0 : done:
255 0 : gpp->cb (gpp->cb_cls,
256 : &pgr);
257 0 : TALER_MERCHANT_get_private_product_cancel (gpp);
258 : }
259 :
260 :
261 : struct TALER_MERCHANT_GetPrivateProductHandle *
262 0 : TALER_MERCHANT_get_private_product_create (
263 : struct GNUNET_CURL_Context *ctx,
264 : const char *url,
265 : const char *product_id)
266 : {
267 : struct TALER_MERCHANT_GetPrivateProductHandle *gpp;
268 :
269 0 : gpp = GNUNET_new (struct TALER_MERCHANT_GetPrivateProductHandle);
270 0 : gpp->ctx = ctx;
271 0 : gpp->base_url = GNUNET_strdup (url);
272 0 : gpp->product_id = GNUNET_strdup (product_id);
273 0 : return gpp;
274 : }
275 :
276 :
277 : enum TALER_ErrorCode
278 0 : TALER_MERCHANT_get_private_product_start (
279 : struct TALER_MERCHANT_GetPrivateProductHandle *gpp,
280 : TALER_MERCHANT_GetPrivateProductCallback cb,
281 : TALER_MERCHANT_GET_PRIVATE_PRODUCT_RESULT_CLOSURE *cb_cls)
282 : {
283 : CURL *eh;
284 :
285 0 : gpp->cb = cb;
286 0 : gpp->cb_cls = cb_cls;
287 : {
288 : char *path;
289 :
290 0 : GNUNET_asprintf (&path,
291 : "private/products/%s",
292 : gpp->product_id);
293 0 : gpp->url = TALER_url_join (gpp->base_url,
294 : path,
295 : NULL);
296 0 : GNUNET_free (path);
297 : }
298 0 : if (NULL == gpp->url)
299 0 : return TALER_EC_GENERIC_CONFIGURATION_INVALID;
300 0 : eh = TALER_MERCHANT_curl_easy_get_ (gpp->url);
301 0 : if (NULL == eh)
302 0 : return TALER_EC_GENERIC_CONFIGURATION_INVALID;
303 0 : gpp->job = GNUNET_CURL_job_add (gpp->ctx,
304 : eh,
305 : &handle_get_product_finished,
306 : gpp);
307 0 : if (NULL == gpp->job)
308 0 : return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
309 0 : return TALER_EC_NONE;
310 : }
311 :
312 :
313 : void
314 0 : TALER_MERCHANT_get_private_product_cancel (
315 : struct TALER_MERCHANT_GetPrivateProductHandle *gpp)
316 : {
317 0 : if (NULL != gpp->job)
318 : {
319 0 : GNUNET_CURL_job_cancel (gpp->job);
320 0 : gpp->job = NULL;
321 : }
322 0 : GNUNET_free (gpp->url);
323 0 : GNUNET_free (gpp->product_id);
324 0 : GNUNET_free (gpp->base_url);
325 0 : GNUNET_free (gpp);
326 0 : }
327 :
328 :
329 : /* end of merchant_api_get-private-products-PRODUCT_ID-new.c */
|