source: lib/misc.c @ bb09b3c

Last change on this file since bb09b3c was bb09b3c, checked in by Sven Moritz Hallberg <pesco@…>, at 2010-06-03T21:13:57Z

merge in bitlbee 1.2.7

  • Property mode set to 100644
File size: 14.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 = g_malloc( strlen( in ) + 1 );
157        char *s = out, *cs;
158        int i, matched;
159        int taglen;
160       
161        memset( out, 0, strlen( in ) + 1 );
162       
163        while( *in )
164        {
165                if( *in == '<' && ( isalpha( *(in+1) ) || *(in+1) == '/' ) )
166                {
167                        /* If in points at a < and in+1 points at a letter or a slash, this is probably
168                           a HTML-tag. Try to find a closing > and continue there. If the > can't be
169                           found, assume that it wasn't a HTML-tag after all. */
170                       
171                        cs = in;
172                       
173                        while( *in && *in != '>' )
174                                in ++;
175                       
176                        taglen = in-cs-1;   /* not <0 because the above loop runs at least once */
177                        if( *in )
178                        {
179                                if( g_strncasecmp( cs+1, "b", taglen) == 0 )
180                                        *(s++) = '\x02';
181                                else if( g_strncasecmp( cs+1, "/b", taglen) == 0 )
182                                        *(s++) = '\x02';
183                                else if( g_strncasecmp( cs+1, "i", taglen) == 0 )
184                                        *(s++) = '\x1f';
185                                else if( g_strncasecmp( cs+1, "/i", taglen) == 0 )
186                                        *(s++) = '\x1f';
187                                else if( g_strncasecmp( cs+1, "br", 2) == 0 )
188                                        *(s++) = '\n';
189                                in ++;
190                        }
191                        else
192                        {
193                                in = cs;
194                                *(s++) = *(in++);
195                        }
196                }
197                else if( *in == '&' )
198                {
199                        cs = ++in;
200                        while( *in && isalpha( *in ) )
201                                in ++;
202                       
203                        if( *in == ';' ) in ++;
204                        matched = 0;
205                       
206                        for( i = 0; *ent[i].code; i ++ )
207                                if( g_strncasecmp( ent[i].code, cs, strlen( ent[i].code ) ) == 0 )
208                                {
209                                        int j;
210                                       
211                                        for( j = 0; ent[i].is[j]; j ++ )
212                                                *(s++) = ent[i].is[j];
213                                       
214                                        matched = 1;
215                                        break;
216                                }
217
218                        /* None of the entities were matched, so return the string */
219                        if( !matched )
220                        {
221                                in = cs - 1;
222                                *(s++) = *(in++);
223                        }
224                }
225                else
226                {
227                        *(s++) = *(in++);
228                }
229        }
230       
231        strcpy( start, out );
232        g_free( out );
233}
234
235char *escape_html( const char *html )
236{
237        const char *c = html;
238        GString *ret;
239        char *str;
240       
241        if( html == NULL )
242                return( NULL );
243       
244        ret = g_string_new( "" );
245       
246        while( *c )
247        {
248                switch( *c )
249                {
250                        case '&':
251                                ret = g_string_append( ret, "&amp;" );
252                                break;
253                        case '<':
254                                ret = g_string_append( ret, "&lt;" );
255                                break;
256                        case '>':
257                                ret = g_string_append( ret, "&gt;" );
258                                break;
259                        case '"':
260                                ret = g_string_append( ret, "&quot;" );
261                                break;
262                        default:
263                                ret = g_string_append_c( ret, *c );
264                }
265                c ++;
266        }
267       
268        str = ret->str;
269        g_string_free( ret, FALSE );
270        return( str );
271}
272
273/* Decode%20a%20file%20name                                             */
274void http_decode( char *s )
275{
276        char *t;
277        int i, j, k;
278       
279        t = g_new( char, strlen( s ) + 1 );
280       
281        for( i = j = 0; s[i]; i ++, j ++ )
282        {
283                if( s[i] == '%' )
284                {
285                        if( sscanf( s + i + 1, "%2x", &k ) )
286                        {
287                                t[j] = k;
288                                i += 2;
289                        }
290                        else
291                        {
292                                *t = 0;
293                                break;
294                        }
295                }
296                else
297                {
298                        t[j] = s[i];
299                }
300        }
301        t[j] = 0;
302       
303        strcpy( s, t );
304        g_free( t );
305}
306
307/* Warning: This one explodes the string. Worst-cases can make the string 3x its original size! */
308/* This fuction is safe, but make sure you call it safely as well! */
309void http_encode( char *s )
310{
311        char *t;
312        int i, j;
313       
314        t = g_strdup( s );
315       
316        for( i = j = 0; t[i]; i ++, j ++ )
317        {
318                if( !isalnum( t[i] ) && !strchr( "._-~", t[i] ) )
319                {
320                        sprintf( s + j, "%%%02X", ((unsigned char*)t)[i] );
321                        j += 2;
322                }
323                else
324                {
325                        s[j] = t[i];
326                }
327        }
328        s[j] = 0;
329       
330        g_free( t );
331}
332
333/* Strip newlines from a string. Modifies the string passed to it. */ 
334char *strip_newlines( char *source )
335{
336        int i; 
337
338        for( i = 0; source[i] != '\0'; i ++ )
339                if( source[i] == '\n' || source[i] == '\r' )
340                        source[i] = ' ';
341       
342        return source;
343}
344
345/* Wrap an IPv4 address into IPv6 space. Not thread-safe... */
346char *ipv6_wrap( char *src )
347{
348        static char dst[64];
349        int i;
350       
351        for( i = 0; src[i]; i ++ )
352                if( ( src[i] < '0' || src[i] > '9' ) && src[i] != '.' )
353                        break;
354       
355        /* Hmm, it's not even an IP... */
356        if( src[i] )
357                return src;
358       
359        g_snprintf( dst, sizeof( dst ), "::ffff:%s", src );
360       
361        return dst;
362}
363
364/* Unwrap an IPv4 address into IPv6 space. Thread-safe, because it's very simple. :-) */
365char *ipv6_unwrap( char *src )
366{
367        int i;
368       
369        if( g_strncasecmp( src, "::ffff:", 7 ) != 0 )
370                return src;
371       
372        for( i = 7; src[i]; i ++ )
373                if( ( src[i] < '0' || src[i] > '9' ) && src[i] != '.' )
374                        break;
375       
376        /* Hmm, it's not even an IP... */
377        if( src[i] )
378                return src;
379       
380        return ( src + 7 );
381}
382
383/* Convert from one charset to another.
384   
385   from_cs, to_cs: Source and destination charsets
386   src, dst: Source and destination strings
387   size: Size if src. 0 == use strlen(). strlen() is not reliable for UNICODE/UTF16 strings though.
388   maxbuf: Maximum number of bytes to write to dst
389   
390   Returns the number of bytes written to maxbuf or -1 on an error.
391*/
392signed int do_iconv( char *from_cs, char *to_cs, char *src, char *dst, size_t size, size_t maxbuf )
393{
394        GIConv cd;
395        size_t res;
396        size_t inbytesleft, outbytesleft;
397        char *inbuf = src;
398        char *outbuf = dst;
399       
400        cd = g_iconv_open( to_cs, from_cs );
401        if( cd == (GIConv) -1 )
402                return( -1 );
403       
404        inbytesleft = size ? size : strlen( src );
405        outbytesleft = maxbuf - 1;
406        res = g_iconv( cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft );
407        *outbuf = '\0';
408        g_iconv_close( cd );
409       
410        if( res == (size_t) -1 )
411                return( -1 );
412        else
413                return( outbuf - dst );
414}
415
416/* A pretty reliable random number generator. Tries to use the /dev/random
417   devices first, and falls back to the random number generator from libc
418   when it fails. Opens randomizer devices with O_NONBLOCK to make sure a
419   lack of entropy won't halt BitlBee. */
420void random_bytes( unsigned char *buf, int count )
421{
422#ifndef _WIN32
423        static int use_dev = -1;
424       
425        /* Actually this probing code isn't really necessary, is it? */
426        if( use_dev == -1 )
427        {
428                if( access( "/dev/random", R_OK ) == 0 || access( "/dev/urandom", R_OK ) == 0 )
429                        use_dev = 1;
430                else
431                {
432                        use_dev = 0;
433                        srand( ( getpid() << 16 ) ^ time( NULL ) );
434                }
435        }
436       
437        if( use_dev )
438        {
439                int fd;
440               
441                /* At least on Linux, /dev/random can block if there's not
442                   enough entropy. We really don't want that, so if it can't
443                   give anything, use /dev/urandom instead. */
444                if( ( fd = open( "/dev/random", O_RDONLY | O_NONBLOCK ) ) >= 0 )
445                        if( read( fd, buf, count ) == count )
446                        {
447                                close( fd );
448                                return;
449                        }
450                close( fd );
451               
452                /* urandom isn't supposed to block at all, but just to be
453                   sure. If it blocks, we'll disable use_dev and use the libc
454                   randomizer instead. */
455                if( ( fd = open( "/dev/urandom", O_RDONLY | O_NONBLOCK ) ) >= 0 )
456                        if( read( fd, buf, count ) == count )
457                        {
458                                close( fd );
459                                return;
460                        }
461                close( fd );
462               
463                /* If /dev/random blocks once, we'll still try to use it
464                   again next time. If /dev/urandom also fails for some
465                   reason, stick with libc during this session. */
466               
467                use_dev = 0;
468                srand( ( getpid() << 16 ) ^ time( NULL ) );
469        }
470       
471        if( !use_dev )
472#endif
473        {
474                int i;
475               
476                /* Possibly the LSB of rand() isn't very random on some
477                   platforms. Seems okay on at least Linux and OSX though. */
478                for( i = 0; i < count; i ++ )
479                        buf[i] = rand() & 0xff;
480        }
481}
482
483int is_bool( char *value )
484{
485        if( *value == 0 )
486                return 0;
487       
488        if( ( g_strcasecmp( value, "true" ) == 0 ) || ( g_strcasecmp( value, "yes" ) == 0 ) || ( g_strcasecmp( value, "on" ) == 0 ) )
489                return 1;
490        if( ( g_strcasecmp( value, "false" ) == 0 ) || ( g_strcasecmp( value, "no" ) == 0 ) || ( g_strcasecmp( value, "off" ) == 0 ) )
491                return 1;
492       
493        while( *value )
494                if( !isdigit( *value ) )
495                        return 0;
496                else
497                        value ++;
498       
499        return 1;
500}
501
502int bool2int( char *value )
503{
504        int i;
505       
506        if( ( g_strcasecmp( value, "true" ) == 0 ) || ( g_strcasecmp( value, "yes" ) == 0 ) || ( g_strcasecmp( value, "on" ) == 0 ) )
507                return 1;
508        if( ( g_strcasecmp( value, "false" ) == 0 ) || ( g_strcasecmp( value, "no" ) == 0 ) || ( g_strcasecmp( value, "off" ) == 0 ) )
509                return 0;
510       
511        if( sscanf( value, "%d", &i ) == 1 )
512                return i;
513       
514        return 0;
515}
516
517struct ns_srv_reply *srv_lookup( char *service, char *protocol, char *domain )
518{       
519        struct ns_srv_reply *reply = NULL;
520#ifdef HAVE_RESOLV_A
521        char name[1024];
522        unsigned char querybuf[1024];
523        const unsigned char *buf;
524        ns_msg nsh;
525        ns_rr rr;
526        int i, len, size;
527       
528        g_snprintf( name, sizeof( name ), "_%s._%s.%s", service, protocol, domain );
529       
530        if( ( size = res_query( name, ns_c_in, ns_t_srv, querybuf, sizeof( querybuf ) ) ) <= 0 )
531                return NULL;
532       
533        if( ns_initparse( querybuf, size, &nsh ) != 0 )
534                return NULL;
535       
536        if( ns_parserr( &nsh, ns_s_an, 0, &rr ) != 0 )
537                return NULL;
538       
539        size = ns_rr_rdlen( rr );
540        buf = ns_rr_rdata( rr );
541       
542        len = 0;
543        for( i = 6; i < size && buf[i]; i += buf[i] + 1 )
544                len += buf[i] + 1;
545       
546        if( i > size )
547                return NULL;
548       
549        reply = g_malloc( sizeof( struct ns_srv_reply ) + len );
550        memcpy( reply->name, buf + 7, len );
551       
552        for( i = buf[6]; i < len && buf[7+i]; i += buf[7+i] + 1 )
553                reply->name[i] = '.';
554       
555        if( i > len )
556        {
557                g_free( reply );
558                return NULL;
559        }
560       
561        reply->prio = ( buf[0] << 8 ) | buf[1];
562        reply->weight = ( buf[2] << 8 ) | buf[3];
563        reply->port = ( buf[4] << 8 ) | buf[5];
564#endif
565       
566        return reply;
567}
568
569/* Word wrapping. Yes, I know this isn't UTF-8 clean. I'm willing to take the risk. */
570char *word_wrap( const char *msg, int line_len )
571{
572        GString *ret = g_string_sized_new( strlen( msg ) + 16 );
573       
574        while( strlen( msg ) > line_len )
575        {
576                int i;
577               
578                /* First try to find out if there's a newline already. Don't
579                   want to add more splits than necessary. */
580                for( i = line_len; i > 0 && msg[i] != '\n'; i -- );
581                if( msg[i] == '\n' )
582                {
583                        g_string_append_len( ret, msg, i + 1 );
584                        msg += i + 1;
585                        continue;
586                }
587               
588                for( i = line_len; i > 0; i -- )
589                {
590                        if( msg[i] == '-' )
591                        {
592                                g_string_append_len( ret, msg, i + 1 );
593                                g_string_append_c( ret, '\n' );
594                                msg += i + 1;
595                                break;
596                        }
597                        else if( msg[i] == ' ' )
598                        {
599                                g_string_append_len( ret, msg, i );
600                                g_string_append_c( ret, '\n' );
601                                msg += i + 1;
602                                break;
603                        }
604                }
605                if( i == 0 )
606                {
607                        g_string_append_len( ret, msg, line_len );
608                        g_string_append_c( ret, '\n' );
609                        msg += line_len;
610                }
611        }
612        g_string_append( ret, msg );
613       
614        return g_string_free( ret, FALSE );
615}
616
617gboolean ssl_sockerr_again( void *ssl )
618{
619        if( ssl )
620                return ssl_errno == SSL_AGAIN;
621        else
622                return sockerr_again();
623}
624
625/* Returns values: -1 == Failure (base64-decoded to something unexpected)
626                    0 == Okay
627                    1 == Password doesn't match the hash. */
628int md5_verify_password( char *password, char *hash )
629{
630        md5_byte_t *pass_dec = NULL;
631        md5_byte_t pass_md5[16];
632        md5_state_t md5_state;
633        int ret = -1, i;
634       
635        if( base64_decode( hash, &pass_dec ) == 21 )
636        {
637                md5_init( &md5_state );
638                md5_append( &md5_state, (md5_byte_t*) password, strlen( password ) );
639                md5_append( &md5_state, (md5_byte_t*) pass_dec + 16, 5 ); /* Hmmm, salt! */
640                md5_finish( &md5_state, pass_md5 );
641               
642                for( i = 0; i < 16; i ++ )
643                {
644                        if( pass_dec[i] != pass_md5[i] )
645                        {
646                                ret = 1;
647                                break;
648                        }
649                }
650               
651                /* If we reached the end of the loop, it was a match! */
652                if( i == 16 )
653                        ret = 0;
654        }
655       
656        g_free( pass_dec );
657
658        return ret;
659}
Note: See TracBrowser for help on using the repository browser.