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

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

(2.7x) add '#include locale.h' to json.c to ensure setlocale() is declared

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