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

Last change on this file since 11575 was 11575, checked in by charles, 11 years ago

(2.0x) backport r11574 from #3844 "Error popup when adding a relative path"

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