source: branches/2.0x/libtransmission/utils.c @ 11050

Last change on this file since 11050 was 11050, checked in by charles, 12 years ago

(2.0x libT) #3449 "overflow error in tr_truncd()" -- fixed

  • Property svn:keywords set to Date Rev Author Id
File size: 33.5 KB
Line 
1/*
2 * This file Copyright (C) 2009-2010 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: utils.c 11050 2010-07-24 17:09:41Z charles $
11 */
12
13#ifdef HAVE_MEMMEM
14 #define _GNU_SOURCE /* glibc's string.h needs this to pick up memmem */
15#endif
16
17#if defined(SYS_DARWIN)
18 #define HAVE_GETPAGESIZE
19 #define HAVE_ICONV_OPEN
20 #define HAVE_VALLOC
21 #undef HAVE_POSIX_MEMALIGN /* not supported on OS X 10.5 and lower */
22#endif
23
24#include <assert.h>
25#include <ctype.h> /* isalpha, tolower */
26#include <errno.h>
27#include <math.h> /* pow */
28#include <stdarg.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h> /* strerror, memset, memmem */
32#include <time.h> /* nanosleep() */
33
34#ifdef HAVE_ICONV_OPEN
35 #include <iconv.h>
36#endif
37#include <libgen.h> /* basename */
38#include <sys/time.h>
39#include <sys/types.h>
40#include <sys/stat.h>
41#include <unistd.h> /* stat(), getcwd(), getpagesize() */
42
43#include "event.h"
44
45#ifdef WIN32
46 #include <direct.h> /* _getcwd */
47 #include <windows.h> /* Sleep */
48#endif
49
50#include "transmission.h"
51#include "fdlimit.h"
52#include "ConvertUTF.h"
53#include "list.h"
54#include "net.h"
55#include "utils.h"
56#include "platform.h"
57#include "version.h"
58
59
60time_t transmission_now = 0;
61
62int                   messageLevel = TR_MSG_INF;
63static tr_lock *      messageLock = NULL;
64static tr_bool        messageQueuing = FALSE;
65static tr_msg_list *  messageQueue = NULL;
66static tr_msg_list ** messageQueueTail = &messageQueue;
67static int            messageQueueCount = 0;
68
69#ifndef WIN32
70    /* make null versions of these win32 functions */
71    static inline int IsDebuggerPresent( void ) { return FALSE; }
72    static inline void OutputDebugString( const void * unused UNUSED ) { }
73#endif
74
75/***
76****
77***/
78
79void
80tr_msgInit( void )
81{
82    const char * env = getenv( "TR_DEBUG" );
83    messageLevel = ( env ? atoi( env ) : 0 ) + 1;
84    messageLevel = MAX( 1, messageLevel );
85
86    if( messageLock == NULL )
87        messageLock = tr_lockNew( );
88}
89
90FILE*
91tr_getLog( void )
92{
93    static tr_bool initialized = FALSE;
94    static FILE * file = NULL;
95
96    if( !initialized )
97    {
98        const char * str = getenv( "TR_DEBUG_FD" );
99        int          fd = 0;
100        if( str && *str )
101            fd = atoi( str );
102        switch( fd )
103        {
104            case 1:
105                file = stdout; break;
106
107            case 2:
108                file = stderr; break;
109
110            default:
111                file = NULL; break;
112        }
113        initialized = TRUE;
114    }
115
116    return file;
117}
118
119void
120tr_setMessageLevel( int level )
121{
122    tr_lockLock( messageLock );
123
124    messageLevel = MAX( 0, level );
125
126    tr_lockUnlock( messageLock );
127}
128
129int
130tr_getMessageLevel( void )
131{
132    int ret;
133    tr_lockLock( messageLock );
134
135    ret = messageLevel;
136
137    tr_lockUnlock( messageLock );
138    return ret;
139}
140
141void
142tr_setMessageQueuing( tr_bool enabled )
143{
144    tr_lockLock( messageLock );
145
146    messageQueuing = enabled;
147
148    tr_lockUnlock( messageLock );
149}
150
151tr_bool
152tr_getMessageQueuing( void )
153{
154    int ret;
155    tr_lockLock( messageLock );
156
157    ret = messageQueuing;
158
159    tr_lockUnlock( messageLock );
160    return ret;
161}
162
163tr_msg_list *
164tr_getQueuedMessages( void )
165{
166    tr_msg_list * ret;
167    tr_lockLock( messageLock );
168
169    ret = messageQueue;
170    messageQueue = NULL;
171    messageQueueTail = &messageQueue;
172
173    messageQueueCount = 0;
174
175    tr_lockUnlock( messageLock );
176    return ret;
177}
178
179void
180tr_freeMessageList( tr_msg_list * list )
181{
182    tr_msg_list * next;
183
184    while( NULL != list )
185    {
186        next = list->next;
187        free( list->message );
188        free( list->name );
189        free( list );
190        list = next;
191    }
192}
193
194/**
195***
196**/
197
198struct tm *
199tr_localtime_r( const time_t *_clock, struct tm *_result )
200{
201#ifdef HAVE_LOCALTIME_R
202    return localtime_r( _clock, _result );
203#else
204    struct tm *p = localtime( _clock );
205    if( p )
206        *(_result) = *p;
207    return p;
208#endif
209}
210
211char*
212tr_getLogTimeStr( char * buf, int buflen )
213{
214    char           tmp[64];
215    struct tm      now_tm;
216    struct timeval tv;
217    time_t         seconds;
218    int            milliseconds;
219
220    gettimeofday( &tv, NULL );
221
222    seconds = tv.tv_sec;
223    tr_localtime_r( &seconds, &now_tm );
224    strftime( tmp, sizeof( tmp ), "%H:%M:%S", &now_tm );
225    milliseconds = (int)( tv.tv_usec / 1000 );
226    tr_snprintf( buf, buflen, "%s.%03d", tmp, milliseconds );
227
228    return buf;
229}
230
231tr_bool
232tr_deepLoggingIsActive( void )
233{
234    static int8_t deepLoggingIsActive = -1;
235
236    if( deepLoggingIsActive < 0 )
237        deepLoggingIsActive = IsDebuggerPresent() || (tr_getLog()!=NULL);
238
239    return deepLoggingIsActive != 0;
240}
241
242void
243tr_deepLog( const char  * file,
244            int           line,
245            const char  * name,
246            const char  * fmt,
247            ... )
248{
249    FILE * fp = tr_getLog( );
250    if( fp || IsDebuggerPresent( ) )
251    {
252        va_list           args;
253        char              timestr[64];
254        struct evbuffer * buf = evbuffer_new( );
255        char *            base = tr_basename( file );
256
257        evbuffer_add_printf( buf, "[%s] ",
258                            tr_getLogTimeStr( timestr, sizeof( timestr ) ) );
259        if( name )
260            evbuffer_add_printf( buf, "%s ", name );
261        va_start( args, fmt );
262        evbuffer_add_vprintf( buf, fmt, args );
263        va_end( args );
264        evbuffer_add_printf( buf, " (%s:%d)\n", base, line );
265        /* FIXME(libevent2) ifdef this out for nonwindows platforms */
266        OutputDebugString( EVBUFFER_DATA( buf ) );
267        if(fp) /* FIXME(libevent2) tr_getLog() should return an fd, then use evbuffer_write() here ) */
268            (void) fwrite( EVBUFFER_DATA( buf ), 1, EVBUFFER_LENGTH( buf ), fp );
269
270        tr_free( base );
271        evbuffer_free( buf );
272    }
273}
274
275/***
276****
277***/
278
279void
280tr_msg( const char * file, int line,
281        int level, const char * name,
282        const char * fmt, ... )
283{
284    const int err = errno; /* message logging shouldn't affect errno */
285    char buf[1024];
286    va_list ap;
287
288    if( messageLock != NULL )
289        tr_lockLock( messageLock );
290
291    /* build the text message */
292    *buf = '\0';
293    va_start( ap, fmt );
294    evutil_vsnprintf( buf, sizeof( buf ), fmt, ap );
295    va_end( ap );
296
297    OutputDebugString( buf );
298
299    if( *buf )
300    {
301        if( messageQueuing )
302        {
303            tr_msg_list * newmsg;
304            newmsg = tr_new0( tr_msg_list, 1 );
305            newmsg->level = level;
306            newmsg->when = tr_time( );
307            newmsg->message = tr_strdup( buf );
308            newmsg->file = file;
309            newmsg->line = line;
310            newmsg->name = tr_strdup( name );
311
312            *messageQueueTail = newmsg;
313            messageQueueTail = &newmsg->next;
314            ++messageQueueCount;
315
316            if( messageQueueCount > TR_MAX_MSG_LOG )
317            {
318                tr_msg_list * old = messageQueue;
319                messageQueue = old->next;
320                old->next = NULL;
321                tr_freeMessageList(old);
322
323                --messageQueueCount;
324
325                assert( messageQueueCount == TR_MAX_MSG_LOG );
326            }
327        }
328        else
329        {
330            char timestr[64];
331            FILE * fp;
332
333            fp = tr_getLog( );
334            if( fp == NULL )
335                fp = stderr;
336
337            tr_getLogTimeStr( timestr, sizeof( timestr ) );
338
339            if( name )
340                fprintf( fp, "[%s] %s: %s\n", timestr, name, buf );
341            else
342                fprintf( fp, "[%s] %s\n", timestr, buf );
343            fflush( fp );
344        }
345    }
346
347    if( messageLock != NULL )
348        tr_lockUnlock( messageLock );
349
350    errno = err;
351}
352
353/***
354****
355***/
356
357void
358tr_set_compare( const void * va,
359                size_t aCount,
360                const void * vb,
361                size_t bCount,
362                int compare( const void * a, const void * b ),
363                size_t elementSize,
364                tr_set_func in_a_cb,
365                tr_set_func in_b_cb,
366                tr_set_func in_both_cb,
367                void * userData )
368{
369    const uint8_t * a = (const uint8_t *) va;
370    const uint8_t * b = (const uint8_t *) vb;
371    const uint8_t * aend = a + elementSize * aCount;
372    const uint8_t * bend = b + elementSize * bCount;
373
374    while( a != aend || b != bend )
375    {
376        if( a == aend )
377        {
378            ( *in_b_cb )( (void*)b, userData );
379            b += elementSize;
380        }
381        else if( b == bend )
382        {
383            ( *in_a_cb )( (void*)a, userData );
384            a += elementSize;
385        }
386        else
387        {
388            const int val = ( *compare )( a, b );
389
390            if( !val )
391            {
392                ( *in_both_cb )( (void*)a, userData );
393                a += elementSize;
394                b += elementSize;
395            }
396            else if( val < 0 )
397            {
398                ( *in_a_cb )( (void*)a, userData );
399                a += elementSize;
400            }
401            else if( val > 0 )
402            {
403                ( *in_b_cb )( (void*)b, userData );
404                b += elementSize;
405            }
406        }
407    }
408}
409
410/***
411****
412***/
413
414#ifdef DISABLE_GETTEXT
415
416const char*
417tr_strip_positional_args( const char* str )
418{
419    const char * in = str;
420    static size_t bufsize = 0;
421    static char * buf = NULL;
422    const size_t  len = strlen( str );
423    char *        out;
424
425    if( bufsize < len )
426    {
427        bufsize = len * 2;
428        buf = tr_renew( char, buf, bufsize );
429    }
430
431    for( out = buf; *str; ++str )
432    {
433        *out++ = *str;
434        if( ( *str == '%' ) && isdigit( str[1] ) )
435        {
436            const char * tmp = str + 1;
437            while( isdigit( *tmp ) )
438                ++tmp;
439
440            if( *tmp == '$' )
441                str = tmp;
442        }
443    }
444    *out = '\0';
445
446    return strcmp( buf, in ) ? buf : in;
447}
448
449#endif
450
451/**
452***
453**/
454
455void
456tr_timerAdd( struct event * timer, int seconds, int microseconds )
457{
458    struct timeval tv;
459    tv.tv_sec = seconds;
460    tv.tv_usec = microseconds;
461
462    assert( tv.tv_sec >= 0 );
463    assert( tv.tv_usec >= 0 );
464    assert( tv.tv_usec < 1000000 );
465
466    evtimer_add( timer, &tv );
467}
468
469void
470tr_timerAddMsec( struct event * timer, int msec )
471{
472    const int seconds =  msec / 1000;
473    const int usec = (msec%1000) * 1000;
474    tr_timerAdd( timer, seconds, usec );
475}
476
477/**
478***
479**/
480
481uint8_t *
482tr_loadFile( const char * path,
483             size_t *     size )
484{
485    uint8_t * buf;
486    struct stat  sb;
487    int fd;
488    ssize_t n;
489    const char * err_fmt = _( "Couldn't read \"%1$s\": %2$s" );
490
491    /* try to stat the file */
492    errno = 0;
493    if( stat( path, &sb ) )
494    {
495        const int err = errno;
496        tr_dbg( err_fmt, path, tr_strerror( errno ) );
497        errno = err;
498        return NULL;
499    }
500
501    if( ( sb.st_mode & S_IFMT ) != S_IFREG )
502    {
503        tr_err( err_fmt, path, _( "Not a regular file" ) );
504        errno = EISDIR;
505        return NULL;
506    }
507
508    /* Load the torrent file into our buffer */
509    fd = tr_open_file_for_scanning( path );
510    if( fd < 0 )
511    {
512        const int err = errno;
513        tr_err( err_fmt, path, tr_strerror( errno ) );
514        errno = err;
515        return NULL;
516    }
517    buf = malloc( sb.st_size + 1 );
518    if( !buf )
519    {
520        const int err = errno;
521        tr_err( err_fmt, path, _( "Memory allocation failed" ) );
522        tr_close_file( fd );
523        errno = err;
524        return NULL;
525    }
526    n = read( fd, buf, sb.st_size );
527    if( n == -1 )
528    {
529        const int err = errno;
530        tr_err( err_fmt, path, tr_strerror( errno ) );
531        tr_close_file( fd );
532        free( buf );
533        errno = err;
534        return NULL;
535    }
536
537    tr_close_file( fd );
538    buf[ sb.st_size ] = '\0';
539    *size = sb.st_size;
540    return buf;
541}
542
543char*
544tr_basename( const char * path )
545{
546    char * tmp = tr_strdup( path );
547    char * ret = tr_strdup( basename( tmp ) );
548    tr_free( tmp );
549    return ret;
550}
551
552char*
553tr_dirname( const char * path )
554{
555    char * tmp = tr_strdup( path );
556    char * ret = tr_strdup( dirname( tmp ) );
557    tr_free( tmp );
558    return ret;
559}
560
561int
562tr_mkdir( const char * path,
563          int permissions
564#ifdef WIN32
565                       UNUSED
566#endif
567        )
568{
569#ifdef WIN32
570    if( path && isalpha( path[0] ) && path[1] == ':' && !path[2] )
571        return 0;
572    return mkdir( path );
573#else
574    return mkdir( path, permissions );
575#endif
576}
577
578int
579tr_mkdirp( const char * path_in,
580           int          permissions )
581{
582    char *      path = tr_strdup( path_in );
583    char *      p, * pp;
584    struct stat sb;
585    int         done;
586
587    /* walk past the root */
588    p = path;
589    while( *p == TR_PATH_DELIMITER )
590        ++p;
591
592    pp = p;
593    done = 0;
594    while( ( p =
595                strchr( pp, TR_PATH_DELIMITER ) ) || ( p = strchr( pp, '\0' ) ) )
596    {
597        if( !*p )
598            done = 1;
599        else
600            *p = '\0';
601
602        if( stat( path, &sb ) )
603        {
604            /* Folder doesn't exist yet */
605            if( tr_mkdir( path, permissions ) )
606            {
607                const int err = errno;
608                tr_err( _(
609                           "Couldn't create \"%1$s\": %2$s" ), path,
610                       tr_strerror( err ) );
611                tr_free( path );
612                errno = err;
613                return -1;
614            }
615        }
616        else if( ( sb.st_mode & S_IFMT ) != S_IFDIR )
617        {
618            /* Node exists but isn't a folder */
619            char * buf = tr_strdup_printf( _( "File \"%s\" is in the way" ), path );
620            tr_err( _( "Couldn't create \"%1$s\": %2$s" ), path_in, buf );
621            tr_free( buf );
622            tr_free( path );
623            errno = ENOTDIR;
624            return -1;
625        }
626
627        if( done )
628            break;
629
630        *p = TR_PATH_DELIMITER;
631        p++;
632        pp = p;
633    }
634
635    tr_free( path );
636    return 0;
637}
638
639char*
640tr_buildPath( const char *first_element, ... )
641{
642    size_t bufLen = 0;
643    const char * element;
644    char * buf;
645    char * pch;
646    va_list vl;
647
648    /* pass 1: allocate enough space for the string */
649    va_start( vl, first_element );
650    element = first_element;
651    while( element ) {
652        bufLen += strlen( element ) + 1;
653        element = (const char*) va_arg( vl, const char* );
654    }
655    pch = buf = tr_new( char, bufLen );
656    va_end( vl );
657
658    /* pass 2: build the string piece by piece */
659    va_start( vl, first_element );
660    element = first_element;
661    while( element ) {
662        const size_t elementLen = strlen( element );
663        memcpy( pch, element, elementLen );
664        pch += elementLen;
665        *pch++ = TR_PATH_DELIMITER;
666        element = (const char*) va_arg( vl, const char* );
667    }
668    va_end( vl );
669
670    /* terminate the string.  if nonempty, eat the unwanted trailing slash */
671    if( pch != buf )
672        --pch;
673    *pch++ = '\0';
674
675    /* sanity checks & return */
676    assert( pch - buf == (off_t)bufLen );
677    return buf;
678}
679
680/****
681*****
682****/
683
684char*
685tr_strndup( const void * in, int len )
686{
687    char * out = NULL;
688
689    if( len < 0 )
690    {
691        out = tr_strdup( in );
692    }
693    else if( in )
694    {
695        out = tr_malloc( len + 1 );
696        memcpy( out, in, len );
697        out[len] = '\0';
698    }
699
700    return out;
701}
702
703const char*
704tr_memmem( const char * haystack, size_t haystacklen,
705           const char * needle, size_t needlelen )
706{
707#ifdef HAVE_MEMMEM
708    return memmem( haystack, haystacklen, needle, needlelen );
709#else
710    size_t i;
711    if( !needlelen )
712        return haystack;
713    if( needlelen > haystacklen || !haystack || !needle )
714        return NULL;
715    for( i=0; i<=haystacklen-needlelen; ++i )
716        if( !memcmp( haystack+i, needle, needlelen ) )
717            return haystack+i;
718    return NULL;
719#endif
720}
721
722char*
723tr_strdup_printf( const char * fmt, ... )
724{
725    va_list ap;
726    char * ret;
727    size_t len;
728    char statbuf[2048];
729
730    va_start( ap, fmt );
731    len = evutil_vsnprintf( statbuf, sizeof( statbuf ), fmt, ap );
732    va_end( ap );
733    if( len < sizeof( statbuf ) )
734        ret = tr_strndup( statbuf, len );
735    else {
736        ret = tr_new( char, len + 1 );
737        va_start( ap, fmt );
738        evutil_vsnprintf( ret, len + 1, fmt, ap );
739        va_end( ap );
740    }
741
742    return ret;
743}
744
745const char*
746tr_strerror( int i )
747{
748    const char * ret = strerror( i );
749
750    if( ret == NULL )
751        ret = "Unknown Error";
752    return ret;
753}
754
755/****
756*****
757****/
758
759char*
760tr_strstrip( char * str )
761{
762    if( str != NULL )
763    {
764        size_t pos;
765        size_t len = strlen( str );
766
767        while( len && isspace( str[len - 1] ) )
768            --len;
769
770        for( pos = 0; pos < len && isspace( str[pos] ); )
771            ++pos;
772
773        len -= pos;
774        memmove( str, str + pos, len );
775        str[len] = '\0';
776    }
777
778    return str;
779}
780
781tr_bool
782tr_str_has_suffix( const char *str, const char *suffix )
783{
784    size_t str_len;
785    size_t suffix_len;
786
787    if( !str )
788        return FALSE;
789    if( !suffix )
790        return TRUE;
791
792    str_len = strlen( str );
793    suffix_len = strlen( suffix );
794    if( str_len < suffix_len )
795        return FALSE;
796
797    return !strncasecmp( str + str_len - suffix_len, suffix, suffix_len );
798}
799
800/****
801*****
802****/
803
804uint64_t
805tr_date( void )
806{
807    struct timeval tv;
808
809    gettimeofday( &tv, NULL );
810    return (uint64_t) tv.tv_sec * 1000 + ( tv.tv_usec / 1000 );
811}
812
813void
814tr_wait_msec( long int msec )
815{
816#ifdef WIN32
817    Sleep( (DWORD)msec );
818#else
819    struct timespec ts;
820    ts.tv_sec = msec / 1000;
821    ts.tv_nsec = ( msec % 1000 ) * 1000000;
822    nanosleep( &ts, NULL );
823#endif
824}
825
826/***
827****
828***/
829
830int
831tr_snprintf( char * buf, size_t buflen, const char * fmt, ... )
832{
833    int     len;
834    va_list args;
835
836    va_start( args, fmt );
837    len = evutil_vsnprintf( buf, buflen, fmt, args );
838    va_end( args );
839    return len;
840}
841
842/*
843 * Copy src to string dst of size siz.  At most siz-1 characters
844 * will be copied.  Always NUL terminates (unless siz == 0).
845 * Returns strlen(src); if retval >= siz, truncation occurred.
846 */
847size_t
848tr_strlcpy( char *       dst,
849            const void * src,
850            size_t       siz )
851{
852#ifdef HAVE_STRLCPY
853    return strlcpy( dst, src, siz );
854#else
855    char *      d = dst;
856    const char *s = src;
857    size_t      n = siz;
858
859    assert( s );
860    assert( d );
861
862    /* Copy as many bytes as will fit */
863    if( n != 0 )
864    {
865        while( --n != 0 )
866        {
867            if( ( *d++ = *s++ ) == '\0' )
868                break;
869        }
870    }
871
872    /* Not enough room in dst, add NUL and traverse rest of src */
873    if( n == 0 )
874    {
875        if( siz != 0 )
876            *d = '\0'; /* NUL-terminate dst */
877        while( *s++ )
878            ;
879    }
880
881    return s - (char*)src - 1;  /* count does not include NUL */
882#endif
883}
884
885/***
886****
887***/
888
889double
890tr_getRatio( double numerator,
891             double denominator )
892{
893    double ratio;
894
895    if( denominator )
896        ratio = numerator / denominator;
897    else if( numerator )
898        ratio = TR_RATIO_INF;
899    else
900        ratio = TR_RATIO_NA;
901
902    return ratio;
903}
904
905void
906tr_sha1_to_hex( char * out, const uint8_t * sha1 )
907{
908    int i;
909    static const char hex[] = "0123456789abcdef";
910
911    for( i=0; i<20; ++i )
912    {
913        const unsigned int val = *sha1++;
914        *out++ = hex[val >> 4];
915        *out++ = hex[val & 0xf];
916    }
917
918    *out = '\0';
919}
920
921void
922tr_hex_to_sha1( uint8_t * out, const char * in )
923{
924    int i;
925    static const char hex[] = "0123456789abcdef";
926
927    for( i=0; i<20; ++i )
928    {
929        const int hi = strchr( hex, tolower( *in++ ) ) - hex;
930        const int lo = strchr( hex, tolower( *in++ ) ) - hex;
931        *out++ = (uint8_t)( (hi<<4) | lo );
932    }
933}
934
935/***
936****
937***/
938
939static tr_bool
940isValidURLChars( const char * url )
941{
942    const char * c;
943    static const char * rfc2396_valid_chars =
944        "abcdefghijklmnopqrstuvwxyz" /* lowalpha */
945        "ABCDEFGHIJKLMNOPQRSTUVWXYZ" /* upalpha */
946        "0123456789"                 /* digit */
947        "-_.!~*'()"                  /* mark */
948        ";/?:@&=+$,"                 /* reserved */
949        "<>#%<\""                    /* delims */
950        "{}|\\^[]`";                 /* unwise */
951
952    if( url == NULL )
953        return FALSE;
954
955    for( c = url; c && *c; ++c )
956        if( !strchr( rfc2396_valid_chars, *c ) )
957            return FALSE;
958
959    return TRUE;
960}
961
962/** @brief return TRUE if the url is a http or https url that Transmission understands */
963tr_bool
964tr_urlIsValidTracker( const char * url )
965{
966    tr_bool valid;
967    char * scheme = NULL;
968
969    valid = isValidURLChars( url )
970         && !tr_urlParse( url, -1, &scheme, NULL, NULL, NULL )
971         && ( scheme != NULL )
972         && ( !strcmp(scheme,"http") || !strcmp(scheme,"https") );
973
974    tr_free( scheme );
975    return valid;
976}
977
978/** @brief return TRUE if the url is a http or https or ftp or sftp url that Transmission understands */
979tr_bool
980tr_urlIsValid( const char * url )
981{
982    tr_bool valid;
983    char * scheme = NULL;
984
985    valid = isValidURLChars( url )
986         && !tr_urlParse( url, -1, &scheme, NULL, NULL, NULL )
987         && ( scheme != NULL )
988         && ( !strcmp(scheme,"http") || !strcmp(scheme,"https") || !strcmp(scheme,"ftp") || !strcmp(scheme,"sftp") );
989
990    tr_free( scheme );
991    return valid;
992}
993
994tr_bool
995tr_addressIsIP( const char * address )
996{
997    tr_address tempAddr;
998    return tr_pton(address, &tempAddr) != NULL;
999}
1000
1001int
1002tr_urlParse( const char * url_in,
1003             int          len,
1004             char **      setme_protocol,
1005             char **      setme_host,
1006             int *        setme_port,
1007             char **      setme_path )
1008{
1009    int          err;
1010    int          port = 0;
1011    int          n;
1012    char *       tmp;
1013    char *       pch;
1014    const char * protocol = NULL;
1015    const char * host = NULL;
1016    const char * path = NULL;
1017
1018    tmp = tr_strndup( url_in, len );
1019    if( ( pch = strstr( tmp, "://" ) ) )
1020    {
1021        *pch = '\0';
1022        protocol = tmp;
1023        pch += 3;
1024/*fprintf( stderr, "protocol is [%s]... what's left is [%s]\n", protocol, pch);*/
1025        if( ( n = strcspn( pch, ":/" ) ) )
1026        {
1027            const int havePort = pch[n] == ':';
1028            host = pch;
1029            pch += n;
1030            *pch++ = '\0';
1031/*fprintf( stderr, "host is [%s]... what's left is [%s]\n", host, pch );*/
1032            if( havePort )
1033            {
1034                char * end;
1035                port = strtol( pch, &end, 10 );
1036                pch = end;
1037/*fprintf( stderr, "port is [%d]... what's left is [%s]\n", port, pch );*/
1038            }
1039            path = pch;
1040/*fprintf( stderr, "path is [%s]\n", path );*/
1041        }
1042    }
1043
1044    err = !host || !path || !protocol;
1045
1046    if( !err && !port )
1047    {
1048        if( !strcmp( protocol, "ftp" ) ) port = 21;
1049        if( !strcmp( protocol, "sftp" ) ) port = 22;
1050        if( !strcmp( protocol, "http" ) ) port = 80;
1051        if( !strcmp( protocol, "https" ) ) port = 443;
1052    }
1053
1054    if( !err )
1055    {
1056        if( setme_protocol ) *setme_protocol = tr_strdup( protocol );
1057
1058        if( setme_host ){ ( (char*)host )[-3] = ':'; *setme_host =
1059                              tr_strdup( host ); }
1060        if( setme_path ){ if( path[0] == '/' ) *setme_path = tr_strdup( path );
1061                          else { ( (char*)path )[-1] = '/'; *setme_path = tr_strdup( path - 1 ); } }
1062        if( setme_port ) *setme_port = port;
1063    }
1064
1065
1066    tr_free( tmp );
1067    return err;
1068}
1069
1070#include <string.h>
1071#include <openssl/sha.h>
1072#include <openssl/hmac.h>
1073#include <openssl/evp.h>
1074#include <openssl/bio.h>
1075#include <openssl/buffer.h>
1076
1077char *
1078tr_base64_encode( const void * input, int length, int * setme_len )
1079{
1080    int retlen = 0;
1081    char * ret = NULL;
1082
1083    if( input != NULL )
1084    {
1085        BIO * b64;
1086        BIO * bmem;
1087        BUF_MEM * bptr;
1088
1089        if( length < 1 )
1090            length = strlen( input );
1091
1092        bmem = BIO_new( BIO_s_mem( ) );
1093        b64 = BIO_new( BIO_f_base64( ) );
1094        b64 = BIO_push( b64, bmem );
1095        BIO_write( b64, input, length );
1096        (void) BIO_flush( b64 );
1097        BIO_get_mem_ptr( b64, &bptr );
1098        ret = tr_strndup( bptr->data, bptr->length );
1099        retlen = bptr->length;
1100        BIO_free_all( b64 );
1101    }
1102
1103    if( setme_len )
1104        *setme_len = retlen;
1105
1106    return ret;
1107}
1108
1109char *
1110tr_base64_decode( const void * input,
1111                  int          length,
1112                  int *        setme_len )
1113{
1114    char * ret;
1115    BIO *  b64;
1116    BIO *  bmem;
1117    int    retlen;
1118
1119    if( length < 1 )
1120        length = strlen( input );
1121
1122    ret = tr_new0( char, length );
1123    b64 = BIO_new( BIO_f_base64( ) );
1124    bmem = BIO_new_mem_buf( (unsigned char*)input, length );
1125    bmem = BIO_push( b64, bmem );
1126    retlen = BIO_read( bmem, ret, length );
1127    if( !retlen )
1128    {
1129        /* try again, but with the BIO_FLAGS_BASE64_NO_NL flag */
1130        BIO_free_all( bmem );
1131        b64 = BIO_new( BIO_f_base64( ) );
1132        BIO_set_flags( b64, BIO_FLAGS_BASE64_NO_NL );
1133        bmem = BIO_new_mem_buf( (unsigned char*)input, length );
1134        bmem = BIO_push( b64, bmem );
1135        retlen = BIO_read( bmem, ret, length );
1136    }
1137
1138    if( setme_len )
1139        *setme_len = retlen;
1140
1141    BIO_free_all( bmem );
1142    return ret;
1143}
1144
1145/***
1146****
1147***/
1148
1149int
1150tr_lowerBound( const void * key,
1151               const void * base,
1152               size_t       nmemb,
1153               size_t       size,
1154               int       (* compar)(const void* key, const void* arrayMember),
1155               tr_bool    * exact_match )
1156{
1157    size_t first = 0;
1158    const char * cbase = base;
1159    tr_bool exact = FALSE;
1160
1161    while( nmemb != 0 )
1162    {
1163        const size_t half = nmemb / 2;
1164        const size_t middle = first + half;
1165        const int c = compar( key, cbase + size*middle );
1166
1167        if( c <= 0 ) {
1168            if( c == 0 )
1169                exact = TRUE;
1170            nmemb = half;
1171        } else {
1172            first = middle + 1;
1173            nmemb = nmemb - half - 1;
1174        }
1175    }
1176
1177    *exact_match = exact;
1178
1179    return first;
1180}
1181
1182/***
1183****
1184***/
1185
1186static char*
1187strip_non_utf8( const char * in, size_t inlen )
1188{
1189    char * ret;
1190    const char * end;
1191    const char zero = '\0';
1192    struct evbuffer * buf = evbuffer_new( );
1193 
1194    while( !tr_utf8_validate( in, inlen, &end ) )
1195    {
1196        const int good_len = end - in;
1197
1198        evbuffer_add( buf, in, good_len );
1199        inlen -= ( good_len + 1 );
1200        in += ( good_len + 1 );
1201        evbuffer_add( buf, "?", 1 );
1202    }
1203 
1204    evbuffer_add( buf, in, inlen );
1205    evbuffer_add( buf, &zero, 1 );
1206    ret = tr_memdup( EVBUFFER_DATA( buf ), EVBUFFER_LENGTH( buf ) );
1207    evbuffer_free( buf );
1208    return ret;
1209}
1210
1211static char*
1212to_utf8( const char * in, size_t inlen )
1213{
1214    char * ret = NULL;
1215
1216#ifdef HAVE_ICONV_OPEN
1217    int i;
1218    const char * encodings[] = { "CURRENT", "ISO-8859-15" };
1219    const int encoding_count = sizeof(encodings) / sizeof(encodings[1]);
1220    const size_t buflen = inlen*4 + 10;
1221    char * out = tr_new( char, buflen );
1222
1223    for( i=0; !ret && i<encoding_count; ++i )
1224    {
1225        char * inbuf = (char*) in;
1226        char * outbuf = out;
1227        size_t inbytesleft = inlen;
1228        size_t outbytesleft = buflen;
1229        const char * test_encoding = encodings[i];
1230
1231        iconv_t cd = iconv_open( "UTF-8", test_encoding );
1232        if( cd != (iconv_t)-1 ) {
1233            if( iconv( cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft ) != (size_t)-1 )
1234                ret = tr_strndup( out, buflen-outbytesleft );
1235            iconv_close( cd );
1236        }
1237    }
1238#endif
1239
1240    if( ret == NULL )
1241        ret = strip_non_utf8( in, inlen );
1242
1243    return ret;
1244}
1245
1246char*
1247tr_utf8clean( const char * str, int max_len )
1248{
1249    char * ret;
1250    const char * end;
1251
1252    if( max_len < 0 )
1253        max_len = (int) strlen( str );
1254
1255    if( tr_utf8_validate( str, max_len, &end  ) )
1256        ret = tr_strndup( str, max_len );
1257    else
1258        ret = to_utf8( str, max_len );
1259
1260    assert( tr_utf8_validate( ret, -1, NULL ) );
1261    return ret;
1262}
1263
1264/***
1265****
1266***/
1267
1268struct number_range
1269{
1270    int low;
1271    int high;
1272};
1273
1274/**
1275 * This should be a single number (ex. "6") or a range (ex. "6-9").
1276 * Anything else is an error and will return failure.
1277 */
1278static tr_bool
1279parseNumberSection( const char * str, int len, struct number_range * setme )
1280{
1281    long a, b;
1282    tr_bool success;
1283    char * end;
1284    const int error = errno;
1285    char * tmp = tr_strndup( str, len );
1286
1287    errno = 0;
1288    a = b = strtol( tmp, &end, 10 );
1289    if( errno || ( end == tmp ) ) {
1290        success = FALSE;
1291    } else if( *end != '-' ) {
1292        b = a;
1293        success = TRUE;
1294    } else {
1295        const char * pch = end + 1;
1296        b = strtol( pch, &end, 10 );
1297        if( errno || ( pch == end ) )
1298            success = FALSE;
1299        else if( *end ) /* trailing data */
1300            success = FALSE;
1301        else
1302            success = TRUE;
1303    }
1304    tr_free( tmp );
1305
1306    setme->low = MIN( a, b );
1307    setme->high = MAX( a, b );
1308
1309    errno = error;
1310    return success;
1311}
1312
1313static int
1314compareInt( const void * va, const void * vb )
1315{
1316    const int a = *(const int *)va;
1317    const int b = *(const int *)vb;
1318    return a - b;
1319}
1320
1321/**
1322 * Given a string like "1-4" or "1-4,6,9,14-51", this allocates and returns an
1323 * array of setmeCount ints of all the values in the array.
1324 * For example, "5-8" will return [ 5, 6, 7, 8 ] and setmeCount will be 4.
1325 * It's the caller's responsibility to call tr_free() on the returned array.
1326 * If a fragment of the string can't be parsed, NULL is returned.
1327 */
1328int*
1329tr_parseNumberRange( const char * str_in, int len, int * setmeCount )
1330{
1331    int n = 0;
1332    int * uniq = NULL;
1333    char * str = tr_strndup( str_in, len );
1334    const char * walk;
1335    tr_list * ranges = NULL;
1336    tr_bool success = TRUE;
1337
1338    walk = str;
1339    while( walk && *walk && success ) {
1340        struct number_range range;
1341        const char * pch = strchr( walk, ',' );
1342        if( pch ) {
1343            success = parseNumberSection( walk, pch-walk, &range );
1344            walk = pch + 1;
1345        } else {
1346            success = parseNumberSection( walk, strlen( walk ), &range );
1347            walk += strlen( walk );
1348        }
1349        if( success )
1350            tr_list_append( &ranges, tr_memdup( &range, sizeof( struct number_range ) ) );
1351    }
1352
1353    if( !success )
1354    {
1355        *setmeCount = 0;
1356        uniq = NULL;
1357    }
1358    else
1359    {
1360        int i;
1361        int n2;
1362        tr_list * l;
1363        int * sorted = NULL;
1364
1365        /* build a sorted number array */
1366        n = n2 = 0;
1367        for( l=ranges; l!=NULL; l=l->next ) {
1368            const struct number_range * r = l->data;
1369            n += r->high + 1 - r->low;
1370        }
1371        sorted = tr_new( int, n );
1372        for( l=ranges; l!=NULL; l=l->next ) {
1373            const struct number_range * r = l->data;
1374            int i;
1375            for( i=r->low; i<=r->high; ++i )
1376                sorted[n2++] = i;
1377        }
1378        qsort( sorted, n, sizeof( int ), compareInt );
1379        assert( n == n2 );
1380
1381        /* remove duplicates */
1382        uniq = tr_new( int, n );
1383        for( i=n=0; i<n2; ++i )
1384            if( !n || uniq[n-1] != sorted[i] )
1385                uniq[n++] = sorted[i];
1386
1387        tr_free( sorted );
1388    }
1389
1390    /* cleanup */
1391    tr_list_free( &ranges, tr_free );
1392    tr_free( str );
1393
1394    /* return the result */
1395    *setmeCount = n;
1396    return uniq;
1397}
1398
1399/***
1400****
1401***/
1402
1403double
1404tr_truncd( double x, int decimal_places )
1405{
1406    const int i = (int) pow( 10, decimal_places ); 
1407    const double x2 = (int64_t)(x * i);
1408    return x2 / i;
1409}
1410
1411char*
1412tr_strratio( char * buf, size_t buflen, double ratio, const char * infinity )
1413{
1414    if( (int)ratio == TR_RATIO_NA )
1415        tr_strlcpy( buf, _( "None" ), buflen );
1416    else if( (int)ratio == TR_RATIO_INF )
1417        tr_strlcpy( buf, infinity, buflen );
1418    else if( ratio < 10.0 )
1419        tr_snprintf( buf, buflen, "%.2f", tr_truncd( ratio, 2 ) );
1420    else if( ratio < 100.0 )
1421        tr_snprintf( buf, buflen, "%.1f", tr_truncd( ratio, 1 ) );
1422    else
1423        tr_snprintf( buf, buflen, "%'.0f", ratio );
1424    return buf;
1425}
1426
1427/***
1428****
1429***/
1430
1431int
1432tr_moveFile( const char * oldpath, const char * newpath, tr_bool * renamed )
1433{
1434    int in;
1435    int out;
1436    char * buf;
1437    struct stat st;
1438    off_t bytesLeft;
1439    const off_t buflen = 1024 * 128; /* 128 KiB buffer */
1440
1441    /* make sure the old file exists */
1442    if( stat( oldpath, &st ) ) {
1443        const int err = errno;
1444        errno = err;
1445        return -1;
1446    }
1447    if( !S_ISREG( st.st_mode ) ) {
1448        errno = ENOENT;
1449        return -1;
1450    }
1451    bytesLeft = st.st_size;
1452
1453    /* make sure the target directory exists */
1454    {
1455        char * newdir = tr_dirname( newpath );
1456        int i = tr_mkdirp( newdir, 0777 );
1457        tr_free( newdir );
1458        if( i )
1459            return i;
1460    }
1461
1462    /* they might be on the same filesystem... */
1463    {
1464        const int i = rename( oldpath, newpath );
1465        if( renamed != NULL )
1466            *renamed = i == 0;
1467        if( !i )
1468            return 0;
1469    }
1470
1471    /* copy the file */
1472    in = tr_open_file_for_scanning( oldpath );
1473    out = tr_open_file_for_writing( newpath );
1474    buf = tr_valloc( buflen );
1475    while( bytesLeft > 0 )
1476    {
1477        ssize_t bytesWritten;
1478        const off_t bytesThisPass = MIN( bytesLeft, buflen );
1479        const int numRead = read( in, buf, bytesThisPass );
1480        if( numRead < 0 )
1481            break;
1482        bytesWritten = write( out, buf, numRead );
1483        if( bytesWritten < 0 )
1484            break;
1485        bytesLeft -= bytesWritten;
1486    }
1487
1488    /* cleanup */
1489    tr_free( buf );
1490    tr_close_file( out );
1491    tr_close_file( in );
1492    if( bytesLeft != 0 )
1493        return -1;
1494
1495    unlink( oldpath );
1496    return 0;
1497}
1498
1499/***
1500****
1501***/
1502
1503void*
1504tr_valloc( size_t bufLen )
1505{
1506    size_t allocLen;
1507    void * buf = NULL;
1508    static size_t pageSize = 0;
1509
1510    if( !pageSize ) {
1511#ifdef HAVE_GETPAGESIZE
1512        pageSize = getpagesize();
1513#else /* guess */
1514        pageSize = 4096;
1515#endif
1516    }
1517
1518    allocLen = pageSize;
1519    while( allocLen < bufLen )
1520        allocLen += pageSize;
1521
1522#ifdef HAVE_POSIX_MEMALIGN
1523    if( !buf )
1524        posix_memalign( &buf, pageSize, allocLen );
1525#endif
1526#ifdef HAVE_VALLOC
1527    if( !buf )
1528        buf = valloc( allocLen );
1529#endif
1530    if( !buf )
1531        buf = malloc( allocLen );
1532
1533    tr_dbg( "tr_valloc(%zu) allocating %zu bytes", bufLen, allocLen );
1534    return buf;
1535}
Note: See TracBrowser for help on using the repository browser.