source: lib/misc.c @ 6738a67

Last change on this file since 6738a67 was 6738a67, checked in by Sven Moritz Hallberg <pesco@…>, at 2008-07-16T23:22:52Z

merge in latest trunk

  • Property mode set to 100644
File size: 13.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 <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <ctype.h>
40#include <glib.h>
41#include <time.h>
42
43#ifdef HAVE_RESOLV_A
44#include <arpa/nameser.h>
45#include <resolv.h>
46#endif
47
48#include "ssl_client.h"
49
50void strip_linefeed(gchar *text)
51{
52        int i, j;
53        gchar *text2 = g_malloc(strlen(text) + 1);
54
55        for (i = 0, j = 0; text[i]; i++)
56                if (text[i] != '\r')
57                        text2[j++] = text[i];
58        text2[j] = '\0';
59
60        strcpy(text, text2);
61        g_free(text2);
62}
63
64time_t get_time(int year, int month, int day, int hour, int min, int sec)
65{
66        struct tm tm;
67
68        memset(&tm, 0, sizeof(struct tm));
69        tm.tm_year = year - 1900;
70        tm.tm_mon = month - 1;
71        tm.tm_mday = day;
72        tm.tm_hour = hour;
73        tm.tm_min = min;
74        tm.tm_sec = sec >= 0 ? sec : time(NULL) % 60;
75       
76        return mktime(&tm);
77}
78
79typedef struct htmlentity
80{
81        char code[7];
82        char is[3];
83} htmlentity_t;
84
85static const htmlentity_t ent[] =
86{
87        { "lt",     "<" },
88        { "gt",     ">" },
89        { "amp",    "&" },
90        { "apos",   "'" },
91        { "quot",   "\"" },
92        { "aacute", "á" },
93        { "eacute", "é" },
94        { "iacute", "é" },
95        { "oacute", "ó" },
96        { "uacute", "ú" },
97        { "agrave", "à" },
98        { "egrave", "è" },
99        { "igrave", "ì" },
100        { "ograve", "ò" },
101        { "ugrave", "ù" },
102        { "acirc",  "â" },
103        { "ecirc",  "ê" },
104        { "icirc",  "î" },
105        { "ocirc",  "ô" },
106        { "ucirc",  "û" },
107        { "auml",   "ä" },
108        { "euml",   "ë" },
109        { "iuml",   "ï" },
110        { "ouml",   "ö" },
111        { "uuml",   "ü" },
112        { "nbsp",   " " },
113        { "",        ""  }
114};
115
116void strip_html( char *in )
117{
118        char *start = in;
119        char *out = g_malloc( strlen( in ) + 1 );
120        char *s = out, *cs;
121        int i, matched;
122        int taglen;
123       
124        memset( out, 0, strlen( in ) + 1 );
125       
126        while( *in )
127        {
128                if( *in == '<' && ( isalpha( *(in+1) ) || *(in+1) == '/' ) )
129                {
130                        /* If in points at a < and in+1 points at a letter or a slash, this is probably
131                           a HTML-tag. Try to find a closing > and continue there. If the > can't be
132                           found, assume that it wasn't a HTML-tag after all. */
133                       
134                        cs = in;
135                       
136                        while( *in && *in != '>' )
137                                in ++;
138                       
139                        taglen = in-cs-1;   /* not <0 because the above loop runs at least once */
140                        if( *in )
141                        {
142                                if( g_strncasecmp( cs+1, "b", taglen) == 0 )
143                                        *(s++) = '\x02';
144                                else if( g_strncasecmp( cs+1, "/b", taglen) == 0 )
145                                        *(s++) = '\x02';
146                                else if( g_strncasecmp( cs+1, "i", taglen) == 0 )
147                                        *(s++) = '\x1f';
148                                else if( g_strncasecmp( cs+1, "/i", taglen) == 0 )
149                                        *(s++) = '\x1f';
150                                else if( g_strncasecmp( cs+1, "br", 2) == 0 )
151                                        *(s++) = '\n';
152                                in ++;
153                        }
154                        else
155                        {
156                                in = cs;
157                                *(s++) = *(in++);
158                        }
159                }
160                else if( *in == '&' )
161                {
162                        cs = ++in;
163                        while( *in && isalpha( *in ) )
164                                in ++;
165                       
166                        if( *in == ';' ) in ++;
167                        matched = 0;
168                       
169                        for( i = 0; *ent[i].code; i ++ )
170                                if( g_strncasecmp( ent[i].code, cs, strlen( ent[i].code ) ) == 0 )
171                                {
172                                        int j;
173                                       
174                                        for( j = 0; ent[i].is[j]; j ++ )
175                                                *(s++) = ent[i].is[j];
176                                       
177                                        matched = 1;
178                                        break;
179                                }
180
181                        /* None of the entities were matched, so return the string */
182                        if( !matched )
183                        {
184                                in = cs - 1;
185                                *(s++) = *(in++);
186                        }
187                }
188                else
189                {
190                        *(s++) = *(in++);
191                }
192        }
193       
194        strcpy( start, out );
195        g_free( out );
196}
197
198char *escape_html( const char *html )
199{
200        const char *c = html;
201        GString *ret;
202        char *str;
203       
204        if( html == NULL )
205                return( NULL );
206       
207        ret = g_string_new( "" );
208       
209        while( *c )
210        {
211                switch( *c )
212                {
213                        case '&':
214                                ret = g_string_append( ret, "&amp;" );
215                                break;
216                        case '<':
217                                ret = g_string_append( ret, "&lt;" );
218                                break;
219                        case '>':
220                                ret = g_string_append( ret, "&gt;" );
221                                break;
222                        case '"':
223                                ret = g_string_append( ret, "&quot;" );
224                                break;
225                        default:
226                                ret = g_string_append_c( ret, *c );
227                }
228                c ++;
229        }
230       
231        str = ret->str;
232        g_string_free( ret, FALSE );
233        return( str );
234}
235
236/* Decode%20a%20file%20name                                             */
237void http_decode( char *s )
238{
239        char *t;
240        int i, j, k;
241       
242        t = g_new( char, strlen( s ) + 1 );
243       
244        for( i = j = 0; s[i]; i ++, j ++ )
245        {
246                if( s[i] == '%' )
247                {
248                        if( sscanf( s + i + 1, "%2x", &k ) )
249                        {
250                                t[j] = k;
251                                i += 2;
252                        }
253                        else
254                        {
255                                *t = 0;
256                                break;
257                        }
258                }
259                else
260                {
261                        t[j] = s[i];
262                }
263        }
264        t[j] = 0;
265       
266        strcpy( s, t );
267        g_free( t );
268}
269
270/* Warning: This one explodes the string. Worst-cases can make the string 3x its original size! */
271/* This fuction is safe, but make sure you call it safely as well! */
272void http_encode( char *s )
273{
274        char *t;
275        int i, j;
276       
277        t = g_strdup( s );
278       
279        for( i = j = 0; t[i]; i ++, j ++ )
280        {
281                /* if( t[i] <= ' ' || ((unsigned char *)t)[i] >= 128 || t[i] == '%' ) */
282                if( !isalnum( t[i] ) )
283                {
284                        sprintf( s + j, "%%%02X", ((unsigned char*)t)[i] );
285                        j += 2;
286                }
287                else
288                {
289                        s[j] = t[i];
290                }
291        }
292        s[j] = 0;
293       
294        g_free( t );
295}
296
297/* Strip newlines from a string. Modifies the string passed to it. */ 
298char *strip_newlines( char *source )
299{
300        int i; 
301
302        for( i = 0; source[i] != '\0'; i ++ )
303                if( source[i] == '\n' || source[i] == '\r' )
304                        source[i] = ' ';
305       
306        return source;
307}
308
309/* Wrap an IPv4 address into IPv6 space. Not thread-safe... */
310char *ipv6_wrap( char *src )
311{
312        static char dst[64];
313        int i;
314       
315        for( i = 0; src[i]; i ++ )
316                if( ( src[i] < '0' || src[i] > '9' ) && src[i] != '.' )
317                        break;
318       
319        /* Hmm, it's not even an IP... */
320        if( src[i] )
321                return src;
322       
323        g_snprintf( dst, sizeof( dst ), "::ffff:%s", src );
324       
325        return dst;
326}
327
328/* Unwrap an IPv4 address into IPv6 space. Thread-safe, because it's very simple. :-) */
329char *ipv6_unwrap( char *src )
330{
331        int i;
332       
333        if( g_strncasecmp( src, "::ffff:", 7 ) != 0 )
334                return src;
335       
336        for( i = 7; src[i]; i ++ )
337                if( ( src[i] < '0' || src[i] > '9' ) && src[i] != '.' )
338                        break;
339       
340        /* Hmm, it's not even an IP... */
341        if( src[i] )
342                return src;
343       
344        return ( src + 7 );
345}
346
347/* Convert from one charset to another.
348   
349   from_cs, to_cs: Source and destination charsets
350   src, dst: Source and destination strings
351   size: Size if src. 0 == use strlen(). strlen() is not reliable for UNICODE/UTF16 strings though.
352   maxbuf: Maximum number of bytes to write to dst
353   
354   Returns the number of bytes written to maxbuf or -1 on an error.
355*/
356signed int do_iconv( char *from_cs, char *to_cs, char *src, char *dst, size_t size, size_t maxbuf )
357{
358        GIConv cd;
359        size_t res;
360        size_t inbytesleft, outbytesleft;
361        char *inbuf = src;
362        char *outbuf = dst;
363       
364        cd = g_iconv_open( to_cs, from_cs );
365        if( cd == (GIConv) -1 )
366                return( -1 );
367       
368        inbytesleft = size ? size : strlen( src );
369        outbytesleft = maxbuf - 1;
370        res = g_iconv( cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft );
371        *outbuf = '\0';
372        g_iconv_close( cd );
373       
374        if( res == (size_t) -1 )
375                return( -1 );
376        else
377                return( outbuf - dst );
378}
379
380/* A pretty reliable random number generator. Tries to use the /dev/random
381   devices first, and falls back to the random number generator from libc
382   when it fails. Opens randomizer devices with O_NONBLOCK to make sure a
383   lack of entropy won't halt BitlBee. */
384void random_bytes( unsigned char *buf, int count )
385{
386#ifndef _WIN32
387        static int use_dev = -1;
388       
389        /* Actually this probing code isn't really necessary, is it? */
390        if( use_dev == -1 )
391        {
392                if( access( "/dev/random", R_OK ) == 0 || access( "/dev/urandom", R_OK ) == 0 )
393                        use_dev = 1;
394                else
395                {
396                        use_dev = 0;
397                        srand( ( getpid() << 16 ) ^ time( NULL ) );
398                }
399        }
400       
401        if( use_dev )
402        {
403                int fd;
404               
405                /* At least on Linux, /dev/random can block if there's not
406                   enough entropy. We really don't want that, so if it can't
407                   give anything, use /dev/urandom instead. */
408                if( ( fd = open( "/dev/random", O_RDONLY | O_NONBLOCK ) ) >= 0 )
409                        if( read( fd, buf, count ) == count )
410                        {
411                                close( fd );
412                                return;
413                        }
414                close( fd );
415               
416                /* urandom isn't supposed to block at all, but just to be
417                   sure. If it blocks, we'll disable use_dev and use the libc
418                   randomizer instead. */
419                if( ( fd = open( "/dev/urandom", O_RDONLY | O_NONBLOCK ) ) >= 0 )
420                        if( read( fd, buf, count ) == count )
421                        {
422                                close( fd );
423                                return;
424                        }
425                close( fd );
426               
427                /* If /dev/random blocks once, we'll still try to use it
428                   again next time. If /dev/urandom also fails for some
429                   reason, stick with libc during this session. */
430               
431                use_dev = 0;
432                srand( ( getpid() << 16 ) ^ time( NULL ) );
433        }
434       
435        if( !use_dev )
436#endif
437        {
438                int i;
439               
440                /* Possibly the LSB of rand() isn't very random on some
441                   platforms. Seems okay on at least Linux and OSX though. */
442                for( i = 0; i < count; i ++ )
443                        buf[i] = rand() & 0xff;
444        }
445}
446
447int is_bool( char *value )
448{
449        if( *value == 0 )
450                return 0;
451       
452        if( ( g_strcasecmp( value, "true" ) == 0 ) || ( g_strcasecmp( value, "yes" ) == 0 ) || ( g_strcasecmp( value, "on" ) == 0 ) )
453                return 1;
454        if( ( g_strcasecmp( value, "false" ) == 0 ) || ( g_strcasecmp( value, "no" ) == 0 ) || ( g_strcasecmp( value, "off" ) == 0 ) )
455                return 1;
456       
457        while( *value )
458                if( !isdigit( *value ) )
459                        return 0;
460                else
461                        value ++;
462       
463        return 1;
464}
465
466int bool2int( char *value )
467{
468        int i;
469       
470        if( ( g_strcasecmp( value, "true" ) == 0 ) || ( g_strcasecmp( value, "yes" ) == 0 ) || ( g_strcasecmp( value, "on" ) == 0 ) )
471                return 1;
472        if( ( g_strcasecmp( value, "false" ) == 0 ) || ( g_strcasecmp( value, "no" ) == 0 ) || ( g_strcasecmp( value, "off" ) == 0 ) )
473                return 0;
474       
475        if( sscanf( value, "%d", &i ) == 1 )
476                return i;
477       
478        return 0;
479}
480
481struct ns_srv_reply *srv_lookup( char *service, char *protocol, char *domain )
482{       
483        struct ns_srv_reply *reply = NULL;
484#ifdef HAVE_RESOLV_A
485        char name[1024];
486        unsigned char querybuf[1024];
487        const unsigned char *buf;
488        ns_msg nsh;
489        ns_rr rr;
490        int i, len, size;
491       
492        g_snprintf( name, sizeof( name ), "_%s._%s.%s", service, protocol, domain );
493       
494        if( ( size = res_query( name, ns_c_in, ns_t_srv, querybuf, sizeof( querybuf ) ) ) <= 0 )
495                return NULL;
496       
497        if( ns_initparse( querybuf, size, &nsh ) != 0 )
498                return NULL;
499       
500        if( ns_parserr( &nsh, ns_s_an, 0, &rr ) != 0 )
501                return NULL;
502       
503        size = ns_rr_rdlen( rr );
504        buf = ns_rr_rdata( rr );
505       
506        len = 0;
507        for( i = 6; i < size && buf[i]; i += buf[i] + 1 )
508                len += buf[i] + 1;
509       
510        if( i > size )
511                return NULL;
512       
513        reply = g_malloc( sizeof( struct ns_srv_reply ) + len );
514        memcpy( reply->name, buf + 7, len );
515       
516        for( i = buf[6]; i < len && buf[7+i]; i += buf[7+i] + 1 )
517                reply->name[i] = '.';
518       
519        if( i > len )
520        {
521                g_free( reply );
522                return NULL;
523        }
524       
525        reply->prio = ( buf[0] << 8 ) | buf[1];
526        reply->weight = ( buf[2] << 8 ) | buf[3];
527        reply->port = ( buf[4] << 8 ) | buf[5];
528#endif
529       
530        return reply;
531}
532
533/* Word wrapping. Yes, I know this isn't UTF-8 clean. I'm willing to take the risk. */
534char *word_wrap( char *msg, int line_len )
535{
536        GString *ret = g_string_sized_new( strlen( msg ) + 16 );
537       
538        while( strlen( msg ) > line_len )
539        {
540                int i;
541               
542                /* First try to find out if there's a newline already. Don't
543                   want to add more splits than necessary. */
544                for( i = line_len; i > 0 && msg[i] != '\n'; i -- );
545                if( msg[i] == '\n' )
546                {
547                        g_string_append_len( ret, msg, i + 1 );
548                        msg += i + 1;
549                        continue;
550                }
551               
552                for( i = line_len; i > 0; i -- )
553                {
554                        if( msg[i] == '-' )
555                        {
556                                g_string_append_len( ret, msg, i + 1 );
557                                g_string_append_c( ret, '\n' );
558                                msg += i + 1;
559                                break;
560                        }
561                        else if( msg[i] == ' ' )
562                        {
563                                g_string_append_len( ret, msg, i );
564                                g_string_append_c( ret, '\n' );
565                                msg += i + 1;
566                                break;
567                        }
568                }
569                if( i == 0 )
570                {
571                        g_string_append_len( ret, msg, line_len );
572                        g_string_append_c( ret, '\n' );
573                        msg += line_len;
574                }
575        }
576        g_string_append( ret, msg );
577       
578        return g_string_free( ret, FALSE );
579}
580
581gboolean ssl_sockerr_again( void *ssl )
582{
583        if( ssl )
584                return ssl_errno == SSL_AGAIN;
585        else
586                return sockerr_again();
587}
588
589/* Returns values: -1 == Failure (base64-decoded to something unexpected)
590                    0 == Okay
591                    1 == Password doesn't match the hash. */
592int md5_verify_password( char *password, char *hash )
593{
594        md5_byte_t *pass_dec = NULL;
595        md5_byte_t pass_md5[16];
596        md5_state_t md5_state;
597        int ret = -1, i;
598       
599        if( base64_decode( hash, &pass_dec ) == 21 )
600        {
601                md5_init( &md5_state );
602                md5_append( &md5_state, (md5_byte_t*) password, strlen( password ) );
603                md5_append( &md5_state, (md5_byte_t*) pass_dec + 16, 5 ); /* Hmmm, salt! */
604                md5_finish( &md5_state, pass_md5 );
605               
606                for( i = 0; i < 16; i ++ )
607                {
608                        if( pass_dec[i] != pass_md5[i] )
609                        {
610                                ret = 1;
611                                break;
612                        }
613                }
614               
615                /* If we reached the end of the loop, it was a match! */
616                if( i == 16 )
617                        ret = 0;
618        }
619       
620        g_free( pass_dec );
621
622        return ret;
623}
Note: See TracBrowser for help on using the repository browser.