source: trunk/libtransmission/utils.c @ 11209

Last change on this file since 11209 was 11209, checked in by Longinus00, 11 years ago

switch trackerRemove and trackerReplace rpc calls to use tracker id instead of announce urls as identifiers

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