source: lib/misc.c @ 858ea01

Last change on this file since 858ea01 was be999a5, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-08-23T23:12:24Z

First step in this merge. Mostly a bzr merge and then a cleanup of conflicts
and parts I want to/have to redo (because of ui-fix).

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