aboutsummaryrefslogtreecommitdiff
path: root/src/mint/taler-mint-httpd_parsing.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mint/taler-mint-httpd_parsing.c')
-rw-r--r--src/mint/taler-mint-httpd_parsing.c453
1 files changed, 453 insertions, 0 deletions
diff --git a/src/mint/taler-mint-httpd_parsing.c b/src/mint/taler-mint-httpd_parsing.c
new file mode 100644
index 000000000..a976c0c06
--- /dev/null
+++ b/src/mint/taler-mint-httpd_parsing.c
@@ -0,0 +1,453 @@
1/*
2 This file is part of TALER
3 (C) 2014 GNUnet e.V.
4
5 TALER is free software; you can redistribute it and/or modify it under the
6 terms of the GNU Affero 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 Affero General Public License for more details.
12
13 You should have received a copy of the GNU Affero General Public License along with
14 TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
15*/
16
17/**
18 * @file taler-mint-httpd_parsing.c
19 * @brief functions to parse incoming requests (MHD arguments and JSON snippets)
20 * @author Florian Dold
21 * @author Benedikt Mueller
22 * @author Christian Grothoff
23 */
24
25#include "platform.h"
26#include <gnunet/gnunet_util_lib.h>
27#include "taler-mint-httpd_parsing.h"
28#include "taler-mint-httpd_responses.h"
29
30
31/**
32 * Initial size for POST
33 * request buffer.
34 */
35#define REQUEST_BUFFER_INITIAL 1024
36
37/**
38 * Maximum POST request size
39 */
40#define REQUEST_BUFFER_MAX (1024*1024)
41
42
43/**
44 * Buffer for POST requests.
45 */
46struct Buffer
47{
48 /**
49 * Allocated memory
50 */
51 char *data;
52
53 /**
54 * Number of valid bytes in buffer.
55 */
56 size_t fill;
57
58 /**
59 * Number of allocated bytes in buffer.
60 */
61 size_t alloc;
62};
63
64
65/**
66 * Initialize a buffer.
67 *
68 * @param buf the buffer to initialize
69 * @param data the initial data
70 * @param data_size size of the initial data
71 * @param alloc_size size of the buffer
72 * @param max_size maximum size that the buffer can grow to
73 * @return a GNUnet result code
74 */
75static int
76buffer_init (struct Buffer *buf,
77 const void *data,
78 size_t data_size,
79 size_t alloc_size,
80 size_t max_size)
81{
82 if (data_size > max_size || alloc_size > max_size)
83 return GNUNET_SYSERR;
84 if (data_size > alloc_size)
85 alloc_size = data_size;
86 buf->data = GNUNET_malloc (alloc_size);
87 memcpy (buf->data, data, data_size);
88 return GNUNET_OK;
89}
90
91
92/**
93 * Free the data in a buffer. Does *not* free
94 * the buffer object itself.
95 *
96 * @param buf buffer to de-initialize
97 */
98static void
99buffer_deinit (struct Buffer *buf)
100{
101 GNUNET_free (buf->data);
102 buf->data = NULL;
103}
104
105
106/**
107 * Append data to a buffer, growing the buffer if necessary.
108 *
109 * @param buf the buffer to append to
110 * @param data the data to append
111 * @param size the size of @a data
112 * @param max_size maximum size that the buffer can grow to
113 * @return GNUNET_OK on success,
114 * GNUNET_NO if the buffer can't accomodate for the new data
115 * GNUNET_SYSERR on fatal error (out of memory?)
116 */
117static int
118buffer_append (struct Buffer *buf,
119 const void *data,
120 size_t data_size,
121 size_t max_size)
122{
123 if (buf->fill + data_size > max_size)
124 return GNUNET_NO;
125 if (data_size + buf->fill > buf->alloc)
126 {
127 char *new_buf;
128 size_t new_size = buf->alloc;
129 while (new_size < buf->fill + data_size)
130 new_size += 2;
131 if (new_size > max_size)
132 return GNUNET_NO;
133 new_buf = GNUNET_malloc (new_size);
134 memcpy (new_buf, buf->data, buf->fill);
135 buf->data = new_buf;
136 buf->alloc = new_size;
137 }
138 memcpy (buf->data + buf->fill, data, data_size);
139 buf->fill += data_size;
140 return GNUNET_OK;
141}
142
143
144/**
145 * Process a POST request containing a JSON object.
146 *
147 * @param connection the MHD connection
148 * @param con_cs the closure (contains a 'struct Buffer *')
149 * @param upload_data the POST data
150 * @param upload_data_size the POST data size
151 * @param json the JSON object for a completed request
152 *
153 * @returns
154 * GNUNET_YES if json object was parsed
155 * GNUNET_NO is request incomplete or invalid
156 * GNUNET_SYSERR on internal error
157 */
158int
159process_post_json (struct MHD_Connection *connection,
160 void **con_cls,
161 const char *upload_data,
162 size_t *upload_data_size,
163 json_t **json)
164{
165 struct Buffer *r = *con_cls;
166
167 if (NULL == *con_cls)
168 {
169 /* We are seeing a fresh POST request. */
170
171 r = GNUNET_new (struct Buffer);
172 if (GNUNET_OK !=
173 buffer_init (r,
174 upload_data,
175 *upload_data_size,
176 REQUEST_BUFFER_INITIAL,
177 REQUEST_BUFFER_MAX))
178 {
179 *con_cls = NULL;
180 buffer_deinit (r);
181 GNUNET_free (r);
182 return GNUNET_SYSERR;
183 }
184 *upload_data_size = 0;
185 *con_cls = r;
186 return GNUNET_NO;
187 }
188 if (0 != *upload_data_size)
189 {
190 /* We are seeing an old request with more data available. */
191
192 if (GNUNET_OK !=
193 buffer_append (r,
194 upload_data,
195 *upload_data_size,
196 REQUEST_BUFFER_MAX))
197 {
198 /* Request too long or we're out of memory. */
199
200 *con_cls = NULL;
201 buffer_deinit (r);
202 GNUNET_free (r);
203 return GNUNET_SYSERR;
204 }
205 *upload_data_size = 0;
206 return GNUNET_NO;
207 }
208
209 /* We have seen the whole request. */
210
211 *json = json_loadb (r->data, r->fill, 0, NULL);
212 buffer_deinit (r);
213 GNUNET_free (r);
214 if (NULL == *json)
215 {
216 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
217 "Can't parse JSON request body\n");
218 return (MHD_YES ==
219 TALER_MINT_reply_json_pack (connection,
220 MHD_HTTP_BAD_REQUEST,
221 "{s:s}",
222 "error", "invalid json"))
223 ? GNUNET_NO : GNUNET_SYSERR;
224 }
225 *con_cls = NULL;
226
227 return GNUNET_YES;
228}
229
230
231/**
232 * Navigate through a JSON tree.
233 *
234 * Sends an error response if navigation is impossible (i.e.
235 * the JSON object is invalid)
236 *
237 * @param connection the connection to send an error response to
238 * @param root the JSON node to start the navigation at.
239 * @param ... navigation specification (see JNAV_*)
240 * @return GNUNET_YES if navigation was successful
241 * GNUNET_NO if json is malformed, error response was generated
242 * GNUNET_SYSERR on internal error
243 */
244int
245request_json_require_nav (struct MHD_Connection *connection,
246 const json_t *root, ...)
247{
248 va_list argp;
249 int ignore = GNUNET_NO;
250 // what's our current path from 'root'?
251 json_t *path;
252
253 path = json_array ();
254
255 va_start(argp, root);
256
257 while (1)
258 {
259 int command = va_arg(argp, int);
260 switch (command)
261 {
262 case JNAV_FIELD:
263 {
264 const char *fname = va_arg(argp, const char *);
265 if (GNUNET_YES == ignore)
266 break;
267 json_array_append_new (path, json_string (fname));
268 root = json_object_get (root, fname);
269 if (NULL == root)
270 {
271 /* FIXME: can't IGNORE this return value! */
272 (void) TALER_MINT_reply_json_pack (connection,
273 MHD_HTTP_BAD_REQUEST,
274 "{s:s,s:o}",
275 "error",
276 "missing field in JSON",
277 "path",
278 path);
279 ignore = GNUNET_YES;
280 break;
281 }
282 }
283 break;
284 case JNAV_INDEX:
285 {
286 int fnum = va_arg(argp, int);
287 if (GNUNET_YES == ignore)
288 break;
289 json_array_append_new (path, json_integer (fnum));
290 root = json_array_get (root, fnum);
291 if (NULL == root)
292 {
293 /* FIXME: can't IGNORE this return value! */
294 (void) TALER_MINT_reply_json_pack (connection,
295 MHD_HTTP_BAD_REQUEST,
296 "{s:s, s:o}",
297 "error", "missing index in JSON",
298 "path", path);
299 ignore = GNUNET_YES;
300 break;
301 }
302 }
303 break;
304 case JNAV_RET_DATA:
305 {
306 void *where = va_arg (argp, void *);
307 size_t len = va_arg (argp, size_t);
308 const char *str;
309 int res;
310
311 va_end(argp);
312 if (GNUNET_YES == ignore)
313 return GNUNET_NO;
314 str = json_string_value (root);
315 if (NULL == str)
316 {
317 /* FIXME: can't IGNORE this return value! */
318 (void) TALER_MINT_reply_json_pack (connection,
319 MHD_HTTP_BAD_REQUEST,
320 "{s:s, s:o}",
321 "error",
322 "string expected",
323 "path",
324 path);
325 return GNUNET_NO;
326 }
327 res = GNUNET_STRINGS_string_to_data (str, strlen (str),
328 where, len);
329 if (GNUNET_OK != res)
330 {
331 /* FIXME: can't IGNORE this return value! */
332 (void) TALER_MINT_reply_json_pack (connection,
333 MHD_HTTP_BAD_REQUEST,
334 "{s:s,s:o}",
335 "error",
336 "malformed binary data in JSON",
337 "path", path);
338 return GNUNET_NO;
339 }
340 return GNUNET_YES;
341 }
342 break;
343 case JNAV_RET_DATA_VAR:
344 {
345 void **where = va_arg (argp, void **);
346 size_t *len = va_arg (argp, size_t *);
347 const char *str;
348
349 va_end(argp);
350 if (GNUNET_YES == ignore)
351 return GNUNET_NO;
352 str = json_string_value (root);
353 if (NULL == str)
354 {
355 GNUNET_break (0);
356 return GNUNET_SYSERR;
357 }
358 *len = (strlen (str) * 5) / 8;
359 if (where != NULL)
360 {
361 int res;
362 *where = GNUNET_malloc (*len);
363 res = GNUNET_STRINGS_string_to_data (str, strlen (str),
364 *where, *len);
365 if (GNUNET_OK != res)
366 {
367 /* FIXME: can't IGNORE this return value! */
368 (void) TALER_MINT_reply_json_pack (connection,
369 MHD_HTTP_BAD_REQUEST,
370 "{s:s, s:o}",
371 "error",
372 "malformed binary data in JSON",
373 "path", path);
374 return GNUNET_NO;
375 }
376 }
377 return GNUNET_OK;
378 }
379 break;
380 case JNAV_RET_TYPED_JSON:
381 {
382 int typ = va_arg (argp, int);
383 const json_t **r_json = va_arg (argp, const json_t **);
384
385 va_end(argp);
386 if (GNUNET_YES == ignore)
387 return GNUNET_NO;
388 if (typ != -1 && json_typeof (root) != typ)
389 {
390 /* FIXME: can't IGNORE this return value! */
391 (void) TALER_MINT_reply_json_pack (connection,
392 MHD_HTTP_BAD_REQUEST,
393 "{s:s, s:i, s:i s:o}",
394 "error", "wrong JSON field type",
395 "type_expected", typ,
396 "type_actual", json_typeof (root),
397 "path", path);
398 return GNUNET_NO;
399 }
400 *r_json = root;
401 return GNUNET_OK;
402 }
403 break;
404 default:
405 GNUNET_assert (0);
406 }
407 }
408 GNUNET_assert (0);
409}
410
411
412/**
413 * Extract base32crockford encoded data from request.
414 *
415 * Queues an error response to the connection if the parameter is missing or
416 * invalid.
417 *
418 * @param connection the MHD connection
419 * @param param_name the name of the parameter with the key
420 * @param[out] out_data pointer to store the result
421 * @param out_size expected size of data
422 * @return
423 * GNUNET_YES if the the argument is present
424 * GNUNET_NO if the argument is absent or malformed
425 * GNUNET_SYSERR on internal error (error response could not be sent)
426 */
427int
428TALER_MINT_mhd_request_arg_data (struct MHD_Connection *connection,
429 const char *param_name,
430 void *out_data,
431 size_t out_size)
432{
433 const char *str;
434
435 str = MHD_lookup_connection_value (connection,
436 MHD_GET_ARGUMENT_KIND,
437 param_name);
438 if (NULL == str)
439 {
440 return (MHD_NO ==
441 TALER_MINT_reply_arg_missing (connection, param_name))
442 ? GNUNET_SYSERR : GNUNET_NO;
443 }
444 if (GNUNET_OK !=
445 GNUNET_STRINGS_string_to_data (str,
446 strlen (str),
447 out_data,
448 out_size))
449 return (MHD_NO ==
450 TALER_MINT_reply_arg_invalid (connection, param_name))
451 ? GNUNET_SYSERR : GNUNET_NO;
452 return GNUNET_OK;
453}