source: lib/misc.c @ eb6df6a

Last change on this file since eb6df6a was 3759849, checked in by Sven Moritz Hallberg <pesco@…>, at 2010-06-03T22:08:23Z

merge in bitlbee head

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