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