source: lib/misc.c @ 8076ec0

Last change on this file since 8076ec0 was 2231302, checked in by Wilmer van der Gaast <wilmer@…>, at 2007-11-05T22:59:49Z

Merging from Jelmer.

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