glitch in the license text detected by hyazinthe, thank you!
[oweals/gnunet.git] / src / json / json_mhd.c
1 /*
2   This file is part of GNUnet
3   Copyright (C) 2014, 2015, 2016 GNUnet e.V.
4
5   GNUnet is free software: you can redistribute it and/or modify it
6   under the terms of the GNU Affero General Public License as published
7   by the Free Software Foundation, either version 3 of the License,
8   or (at your option) any later version.
9
10   GNUnet 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 GNU
13   Affero General Public License for more details.
14 */
15 /**
16  * @file json/json_mhd.c
17  * @brief functions to parse JSON snippets we receive via MHD
18  * @author Florian Dold
19  * @author Benedikt Mueller
20  * @author Christian Grothoff
21  */
22 #include "platform.h"
23 #include "gnunet_json_lib.h"
24
25 /**
26  * Initial size for POST request buffers.  Should be big enough to
27  * usually not require a reallocation, but not so big that it hurts in
28  * terms of memory use.
29  */
30 #define REQUEST_BUFFER_INITIAL (2*1024)
31
32
33 /**
34  * Buffer for POST requests.
35  */
36 struct Buffer
37 {
38   /**
39    * Allocated memory
40    */
41   char *data;
42
43   /**
44    * Number of valid bytes in buffer.
45    */
46   size_t fill;
47
48   /**
49    * Number of allocated bytes in buffer.
50    */
51   size_t alloc;
52 };
53
54
55 /**
56  * Initialize a buffer.
57  *
58  * @param buf the buffer to initialize
59  * @param data the initial data
60  * @param data_size size of the initial data
61  * @param alloc_size size of the buffer
62  * @param max_size maximum size that the buffer can grow to
63  * @return a GNUnet result code
64  */
65 static int
66 buffer_init (struct Buffer *buf,
67              const void *data,
68              size_t data_size,
69              size_t alloc_size,
70              size_t max_size)
71 {
72   if ( (data_size > max_size) ||
73        (alloc_size > max_size) )
74     return GNUNET_SYSERR;
75   if (data_size > alloc_size)
76     alloc_size = data_size;
77   buf->data = GNUNET_malloc (alloc_size);
78   GNUNET_memcpy (buf->data, data, data_size);
79   return GNUNET_OK;
80 }
81
82
83 /**
84  * Free the data in a buffer.  Does *not* free
85  * the buffer object itself.
86  *
87  * @param buf buffer to de-initialize
88  */
89 static void
90 buffer_deinit (struct Buffer *buf)
91 {
92   GNUNET_free (buf->data);
93   buf->data = NULL;
94 }
95
96
97 /**
98  * Append data to a buffer, growing the buffer if necessary.
99  *
100  * @param buf the buffer to append to
101  * @param data the data to append
102  * @param data_size the size of @a data
103  * @param max_size maximum size that the buffer can grow to
104  * @return #GNUNET_OK on success,
105  *         #GNUNET_NO if the buffer can't accomodate for the new data
106  */
107 static int
108 buffer_append (struct Buffer *buf,
109                const void *data,
110                size_t data_size,
111                size_t max_size)
112 {
113   if (buf->fill + data_size > max_size)
114     return GNUNET_NO;
115   if (data_size + buf->fill > buf->alloc)
116   {
117     char *new_buf;
118     size_t new_size = buf->alloc;
119     while (new_size < buf->fill + data_size)
120       new_size += 2;
121     if (new_size > max_size)
122       return GNUNET_NO;
123     new_buf = GNUNET_malloc (new_size);
124     GNUNET_memcpy (new_buf, buf->data, buf->fill);
125     GNUNET_free (buf->data);
126     buf->data = new_buf;
127     buf->alloc = new_size;
128   }
129   GNUNET_memcpy (buf->data + buf->fill, data, data_size);
130   buf->fill += data_size;
131   return GNUNET_OK;
132 }
133
134
135 /**
136  * Process a POST request containing a JSON object.  This function
137  * realizes an MHD POST processor that will (incrementally) process
138  * JSON data uploaded to the HTTP server.  It will store the required
139  * state in the @a con_cls, which must be cleaned up using
140  * #GNUNET_JSON_post_parser_callback().
141  *
142  * @param buffer_max maximum allowed size for the buffer
143  * @param con_cls the closure (will point to a `struct Buffer *`)
144  * @param upload_data the POST data
145  * @param upload_data_size number of bytes in @a upload_data
146  * @param json the JSON object for a completed request
147  * @return result code indicating the status of the operation
148  */
149 enum GNUNET_JSON_PostResult
150 GNUNET_JSON_post_parser (size_t buffer_max,
151                          void **con_cls,
152                          const char *upload_data,
153                          size_t *upload_data_size,
154                          json_t **json)
155 {
156   struct Buffer *r = *con_cls;
157
158   *json = NULL;
159   if (NULL == *con_cls)
160   {
161     /* We are seeing a fresh POST request. */
162     r = GNUNET_new (struct Buffer);
163     if (GNUNET_OK !=
164         buffer_init (r,
165                      upload_data,
166                      *upload_data_size,
167                      REQUEST_BUFFER_INITIAL,
168                      buffer_max))
169     {
170       *con_cls = NULL;
171       buffer_deinit (r);
172       GNUNET_free (r);
173       return GNUNET_JSON_PR_OUT_OF_MEMORY;
174     }
175     /* everything OK, wait for more POST data */
176     *upload_data_size = 0;
177     *con_cls = r;
178     return GNUNET_JSON_PR_CONTINUE;
179   }
180   if (0 != *upload_data_size)
181   {
182     /* We are seeing an old request with more data available. */
183
184     if (GNUNET_OK !=
185         buffer_append (r,
186                        upload_data,
187                        *upload_data_size,
188                        buffer_max))
189     {
190       /* Request too long */
191       *con_cls = NULL;
192       buffer_deinit (r);
193       GNUNET_free (r);
194       return GNUNET_JSON_PR_REQUEST_TOO_LARGE;
195     }
196     /* everything OK, wait for more POST data */
197     *upload_data_size = 0;
198     return GNUNET_JSON_PR_CONTINUE;
199   }
200
201   /* We have seen the whole request. */
202
203   *json = json_loadb (r->data,
204                       r->fill,
205                       0,
206                       NULL);
207   if (NULL == *json)
208   {
209     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
210                 "Failed to parse JSON request body\n");
211     return GNUNET_JSON_PR_JSON_INVALID;
212   }
213   buffer_deinit (r);
214   GNUNET_free (r);
215   *con_cls = NULL;
216
217   return GNUNET_JSON_PR_SUCCESS;
218 }
219
220
221 /**
222  * Function called whenever we are done with a request
223  * to clean up our state.
224  *
225  * @param con_cls value as it was left by
226  *        #GNUNET_JSON_post_parser(), to be cleaned up
227  */
228 void
229 GNUNET_JSON_post_parser_cleanup (void *con_cls)
230 {
231   struct Buffer *r = con_cls;
232
233   if (NULL != r)
234   {
235     buffer_deinit (r);
236     GNUNET_free (r);
237   }
238 }
239
240 /* end of mhd_json.c */