source: lib/misc.c @ a6b2f13

Last change on this file since a6b2f13 was 814aa52, checked in by Sven Moritz Hallberg <pesco@…>, at 2010-06-03T11:00:45Z

merge in bitlbee 1.2.6

  • Property mode set to 100644
File size: 14.5 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( t[i] <= ' ' || ((unsigned char *)t)[i] >= 128 || t[i] == '%' ) */
319                if( !isalnum( t[i] ) )
320                {
321                        sprintf( s + j, "%%%02X", ((unsigned char*)t)[i] );
322                        j += 2;
323                }
324                else
325                {
326                        s[j] = t[i];
327                }
328        }
329        s[j] = 0;
330       
331        g_free( t );
332}
333
334/* Strip newlines from a string. Modifies the string passed to it. */ 
335char *strip_newlines( char *source )
336{
337        int i; 
338
339        for( i = 0; source[i] != '\0'; i ++ )
340                if( source[i] == '\n' || source[i] == '\r' )
341                        source[i] = ' ';
342       
343        return source;
344}
345
346/* Wrap an IPv4 address into IPv6 space. Not thread-safe... */
347char *ipv6_wrap( char *src )
348{
349        static char dst[64];
350        int i;
351       
352        for( i = 0; src[i]; i ++ )
353                if( ( src[i] < '0' || src[i] > '9' ) && src[i] != '.' )
354                        break;
355       
356        /* Hmm, it's not even an IP... */
357        if( src[i] )
358                return src;
359       
360        g_snprintf( dst, sizeof( dst ), "::ffff:%s", src );
361       
362        return dst;
363}
364
365/* Unwrap an IPv4 address into IPv6 space. Thread-safe, because it's very simple. :-) */
366char *ipv6_unwrap( char *src )
367{
368        int i;
369       
370        if( g_strncasecmp( src, "::ffff:", 7 ) != 0 )
371                return src;
372       
373        for( i = 7; src[i]; i ++ )
374                if( ( src[i] < '0' || src[i] > '9' ) && src[i] != '.' )
375                        break;
376       
377        /* Hmm, it's not even an IP... */
378        if( src[i] )
379                return src;
380       
381        return ( src + 7 );
382}
383
384/* Convert from one charset to another.
385   
386   from_cs, to_cs: Source and destination charsets
387   src, dst: Source and destination strings
388   size: Size if src. 0 == use strlen(). strlen() is not reliable for UNICODE/UTF16 strings though.
389   maxbuf: Maximum number of bytes to write to dst
390   
391   Returns the number of bytes written to maxbuf or -1 on an error.
392*/
393signed int do_iconv( char *from_cs, char *to_cs, char *src, char *dst, size_t size, size_t maxbuf )
394{
395        GIConv cd;
396        size_t res;
397        size_t inbytesleft, outbytesleft;
398        char *inbuf = src;
399        char *outbuf = dst;
400       
401        cd = g_iconv_open( to_cs, from_cs );
402        if( cd == (GIConv) -1 )
403                return( -1 );
404       
405        inbytesleft = size ? size : strlen( src );
406        outbytesleft = maxbuf - 1;
407        res = g_iconv( cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft );
408        *outbuf = '\0';
409        g_iconv_close( cd );
410       
411        if( res == (size_t) -1 )
412                return( -1 );
413        else
414                return( outbuf - dst );
415}
416
417/* A pretty reliable random number generator. Tries to use the /dev/random
418   devices first, and falls back to the random number generator from libc
419   when it fails. Opens randomizer devices with O_NONBLOCK to make sure a
420   lack of entropy won't halt BitlBee. */
421void random_bytes( unsigned char *buf, int count )
422{
423#ifndef _WIN32
424        static int use_dev = -1;
425       
426        /* Actually this probing code isn't really necessary, is it? */
427        if( use_dev == -1 )
428        {
429                if( access( "/dev/random", R_OK ) == 0 || access( "/dev/urandom", R_OK ) == 0 )
430                        use_dev = 1;
431                else
432                {
433                        use_dev = 0;
434                        srand( ( getpid() << 16 ) ^ time( NULL ) );
435                }
436        }
437       
438        if( use_dev )
439        {
440                int fd;
441               
442                /* At least on Linux, /dev/random can block if there's not
443                   enough entropy. We really don't want that, so if it can't
444                   give anything, use /dev/urandom instead. */
445                if( ( fd = open( "/dev/random", O_RDONLY | O_NONBLOCK ) ) >= 0 )
446                        if( read( fd, buf, count ) == count )
447                        {
448                                close( fd );
449                                return;
450                        }
451                close( fd );
452               
453                /* urandom isn't supposed to block at all, but just to be
454                   sure. If it blocks, we'll disable use_dev and use the libc
455                   randomizer instead. */
456                if( ( fd = open( "/dev/urandom", O_RDONLY | O_NONBLOCK ) ) >= 0 )
457                        if( read( fd, buf, count ) == count )
458                        {
459                                close( fd );
460                                return;
461                        }
462                close( fd );
463               
464                /* If /dev/random blocks once, we'll still try to use it
465                   again next time. If /dev/urandom also fails for some
466                   reason, stick with libc during this session. */
467               
468                use_dev = 0;
469                srand( ( getpid() << 16 ) ^ time( NULL ) );
470        }
471       
472        if( !use_dev )
473#endif
474        {
475                int i;
476               
477                /* Possibly the LSB of rand() isn't very random on some
478                   platforms. Seems okay on at least Linux and OSX though. */
479                for( i = 0; i < count; i ++ )
480                        buf[i] = rand() & 0xff;
481        }
482}
483
484int is_bool( char *value )
485{
486        if( *value == 0 )
487                return 0;
488       
489        if( ( g_strcasecmp( value, "true" ) == 0 ) || ( g_strcasecmp( value, "yes" ) == 0 ) || ( g_strcasecmp( value, "on" ) == 0 ) )
490                return 1;
491        if( ( g_strcasecmp( value, "false" ) == 0 ) || ( g_strcasecmp( value, "no" ) == 0 ) || ( g_strcasecmp( value, "off" ) == 0 ) )
492                return 1;
493       
494        while( *value )
495                if( !isdigit( *value ) )
496                        return 0;
497                else
498                        value ++;
499       
500        return 1;
501}
502
503int bool2int( char *value )
504{
505        int i;
506       
507        if( ( g_strcasecmp( value, "true" ) == 0 ) || ( g_strcasecmp( value, "yes" ) == 0 ) || ( g_strcasecmp( value, "on" ) == 0 ) )
508                return 1;
509        if( ( g_strcasecmp( value, "false" ) == 0 ) || ( g_strcasecmp( value, "no" ) == 0 ) || ( g_strcasecmp( value, "off" ) == 0 ) )
510                return 0;
511       
512        if( sscanf( value, "%d", &i ) == 1 )
513                return i;
514       
515        return 0;
516}
517
518struct ns_srv_reply *srv_lookup( char *service, char *protocol, char *domain )
519{       
520        struct ns_srv_reply *reply = NULL;
521#ifdef HAVE_RESOLV_A
522        char name[1024];
523        unsigned char querybuf[1024];
524        const unsigned char *buf;
525        ns_msg nsh;
526        ns_rr rr;
527        int i, len, size;
528       
529        g_snprintf( name, sizeof( name ), "_%s._%s.%s", service, protocol, domain );
530       
531        if( ( size = res_query( name, ns_c_in, ns_t_srv, querybuf, sizeof( querybuf ) ) ) <= 0 )
532                return NULL;
533       
534        if( ns_initparse( querybuf, size, &nsh ) != 0 )
535                return NULL;
536       
537        if( ns_parserr( &nsh, ns_s_an, 0, &rr ) != 0 )
538                return NULL;
539       
540        size = ns_rr_rdlen( rr );
541        buf = ns_rr_rdata( rr );
542       
543        len = 0;
544        for( i = 6; i < size && buf[i]; i += buf[i] + 1 )
545                len += buf[i] + 1;
546       
547        if( i > size )
548                return NULL;
549       
550        reply = g_malloc( sizeof( struct ns_srv_reply ) + len );
551        memcpy( reply->name, buf + 7, len );
552       
553        for( i = buf[6]; i < len && buf[7+i]; i += buf[7+i] + 1 )
554                reply->name[i] = '.';
555       
556        if( i > len )
557        {
558                g_free( reply );
559                return NULL;
560        }
561       
562        reply->prio = ( buf[0] << 8 ) | buf[1];
563        reply->weight = ( buf[2] << 8 ) | buf[3];
564        reply->port = ( buf[4] << 8 ) | buf[5];
565#endif
566       
567        return reply;
568}
569
570/* Word wrapping. Yes, I know this isn't UTF-8 clean. I'm willing to take the risk. */
571char *word_wrap( const char *msg, int line_len )
572{
573        GString *ret = g_string_sized_new( strlen( msg ) + 16 );
574       
575        while( strlen( msg ) > line_len )
576        {
577                int i;
578               
579                /* First try to find out if there's a newline already. Don't
580                   want to add more splits than necessary. */
581                for( i = line_len; i > 0 && msg[i] != '\n'; i -- );
582                if( msg[i] == '\n' )
583                {
584                        g_string_append_len( ret, msg, i + 1 );
585                        msg += i + 1;
586                        continue;
587                }
588               
589                for( i = line_len; i > 0; i -- )
590                {
591                        if( msg[i] == '-' )
592                        {
593                                g_string_append_len( ret, msg, i + 1 );
594                                g_string_append_c( ret, '\n' );
595                                msg += i + 1;
596                                break;
597                        }
598                        else if( msg[i] == ' ' )
599                        {
600                                g_string_append_len( ret, msg, i );
601                                g_string_append_c( ret, '\n' );
602                                msg += i + 1;
603                                break;
604                        }
605                }
606                if( i == 0 )
607                {
608                        g_string_append_len( ret, msg, line_len );
609                        g_string_append_c( ret, '\n' );
610                        msg += line_len;
611                }
612        }
613        g_string_append( ret, msg );
614       
615        return g_string_free( ret, FALSE );
616}
617
618gboolean ssl_sockerr_again( void *ssl )
619{
620        if( ssl )
621                return ssl_errno == SSL_AGAIN;
622        else
623                return sockerr_again();
624}
625
626/* Returns values: -1 == Failure (base64-decoded to something unexpected)
627                    0 == Okay
628                    1 == Password doesn't match the hash. */
629int md5_verify_password( char *password, char *hash )
630{
631        md5_byte_t *pass_dec = NULL;
632        md5_byte_t pass_md5[16];
633        md5_state_t md5_state;
634        int ret = -1, i;
635       
636        if( base64_decode( hash, &pass_dec ) == 21 )
637        {
638                md5_init( &md5_state );
639                md5_append( &md5_state, (md5_byte_t*) password, strlen( password ) );
640                md5_append( &md5_state, (md5_byte_t*) pass_dec + 16, 5 ); /* Hmmm, salt! */
641                md5_finish( &md5_state, pass_md5 );
642               
643                for( i = 0; i < 16; i ++ )
644                {
645                        if( pass_dec[i] != pass_md5[i] )
646                        {
647                                ret = 1;
648                                break;
649                        }
650                }
651               
652                /* If we reached the end of the loop, it was a match! */
653                if( i == 16 )
654                        ret = 0;
655        }
656       
657        g_free( pass_dec );
658
659        return ret;
660}
Note: See TracBrowser for help on using the repository browser.