source: lib/misc.c @ b95932e

Last change on this file since b95932e was fb00989, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-03-14T17:55:27Z

Adding a few consts and other misc fixes from bug #431. Doing this via a
merge because bzr can probably deal with the conflicts better than patch.

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