source: lib/misc.c @ 5e81e60

Last change on this file since 5e81e60 was 4e8db1c, checked in by Wilmer van der Gaast <wilmer@…>, at 2008-03-16T16:03:52Z

Moved password hash verification to md5_verify_password() so this can be
reused for IRC/OPER passwords (to have encrypted in bitlbee.conf).

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