source: trunk/libtransmission/utils.c @ 10069

Last change on this file since 10069 was 10069, checked in by livings124, 12 years ago

add a convenience function for determining if an address is an IP address, and when picking a favicon in the Mac UI's tracker tab, use the full IP address when applicable

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