source: lib/misc.c @ e88fbe27

Last change on this file since e88fbe27 was 2e3a857, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-04-08T00:27:42Z

Added a mktime_utc() to misc.c using code that used to be in jabber_util.c.
I want to use this in the Twitter module.

  • Property mode set to 100644
File size: 14.1 KB
Line 
1  /********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
4  * Copyright 2002-2006 Wilmer van der Gaast and others                *
5  \********************************************************************/
6
7/*
8 * Various utility functions. Some are copied from Gaim to support the
9 * IM-modules, most are from BitlBee.
10 *
11 * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
12 *                          (and possibly other members of the Gaim team)
13 * Copyright 2002-2006 Wilmer van der Gaast <wilmer@gaast.net>
14 */
15
16/*
17  This program is free software; you can redistribute it and/or modify
18  it under the terms of the GNU General Public License as published by
19  the Free Software Foundation; either version 2 of the License, or
20  (at your option) any later version.
21
22  This program is distributed in the hope that it will be useful,
23  but WITHOUT ANY WARRANTY; without even the implied warranty of
24  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25  GNU General Public License for more details.
26
27  You should have received a copy of the GNU General Public License with
28  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
29  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
30  Suite 330, Boston, MA  02111-1307  USA
31*/
32
33#define BITLBEE_CORE
34#include "nogaim.h"
35#include "base64.h"
36#include "md5.h"
37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
40#include <ctype.h>
41#include <glib.h>
42#include <time.h>
43
44#ifdef HAVE_RESOLV_A
45#include <arpa/nameser.h>
46#include <resolv.h>
47#endif
48
49#include "md5.h"
50#include "ssl_client.h"
51
52void strip_linefeed(gchar *text)
53{
54        int i, j;
55        gchar *text2 = g_malloc(strlen(text) + 1);
56
57        for (i = 0, j = 0; text[i]; i++)
58                if (text[i] != '\r')
59                        text2[j++] = text[i];
60        text2[j] = '\0';
61
62        strcpy(text, text2);
63        g_free(text2);
64}
65
66time_t get_time(int year, int month, int day, int hour, int min, int sec)
67{
68        struct tm tm;
69
70        memset(&tm, 0, sizeof(struct tm));
71        tm.tm_year = year - 1900;
72        tm.tm_mon = month - 1;
73        tm.tm_mday = day;
74        tm.tm_hour = hour;
75        tm.tm_min = min;
76        tm.tm_sec = sec >= 0 ? sec : time(NULL) % 60;
77       
78        return mktime(&tm);
79}
80
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 = g_malloc( strlen( in ) + 1 );
157        char *s = out, *cs;
158        int i, matched;
159       
160        memset( out, 0, strlen( in ) + 1 );
161       
162        while( *in )
163        {
164                if( *in == '<' && ( isalpha( *(in+1) ) || *(in+1) == '/' ) )
165                {
166                        /* If in points at a < and in+1 points at a letter or a slash, this is probably
167                           a HTML-tag. Try to find a closing > and continue there. If the > can't be
168                           found, assume that it wasn't a HTML-tag after all. */
169                       
170                        cs = in;
171                       
172                        while( *in && *in != '>' )
173                                in ++;
174                       
175                        if( *in )
176                        {
177                                if( g_strncasecmp( cs+1, "br", 2) == 0 )
178                                        *(s++) = '\n';
179                                in ++;
180                        }
181                        else
182                        {
183                                in = cs;
184                                *(s++) = *(in++);
185                        }
186                }
187                else if( *in == '&' )
188                {
189                        cs = ++in;
190                        while( *in && isalpha( *in ) )
191                                in ++;
192                       
193                        if( *in == ';' ) in ++;
194                        matched = 0;
195                       
196                        for( i = 0; *ent[i].code; i ++ )
197                                if( g_strncasecmp( ent[i].code, cs, strlen( ent[i].code ) ) == 0 )
198                                {
199                                        int j;
200                                       
201                                        for( j = 0; ent[i].is[j]; j ++ )
202                                                *(s++) = ent[i].is[j];
203                                       
204                                        matched = 1;
205                                        break;
206                                }
207
208                        /* None of the entities were matched, so return the string */
209                        if( !matched )
210                        {
211                                in = cs - 1;
212                                *(s++) = *(in++);
213                        }
214                }
215                else
216                {
217                        *(s++) = *(in++);
218                }
219        }
220       
221        strcpy( start, out );
222        g_free( out );
223}
224
225char *escape_html( const char *html )
226{
227        const char *c = html;
228        GString *ret;
229        char *str;
230       
231        if( html == NULL )
232                return( NULL );
233       
234        ret = g_string_new( "" );
235       
236        while( *c )
237        {
238                switch( *c )
239                {
240                        case '&':
241                                ret = g_string_append( ret, "&amp;" );
242                                break;
243                        case '<':
244                                ret = g_string_append( ret, "&lt;" );
245                                break;
246                        case '>':
247                                ret = g_string_append( ret, "&gt;" );
248                                break;
249                        case '"':
250                                ret = g_string_append( ret, "&quot;" );
251                                break;
252                        default:
253                                ret = g_string_append_c( ret, *c );
254                }
255                c ++;
256        }
257       
258        str = ret->str;
259        g_string_free( ret, FALSE );
260        return( str );
261}
262
263/* Decode%20a%20file%20name                                             */
264void http_decode( char *s )
265{
266        char *t;
267        int i, j, k;
268       
269        t = g_new( char, strlen( s ) + 1 );
270       
271        for( i = j = 0; s[i]; i ++, j ++ )
272        {
273                if( s[i] == '%' )
274                {
275                        if( sscanf( s + i + 1, "%2x", &k ) )
276                        {
277                                t[j] = k;
278                                i += 2;
279                        }
280                        else
281                        {
282                                *t = 0;
283                                break;
284                        }
285                }
286                else
287                {
288                        t[j] = s[i];
289                }
290        }
291        t[j] = 0;
292       
293        strcpy( s, t );
294        g_free( t );
295}
296
297/* Warning: This one explodes the string. Worst-cases can make the string 3x its original size! */
298/* This fuction is safe, but make sure you call it safely as well! */
299void http_encode( char *s )
300{
301        char *t;
302        int i, j;
303       
304        t = g_strdup( s );
305       
306        for( i = j = 0; t[i]; i ++, j ++ )
307        {
308                /* if( t[i] <= ' ' || ((unsigned char *)t)[i] >= 128 || t[i] == '%' ) */
309                if( !isalnum( t[i] ) )
310                {
311                        sprintf( s + j, "%%%02X", ((unsigned char*)t)[i] );
312                        j += 2;
313                }
314                else
315                {
316                        s[j] = t[i];
317                }
318        }
319        s[j] = 0;
320       
321        g_free( t );
322}
323
324/* Strip newlines from a string. Modifies the string passed to it. */ 
325char *strip_newlines( char *source )
326{
327        int i; 
328
329        for( i = 0; source[i] != '\0'; i ++ )
330                if( source[i] == '\n' || source[i] == '\r' )
331                        source[i] = ' ';
332       
333        return source;
334}
335
336/* Wrap an IPv4 address into IPv6 space. Not thread-safe... */
337char *ipv6_wrap( char *src )
338{
339        static char dst[64];
340        int i;
341       
342        for( i = 0; src[i]; i ++ )
343                if( ( src[i] < '0' || src[i] > '9' ) && src[i] != '.' )
344                        break;
345       
346        /* Hmm, it's not even an IP... */
347        if( src[i] )
348                return src;
349       
350        g_snprintf( dst, sizeof( dst ), "::ffff:%s", src );
351       
352        return dst;
353}
354
355/* Unwrap an IPv4 address into IPv6 space. Thread-safe, because it's very simple. :-) */
356char *ipv6_unwrap( char *src )
357{
358        int i;
359       
360        if( g_strncasecmp( src, "::ffff:", 7 ) != 0 )
361                return src;
362       
363        for( i = 7; src[i]; i ++ )
364                if( ( src[i] < '0' || src[i] > '9' ) && src[i] != '.' )
365                        break;
366       
367        /* Hmm, it's not even an IP... */
368        if( src[i] )
369                return src;
370       
371        return ( src + 7 );
372}
373
374/* Convert from one charset to another.
375   
376   from_cs, to_cs: Source and destination charsets
377   src, dst: Source and destination strings
378   size: Size if src. 0 == use strlen(). strlen() is not reliable for UNICODE/UTF16 strings though.
379   maxbuf: Maximum number of bytes to write to dst
380   
381   Returns the number of bytes written to maxbuf or -1 on an error.
382*/
383signed int do_iconv( char *from_cs, char *to_cs, char *src, char *dst, size_t size, size_t maxbuf )
384{
385        GIConv cd;
386        size_t res;
387        size_t inbytesleft, outbytesleft;
388        char *inbuf = src;
389        char *outbuf = dst;
390       
391        cd = g_iconv_open( to_cs, from_cs );
392        if( cd == (GIConv) -1 )
393                return( -1 );
394       
395        inbytesleft = size ? size : strlen( src );
396        outbytesleft = maxbuf - 1;
397        res = g_iconv( cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft );
398        *outbuf = '\0';
399        g_iconv_close( cd );
400       
401        if( res == (size_t) -1 )
402                return( -1 );
403        else
404                return( outbuf - dst );
405}
406
407/* A pretty reliable random number generator. Tries to use the /dev/random
408   devices first, and falls back to the random number generator from libc
409   when it fails. Opens randomizer devices with O_NONBLOCK to make sure a
410   lack of entropy won't halt BitlBee. */
411void random_bytes( unsigned char *buf, int count )
412{
413#ifndef _WIN32
414        static int use_dev = -1;
415       
416        /* Actually this probing code isn't really necessary, is it? */
417        if( use_dev == -1 )
418        {
419                if( access( "/dev/random", R_OK ) == 0 || access( "/dev/urandom", R_OK ) == 0 )
420                        use_dev = 1;
421                else
422                {
423                        use_dev = 0;
424                        srand( ( getpid() << 16 ) ^ time( NULL ) );
425                }
426        }
427       
428        if( use_dev )
429        {
430                int fd;
431               
432                /* At least on Linux, /dev/random can block if there's not
433                   enough entropy. We really don't want that, so if it can't
434                   give anything, use /dev/urandom instead. */
435                if( ( fd = open( "/dev/random", O_RDONLY | O_NONBLOCK ) ) >= 0 )
436                        if( read( fd, buf, count ) == count )
437                        {
438                                close( fd );
439                                return;
440                        }
441                close( fd );
442               
443                /* urandom isn't supposed to block at all, but just to be
444                   sure. If it blocks, we'll disable use_dev and use the libc
445                   randomizer instead. */
446                if( ( fd = open( "/dev/urandom", O_RDONLY | O_NONBLOCK ) ) >= 0 )
447                        if( read( fd, buf, count ) == count )
448                        {
449                                close( fd );
450                                return;
451                        }
452                close( fd );
453               
454                /* If /dev/random blocks once, we'll still try to use it
455                   again next time. If /dev/urandom also fails for some
456                   reason, stick with libc during this session. */
457               
458                use_dev = 0;
459                srand( ( getpid() << 16 ) ^ time( NULL ) );
460        }
461       
462        if( !use_dev )
463#endif
464        {
465                int i;
466               
467                /* Possibly the LSB of rand() isn't very random on some
468                   platforms. Seems okay on at least Linux and OSX though. */
469                for( i = 0; i < count; i ++ )
470                        buf[i] = rand() & 0xff;
471        }
472}
473
474int is_bool( char *value )
475{
476        if( *value == 0 )
477                return 0;
478       
479        if( ( g_strcasecmp( value, "true" ) == 0 ) || ( g_strcasecmp( value, "yes" ) == 0 ) || ( g_strcasecmp( value, "on" ) == 0 ) )
480                return 1;
481        if( ( g_strcasecmp( value, "false" ) == 0 ) || ( g_strcasecmp( value, "no" ) == 0 ) || ( g_strcasecmp( value, "off" ) == 0 ) )
482                return 1;
483       
484        while( *value )
485                if( !isdigit( *value ) )
486                        return 0;
487                else
488                        value ++;
489       
490        return 1;
491}
492
493int bool2int( char *value )
494{
495        int i;
496       
497        if( ( g_strcasecmp( value, "true" ) == 0 ) || ( g_strcasecmp( value, "yes" ) == 0 ) || ( g_strcasecmp( value, "on" ) == 0 ) )
498                return 1;
499        if( ( g_strcasecmp( value, "false" ) == 0 ) || ( g_strcasecmp( value, "no" ) == 0 ) || ( g_strcasecmp( value, "off" ) == 0 ) )
500                return 0;
501       
502        if( sscanf( value, "%d", &i ) == 1 )
503                return i;
504       
505        return 0;
506}
507
508struct ns_srv_reply *srv_lookup( char *service, char *protocol, char *domain )
509{       
510        struct ns_srv_reply *reply = NULL;
511#ifdef HAVE_RESOLV_A
512        char name[1024];
513        unsigned char querybuf[1024];
514        const unsigned char *buf;
515        ns_msg nsh;
516        ns_rr rr;
517        int i, len, size;
518       
519        g_snprintf( name, sizeof( name ), "_%s._%s.%s", service, protocol, domain );
520       
521        if( ( size = res_query( name, ns_c_in, ns_t_srv, querybuf, sizeof( querybuf ) ) ) <= 0 )
522                return NULL;
523       
524        if( ns_initparse( querybuf, size, &nsh ) != 0 )
525                return NULL;
526       
527        if( ns_parserr( &nsh, ns_s_an, 0, &rr ) != 0 )
528                return NULL;
529       
530        size = ns_rr_rdlen( rr );
531        buf = ns_rr_rdata( rr );
532       
533        len = 0;
534        for( i = 6; i < size && buf[i]; i += buf[i] + 1 )
535                len += buf[i] + 1;
536       
537        if( i > size )
538                return NULL;
539       
540        reply = g_malloc( sizeof( struct ns_srv_reply ) + len );
541        memcpy( reply->name, buf + 7, len );
542       
543        for( i = buf[6]; i < len && buf[7+i]; i += buf[7+i] + 1 )
544                reply->name[i] = '.';
545       
546        if( i > len )
547        {
548                g_free( reply );
549                return NULL;
550        }
551       
552        reply->prio = ( buf[0] << 8 ) | buf[1];
553        reply->weight = ( buf[2] << 8 ) | buf[3];
554        reply->port = ( buf[4] << 8 ) | buf[5];
555#endif
556       
557        return reply;
558}
559
560/* Word wrapping. Yes, I know this isn't UTF-8 clean. I'm willing to take the risk. */
561char *word_wrap( const char *msg, int line_len )
562{
563        GString *ret = g_string_sized_new( strlen( msg ) + 16 );
564       
565        while( strlen( msg ) > line_len )
566        {
567                int i;
568               
569                /* First try to find out if there's a newline already. Don't
570                   want to add more splits than necessary. */
571                for( i = line_len; i > 0 && msg[i] != '\n'; i -- );
572                if( msg[i] == '\n' )
573                {
574                        g_string_append_len( ret, msg, i + 1 );
575                        msg += i + 1;
576                        continue;
577                }
578               
579                for( i = line_len; i > 0; i -- )
580                {
581                        if( msg[i] == '-' )
582                        {
583                                g_string_append_len( ret, msg, i + 1 );
584                                g_string_append_c( ret, '\n' );
585                                msg += i + 1;
586                                break;
587                        }
588                        else if( msg[i] == ' ' )
589                        {
590                                g_string_append_len( ret, msg, i );
591                                g_string_append_c( ret, '\n' );
592                                msg += i + 1;
593                                break;
594                        }
595                }
596                if( i == 0 )
597                {
598                        g_string_append_len( ret, msg, line_len );
599                        g_string_append_c( ret, '\n' );
600                        msg += line_len;
601                }
602        }
603        g_string_append( ret, msg );
604       
605        return g_string_free( ret, FALSE );
606}
607
608gboolean ssl_sockerr_again( void *ssl )
609{
610        if( ssl )
611                return ssl_errno == SSL_AGAIN;
612        else
613                return sockerr_again();
614}
615
616/* Returns values: -1 == Failure (base64-decoded to something unexpected)
617                    0 == Okay
618                    1 == Password doesn't match the hash. */
619int md5_verify_password( char *password, char *hash )
620{
621        md5_byte_t *pass_dec = NULL;
622        md5_byte_t pass_md5[16];
623        md5_state_t md5_state;
624        int ret = -1, i;
625       
626        if( base64_decode( hash, &pass_dec ) == 21 )
627        {
628                md5_init( &md5_state );
629                md5_append( &md5_state, (md5_byte_t*) password, strlen( password ) );
630                md5_append( &md5_state, (md5_byte_t*) pass_dec + 16, 5 ); /* Hmmm, salt! */
631                md5_finish( &md5_state, pass_md5 );
632               
633                for( i = 0; i < 16; i ++ )
634                {
635                        if( pass_dec[i] != pass_md5[i] )
636                        {
637                                ret = 1;
638                                break;
639                        }
640                }
641               
642                /* If we reached the end of the loop, it was a match! */
643                if( i == 16 )
644                        ret = 0;
645        }
646       
647        g_free( pass_dec );
648
649        return ret;
650}
Note: See TracBrowser for help on using the repository browser.