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