source: lib/misc.c @ 6ddb223

Last change on this file since 6ddb223 was 07874be, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-08-14T13:44:35Z

Merge mainline stuff.

  • Property mode set to 100644
File size: 15.4 KB
Line 
1  /********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
4  * Copyright 2002-2006 Wilmer van der Gaast and others                *
5  \********************************************************************/
6
7/*
8 * Various utility functions. Some are copied from Gaim to support the
9 * IM-modules, most are from BitlBee.
10 *
11 * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
12 *                          (and possibly other members of the Gaim team)
13 * Copyright 2002-2006 Wilmer van der Gaast <wilmer@gaast.net>
14 */
15
16/*
17  This program is free software; you can redistribute it and/or modify
18  it under the terms of the GNU General Public License as published by
19  the Free Software Foundation; either version 2 of the License, or
20  (at your option) any later version.
21
22  This program is distributed in the hope that it will be useful,
23  but WITHOUT ANY WARRANTY; without even the implied warranty of
24  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25  GNU General Public License for more details.
26
27  You should have received a copy of the GNU General Public License with
28  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
29  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
30  Suite 330, Boston, MA  02111-1307  USA
31*/
32
33#define BITLBEE_CORE
34#include "nogaim.h"
35#include "base64.h"
36#include "md5.h"
37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
40#include <ctype.h>
41#include <glib.h>
42#include <time.h>
43
44#ifdef HAVE_RESOLV_A
45#include <arpa/nameser.h>
46#include <resolv.h>
47#endif
48
49#include "md5.h"
50#include "ssl_client.h"
51
52void strip_linefeed(gchar *text)
53{
54        int i, j;
55        gchar *text2 = g_malloc(strlen(text) + 1);
56
57        for (i = 0, j = 0; text[i]; i++)
58                if (text[i] != '\r')
59                        text2[j++] = text[i];
60        text2[j] = '\0';
61
62        strcpy(text, text2);
63        g_free(text2);
64}
65
66time_t get_time(int year, int month, int day, int hour, int min, int sec)
67{
68        struct tm tm;
69
70        memset(&tm, 0, sizeof(struct tm));
71        tm.tm_year = year - 1900;
72        tm.tm_mon = month - 1;
73        tm.tm_mday = day;
74        tm.tm_hour = hour;
75        tm.tm_min = min;
76        tm.tm_sec = sec >= 0 ? sec : time(NULL) % 60;
77       
78        return mktime(&tm);
79}
80
81time_t mktime_utc( struct tm *tp )
82{
83        struct tm utc;
84        time_t res, tres;
85       
86        tp->tm_isdst = -1;
87        res = mktime( tp );
88        /* Problem is, mktime() just gave us the GMT timestamp for the
89           given local time... While the given time WAS NOT local. So
90           we should fix this now.
91           
92           Now I could choose between messing with environment variables
93           (kludgy) or using timegm() (not portable)... Or doing the
94           following, which I actually prefer...
95           
96           tzset() may also work but in other places I actually want to
97           use local time.
98           
99           FFFFFFFFFFFFFFFFFFFFFUUUUUUUUUUUUUUUUUUUU!! */
100        gmtime_r( &res, &utc );
101        utc.tm_isdst = -1;
102        if( utc.tm_hour == tp->tm_hour && utc.tm_min == tp->tm_min )
103                /* Sweet! We're in UTC right now... */
104                return res;
105       
106        tres = mktime( &utc );
107        res += res - tres;
108       
109        /* Yes, this is a hack. And it will go wrong around DST changes.
110           BUT this is more likely to be threadsafe than messing with
111           environment variables, and possibly more portable... */
112       
113        return res;
114}
115
116typedef struct htmlentity
117{
118        char code[7];
119        char is[3];
120} htmlentity_t;
121
122static const htmlentity_t ent[] =
123{
124        { "lt",     "<" },
125        { "gt",     ">" },
126        { "amp",    "&" },
127        { "apos",   "'" },
128        { "quot",   "\"" },
129        { "aacute", "á" },
130        { "eacute", "é" },
131        { "iacute", "é" },
132        { "oacute", "ó" },
133        { "uacute", "ú" },
134        { "agrave", "à" },
135        { "egrave", "è" },
136        { "igrave", "ì" },
137        { "ograve", "ò" },
138        { "ugrave", "ù" },
139        { "acirc",  "â" },
140        { "ecirc",  "ê" },
141        { "icirc",  "î" },
142        { "ocirc",  "ô" },
143        { "ucirc",  "û" },
144        { "auml",   "ä" },
145        { "euml",   "ë" },
146        { "iuml",   "ï" },
147        { "ouml",   "ö" },
148        { "uuml",   "ü" },
149        { "nbsp",   " " },
150        { "",        ""  }
151};
152
153void strip_html( char *in )
154{
155        char *start = in;
156        char out[strlen(in)+1];
157        char *s = out, *cs;
158        int i, matched;
159       
160        memset( out, 0, sizeof( out ) );
161       
162        while( *in )
163        {
164                if( *in == '<' && ( isalpha( *(in+1) ) || *(in+1) == '/' ) )
165                {
166                        /* If in points at a < and in+1 points at a letter or a slash, this is probably
167                           a HTML-tag. Try to find a closing > and continue there. If the > can't be
168                           found, assume that it wasn't a HTML-tag after all. */
169                       
170                        cs = in;
171                       
172                        while( *in && *in != '>' )
173                                in ++;
174                       
175                        if( *in )
176                        {
177                                if( g_strncasecmp( cs+1, "br", 2) == 0 )
178                                        *(s++) = '\n';
179                                in ++;
180                        }
181                        else
182                        {
183                                in = cs;
184                                *(s++) = *(in++);
185                        }
186                }
187                else if( *in == '&' )
188                {
189                        cs = ++in;
190                        while( *in && isalpha( *in ) )
191                                in ++;
192                       
193                        if( *in == ';' ) in ++;
194                        matched = 0;
195                       
196                        for( i = 0; *ent[i].code; i ++ )
197                                if( g_strncasecmp( ent[i].code, cs, strlen( ent[i].code ) ) == 0 )
198                                {
199                                        int j;
200                                       
201                                        for( j = 0; ent[i].is[j]; j ++ )
202                                                *(s++) = ent[i].is[j];
203                                       
204                                        matched = 1;
205                                        break;
206                                }
207
208                        /* None of the entities were matched, so return the string */
209                        if( !matched )
210                        {
211                                in = cs - 1;
212                                *(s++) = *(in++);
213                        }
214                }
215                else
216                {
217                        *(s++) = *(in++);
218                }
219        }
220       
221        strcpy( start, out );
222}
223
224char *escape_html( const char *html )
225{
226        const char *c = html;
227        GString *ret;
228        char *str;
229       
230        if( html == NULL )
231                return( NULL );
232       
233        ret = g_string_new( "" );
234       
235        while( *c )
236        {
237                switch( *c )
238                {
239                        case '&':
240                                ret = g_string_append( ret, "&amp;" );
241                                break;
242                        case '<':
243                                ret = g_string_append( ret, "&lt;" );
244                                break;
245                        case '>':
246                                ret = g_string_append( ret, "&gt;" );
247                                break;
248                        case '"':
249                                ret = g_string_append( ret, "&quot;" );
250                                break;
251                        default:
252                                ret = g_string_append_c( ret, *c );
253                }
254                c ++;
255        }
256       
257        str = ret->str;
258        g_string_free( ret, FALSE );
259        return( str );
260}
261
262/* Decode%20a%20file%20name                                             */
263void http_decode( char *s )
264{
265        char *t;
266        int i, j, k;
267       
268        t = g_new( char, strlen( s ) + 1 );
269       
270        for( i = j = 0; s[i]; i ++, j ++ )
271        {
272                if( s[i] == '%' )
273                {
274                        if( sscanf( s + i + 1, "%2x", &k ) )
275                        {
276                                t[j] = k;
277                                i += 2;
278                        }
279                        else
280                        {
281                                *t = 0;
282                                break;
283                        }
284                }
285                else
286                {
287                        t[j] = s[i];
288                }
289        }
290        t[j] = 0;
291       
292        strcpy( s, t );
293        g_free( t );
294}
295
296/* Warning: This one explodes the string. Worst-cases can make the string 3x its original size! */
297/* This fuction is safe, but make sure you call it safely as well! */
298void http_encode( char *s )
299{
300        char t[strlen(s)+1];
301        int i, j;
302       
303        strcpy( t, s );
304        for( i = j = 0; t[i]; i ++, j ++ )
305        {
306                /* Warning: isalnum() is locale-aware, so don't use it here! */
307                if( ( t[i] >= 'A' && t[i] <= 'Z' ) ||
308                    ( t[i] >= 'a' && t[i] <= 'z' ) ||
309                    ( t[i] >= '0' && t[i] <= '9' ) ||
310                    strchr( "._-~", t[i] ) )
311                {
312                        s[j] = t[i];
313                }
314                else
315                {
316                        sprintf( s + j, "%%%02X", ((unsigned char*)t)[i] );
317                        j += 2;
318                }
319        }
320        s[j] = 0;
321}
322
323/* Strip newlines from a string. Modifies the string passed to it. */ 
324char *strip_newlines( char *source )
325{
326        int i; 
327
328        for( i = 0; source[i] != '\0'; i ++ )
329                if( source[i] == '\n' || source[i] == '\r' )
330                        source[i] = ' ';
331       
332        return source;
333}
334
335/* Wrap an IPv4 address into IPv6 space. Not thread-safe... */
336char *ipv6_wrap( char *src )
337{
338        static char dst[64];
339        int i;
340       
341        for( i = 0; src[i]; i ++ )
342                if( ( src[i] < '0' || src[i] > '9' ) && src[i] != '.' )
343                        break;
344       
345        /* Hmm, it's not even an IP... */
346        if( src[i] )
347                return src;
348       
349        g_snprintf( dst, sizeof( dst ), "::ffff:%s", src );
350       
351        return dst;
352}
353
354/* Unwrap an IPv4 address into IPv6 space. Thread-safe, because it's very simple. :-) */
355char *ipv6_unwrap( char *src )
356{
357        int i;
358       
359        if( g_strncasecmp( src, "::ffff:", 7 ) != 0 )
360                return src;
361       
362        for( i = 7; src[i]; i ++ )
363                if( ( src[i] < '0' || src[i] > '9' ) && src[i] != '.' )
364                        break;
365       
366        /* Hmm, it's not even an IP... */
367        if( src[i] )
368                return src;
369       
370        return ( src + 7 );
371}
372
373/* Convert from one charset to another.
374   
375   from_cs, to_cs: Source and destination charsets
376   src, dst: Source and destination strings
377   size: Size if src. 0 == use strlen(). strlen() is not reliable for UNICODE/UTF16 strings though.
378   maxbuf: Maximum number of bytes to write to dst
379   
380   Returns the number of bytes written to maxbuf or -1 on an error.
381*/
382signed int do_iconv( char *from_cs, char *to_cs, char *src, char *dst, size_t size, size_t maxbuf )
383{
384        GIConv cd;
385        size_t res;
386        size_t inbytesleft, outbytesleft;
387        char *inbuf = src;
388        char *outbuf = dst;
389       
390        cd = g_iconv_open( to_cs, from_cs );
391        if( cd == (GIConv) -1 )
392                return( -1 );
393       
394        inbytesleft = size ? size : strlen( src );
395        outbytesleft = maxbuf - 1;
396        res = g_iconv( cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft );
397        *outbuf = '\0';
398        g_iconv_close( cd );
399       
400        if( res == (size_t) -1 )
401                return( -1 );
402        else
403                return( outbuf - dst );
404}
405
406/* A pretty reliable random number generator. Tries to use the /dev/random
407   devices first, and falls back to the random number generator from libc
408   when it fails. Opens randomizer devices with O_NONBLOCK to make sure a
409   lack of entropy won't halt BitlBee. */
410void random_bytes( unsigned char *buf, int count )
411{
412#ifndef _WIN32
413        static int use_dev = -1;
414       
415        /* Actually this probing code isn't really necessary, is it? */
416        if( use_dev == -1 )
417        {
418                if( access( "/dev/random", R_OK ) == 0 || access( "/dev/urandom", R_OK ) == 0 )
419                        use_dev = 1;
420                else
421                {
422                        use_dev = 0;
423                        srand( ( getpid() << 16 ) ^ time( NULL ) );
424                }
425        }
426       
427        if( use_dev )
428        {
429                int fd;
430               
431                /* At least on Linux, /dev/random can block if there's not
432                   enough entropy. We really don't want that, so if it can't
433                   give anything, use /dev/urandom instead. */
434                if( ( fd = open( "/dev/random", O_RDONLY | O_NONBLOCK ) ) >= 0 )
435                        if( read( fd, buf, count ) == count )
436                        {
437                                close( fd );
438                                return;
439                        }
440                close( fd );
441               
442                /* urandom isn't supposed to block at all, but just to be
443                   sure. If it blocks, we'll disable use_dev and use the libc
444                   randomizer instead. */
445                if( ( fd = open( "/dev/urandom", O_RDONLY | O_NONBLOCK ) ) >= 0 )
446                        if( read( fd, buf, count ) == count )
447                        {
448                                close( fd );
449                                return;
450                        }
451                close( fd );
452               
453                /* If /dev/random blocks once, we'll still try to use it
454                   again next time. If /dev/urandom also fails for some
455                   reason, stick with libc during this session. */
456               
457                use_dev = 0;
458                srand( ( getpid() << 16 ) ^ time( NULL ) );
459        }
460       
461        if( !use_dev )
462#endif
463        {
464                int i;
465               
466                /* Possibly the LSB of rand() isn't very random on some
467                   platforms. Seems okay on at least Linux and OSX though. */
468                for( i = 0; i < count; i ++ )
469                        buf[i] = rand() & 0xff;
470        }
471}
472
473int is_bool( char *value )
474{
475        if( *value == 0 )
476                return 0;
477       
478        if( ( g_strcasecmp( value, "true" ) == 0 ) || ( g_strcasecmp( value, "yes" ) == 0 ) || ( g_strcasecmp( value, "on" ) == 0 ) )
479                return 1;
480        if( ( g_strcasecmp( value, "false" ) == 0 ) || ( g_strcasecmp( value, "no" ) == 0 ) || ( g_strcasecmp( value, "off" ) == 0 ) )
481                return 1;
482       
483        while( *value )
484                if( !isdigit( *value ) )
485                        return 0;
486                else
487                        value ++;
488       
489        return 1;
490}
491
492int bool2int( char *value )
493{
494        int i;
495       
496        if( ( g_strcasecmp( value, "true" ) == 0 ) || ( g_strcasecmp( value, "yes" ) == 0 ) || ( g_strcasecmp( value, "on" ) == 0 ) )
497                return 1;
498        if( ( g_strcasecmp( value, "false" ) == 0 ) || ( g_strcasecmp( value, "no" ) == 0 ) || ( g_strcasecmp( value, "off" ) == 0 ) )
499                return 0;
500       
501        if( sscanf( value, "%d", &i ) == 1 )
502                return i;
503       
504        return 0;
505}
506
507struct ns_srv_reply **srv_lookup( char *service, char *protocol, char *domain )
508{       
509        struct ns_srv_reply **replies = NULL;
510#ifdef HAVE_RESOLV_A
511        struct ns_srv_reply *reply = NULL;
512        char name[1024];
513        unsigned char querybuf[1024];
514        const unsigned char *buf;
515        ns_msg nsh;
516        ns_rr rr;
517        int i, n, len, size;
518       
519        g_snprintf( name, sizeof( name ), "_%s._%s.%s", service, protocol, domain );
520       
521        if( ( size = res_query( name, ns_c_in, ns_t_srv, querybuf, sizeof( querybuf ) ) ) <= 0 )
522                return NULL;
523       
524        if( ns_initparse( querybuf, size, &nsh ) != 0 )
525                return NULL;
526       
527        n = 0;
528        while( ns_parserr( &nsh, ns_s_an, n, &rr ) == 0 )
529        {
530                size = ns_rr_rdlen( rr );
531                buf = ns_rr_rdata( rr );
532               
533                len = 0;
534                for( i = 6; i < size && buf[i]; i += buf[i] + 1 )
535                        len += buf[i] + 1;
536               
537                if( i > size )
538                        break;
539               
540                reply = g_malloc( sizeof( struct ns_srv_reply ) + len );
541                memcpy( reply->name, buf + 7, len );
542               
543                for( i = buf[6]; i < len && buf[7+i]; i += buf[7+i] + 1 )
544                        reply->name[i] = '.';
545               
546                if( i > len )
547                {
548                        g_free( reply );
549                        break;
550                }
551               
552                reply->prio = ( buf[0] << 8 ) | buf[1];
553                reply->weight = ( buf[2] << 8 ) | buf[3];
554                reply->port = ( buf[4] << 8 ) | buf[5];
555               
556                n ++;
557                replies = g_renew( struct ns_srv_reply *, replies, n + 1 );
558                replies[n-1] = reply;
559        }
560        if( replies )
561                replies[n] = NULL;
562#endif
563       
564        return replies;
565}
566
567void srv_free( struct ns_srv_reply **srv )
568{
569        int i;
570       
571        if( srv == NULL )
572                return;
573       
574        for( i = 0; srv[i]; i ++ )
575                g_free( srv[i] );
576        g_free( srv );
577}
578
579/* Word wrapping. Yes, I know this isn't UTF-8 clean. I'm willing to take the risk. */
580char *word_wrap( const char *msg, int line_len )
581{
582        GString *ret = g_string_sized_new( strlen( msg ) + 16 );
583       
584        while( strlen( msg ) > line_len )
585        {
586                int i;
587               
588                /* First try to find out if there's a newline already. Don't
589                   want to add more splits than necessary. */
590                for( i = line_len; i > 0 && msg[i] != '\n'; i -- );
591                if( msg[i] == '\n' )
592                {
593                        g_string_append_len( ret, msg, i + 1 );
594                        msg += i + 1;
595                        continue;
596                }
597               
598                for( i = line_len; i > 0; i -- )
599                {
600                        if( msg[i] == '-' )
601                        {
602                                g_string_append_len( ret, msg, i + 1 );
603                                g_string_append_c( ret, '\n' );
604                                msg += i + 1;
605                                break;
606                        }
607                        else if( msg[i] == ' ' )
608                        {
609                                g_string_append_len( ret, msg, i );
610                                g_string_append_c( ret, '\n' );
611                                msg += i + 1;
612                                break;
613                        }
614                }
615                if( i == 0 )
616                {
617                        g_string_append_len( ret, msg, line_len );
618                        g_string_append_c( ret, '\n' );
619                        msg += line_len;
620                }
621        }
622        g_string_append( ret, msg );
623       
624        return g_string_free( ret, FALSE );
625}
626
627gboolean ssl_sockerr_again( void *ssl )
628{
629        if( ssl )
630                return ssl_errno == SSL_AGAIN;
631        else
632                return sockerr_again();
633}
634
635/* Returns values: -1 == Failure (base64-decoded to something unexpected)
636                    0 == Okay
637                    1 == Password doesn't match the hash. */
638int md5_verify_password( char *password, char *hash )
639{
640        md5_byte_t *pass_dec = NULL;
641        md5_byte_t pass_md5[16];
642        md5_state_t md5_state;
643        int ret = -1, i;
644       
645        if( base64_decode( hash, &pass_dec ) == 21 )
646        {
647                md5_init( &md5_state );
648                md5_append( &md5_state, (md5_byte_t*) password, strlen( password ) );
649                md5_append( &md5_state, (md5_byte_t*) pass_dec + 16, 5 ); /* Hmmm, salt! */
650                md5_finish( &md5_state, pass_md5 );
651               
652                for( i = 0; i < 16; i ++ )
653                {
654                        if( pass_dec[i] != pass_md5[i] )
655                        {
656                                ret = 1;
657                                break;
658                        }
659                }
660               
661                /* If we reached the end of the loop, it was a match! */
662                if( i == 16 )
663                        ret = 0;
664        }
665       
666        g_free( pass_dec );
667
668        return ret;
669}
670
671/* Split commands (root-style, *not* IRC-style). Handles "quoting of"
672   white\ space in 'various ways'. Returns a NULL-terminated static
673   char** so watch out with nested use! Definitely not thread-safe. */
674char **split_command_parts( char *command )
675{
676        static char *cmd[IRC_MAX_ARGS+1];
677        char *s, q = 0;
678        int k;
679       
680        memset( cmd, 0, sizeof( cmd ) );
681        cmd[0] = command;
682        k = 1;
683        for( s = command; *s && k < IRC_MAX_ARGS; s ++ )
684                if( *s == ' ' && !q )
685                {
686                        *s = 0;
687                        while( *++s == ' ' );
688                        if( *s == '"' || *s == '\'' )
689                        {
690                                q = *s;
691                                s ++;
692                        }
693                        if( *s )
694                        {
695                                cmd[k++] = s;
696                                s --;
697                        }
698                        else
699                        {
700                                break;
701                        }
702                }
703                else if( *s == '\\' && ( ( !q && s[1] ) || ( q && q == s[1] ) ) )
704                {
705                        char *cpy;
706                       
707                        for( cpy = s; *cpy; cpy ++ )
708                                cpy[0] = cpy[1];
709                }
710                else if( *s == q )
711                {
712                        q = *s = 0;
713                }
714       
715        /* Full zero-padding for easier argc checking. */
716        while( k <= IRC_MAX_ARGS )
717                cmd[k++] = NULL;
718       
719        return cmd;
720}
Note: See TracBrowser for help on using the repository browser.