source: lib/misc.c @ af359b4

Last change on this file since af359b4 was 5eab298f, checked in by dequis <dx@…>, at 2015-01-26T03:27:24Z

random_bytes: Use /dev/urandom only, don't bother trying /dev/random

Also abort() if there's no /dev/urandom

See http://www.2uo.de/myths-about-urandom/ for details.

  • Property mode set to 100644
File size: 16.2 KB
Line 
1  /********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
4  * Copyright 2002-2012 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-2012 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., 51 Franklin St.,
30  Fifth Floor, Boston, MA  02110-1301  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 == '<' && ( g_ascii_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 && g_ascii_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[strlen(s)+1];
311        int i, j;
312       
313        strcpy( t, s );
314        for( i = j = 0; t[i]; i ++, j ++ )
315        {
316                /* Warning: g_ascii_isalnum() is locale-aware, so don't use it here! */
317                if( ( t[i] >= 'A' && t[i] <= 'Z' ) ||
318                    ( t[i] >= 'a' && t[i] <= 'z' ) ||
319                    ( t[i] >= '0' && t[i] <= '9' ) ||
320                    strchr( "._-~", t[i] ) )
321                {
322                        s[j] = t[i];
323                }
324                else
325                {
326                        sprintf( s + j, "%%%02X", ((unsigned char*)t)[i] );
327                        j += 2;
328                }
329        }
330        s[j] = 0;
331}
332
333/* Strip newlines from a string. Modifies the string passed to it. */ 
334char *strip_newlines( char *source )
335{
336        int i; 
337
338        for( i = 0; source[i] != '\0'; i ++ )
339                if( source[i] == '\n' || source[i] == '\r' )
340                        source[i] = ' ';
341       
342        return source;
343}
344
345/* Wrap an IPv4 address into IPv6 space. Not thread-safe... */
346char *ipv6_wrap( char *src )
347{
348        static char dst[64];
349        int i;
350       
351        for( i = 0; src[i]; i ++ )
352                if( ( src[i] < '0' || src[i] > '9' ) && src[i] != '.' )
353                        break;
354       
355        /* Hmm, it's not even an IP... */
356        if( src[i] )
357                return src;
358       
359        g_snprintf( dst, sizeof( dst ), "::ffff:%s", src );
360       
361        return dst;
362}
363
364/* Unwrap an IPv4 address into IPv6 space. Thread-safe, because it's very simple. :-) */
365char *ipv6_unwrap( char *src )
366{
367        int i;
368       
369        if( g_strncasecmp( src, "::ffff:", 7 ) != 0 )
370                return src;
371       
372        for( i = 7; src[i]; i ++ )
373                if( ( src[i] < '0' || src[i] > '9' ) && src[i] != '.' )
374                        break;
375       
376        /* Hmm, it's not even an IP... */
377        if( src[i] )
378                return src;
379       
380        return ( src + 7 );
381}
382
383/* Convert from one charset to another.
384   
385   from_cs, to_cs: Source and destination charsets
386   src, dst: Source and destination strings
387   size: Size if src. 0 == use strlen(). strlen() is not reliable for UNICODE/UTF16 strings though.
388   maxbuf: Maximum number of bytes to write to dst
389   
390   Returns the number of bytes written to maxbuf or -1 on an error.
391*/
392signed int do_iconv( char *from_cs, char *to_cs, char *src, char *dst, size_t size, size_t maxbuf )
393{
394        GIConv cd;
395        size_t res;
396        size_t inbytesleft, outbytesleft;
397        char *inbuf = src;
398        char *outbuf = dst;
399       
400        cd = g_iconv_open( to_cs, from_cs );
401        if( cd == (GIConv) -1 )
402                return -1;
403       
404        inbytesleft = size ? size : strlen( src );
405        outbytesleft = maxbuf - 1;
406        res = g_iconv( cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft );
407        *outbuf = '\0';
408        g_iconv_close( cd );
409       
410        if( res != 0 )
411                return -1;
412        else
413                return outbuf - dst;
414}
415
416/* A wrapper for /dev/urandom.
417 * If /dev/urandom is not present or not usable, it calls abort()
418 * to prevent bitlbee from working without a decent entropy source */
419void random_bytes( unsigned char *buf, int count )
420{
421        int fd;
422        if( ( ( fd = open( "/dev/urandom", O_RDONLY ) ) == -1 ) ||
423            ( read( fd, buf, count ) == -1 ) )
424        {
425                log_message( LOGLVL_ERROR, "/dev/urandom not present - aborting" );
426                abort();
427        }
428
429        close( fd );
430}
431
432int is_bool( char *value )
433{
434        if( *value == 0 )
435                return 0;
436       
437        if( ( g_strcasecmp( value, "true" ) == 0 ) || ( g_strcasecmp( value, "yes" ) == 0 ) || ( g_strcasecmp( value, "on" ) == 0 ) )
438                return 1;
439        if( ( g_strcasecmp( value, "false" ) == 0 ) || ( g_strcasecmp( value, "no" ) == 0 ) || ( g_strcasecmp( value, "off" ) == 0 ) )
440                return 1;
441       
442        while( *value )
443                if( !g_ascii_isdigit( *value ) )
444                        return 0;
445                else
446                        value ++;
447       
448        return 1;
449}
450
451int bool2int( char *value )
452{
453        int i;
454       
455        if( ( g_strcasecmp( value, "true" ) == 0 ) || ( g_strcasecmp( value, "yes" ) == 0 ) || ( g_strcasecmp( value, "on" ) == 0 ) )
456                return 1;
457        if( ( g_strcasecmp( value, "false" ) == 0 ) || ( g_strcasecmp( value, "no" ) == 0 ) || ( g_strcasecmp( value, "off" ) == 0 ) )
458                return 0;
459       
460        if( sscanf( value, "%d", &i ) == 1 )
461                return i;
462       
463        return 0;
464}
465
466struct ns_srv_reply **srv_lookup( char *service, char *protocol, char *domain )
467{       
468        struct ns_srv_reply **replies = NULL;
469#ifdef HAVE_RESOLV_A
470        struct ns_srv_reply *reply = NULL;
471        char name[1024];
472        unsigned char querybuf[1024];
473        const unsigned char *buf;
474        ns_msg nsh;
475        ns_rr rr;
476        int n, len, size;
477       
478        g_snprintf( name, sizeof( name ), "_%s._%s.%s", service, protocol, domain );
479       
480        if( ( size = res_query( name, ns_c_in, ns_t_srv, querybuf, sizeof( querybuf ) ) ) <= 0 )
481                return NULL;
482       
483        if( ns_initparse( querybuf, size, &nsh ) != 0 )
484                return NULL;
485       
486        n = 0;
487        while( ns_parserr( &nsh, ns_s_an, n, &rr ) == 0 )
488        {
489                char name[NS_MAXDNAME];
490
491                if( ns_rr_rdlen( rr ) < 7)
492                    break;
493
494                buf = ns_rr_rdata( rr );
495               
496                if( dn_expand(querybuf, querybuf + size, &buf[6], name, NS_MAXDNAME) == -1 )
497                        break;
498
499                len = strlen(name) + 1;
500               
501                reply = g_malloc( sizeof( struct ns_srv_reply ) + len );
502                memcpy( reply->name, name, len );
503               
504                reply->prio = ( buf[0] << 8 ) | buf[1];
505                reply->weight = ( buf[2] << 8 ) | buf[3];
506                reply->port = ( buf[4] << 8 ) | buf[5];
507               
508                n ++;
509                replies = g_renew( struct ns_srv_reply *, replies, n + 1 );
510                replies[n-1] = reply;
511        }
512        if( replies )
513                replies[n] = NULL;
514#endif
515       
516        return replies;
517}
518
519void srv_free( struct ns_srv_reply **srv )
520{
521        int i;
522       
523        if( srv == NULL )
524                return;
525       
526        for( i = 0; srv[i]; i ++ )
527                g_free( srv[i] );
528        g_free( srv );
529}
530
531/* Word wrapping. Yes, I know this isn't UTF-8 clean. I'm willing to take the risk. */
532char *word_wrap( const char *msg, int line_len )
533{
534        GString *ret = g_string_sized_new( strlen( msg ) + 16 );
535       
536        while( strlen( msg ) > line_len )
537        {
538                int i;
539               
540                /* First try to find out if there's a newline already. Don't
541                   want to add more splits than necessary. */
542                for( i = line_len; i > 0 && msg[i] != '\n'; i -- );
543                if( msg[i] == '\n' )
544                {
545                        g_string_append_len( ret, msg, i + 1 );
546                        msg += i + 1;
547                        continue;
548                }
549               
550                for( i = line_len; i > 0; i -- )
551                {
552                        if( msg[i] == '-' )
553                        {
554                                g_string_append_len( ret, msg, i + 1 );
555                                g_string_append_c( ret, '\n' );
556                                msg += i + 1;
557                                break;
558                        }
559                        else if( msg[i] == ' ' )
560                        {
561                                g_string_append_len( ret, msg, i );
562                                g_string_append_c( ret, '\n' );
563                                msg += i + 1;
564                                break;
565                        }
566                }
567                if( i == 0 )
568                {
569                        g_string_append_len( ret, msg, line_len );
570                        g_string_append_c( ret, '\n' );
571                        msg += line_len;
572                }
573        }
574        g_string_append( ret, msg );
575       
576        return g_string_free( ret, FALSE );
577}
578
579gboolean ssl_sockerr_again( void *ssl )
580{
581        if( ssl )
582                return ssl_errno == SSL_AGAIN;
583        else
584                return sockerr_again();
585}
586
587/* Returns values: -1 == Failure (base64-decoded to something unexpected)
588                    0 == Okay
589                    1 == Password doesn't match the hash. */
590int md5_verify_password( char *password, char *hash )
591{
592        md5_byte_t *pass_dec = NULL;
593        md5_byte_t pass_md5[16];
594        md5_state_t md5_state;
595        int ret = -1, i;
596       
597        if( base64_decode( hash, &pass_dec ) == 21 )
598        {
599                md5_init( &md5_state );
600                md5_append( &md5_state, (md5_byte_t*) password, strlen( password ) );
601                md5_append( &md5_state, (md5_byte_t*) pass_dec + 16, 5 ); /* Hmmm, salt! */
602                md5_finish( &md5_state, pass_md5 );
603               
604                for( i = 0; i < 16; i ++ )
605                {
606                        if( pass_dec[i] != pass_md5[i] )
607                        {
608                                ret = 1;
609                                break;
610                        }
611                }
612               
613                /* If we reached the end of the loop, it was a match! */
614                if( i == 16 )
615                        ret = 0;
616        }
617       
618        g_free( pass_dec );
619
620        return ret;
621}
622
623/* Split commands (root-style, *not* IRC-style). Handles "quoting of"
624   white\ space in 'various ways'. Returns a NULL-terminated static
625   char** so watch out with nested use! Definitely not thread-safe. */
626char **split_command_parts( char *command, int limit )
627{
628        static char *cmd[IRC_MAX_ARGS+1];
629        char *s, q = 0;
630        int k;
631       
632        memset( cmd, 0, sizeof( cmd ) );
633        cmd[0] = command;
634        k = 1;
635        for( s = command; *s && k < IRC_MAX_ARGS; s ++ )
636        {
637                if( *s == ' ' && !q )
638                {
639                        *s = 0;
640                        while( *++s == ' ' );
641                        if( k != limit && (*s == '"' || *s == '\'') )
642                        {
643                                q = *s;
644                                s ++;
645                        }
646                        if( *s )
647                        {
648                                cmd[k++] = s;
649                                if (limit && k > limit) {
650                                        break;
651                                }
652                                s --;
653                        }
654                        else
655                        {
656                                break;
657                        }
658                }
659                else if( *s == '\\' && ( ( !q && s[1] ) || ( q && q == s[1] ) ) )
660                {
661                        char *cpy;
662                       
663                        for( cpy = s; *cpy; cpy ++ )
664                                cpy[0] = cpy[1];
665                }
666                else if( *s == q )
667                {
668                        q = *s = 0;
669                }
670        }
671       
672        /* Full zero-padding for easier argc checking. */
673        while( k <= IRC_MAX_ARGS )
674                cmd[k++] = NULL;
675       
676        return cmd;
677}
678
679char *get_rfc822_header( const char *text, const char *header, int len )
680{
681        int hlen = strlen( header ), i;
682        const char *ret;
683       
684        if( text == NULL )
685                return NULL;
686       
687        if( len == 0 )
688                len = strlen( text );
689       
690        i = 0;
691        while( ( i + hlen ) < len )
692        {
693                /* Maybe this is a bit over-commented, but I just hate this part... */
694                if( g_strncasecmp( text + i, header, hlen ) == 0 )
695                {
696                        /* Skip to the (probable) end of the header */
697                        i += hlen;
698                       
699                        /* Find the first non-[: \t] character */
700                        while( i < len && ( text[i] == ':' || text[i] == ' ' || text[i] == '\t' ) ) i ++;
701                       
702                        /* Make sure we're still inside the string */
703                        if( i >= len ) return( NULL );
704                       
705                        /* Save the position */
706                        ret = text + i;
707                       
708                        /* Search for the end of this line */
709                        while( i < len && text[i] != '\r' && text[i] != '\n' ) i ++;
710                       
711                        /* Make sure we're still inside the string */
712                        if( i >= len ) return( NULL );
713                       
714                        /* Copy the found data */
715                        return( g_strndup( ret, text + i - ret ) );
716                }
717               
718                /* This wasn't the header we were looking for, skip to the next line. */
719                while( i < len && ( text[i] != '\r' && text[i] != '\n' ) ) i ++;
720                while( i < len && ( text[i] == '\r' || text[i] == '\n' ) ) i ++;
721               
722                /* End of headers? */
723                if( ( i >= 4 && strncmp( text + i - 4, "\r\n\r\n", 4 ) == 0 ) ||
724                    ( i >= 2 && ( strncmp( text + i - 2, "\n\n", 2 ) == 0 ||   
725                                  strncmp( text + i - 2, "\r\r", 2 ) == 0 ) ) )
726                {
727                        break;
728                }
729        }
730       
731        return NULL;
732}
733
734/* Takes a string, truncates it where it's safe, returns the new length */
735int truncate_utf8( char *string, int maxlen )
736{
737        char *end;
738        g_utf8_validate( (const gchar *) string, maxlen, (const gchar **) &end );
739        *end = '\0';
740        return end - string;
741}
Note: See TracBrowser for help on using the repository browser.