source: lib/misc.c @ 5a71d9c

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