source: lib/misc.c @ 5f8ab6a9

Last change on this file since 5f8ab6a9 was 5f8ab6a9, checked in by Sven Moritz Hallberg <pesco@…>, at 2010-06-03T10:41:03Z

merge in bitlbee 1.2.5

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