source: trunk/libtransmission/utils.c @ 10277

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

(trunk libT) use tr_valloc() in a couple of places where we allocate largish, short-term buffers

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