source: trunk/libtransmission/utils.c @ 10814

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

(trunk libT) part of rb07's win32 portability patches from ticket #3311

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