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