source: branches/2.7x/libtransmission/json.c @ 13918

Last change on this file since 13918 was 13918, checked in by jordan, 8 years ago

(2.7x) #5249 'error parsing json in locales that use , as a decimal separator': backport unit tests and fix from trunk.

  • Property svn:keywords set to Date Rev Author Id
File size: 8.6 KB
Line 
1/*
2 * This file Copyright (C) Mnemosyne LLC
3 *
4 * This file is licensed by the GPL version 2. Works owned by the
5 * Transmission project are granted a special exemption to clause 2 (b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
9 *
10 * $Id: json.c 13918 2013-02-01 18:45:23Z jordan $
11 */
12
13#include <assert.h>
14#include <ctype.h>
15#include <stdio.h>
16#include <string.h>
17#include <errno.h> /* EILSEQ, EINVAL */
18
19#include <event2/util.h> /* evutil_strtoll () */
20
21#define JSONSL_STATE_USER_FIELDS /* no fields */
22#include "jsonsl.h"
23#include "jsonsl.c"
24
25#include "transmission.h"
26#include "ConvertUTF.h"
27#include "bencode.h"
28#include "json.h"
29#include "ptrarray.h"
30#include "utils.h"
31
32/* arbitrary value... this is much deeper than our code goes */
33#define MAX_DEPTH 64
34
35struct json_wrapper_data
36{
37  int error;
38  bool has_content;
39  tr_benc * top;
40  char * key;
41  const char * source;
42  tr_ptrArray stack;
43};
44
45static tr_benc*
46get_node (struct jsonsl_st * jsn)
47{
48  tr_benc * parent;
49  tr_benc * node = NULL;
50  struct json_wrapper_data * data = jsn->data;
51
52  parent = tr_ptrArrayEmpty (&data->stack)
53         ? NULL
54         : tr_ptrArrayBack (&data->stack);
55
56  if (!parent)
57    {
58      node = data->top;
59    }
60  else if (tr_bencIsList (parent))
61    {
62      node = tr_bencListAdd (parent);
63    }
64  else if (tr_bencIsDict (parent) && (data->key!=NULL))
65    {
66      node = tr_bencDictAdd (parent, data->key);
67      tr_free (data->key);
68      data->key = NULL;
69    }
70
71  return node;
72}
73
74
75static void
76error_handler (jsonsl_t                  jsn,
77               jsonsl_error_t            error,
78               struct jsonsl_state_st  * state   UNUSED,
79               const jsonsl_char_t     * buf)
80{
81  struct json_wrapper_data * data = jsn->data;
82
83  if (data->source)
84    {
85      tr_err ("JSON parse failed in %s at pos %zu: %s -- remaining text \"%.16s\"",
86              data->source,
87              jsn->pos,
88              jsonsl_strerror (error),
89              buf);
90    }
91  else
92    {
93      tr_err ("JSON parse failed at pos %zu: %s -- remaining text \"%.16s\"",
94              jsn->pos,
95              jsonsl_strerror (error),
96              buf);
97    }
98
99  data->error = EILSEQ;
100}
101
102static int
103error_callback (jsonsl_t                  jsn,
104                jsonsl_error_t            error,
105                struct jsonsl_state_st  * state,
106                jsonsl_char_t           * at)
107{
108  error_handler (jsn, error, state, at);
109  return 0; /* bail */
110}
111
112static void
113action_callback_PUSH (jsonsl_t                  jsn,
114                      jsonsl_action_t           action  UNUSED,
115                      struct jsonsl_state_st  * state,
116                      const jsonsl_char_t     * buf     UNUSED)
117{
118  tr_benc * node;
119  struct json_wrapper_data * data = jsn->data;
120
121  switch (state->type)
122    {
123      case JSONSL_T_LIST:
124        data->has_content = true;
125        node = get_node (jsn);
126        tr_bencInitList (node, 0);
127        tr_ptrArrayAppend (&data->stack, node);
128        break;
129
130      case JSONSL_T_OBJECT:
131        data->has_content = true;
132        node = get_node (jsn);
133        tr_bencInitDict (node, 0);
134        tr_ptrArrayAppend (&data->stack, node);
135        break;
136
137      default:
138        /* nothing else interesting on push */
139        break;
140    }
141}
142
143static char*
144extract_string (jsonsl_t jsn, struct jsonsl_state_st * state, size_t * len)
145{
146  const char * in_begin;
147  const char * in_end;
148  const char * in_it;
149  size_t out_buflen;
150  char * out_buf;
151  char * out_it;
152
153  in_begin = jsn->base + state->pos_begin;
154  if (*in_begin == '"')
155    in_begin++;
156  in_end = jsn->base + state->pos_cur;
157
158  out_buflen = (in_end-in_begin)*3 + 1;
159  out_buf = tr_new0 (char, out_buflen);
160  out_it = out_buf;
161
162  for (in_it=in_begin; in_it!=in_end;)
163    {
164      bool unescaped = false;
165
166      if (*in_it=='\\' && in_end-in_it>=2)
167        {
168          switch (in_it[1])
169            {
170              case 'b' : *out_it++ = '\b'; in_it+=2; unescaped = true; break;
171              case 'f' : *out_it++ = '\f'; in_it+=2; unescaped = true; break;
172              case 'n' : *out_it++ = '\n'; in_it+=2; unescaped = true; break;
173              case 'r' : *out_it++ = '\r'; in_it+=2; unescaped = true; break;
174              case 't' : *out_it++ = '\t'; in_it+=2; unescaped = true; break;
175              case '/' : *out_it++ = '/' ; in_it+=2; unescaped = true; break;
176              case '"' : *out_it++ = '"' ; in_it+=2; unescaped = true; break;
177              case '\\': *out_it++ = '\\'; in_it+=2; unescaped = true; break;
178              case 'u':
179                {
180                  if (in_end - in_it >= 6)
181                    {
182                      unsigned int val = 0;
183                      if (sscanf (in_it+2, "%4x", &val) == 1)
184                        {
185                          UTF32 str32_buf[2] = { val, 0 };
186                          const UTF32 * str32_walk = str32_buf;
187                          const UTF32 * str32_end = str32_buf + 1;
188                          UTF8 str8_buf[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
189                          UTF8 * str8_walk = str8_buf;
190                          UTF8 * str8_end = str8_buf + 8;
191
192                          if (ConvertUTF32toUTF8 (&str32_walk, str32_end, &str8_walk, str8_end, 0) == 0)
193                            {
194                              const size_t len = str8_walk - str8_buf;
195                              memcpy (out_it, str8_buf, len);
196                              out_it += len;
197                              unescaped = true;
198                            }
199
200                          in_it += 6;
201                          break;
202                        }
203                    }
204                }
205            }
206        }
207
208      if (!unescaped)
209        *out_it++ = *in_it++;
210    }
211
212  if (len != NULL)
213    *len = out_it - out_buf;
214
215  return out_buf;
216}
217
218static void
219action_callback_POP (jsonsl_t                  jsn,
220                     jsonsl_action_t           action  UNUSED,
221                     struct jsonsl_state_st  * state,
222                     const jsonsl_char_t     * buf     UNUSED)
223{
224  struct json_wrapper_data * data = jsn->data;
225
226  if (state->type == JSONSL_T_STRING)
227    {
228      size_t len = 0;
229      char * str = extract_string (jsn, state, &len);
230      tr_bencInitStr (get_node (jsn), str, len);
231      data->has_content = true;
232      tr_free (str);
233    }
234  else if (state->type == JSONSL_T_HKEY)
235    {
236      char * str = extract_string (jsn, state, NULL);
237      data->has_content = true;
238      data->key = str;
239    }
240  else if ((state->type == JSONSL_T_LIST) || (state->type == JSONSL_T_OBJECT))
241    {
242      tr_ptrArrayPop (&data->stack);
243    }
244  else if (state->type == JSONSL_T_SPECIAL)
245    {
246      if (state->special_flags & JSONSL_SPECIALf_NUMNOINT)
247        {
248          const char * begin = jsn->base + state->pos_begin;
249          data->has_content = true;
250          tr_bencInitReal (get_node (jsn), strtod (begin, NULL));
251        }
252      else if (state->special_flags & JSONSL_SPECIALf_NUMERIC)
253        {
254          const char * begin = jsn->base + state->pos_begin;
255          data->has_content = true;
256          tr_bencInitInt (get_node (jsn), evutil_strtoll (begin, NULL, 10));
257        }
258      else if (state->special_flags & JSONSL_SPECIALf_BOOLEAN)
259        {
260          const bool b = (state->special_flags & JSONSL_SPECIALf_TRUE) != 0;
261          data->has_content = true;
262          tr_bencInitBool (get_node (jsn), b);
263        }
264      else if (state->special_flags & JSONSL_SPECIALf_NULL)
265        {
266          data->has_content = true;
267          tr_bencInitStr (get_node (jsn), "", 0);
268        }
269    }
270}
271
272int
273tr_jsonParse (const char     * source,
274              const void     * vbuf,
275              size_t           len,
276              tr_benc        * setme_benc,
277              const uint8_t ** setme_end)
278{
279  int error;
280  jsonsl_t jsn;
281  struct json_wrapper_data data;
282  char lc_numeric[128];
283
284  tr_strlcpy (lc_numeric, setlocale (LC_NUMERIC, NULL), sizeof (lc_numeric));
285  setlocale (LC_NUMERIC, "C");
286
287  jsn = jsonsl_new (MAX_DEPTH);
288  jsn->action_callback_PUSH = action_callback_PUSH;
289  jsn->action_callback_POP = action_callback_POP;
290  jsn->error_callback = error_callback;
291  jsn->data = &data;
292  jsonsl_enable_all_callbacks (jsn);
293
294  data.error = 0;
295  data.has_content = false;
296  data.key = NULL;
297  data.top = setme_benc;
298  data.stack = TR_PTR_ARRAY_INIT;
299  data.source = source;
300
301  /* parse it */
302  jsonsl_feed (jsn, vbuf, len);
303
304  /* EINVAL if there was no content */
305  if (!data.error && !data.has_content)
306    data.error = EINVAL;
307
308  /* maybe set the end ptr */
309  if (setme_end)
310    *setme_end = ((const uint8_t*)vbuf) + jsn->pos;
311
312  /* cleanup */
313  error = data.error;
314  tr_ptrArrayDestruct (&data.stack, NULL);
315  jsonsl_destroy (jsn);
316  setlocale (LC_NUMERIC, lc_numeric);
317  return error;
318}
Note: See TracBrowser for help on using the repository browser.