source: trunk/libtransmission/bencode.c @ 8159

Last change on this file since 8159 was 8159, checked in by charles, 13 years ago

better bool, real handling in (1) the gtk prefs interface, (2) the dictionary merge code, (2) the json parser

  • Property svn:keywords set to Date Rev Author Id
File size: 39.8 KB
Line 
1/*
2 * This file Copyright (C) 2008-2009 Charles Kerr <charles@transmissionbt.com>
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: bencode.c 8159 2009-04-05 23:46:53Z charles $
11 */
12
13#include <assert.h>
14#include <ctype.h> /* isdigit, isprint, isspace */
15#include <errno.h>
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19
20#include <locale.h>
21
22#include <event.h> /* evbuffer */
23
24#include "ConvertUTF.h"
25
26#include "transmission.h"
27#include "bencode.h"
28#include "json.h"
29#include "list.h"
30#include "ptrarray.h"
31#include "utils.h" /* tr_new(), tr_free() */
32
33#ifndef ENODATA
34 #define ENODATA EIO
35#endif
36
37/**
38***
39**/
40
41static tr_bool
42isContainer( const tr_benc * val )
43{
44    return tr_bencIsList( val ) || tr_bencIsDict( val );
45}
46
47static tr_bool
48isSomething( const tr_benc * val )
49{
50    return isContainer( val ) || tr_bencIsInt( val )
51                              || tr_bencIsString( val )
52                              || tr_bencIsReal( val )
53                              || tr_bencIsBool( val );
54}
55
56static void
57tr_bencInit( tr_benc * val,
58             int       type )
59{
60    memset( val, 0, sizeof( *val ) );
61    val->type = type;
62}
63
64/***
65****  tr_bencParse()
66****  tr_bencLoad()
67***/
68
69/**
70 * The initial i and trailing e are beginning and ending delimiters.
71 * You can have negative numbers such as i-3e. You cannot prefix the
72 * number with a zero such as i04e. However, i0e is valid.
73 * Example: i3e represents the integer "3"
74 * NOTE: The maximum number of bit of this integer is unspecified,
75 * but to handle it as a signed 64bit integer is mandatory to handle
76 * "large files" aka .torrent for more that 4Gbyte
77 */
78int
79tr_bencParseInt( const uint8_t *  buf,
80                 const uint8_t *  bufend,
81                 const uint8_t ** setme_end,
82                 int64_t *        setme_val )
83{
84    int          err = 0;
85    char *       endptr;
86    const void * begin;
87    const void * end;
88    int64_t      val;
89
90    if( buf >= bufend )
91        return EILSEQ;
92    if( *buf != 'i' )
93        return EILSEQ;
94
95    begin = buf + 1;
96    end = memchr( begin, 'e', ( bufend - buf ) - 1 );
97    if( end == NULL )
98        return EILSEQ;
99
100    errno = 0;
101    val = evutil_strtoll( begin, &endptr, 10 );
102    if( errno || ( endptr != end ) ) /* incomplete parse */
103        err = EILSEQ;
104    else if( val && *(const char*)begin == '0' ) /* no leading zeroes! */
105        err = EILSEQ;
106    else
107    {
108        *setme_end = (const uint8_t*)end + 1;
109        *setme_val = val;
110    }
111
112    return err;
113}
114
115/**
116 * Byte strings are encoded as follows:
117 * <string length encoded in base ten ASCII>:<string data>
118 * Note that there is no constant beginning delimiter, and no ending delimiter.
119 * Example: 4:spam represents the string "spam"
120 */
121int
122tr_bencParseStr( const uint8_t *  buf,
123                 const uint8_t *  bufend,
124                 const uint8_t ** setme_end,
125                 const uint8_t ** setme_str,
126                 size_t *         setme_strlen )
127{
128    size_t       len;
129    const void * end;
130    char *       endptr;
131
132    if( buf >= bufend )
133        return EILSEQ;
134
135    if( !isdigit( *buf  ) )
136        return EILSEQ;
137
138    end = memchr( buf, ':', bufend - buf );
139    if( end == NULL )
140        return EILSEQ;
141
142    errno = 0;
143    len = strtoul( (const char*)buf, &endptr, 10 );
144    if( errno || endptr != end )
145        return EILSEQ;
146
147    if( (const uint8_t*)end + 1 + len > bufend )
148        return EILSEQ;
149
150    *setme_end = (const uint8_t*)end + 1 + len;
151    *setme_str = (const uint8_t*)end + 1;
152    *setme_strlen = len;
153    return 0;
154}
155
156/* set to 1 to help expose bugs with tr_bencListAdd and tr_bencDictAdd */
157#define LIST_SIZE 8 /* number of items to increment list/dict buffer by */
158
159static int
160makeroom( tr_benc * val,
161          size_t    count )
162{
163    assert( TR_TYPE_LIST == val->type || TR_TYPE_DICT == val->type );
164
165    if( val->val.l.count + count > val->val.l.alloc )
166    {
167        /* We need a bigger boat */
168        const int len = val->val.l.alloc + count +
169                        ( count % LIST_SIZE ? LIST_SIZE -
170                          ( count % LIST_SIZE ) : 0 );
171        void * tmp = realloc( val->val.l.vals, len * sizeof( tr_benc ) );
172        if( !tmp )
173            return 1;
174
175        val->val.l.alloc = len;
176        val->val.l.vals  = tmp;
177    }
178
179    return 0;
180}
181
182static tr_benc*
183getNode( tr_benc *     top,
184         tr_ptrArray * parentStack,
185         int           type )
186{
187    tr_benc * parent;
188
189    assert( top );
190    assert( parentStack );
191
192    if( tr_ptrArrayEmpty( parentStack ) )
193        return top;
194
195    parent = tr_ptrArrayBack( parentStack );
196    assert( parent );
197
198    /* dictionary keys must be strings */
199    if( ( parent->type == TR_TYPE_DICT )
200      && ( type != TR_TYPE_STR )
201      && ( !( parent->val.l.count % 2 ) ) )
202        return NULL;
203
204    makeroom( parent, 1 );
205    return parent->val.l.vals + parent->val.l.count++;
206}
207
208/**
209 * This function's previous recursive implementation was
210 * easier to read, but was vulnerable to a smash-stacking
211 * attack via maliciously-crafted bencoded data. (#667)
212 */
213static int
214tr_bencParseImpl( const void *     buf_in,
215                  const void *     bufend_in,
216                  tr_benc *        top,
217                  tr_ptrArray *    parentStack,
218                  const uint8_t ** setme_end )
219{
220    int             err;
221    const uint8_t * buf = buf_in;
222    const uint8_t * bufend = bufend_in;
223
224    tr_bencInit( top, 0 );
225
226    while( buf != bufend )
227    {
228        if( buf > bufend ) /* no more text to parse... */
229            return 1;
230
231        if( *buf == 'i' ) /* int */
232        {
233            int64_t         val;
234            const uint8_t * end;
235            tr_benc *       node;
236
237            if( ( err = tr_bencParseInt( buf, bufend, &end, &val ) ) )
238                return err;
239
240            node = getNode( top, parentStack, TR_TYPE_INT );
241            if( !node )
242                return EILSEQ;
243
244            tr_bencInitInt( node, val );
245            buf = end;
246
247            if( tr_ptrArrayEmpty( parentStack ) )
248                break;
249        }
250        else if( *buf == 'l' ) /* list */
251        {
252            tr_benc * node = getNode( top, parentStack, TR_TYPE_LIST );
253            if( !node )
254                return EILSEQ;
255            tr_bencInit( node, TR_TYPE_LIST );
256            tr_ptrArrayAppend( parentStack, node );
257            ++buf;
258        }
259        else if( *buf == 'd' ) /* dict */
260        {
261            tr_benc * node = getNode( top, parentStack, TR_TYPE_DICT );
262            if( !node )
263                return EILSEQ;
264            tr_bencInit( node, TR_TYPE_DICT );
265            tr_ptrArrayAppend( parentStack, node );
266            ++buf;
267        }
268        else if( *buf == 'e' ) /* end of list or dict */
269        {
270            tr_benc * node;
271            ++buf;
272            if( tr_ptrArrayEmpty( parentStack ) )
273                return EILSEQ;
274
275            node = tr_ptrArrayBack( parentStack );
276            if( tr_bencIsDict( node ) && ( node->val.l.count % 2 ) )
277            {
278                /* odd # of children in dict */
279                tr_bencFree( &node->val.l.vals[--node->val.l.count] );
280                return EILSEQ;
281            }
282
283            tr_ptrArrayPop( parentStack );
284            if( tr_ptrArrayEmpty( parentStack ) )
285                break;
286        }
287        else if( isdigit( *buf ) ) /* string? */
288        {
289            const uint8_t * end;
290            const uint8_t * str;
291            size_t          str_len;
292            tr_benc *       node;
293
294            if( ( err = tr_bencParseStr( buf, bufend, &end, &str, &str_len ) ) )
295                return err;
296
297            node = getNode( top, parentStack, TR_TYPE_STR );
298            if( !node )
299                return EILSEQ;
300
301            tr_bencInitStr( node, str, str_len );
302            buf = end;
303
304            if( tr_ptrArrayEmpty( parentStack ) )
305                break;
306        }
307        else /* invalid bencoded text... march past it */
308        {
309            ++buf;
310        }
311    }
312
313    err = !isSomething( top ) || !tr_ptrArrayEmpty( parentStack );
314
315    if( !err && setme_end )
316        *setme_end = buf;
317
318    return err;
319}
320
321int
322tr_bencParse( const void *     buf,
323              const void *     end,
324              tr_benc *        top,
325              const uint8_t ** setme_end )
326{
327    int           err;
328    tr_ptrArray   parentStack = TR_PTR_ARRAY_INIT;
329
330    top->type = 0; /* set to `uninitialized' */
331    err = tr_bencParseImpl( buf, end, top, &parentStack, setme_end );
332    if( err )
333        tr_bencFree( top );
334
335    tr_ptrArrayDestruct( &parentStack, NULL );
336    return err;
337}
338
339int
340tr_bencLoad( const void * buf_in,
341             size_t       buflen,
342             tr_benc *    setme_benc,
343             char **      setme_end )
344{
345    const uint8_t * buf = buf_in;
346    const uint8_t * end;
347    const int       ret = tr_bencParse( buf, buf + buflen, setme_benc, &end );
348
349    if( !ret && setme_end )
350        *setme_end = (char*) end;
351    return ret;
352}
353
354/***
355****
356***/
357
358static int
359dictIndexOf( const tr_benc * val,
360             const char *    key )
361{
362    if( tr_bencIsDict( val ) )
363    {
364        size_t       i;
365        const size_t len = strlen( key );
366
367        for( i = 0; ( i + 1 ) < val->val.l.count; i += 2 )
368        {
369            const tr_benc * child = val->val.l.vals + i;
370
371            if( ( child->type == TR_TYPE_STR )
372              && ( child->val.s.i == len )
373              && !memcmp( child->val.s.s, key, len ) )
374                return i;
375        }
376    }
377
378    return -1;
379}
380
381tr_benc *
382tr_bencDictFind( tr_benc * val, const char * key )
383{
384    const int i = dictIndexOf( val, key );
385
386    return i < 0 ? NULL : &val->val.l.vals[i + 1];
387}
388
389static tr_benc*
390tr_bencDictFindType( tr_benc *    val,
391                     const char * key,
392                     int          type )
393{
394    tr_benc * ret = tr_bencDictFind( val, key );
395
396    return ( ret && ( ret->type == type ) ) ? ret : NULL;
397}
398
399size_t
400tr_bencListSize( const tr_benc * list )
401{
402    return tr_bencIsList( list ) ? list->val.l.count : 0;
403}
404
405tr_benc*
406tr_bencListChild( tr_benc * val,
407                  size_t    i )
408{
409    tr_benc * ret = NULL;
410
411    if( tr_bencIsList( val ) && ( i < val->val.l.count ) )
412        ret = val->val.l.vals + i;
413    return ret;
414}
415
416static void
417tr_benc_warning( const char * err )
418{
419    fprintf( stderr, "warning: %s\n", err );
420}
421
422tr_bool
423tr_bencGetInt( const tr_benc * val,
424               int64_t *       setme )
425{
426    tr_bool success = FALSE;
427
428    if( !success && (( success = tr_bencIsInt( val ))))
429        if( setme )
430            *setme = val->val.i;
431
432    if( !success && (( success = tr_bencIsBool( val )))) {
433        tr_benc_warning( "reading bool as an int" );
434        if( setme )
435            *setme = val->val.b ? 1 : 0;
436    }
437
438    return success;
439}
440
441tr_bool
442tr_bencGetStr( const tr_benc * val,
443               const char **   setme )
444{
445    const int success = tr_bencIsString( val );
446
447    if( success )
448        *setme = val->val.s.s;
449
450    return success;
451}
452
453tr_bool
454tr_bencGetBool( const tr_benc * val, tr_bool * setme )
455{
456    tr_bool success = FALSE;
457
458    if(( success = tr_bencIsBool( val )))
459        *setme = val->val.b;
460
461    if( !success && tr_bencIsInt( val ) ) {
462        if(( success = ( val->val.i==0 || val->val.i==1 ) )) {
463            tr_benc_warning( "warning: reading bool as an int\n" );
464            *setme = val->val.i!=0;
465        }
466    }
467
468    if( !success && tr_bencIsString( val ) )
469        if(( success = ( !strcmp(val->val.s.s,"true") || !strcmp(val->val.s.s,"false"))))
470            *setme = !strcmp(val->val.s.s,"true");
471
472    return success;
473}
474
475tr_bool
476tr_bencGetReal( const tr_benc * val, double * setme )
477{
478    tr_bool success = FALSE;
479
480    if( !success && (( success = tr_bencIsReal( val ))))
481        *setme = val->val.d;
482
483    if( !success && (( success = tr_bencIsInt( val ))))
484        *setme = val->val.i;
485
486    if( !success && tr_bencIsString(val) )
487    {
488        char * endptr;
489        char * locale; 
490        double d;
491
492        /* the json spec requires a '.' decimal point regardless of locale */
493        locale = tr_strdup( setlocale ( LC_NUMERIC, NULL ) );
494        setlocale( LC_NUMERIC, "POSIX" );
495        d  = strtod( val->val.s.s, &endptr );
496        setlocale( LC_NUMERIC, locale );
497        tr_free( locale );
498
499        if(( success = ( val->val.s.s != endptr ) && !*endptr ))
500            *setme = d;
501    }
502
503
504    return success;
505}
506
507tr_bool
508tr_bencDictFindInt( tr_benc * dict, const char * key, int64_t * setme )
509{
510    return tr_bencGetInt( tr_bencDictFind( dict, key ), setme );
511}
512
513tr_bool
514tr_bencDictFindBool( tr_benc * dict, const char * key, tr_bool * setme )
515{
516    return tr_bencGetBool( tr_bencDictFind( dict, key ), setme );
517}
518
519tr_bool
520tr_bencDictFindReal( tr_benc * dict, const char * key, double * setme )
521{
522    return tr_bencGetReal( tr_bencDictFind( dict, key ), setme );
523}
524
525tr_bool
526tr_bencDictFindList( tr_benc * dict, const char * key, tr_benc ** setme )
527{
528    tr_bool found = FALSE;
529    tr_benc * child = tr_bencDictFindType( dict, key, TR_TYPE_LIST );
530
531    if( child )
532    {
533        if( setme != NULL )
534            *setme = child;
535        found = TRUE;
536    }
537
538    return found;
539}
540
541tr_bool
542tr_bencDictFindDict( tr_benc * dict, const char * key, tr_benc ** setme )
543{
544    tr_bool found = FALSE;
545    tr_benc * child = tr_bencDictFindType( dict, key, TR_TYPE_DICT );
546
547    if( child )
548    {
549        if( setme != NULL )
550            *setme = child;
551        found = TRUE;
552    }
553
554    return found;
555}
556
557tr_bool
558tr_bencDictFindStr( tr_benc *  dict, const char *  key, const char ** setme )
559{
560    tr_bool found = FALSE;
561    tr_benc * child = tr_bencDictFindType( dict, key, TR_TYPE_STR );
562
563    if( child )
564    {
565        if( setme )
566            *setme = child->val.s.s;
567        found = TRUE;
568    }
569
570    return found;
571}
572
573tr_bool
574tr_bencDictFindRaw( tr_benc         * dict,
575                    const char      * key,
576                    const uint8_t  ** setme_raw,
577                    size_t          * setme_len )
578{
579    tr_bool found = FALSE;
580    tr_benc * child = tr_bencDictFindType( dict, key, TR_TYPE_STR );
581
582    if( child )
583    {
584        *setme_raw = (uint8_t*) child->val.s.s;
585        *setme_len = child->val.s.i;
586        found = TRUE;
587    }
588
589    return found;
590}
591
592/***
593****
594***/
595
596void
597tr_bencInitRaw( tr_benc *    val,
598                const void * src,
599                size_t       byteCount )
600{
601    tr_bencInit( val, TR_TYPE_STR );
602    val->val.s.i = byteCount;
603    val->val.s.s = tr_memdup( src, byteCount );
604}
605
606void
607tr_bencInitStr( tr_benc *    val,
608                const void * str,
609                int          len )
610{
611    tr_bencInit( val, TR_TYPE_STR );
612
613    val->val.s.s = tr_strndup( str, len );
614
615    if( val->val.s.s == NULL )
616        val->val.s.i = 0;
617    else if( len < 0 )
618        val->val.s.i = strlen( val->val.s.s );
619    else
620        val->val.s.i = len;
621}
622
623void
624tr_bencInitBool( tr_benc * b, int value )
625{
626    tr_bencInit( b, TR_TYPE_BOOL );
627    b->val.b = value != 0;
628}
629
630void
631tr_bencInitReal( tr_benc * b, double value )
632{
633    tr_bencInit( b, TR_TYPE_REAL );
634    b->val.d = value;
635}
636
637void
638tr_bencInitInt( tr_benc * b, int64_t value )
639{
640    tr_bencInit( b, TR_TYPE_INT );
641    b->val.i = value;
642}
643
644int
645tr_bencInitList( tr_benc * b, size_t reserveCount )
646{
647    tr_bencInit( b, TR_TYPE_LIST );
648    return tr_bencListReserve( b, reserveCount );
649}
650
651int
652tr_bencListReserve( tr_benc * b, size_t count )
653{
654    assert( tr_bencIsList( b ) );
655    return makeroom( b, count );
656}
657
658int
659tr_bencInitDict( tr_benc * b, size_t reserveCount )
660{
661    tr_bencInit( b, TR_TYPE_DICT );
662    return tr_bencDictReserve( b, reserveCount );
663}
664
665int
666tr_bencDictReserve( tr_benc * b, size_t reserveCount )
667{
668    assert( tr_bencIsDict( b ) );
669    return makeroom( b, reserveCount * 2 );
670}
671
672tr_benc *
673tr_bencListAdd( tr_benc * list )
674{
675    tr_benc * item;
676
677    assert( tr_bencIsList( list ) );
678
679    if( list->val.l.count == list->val.l.alloc )
680        tr_bencListReserve( list, LIST_SIZE );
681
682    assert( list->val.l.count < list->val.l.alloc );
683
684    item = &list->val.l.vals[list->val.l.count];
685    list->val.l.count++;
686    tr_bencInit( item, TR_TYPE_INT );
687
688    return item;
689}
690
691tr_benc *
692tr_bencListAddInt( tr_benc * list,
693                   int64_t   val )
694{
695    tr_benc * node = tr_bencListAdd( list );
696
697    tr_bencInitInt( node, val );
698    return node;
699}
700
701tr_benc *
702tr_bencListAddStr( tr_benc *    list,
703                   const char * val )
704{
705    tr_benc * node = tr_bencListAdd( list );
706
707    tr_bencInitStr( node, val, -1 );
708    return node;
709}
710
711tr_benc*
712tr_bencListAddList( tr_benc * list,
713                    size_t    reserveCount )
714{
715    tr_benc * child = tr_bencListAdd( list );
716
717    tr_bencInitList( child, reserveCount );
718    return child;
719}
720
721tr_benc*
722tr_bencListAddDict( tr_benc * list,
723                    size_t    reserveCount )
724{
725    tr_benc * child = tr_bencListAdd( list );
726
727    tr_bencInitDict( child, reserveCount );
728    return child;
729}
730
731tr_benc *
732tr_bencDictAdd( tr_benc *    dict,
733                const char * key )
734{
735    tr_benc * keyval, * itemval;
736
737    assert( tr_bencIsDict( dict ) );
738    if( dict->val.l.count + 2 > dict->val.l.alloc )
739        makeroom( dict, 2 );
740    assert( dict->val.l.count + 2 <= dict->val.l.alloc );
741
742    keyval = dict->val.l.vals + dict->val.l.count++;
743    tr_bencInitStr( keyval, key, -1 );
744
745    itemval = dict->val.l.vals + dict->val.l.count++;
746    tr_bencInit( itemval, TR_TYPE_INT );
747
748    return itemval;
749}
750
751static tr_benc*
752dictFindOrAdd( tr_benc * dict, const char * key, int type )
753{
754    tr_benc * child;
755
756    /* see if it already exists, and if so, try to reuse it */
757    if(( child = tr_bencDictFind( dict, key ))) {
758        if( !tr_bencIsType( child, type ) ) {
759            tr_bencDictRemove( dict, key );
760            child = NULL;
761        }
762    }
763
764    /* if it doesn't exist, create it */
765    if( child == NULL )
766        child = tr_bencDictAdd( dict, key );
767
768    return child;
769}
770
771tr_benc*
772tr_bencDictAddInt( tr_benc *    dict,
773                   const char * key,
774                   int64_t      val )
775{
776    tr_benc * child = dictFindOrAdd( dict, key, TR_TYPE_INT );
777    tr_bencInitInt( child, val );
778    return child;
779}
780
781tr_benc*
782tr_bencDictAddBool( tr_benc * dict, const char * key, tr_bool val )
783{
784    tr_benc * child = dictFindOrAdd( dict, key, TR_TYPE_BOOL );
785    tr_bencInitBool( child, val );
786    return child;
787}
788
789tr_benc*
790tr_bencDictAddReal( tr_benc * dict, const char * key, double val )
791{
792    tr_benc * child = dictFindOrAdd( dict, key, TR_TYPE_REAL );
793    tr_bencInitReal( child, val );
794    return child;
795}
796
797tr_benc*
798tr_bencDictAddStr( tr_benc * dict, const char * key, const char * val )
799{
800    tr_benc * child;
801
802    /* see if it already exists, and if so, try to reuse it */
803    if(( child = tr_bencDictFind( dict, key ))) {
804        if( tr_bencIsString( child ) )
805            tr_free( child->val.s.s );
806        else {
807            tr_bencDictRemove( dict, key );
808            child = NULL;
809        }
810    }
811
812    /* if it doesn't exist, create it */
813    if( child == NULL )
814        child = tr_bencDictAdd( dict, key );
815
816    /* set it */
817    tr_bencInitStr( child, val, -1 );
818
819    return child;
820}
821
822#if 0
823tr_benc*
824tr_bencDictAddReal( tr_benc * dict, const char * key, double d )
825{
826    ccc
827    char buf[128];
828    char * locale;
829
830    /* the json spec requires a '.' decimal point regardless of locale */
831    locale = tr_strdup( setlocale ( LC_NUMERIC, NULL ) );
832    setlocale( LC_NUMERIC, "POSIX" );
833    tr_snprintf( buf, sizeof( buf ), "%f", d );
834    setlocale( LC_NUMERIC, locale );
835    tr_free( locale );
836
837    return tr_bencDictAddStr( dict, key, buf );
838}
839#endif
840
841tr_benc*
842tr_bencDictAddList( tr_benc *    dict,
843                    const char * key,
844                    size_t       reserveCount )
845{
846    tr_benc * child = tr_bencDictAdd( dict, key );
847
848    tr_bencInitList( child, reserveCount );
849    return child;
850}
851
852tr_benc*
853tr_bencDictAddDict( tr_benc *    dict,
854                    const char * key,
855                    size_t       reserveCount )
856{
857    tr_benc * child = tr_bencDictAdd( dict, key );
858
859    tr_bencInitDict( child, reserveCount );
860    return child;
861}
862
863tr_benc*
864tr_bencDictAddRaw( tr_benc *    dict,
865                   const char * key,
866                   const void * src,
867                   size_t       len )
868{
869    tr_benc * child = tr_bencDictAdd( dict, key );
870
871    tr_bencInitRaw( child, src, len );
872    return child;
873}
874
875int
876tr_bencDictRemove( tr_benc *    dict,
877                   const char * key )
878{
879    int i = dictIndexOf( dict, key );
880
881    if( i >= 0 )
882    {
883        const int n = dict->val.l.count;
884        tr_bencFree( &dict->val.l.vals[i] );
885        tr_bencFree( &dict->val.l.vals[i + 1] );
886        if( i + 2 < n )
887        {
888            dict->val.l.vals[i]   = dict->val.l.vals[n - 2];
889            dict->val.l.vals[i + 1] = dict->val.l.vals[n - 1];
890        }
891        dict->val.l.count -= 2;
892    }
893    return i >= 0; /* return true if found */
894}
895
896/***
897****  BENC WALKING
898***/
899
900struct KeyIndex
901{
902    const char *  key;
903    int           index;
904};
905
906static int
907compareKeyIndex( const void * va,
908                 const void * vb )
909{
910    const struct KeyIndex * a = va;
911    const struct KeyIndex * b = vb;
912
913    return strcmp( a->key, b->key );
914}
915
916struct SaveNode
917{
918    const tr_benc *  val;
919    int              valIsVisited;
920    int              childCount;
921    int              childIndex;
922    int *            children;
923};
924
925static struct SaveNode*
926nodeNewDict( const tr_benc * val )
927{
928    int               i, j;
929    int               nKeys;
930    struct SaveNode * node;
931    struct KeyIndex * indices;
932
933    assert( tr_bencIsDict( val ) );
934
935    nKeys = val->val.l.count / 2;
936    node = tr_new0( struct SaveNode, 1 );
937    node->val = val;
938    node->children = tr_new0( int, nKeys * 2 );
939
940    /* ugh, a dictionary's children have to be sorted by key... */
941    indices = tr_new( struct KeyIndex, nKeys );
942    for( i = j = 0; i < ( nKeys * 2 ); i += 2, ++j )
943    {
944        indices[j].key = val->val.l.vals[i].val.s.s;
945        indices[j].index = i;
946    }
947    qsort( indices, j, sizeof( struct KeyIndex ), compareKeyIndex );
948    for( i = 0; i < j; ++i )
949    {
950        const int index = indices[i].index;
951        node->children[node->childCount++] = index;
952        node->children[node->childCount++] = index + 1;
953    }
954
955    assert( node->childCount == nKeys * 2 );
956    tr_free( indices );
957    return node;
958}
959
960static struct SaveNode*
961nodeNewList( const tr_benc * val )
962{
963    int               i, n;
964    struct SaveNode * node;
965
966    assert( tr_bencIsList( val ) );
967
968    n = val->val.l.count;
969    node = tr_new0( struct SaveNode, 1 );
970    node->val = val;
971    node->childCount = n;
972    node->children = tr_new0( int, n );
973    for( i = 0; i < n; ++i ) /* a list's children don't need to be reordered */
974        node->children[i] = i;
975
976    return node;
977}
978
979static struct SaveNode*
980nodeNewLeaf( const tr_benc * val )
981{
982    struct SaveNode * node;
983
984    assert( !isContainer( val ) );
985
986    node = tr_new0( struct SaveNode, 1 );
987    node->val = val;
988    return node;
989}
990
991static struct SaveNode*
992nodeNew( const tr_benc * val )
993{
994    struct SaveNode * node;
995
996    if( tr_bencIsList( val ) )
997        node = nodeNewList( val );
998    else if( tr_bencIsDict( val ) )
999        node = nodeNewDict( val );
1000    else
1001        node = nodeNewLeaf( val );
1002
1003    return node;
1004}
1005
1006typedef void ( *BencWalkFunc )( const tr_benc * val, void * user_data );
1007
1008struct WalkFuncs
1009{
1010    BencWalkFunc    intFunc;
1011    BencWalkFunc    boolFunc;
1012    BencWalkFunc    realFunc;
1013    BencWalkFunc    stringFunc;
1014    BencWalkFunc    dictBeginFunc;
1015    BencWalkFunc    listBeginFunc;
1016    BencWalkFunc    containerEndFunc;
1017};
1018
1019/**
1020 * This function's previous recursive implementation was
1021 * easier to read, but was vulnerable to a smash-stacking
1022 * attack via maliciously-crafted bencoded data. (#667)
1023 */
1024static void
1025bencWalk( const tr_benc *    top,
1026          struct WalkFuncs * walkFuncs,
1027          void *             user_data )
1028{
1029    tr_ptrArray stack = TR_PTR_ARRAY_INIT;
1030
1031    tr_ptrArrayAppend( &stack, nodeNew( top ) );
1032
1033    while( !tr_ptrArrayEmpty( &stack ) )
1034    {
1035        struct SaveNode * node = tr_ptrArrayBack( &stack );
1036        const tr_benc *   val;
1037
1038        if( !node->valIsVisited )
1039        {
1040            val = node->val;
1041            node->valIsVisited = TRUE;
1042        }
1043        else if( node->childIndex < node->childCount )
1044        {
1045            const int index = node->children[node->childIndex++];
1046            val = node->val->val.l.vals +  index;
1047        }
1048        else /* done with this node */
1049        {
1050            if( isContainer( node->val ) )
1051                walkFuncs->containerEndFunc( node->val, user_data );
1052            tr_ptrArrayPop( &stack );
1053            tr_free( node->children );
1054            tr_free( node );
1055            continue;
1056        }
1057
1058        if( val ) switch( val->type )
1059            {
1060                case TR_TYPE_INT:
1061                    walkFuncs->intFunc( val, user_data );
1062                    break;
1063
1064                case TR_TYPE_BOOL:
1065                    walkFuncs->boolFunc( val, user_data );
1066                    break;
1067
1068                case TR_TYPE_REAL:
1069                    walkFuncs->realFunc( val, user_data );
1070                    break;
1071
1072                case TR_TYPE_STR:
1073                    walkFuncs->stringFunc( val, user_data );
1074                    break;
1075
1076                case TR_TYPE_LIST:
1077                    if( val != node->val )
1078                        tr_ptrArrayAppend( &stack, nodeNew( val ) );
1079                    else
1080                        walkFuncs->listBeginFunc( val, user_data );
1081                    break;
1082
1083                case TR_TYPE_DICT:
1084                    if( val != node->val )
1085                        tr_ptrArrayAppend( &stack, nodeNew( val ) );
1086                    else
1087                        walkFuncs->dictBeginFunc( val, user_data );
1088                    break;
1089
1090                default:
1091                    /* did caller give us an uninitialized val? */
1092                    tr_err( _( "Invalid metadata" ) );
1093                    break;
1094            }
1095    }
1096
1097    tr_ptrArrayDestruct( &stack, NULL );
1098}
1099
1100/****
1101*****
1102****/
1103
1104static void
1105saveIntFunc( const tr_benc * val,
1106             void *          evbuf )
1107{
1108    evbuffer_add_printf( evbuf, "i%" PRId64 "e", val->val.i );
1109}
1110
1111static void
1112saveBoolFunc( const tr_benc * val, void * evbuf )
1113{
1114    evbuffer_add_printf( evbuf, "i%de", val->val.b?1:0 );
1115}
1116
1117static void
1118saveRealFunc( const tr_benc * val, void * evbuf )
1119{
1120    char buf[128];
1121    char * locale;
1122    size_t len;
1123
1124    /* always use a '.' decimal point s.t. locale-hopping doesn't bite us */
1125    locale = tr_strdup( setlocale ( LC_NUMERIC, NULL ) );
1126    setlocale( LC_NUMERIC, "POSIX" );
1127    tr_snprintf( buf, sizeof( buf ), "%f", val->val.d );
1128    setlocale( LC_NUMERIC, locale );
1129    tr_free( locale );
1130
1131    len = strlen( buf );
1132    evbuffer_add_printf( evbuf, "%lu:", (unsigned long)len );
1133    evbuffer_add( evbuf, buf, len );
1134}
1135
1136static void
1137saveStringFunc( const tr_benc * val,
1138                void *          vevbuf )
1139{
1140    struct evbuffer * evbuf = vevbuf;
1141
1142    evbuffer_add_printf( evbuf, "%lu:", (unsigned long)val->val.s.i );
1143    evbuffer_add( evbuf, val->val.s.s, val->val.s.i );
1144}
1145
1146static void
1147saveDictBeginFunc( const tr_benc * val UNUSED,
1148                   void *              evbuf )
1149{
1150    evbuffer_add_printf( evbuf, "d" );
1151}
1152
1153static void
1154saveListBeginFunc( const tr_benc * val UNUSED,
1155                   void *              evbuf )
1156{
1157    evbuffer_add_printf( evbuf, "l" );
1158}
1159
1160static void
1161saveContainerEndFunc( const tr_benc * val UNUSED,
1162                      void *              evbuf )
1163{
1164    evbuffer_add_printf( evbuf, "e" );
1165}
1166
1167char*
1168tr_bencSave( const tr_benc * top,
1169             int *           len )
1170{
1171    char *            ret;
1172    struct WalkFuncs  walkFuncs;
1173    struct evbuffer * out = tr_getBuffer( );
1174
1175    walkFuncs.intFunc = saveIntFunc;
1176    walkFuncs.boolFunc = saveBoolFunc;
1177    walkFuncs.realFunc = saveRealFunc;
1178    walkFuncs.stringFunc = saveStringFunc;
1179    walkFuncs.dictBeginFunc = saveDictBeginFunc;
1180    walkFuncs.listBeginFunc = saveListBeginFunc;
1181    walkFuncs.containerEndFunc = saveContainerEndFunc;
1182    bencWalk( top, &walkFuncs, out );
1183
1184    if( len )
1185        *len = EVBUFFER_LENGTH( out );
1186    ret = tr_strndup( EVBUFFER_DATA( out ), EVBUFFER_LENGTH( out ) );
1187
1188    tr_releaseBuffer( out );
1189    return ret;
1190}
1191
1192/***
1193****
1194***/
1195
1196static void
1197freeDummyFunc( const tr_benc * val UNUSED,
1198               void * buf          UNUSED  )
1199{}
1200
1201static void
1202freeStringFunc( const tr_benc * val,
1203                void *          freeme )
1204{
1205    tr_ptrArrayAppend( freeme, val->val.s.s );
1206}
1207
1208static void
1209freeContainerBeginFunc( const tr_benc * val,
1210                        void *          freeme )
1211{
1212    tr_ptrArrayAppend( freeme, val->val.l.vals );
1213}
1214
1215void
1216tr_bencFree( tr_benc * val )
1217{
1218    if( val && val->type )
1219    {
1220        tr_ptrArray a = TR_PTR_ARRAY_INIT;
1221        struct WalkFuncs walkFuncs;
1222
1223        walkFuncs.intFunc = freeDummyFunc;
1224        walkFuncs.boolFunc = freeDummyFunc;
1225        walkFuncs.realFunc = freeDummyFunc;
1226        walkFuncs.stringFunc = freeStringFunc;
1227        walkFuncs.dictBeginFunc = freeContainerBeginFunc;
1228        walkFuncs.listBeginFunc = freeContainerBeginFunc;
1229        walkFuncs.containerEndFunc = freeDummyFunc;
1230        bencWalk( val, &walkFuncs, &a );
1231
1232        tr_ptrArrayDestruct( &a, tr_free );
1233    }
1234}
1235
1236/***
1237****
1238***/
1239
1240struct ParentState
1241{
1242    int    bencType;
1243    int    childIndex;
1244    int    childCount;
1245};
1246
1247struct jsonWalk
1248{
1249    tr_list *          parents;
1250    struct evbuffer *  out;
1251};
1252
1253static void
1254jsonIndent( struct jsonWalk * data )
1255{
1256    const int width = tr_list_size( data->parents ) * 4;
1257
1258    evbuffer_add_printf( data->out, "\n%*.*s", width, width, " " );
1259}
1260
1261static void
1262jsonChildFunc( struct jsonWalk * data )
1263{
1264    if( data->parents )
1265    {
1266        struct ParentState * parentState = data->parents->data;
1267
1268        switch( parentState->bencType )
1269        {
1270            case TR_TYPE_DICT:
1271            {
1272                const int i = parentState->childIndex++;
1273                if( !( i % 2 ) )
1274                    evbuffer_add_printf( data->out, ": " );
1275                else
1276                {
1277                    evbuffer_add_printf( data->out, ", " );
1278                    jsonIndent( data );
1279                }
1280                break;
1281            }
1282
1283            case TR_TYPE_LIST:
1284            {
1285                ++parentState->childIndex;
1286                evbuffer_add_printf( data->out, ", " );
1287                jsonIndent( data );
1288                break;
1289            }
1290
1291            default:
1292                break;
1293        }
1294    }
1295}
1296
1297static void
1298jsonPushParent( struct jsonWalk * data,
1299                const tr_benc *   benc )
1300{
1301    struct ParentState * parentState = tr_new( struct ParentState, 1 );
1302
1303    parentState->bencType = benc->type;
1304    parentState->childIndex = 0;
1305    parentState->childCount = benc->val.l.count;
1306    tr_list_prepend( &data->parents, parentState );
1307}
1308
1309static void
1310jsonPopParent( struct jsonWalk * data )
1311{
1312    tr_free( tr_list_pop_front( &data->parents ) );
1313}
1314
1315static void
1316jsonIntFunc( const tr_benc * val,
1317             void *          vdata )
1318{
1319    struct jsonWalk * data = vdata;
1320
1321    evbuffer_add_printf( data->out, "%" PRId64, val->val.i );
1322    jsonChildFunc( data );
1323}
1324
1325static void
1326jsonBoolFunc( const tr_benc * val, void * vdata )
1327{
1328    struct jsonWalk * data = vdata;
1329
1330    evbuffer_add_printf( data->out, "%s", (val->val.b?"true":"false") );
1331    jsonChildFunc( data );
1332}
1333
1334static void
1335jsonRealFunc( const tr_benc * val, void * vdata )
1336{
1337    struct jsonWalk * data = vdata;
1338    char * locale;
1339
1340    /* json requires a '.' decimal point regardless of locale */
1341    locale = tr_strdup( setlocale ( LC_NUMERIC, NULL ) );
1342    setlocale( LC_NUMERIC, "POSIX" );
1343    evbuffer_add_printf( data->out, "%f", val->val.d );
1344    setlocale( LC_NUMERIC, locale );
1345    tr_free( locale );
1346
1347    jsonChildFunc( data );
1348}
1349
1350static void
1351jsonStringFunc( const tr_benc * val,
1352                void *          vdata )
1353{
1354    struct jsonWalk *    data = vdata;
1355    const unsigned char *it, *end;
1356
1357    evbuffer_add_printf( data->out, "\"" );
1358    for( it = (const unsigned char*)val->val.s.s, end = it + val->val.s.i;
1359         it != end; ++it )
1360    {
1361        switch( *it )
1362        {
1363            case '/':
1364                evbuffer_add_printf( data->out, "\\/" ); break;
1365
1366            case '\b':
1367                evbuffer_add_printf( data->out, "\\b" ); break;
1368
1369            case '\f':
1370                evbuffer_add_printf( data->out, "\\f" ); break;
1371
1372            case '\n':
1373                evbuffer_add_printf( data->out, "\\n" ); break;
1374
1375            case '\r':
1376                evbuffer_add_printf( data->out, "\\r" ); break;
1377
1378            case '\t':
1379                evbuffer_add_printf( data->out, "\\t" ); break;
1380
1381            case '"':
1382                evbuffer_add_printf( data->out, "\\\"" ); break;
1383
1384            case '\\':
1385                evbuffer_add_printf( data->out, "\\\\" ); break;
1386
1387            default:
1388                if( isascii( *it ) )
1389                {
1390                    /*fprintf( stderr, "[%c]\n", *it );*/
1391                    evbuffer_add_printf( data->out, "%c", *it );
1392                }
1393                else
1394                {
1395                    const UTF8 * tmp = it;
1396                    UTF32        buf = 0;
1397                    UTF32 *      u32 = &buf;
1398                    ConversionResult result = ConvertUTF8toUTF32( &tmp, end, &u32, &buf + 1, 0 );
1399                    if( ( result != conversionOK ) && ( tmp == it ) )
1400                        ++it; /* it's beyond help; skip it */
1401                    else {
1402                        evbuffer_add_printf( data->out, "\\u%04x", (unsigned int)buf );
1403                        it = tmp - 1;
1404                    }
1405                    /*fprintf( stderr, "[\\u%04x]\n", buf );*/
1406                }
1407        }
1408    }
1409    evbuffer_add_printf( data->out, "\"" );
1410    jsonChildFunc( data );
1411}
1412
1413static void
1414jsonDictBeginFunc( const tr_benc * val,
1415                   void *          vdata )
1416{
1417    struct jsonWalk * data = vdata;
1418
1419    jsonPushParent( data, val );
1420    evbuffer_add_printf( data->out, "{" );
1421    if( val->val.l.count )
1422        jsonIndent( data );
1423}
1424
1425static void
1426jsonListBeginFunc( const tr_benc * val,
1427                   void *          vdata )
1428{
1429    const size_t      nChildren = tr_bencListSize( val );
1430    struct jsonWalk * data = vdata;
1431
1432    jsonPushParent( data, val );
1433    evbuffer_add_printf( data->out, "[" );
1434    if( nChildren )
1435        jsonIndent( data );
1436}
1437
1438static void
1439jsonContainerEndFunc( const tr_benc * val,
1440                      void *          vdata )
1441{
1442    size_t            i;
1443    struct jsonWalk * data = vdata;
1444    char *            str;
1445    int               emptyContainer = FALSE;
1446
1447    /* trim out the trailing comma, if any */
1448    str = (char*) EVBUFFER_DATA( data->out );
1449    for( i = EVBUFFER_LENGTH( data->out ) - 1; i > 0; --i )
1450    {
1451        if( isspace( str[i] ) ) continue;
1452        if( str[i] == ',' )
1453            EVBUFFER_LENGTH( data->out ) = i;
1454        if( str[i] == '{' || str[i] == '[' )
1455            emptyContainer = TRUE;
1456        break;
1457    }
1458
1459    jsonPopParent( data );
1460    if( !emptyContainer )
1461        jsonIndent( data );
1462    if( tr_bencIsDict( val ) )
1463        evbuffer_add_printf( data->out, "}" );
1464    else /* list */
1465        evbuffer_add_printf( data->out, "]" );
1466    jsonChildFunc( data );
1467}
1468
1469char*
1470tr_bencSaveAsJSON( const tr_benc * top, struct evbuffer * out )
1471{
1472    struct WalkFuncs walkFuncs;
1473    struct jsonWalk  data;
1474
1475    evbuffer_drain( out, EVBUFFER_LENGTH( out ) );
1476
1477    data.out = out;
1478    data.parents = NULL;
1479
1480    walkFuncs.intFunc = jsonIntFunc;
1481    walkFuncs.boolFunc = jsonBoolFunc;
1482    walkFuncs.realFunc = jsonRealFunc;
1483    walkFuncs.stringFunc = jsonStringFunc;
1484    walkFuncs.dictBeginFunc = jsonDictBeginFunc;
1485    walkFuncs.listBeginFunc = jsonListBeginFunc;
1486    walkFuncs.containerEndFunc = jsonContainerEndFunc;
1487
1488    bencWalk( top, &walkFuncs, &data );
1489
1490    if( EVBUFFER_LENGTH( out ) )
1491        evbuffer_add_printf( out, "\n" );
1492
1493    return (char*) EVBUFFER_DATA( out );
1494}
1495
1496char*
1497tr_bencToJSON( const tr_benc * top )
1498{
1499    char * ret;
1500    struct evbuffer * buf = evbuffer_new( );
1501    tr_bencSaveAsJSON( top, buf );
1502    ret = tr_strndup( EVBUFFER_DATA( buf ), EVBUFFER_LENGTH( buf ) );
1503    evbuffer_free( buf );
1504    return ret;
1505}
1506
1507/***
1508****
1509***/
1510
1511static size_t
1512tr_bencDictSize( const tr_benc * dict )
1513{
1514    size_t count = 0;
1515
1516    if( tr_bencIsDict( dict ) )
1517        count = dict->val.l.count / 2;
1518
1519    return count;
1520}
1521
1522static tr_bool
1523tr_bencDictChild( const tr_benc * dict, size_t n, const char ** key, const tr_benc ** val )
1524{
1525    tr_bool success = 0;
1526
1527    assert( tr_bencIsDict( dict ) );
1528
1529    if( tr_bencIsDict( dict ) && (n*2)+1 <= dict->val.l.count )
1530    {
1531        tr_benc * k = dict->val.l.vals + (n*2);
1532        tr_benc * v = dict->val.l.vals + (n*2) + 1;
1533        if(( success = tr_bencGetStr( k, key ) && isSomething( v )))
1534            *val = v;
1535    }
1536
1537    return success;
1538}
1539
1540void 
1541tr_bencMergeDicts( tr_benc * target, const tr_benc * source )
1542{
1543    size_t i;
1544    const size_t sourceCount = tr_bencDictSize( source );
1545
1546    assert( tr_bencIsDict( target ) );
1547    assert( tr_bencIsDict( source ) );
1548
1549    for( i=0; i<sourceCount; ++i )
1550    {
1551        const char * key;
1552        const tr_benc * val;
1553        tr_benc * t;
1554
1555        if( tr_bencDictChild( source, i, &key, &val ) )
1556        {
1557            if( tr_bencIsBool( val ) )
1558            {
1559                tr_bool boolVal;
1560                tr_bencGetBool( val, &boolVal );
1561                tr_bencDictAddBool( target, key, boolVal );
1562            }
1563            else if( tr_bencIsReal( val ) )
1564            {
1565                double realVal;
1566                tr_bencGetReal( val, &realVal );
1567                tr_bencDictAddReal( target, key, realVal );
1568            }
1569            else if( tr_bencIsInt( val ) )
1570            {
1571                int64_t intVal;
1572                tr_bencGetInt( val, &intVal );
1573                tr_bencDictAddInt( target, key, intVal );
1574            }
1575            else if( tr_bencIsString( val ) )
1576            {
1577                const char * strVal;
1578                tr_bencGetStr( val, &strVal );
1579                tr_bencDictAddStr( target, key, strVal );
1580            }
1581            else if( tr_bencIsDict( val ) && tr_bencDictFindDict( target, key, &t ) )
1582            {
1583                tr_bencMergeDicts( t, val );
1584            }
1585            else
1586            {
1587                tr_dbg( "tr_bencMergeDicts skipping \"%s\"", key );
1588            }
1589        }
1590    }
1591}
1592
1593/***
1594****
1595***/ 
1596
1597static int
1598saveFile( const char * filename,
1599          const char * content,
1600          size_t       len )
1601{
1602    int    err = 0;
1603    FILE * out = NULL;
1604
1605    out = fopen( filename, "wb+" );
1606
1607    if( !out )
1608    {
1609        err = errno;
1610        tr_err( _( "Couldn't open \"%1$s\": %2$s" ),
1611                filename, tr_strerror( errno ) );
1612    }
1613    else if( fwrite( content, sizeof( char ), len, out ) != (size_t)len )
1614    {
1615        err = errno;
1616        tr_err( _( "Couldn't save file \"%1$s\": %2$s" ),
1617               filename, tr_strerror( errno ) );
1618    }
1619
1620    if( !err )
1621        tr_dbg( "tr_bencSaveFile saved \"%s\"", filename );
1622    if( out )
1623        fclose( out );
1624    return err;
1625}
1626
1627int
1628tr_bencSaveFile( const char *    filename,
1629                 const tr_benc * b )
1630{
1631    int       len;
1632    char *    content = tr_bencSave( b, &len );
1633    const int err = saveFile( filename, content, len );
1634
1635    tr_free( content );
1636    return err;
1637}
1638
1639int
1640tr_bencSaveJSONFile( const char *    filename,
1641                     const tr_benc * b )
1642{
1643    struct evbuffer * buf = tr_getBuffer( );
1644    const char * json = tr_bencSaveAsJSON( b, buf );
1645    const int err = saveFile( filename, json, EVBUFFER_LENGTH( buf ) );
1646    tr_releaseBuffer( buf );
1647    return err;
1648}
1649
1650/***
1651****
1652***/
1653
1654int
1655tr_bencLoadFile( const char * filename, tr_benc * b )
1656{
1657    int       err;
1658    size_t    contentLen;
1659    uint8_t * content;
1660
1661    content = tr_loadFile( filename, &contentLen );
1662    if( !content && errno )
1663        err = errno;
1664    else if( !content )
1665        err = ENODATA;
1666    else
1667        err = tr_bencLoad( content, contentLen, b, NULL );
1668
1669    tr_free( content );
1670    return err;
1671}
1672
1673int
1674tr_bencLoadJSONFile( const char * filename, tr_benc * b )
1675{
1676    int        err;
1677    size_t     contentLen;
1678    uint8_t  * content;
1679
1680    content = tr_loadFile( filename, &contentLen );
1681    if( !content && errno )
1682        err = errno;
1683    else if( !content )
1684        err = ENODATA;
1685    else
1686        err = tr_jsonParse( content, contentLen, b, NULL );
1687
1688    tr_free( content );
1689    return err;
1690}
Note: See TracBrowser for help on using the repository browser.