source: lib/misc.c @ b52e478

Last change on this file since b52e478 was b52e478, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-03-08T01:25:10Z

Added &apos; HTML entity since we're sure getting that one a lot with
libpurple..

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