aboutsummaryrefslogtreecommitdiffstats
path: root/lib/misc.c
diff options
context:
space:
mode:
authorJelmer Vernooij <jelmer@samba.org>2008-04-02 16:22:57 +0200
committerJelmer Vernooij <jelmer@samba.org>2008-04-02 16:22:57 +0200
commit85d7b857fb8ca8e3c03d4abb3368a0966760630c (patch)
treea16163e557bcae3af41bde7d2d771d64ca248a97 /lib/misc.c
parent875ad4201402b1a8f80ba22a6cdcdb152c6e5510 (diff)
parentdd345753c1742905c9f81aa71d8b09109fbc5456 (diff)
Merge trunk.
Diffstat (limited to 'lib/misc.c')
-rw-r--r--lib/misc.c639
1 files changed, 639 insertions, 0 deletions
diff --git a/lib/misc.c b/lib/misc.c
new file mode 100644
index 00000000..ccf208b5
--- /dev/null
+++ b/lib/misc.c
@@ -0,0 +1,639 @@
+ /********************************************************************\
+ * BitlBee -- An IRC to other IM-networks gateway *
+ * *
+ * Copyright 2002-2006 Wilmer van der Gaast and others *
+ \********************************************************************/
+
+/*
+ * Various utility functions. Some are copied from Gaim to support the
+ * IM-modules, most are from BitlBee.
+ *
+ * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
+ * (and possibly other members of the Gaim team)
+ * Copyright 2002-2006 Wilmer van der Gaast <wilmer@gaast.net>
+ */
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License with
+ the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+ if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#define BITLBEE_CORE
+#include "nogaim.h"
+#include "base64.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <glib.h>
+#include <time.h>
+
+#ifdef HAVE_RESOLV_A
+#include <arpa/nameser.h>
+#include <resolv.h>
+#endif
+
+#include "ssl_client.h"
+
+void strip_linefeed(gchar *text)
+{
+ int i, j;
+ gchar *text2 = g_malloc(strlen(text) + 1);
+
+ for (i = 0, j = 0; text[i]; i++)
+ if (text[i] != '\r')
+ text2[j++] = text[i];
+ text2[j] = '\0';
+
+ strcpy(text, text2);
+ g_free(text2);
+}
+
+char *normalize(const char *s)
+{
+ static char buf[BUF_LEN];
+ char *t, *u;
+ int x = 0;
+
+ g_return_val_if_fail((s != NULL), NULL);
+
+ u = t = g_strdup(s);
+
+ strcpy(t, s);
+ g_strdown(t);
+
+ while (*t && (x < BUF_LEN - 1)) {
+ if (*t != ' ') {
+ buf[x] = *t;
+ x++;
+ }
+ t++;
+ }
+ buf[x] = '\0';
+ g_free(u);
+ return buf;
+}
+
+time_t get_time(int year, int month, int day, int hour, int min, int sec)
+{
+ struct tm tm;
+
+ memset(&tm, 0, sizeof(struct tm));
+ tm.tm_year = year - 1900;
+ tm.tm_mon = month - 1;
+ tm.tm_mday = day;
+ tm.tm_hour = hour;
+ tm.tm_min = min;
+ tm.tm_sec = sec >= 0 ? sec : time(NULL) % 60;
+
+ return mktime(&tm);
+}
+
+typedef struct htmlentity
+{
+ char code[7];
+ char is[3];
+} htmlentity_t;
+
+static const htmlentity_t ent[] =
+{
+ { "lt", "<" },
+ { "gt", ">" },
+ { "amp", "&" },
+ { "quot", "\"" },
+ { "aacute", "á" },
+ { "eacute", "é" },
+ { "iacute", "é" },
+ { "oacute", "ó" },
+ { "uacute", "ú" },
+ { "agrave", "à" },
+ { "egrave", "è" },
+ { "igrave", "ì" },
+ { "ograve", "ò" },
+ { "ugrave", "ù" },
+ { "acirc", "â" },
+ { "ecirc", "ê" },
+ { "icirc", "î" },
+ { "ocirc", "ô" },
+ { "ucirc", "û" },
+ { "auml", "ä" },
+ { "euml", "ë" },
+ { "iuml", "ï" },
+ { "ouml", "ö" },
+ { "uuml", "ü" },
+ { "nbsp", " " },
+ { "", "" }
+};
+
+void strip_html( char *in )
+{
+ char *start = in;
+ char *out = g_malloc( strlen( in ) + 1 );
+ char *s = out, *cs;
+ int i, matched;
+
+ memset( out, 0, strlen( in ) + 1 );
+
+ while( *in )
+ {
+ if( *in == '<' && ( isalpha( *(in+1) ) || *(in+1) == '/' ) )
+ {
+ /* If in points at a < and in+1 points at a letter or a slash, this is probably
+ a HTML-tag. Try to find a closing > and continue there. If the > can't be
+ found, assume that it wasn't a HTML-tag after all. */
+
+ cs = in;
+
+ while( *in && *in != '>' )
+ in ++;
+
+ if( *in )
+ {
+ if( g_strncasecmp( cs+1, "br", 2) == 0 )
+ *(s++) = '\n';
+ in ++;
+ }
+ else
+ {
+ in = cs;
+ *(s++) = *(in++);
+ }
+ }
+ else if( *in == '&' )
+ {
+ cs = ++in;
+ while( *in && isalpha( *in ) )
+ in ++;
+
+ if( *in == ';' ) in ++;
+ matched = 0;
+
+ for( i = 0; *ent[i].code; i ++ )
+ if( g_strncasecmp( ent[i].code, cs, strlen( ent[i].code ) ) == 0 )
+ {
+ int j;
+
+ for( j = 0; ent[i].is[j]; j ++ )
+ *(s++) = ent[i].is[j];
+
+ matched = 1;
+ break;
+ }
+
+ /* None of the entities were matched, so return the string */
+ if( !matched )
+ {
+ in = cs - 1;
+ *(s++) = *(in++);
+ }
+ }
+ else
+ {
+ *(s++) = *(in++);
+ }
+ }
+
+ strcpy( start, out );
+ g_free( out );
+}
+
+char *escape_html( const char *html )
+{
+ const char *c = html;
+ GString *ret;
+ char *str;
+
+ if( html == NULL )
+ return( NULL );
+
+ ret = g_string_new( "" );
+
+ while( *c )
+ {
+ switch( *c )
+ {
+ case '&':
+ ret = g_string_append( ret, "&amp;" );
+ break;
+ case '<':
+ ret = g_string_append( ret, "&lt;" );
+ break;
+ case '>':
+ ret = g_string_append( ret, "&gt;" );
+ break;
+ case '"':
+ ret = g_string_append( ret, "&quot;" );
+ break;
+ default:
+ ret = g_string_append_c( ret, *c );
+ }
+ c ++;
+ }
+
+ str = ret->str;
+ g_string_free( ret, FALSE );
+ return( str );
+}
+
+/* Decode%20a%20file%20name */
+void http_decode( char *s )
+{
+ char *t;
+ int i, j, k;
+
+ t = g_new( char, strlen( s ) + 1 );
+
+ for( i = j = 0; s[i]; i ++, j ++ )
+ {
+ if( s[i] == '%' )
+ {
+ if( sscanf( s + i + 1, "%2x", &k ) )
+ {
+ t[j] = k;
+ i += 2;
+ }
+ else
+ {
+ *t = 0;
+ break;
+ }
+ }
+ else
+ {
+ t[j] = s[i];
+ }
+ }
+ t[j] = 0;
+
+ strcpy( s, t );
+ g_free( t );
+}
+
+/* Warning: This one explodes the string. Worst-cases can make the string 3x its original size! */
+/* This fuction is safe, but make sure you call it safely as well! */
+void http_encode( char *s )
+{
+ char *t;
+ int i, j;
+
+ t = g_strdup( s );
+
+ for( i = j = 0; t[i]; i ++, j ++ )
+ {
+ /* if( t[i] <= ' ' || ((unsigned char *)t)[i] >= 128 || t[i] == '%' ) */
+ if( !isalnum( t[i] ) )
+ {
+ sprintf( s + j, "%%%02X", ((unsigned char*)t)[i] );
+ j += 2;
+ }
+ else
+ {
+ s[j] = t[i];
+ }
+ }
+ s[j] = 0;
+
+ g_free( t );
+}
+
+/* Strip newlines from a string. Modifies the string passed to it. */
+char *strip_newlines( char *source )
+{
+ int i;
+
+ for( i = 0; source[i] != '\0'; i ++ )
+ if( source[i] == '\n' || source[i] == '\r' )
+ source[i] = ' ';
+
+ return source;
+}
+
+/* Wrap an IPv4 address into IPv6 space. Not thread-safe... */
+char *ipv6_wrap( char *src )
+{
+ static char dst[64];
+ int i;
+
+ for( i = 0; src[i]; i ++ )
+ if( ( src[i] < '0' || src[i] > '9' ) && src[i] != '.' )
+ break;
+
+ /* Hmm, it's not even an IP... */
+ if( src[i] )
+ return src;
+
+ g_snprintf( dst, sizeof( dst ), "::ffff:%s", src );
+
+ return dst;
+}
+
+/* Unwrap an IPv4 address into IPv6 space. Thread-safe, because it's very simple. :-) */
+char *ipv6_unwrap( char *src )
+{
+ int i;
+
+ if( g_strncasecmp( src, "::ffff:", 7 ) != 0 )
+ return src;
+
+ for( i = 7; src[i]; i ++ )
+ if( ( src[i] < '0' || src[i] > '9' ) && src[i] != '.' )
+ break;
+
+ /* Hmm, it's not even an IP... */
+ if( src[i] )
+ return src;
+
+ return ( src + 7 );
+}
+
+/* Convert from one charset to another.
+
+ from_cs, to_cs: Source and destination charsets
+ src, dst: Source and destination strings
+ size: Size if src. 0 == use strlen(). strlen() is not reliable for UNICODE/UTF16 strings though.
+ maxbuf: Maximum number of bytes to write to dst
+
+ Returns the number of bytes written to maxbuf or -1 on an error.
+*/
+signed int do_iconv( char *from_cs, char *to_cs, char *src, char *dst, size_t size, size_t maxbuf )
+{
+ GIConv cd;
+ size_t res;
+ size_t inbytesleft, outbytesleft;
+ char *inbuf = src;
+ char *outbuf = dst;
+
+ cd = g_iconv_open( to_cs, from_cs );
+ if( cd == (GIConv) -1 )
+ return( -1 );
+
+ inbytesleft = size ? size : strlen( src );
+ outbytesleft = maxbuf - 1;
+ res = g_iconv( cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft );
+ *outbuf = '\0';
+ g_iconv_close( cd );
+
+ if( res == (size_t) -1 )
+ return( -1 );
+ else
+ return( outbuf - dst );
+}
+
+/* A pretty reliable random number generator. Tries to use the /dev/random
+ devices first, and falls back to the random number generator from libc
+ when it fails. Opens randomizer devices with O_NONBLOCK to make sure a
+ lack of entropy won't halt BitlBee. */
+void random_bytes( unsigned char *buf, int count )
+{
+ static int use_dev = -1;
+
+ /* Actually this probing code isn't really necessary, is it? */
+ if( use_dev == -1 )
+ {
+ if( access( "/dev/random", R_OK ) == 0 || access( "/dev/urandom", R_OK ) == 0 )
+ use_dev = 1;
+ else
+ {
+ use_dev = 0;
+ srand( ( getpid() << 16 ) ^ time( NULL ) );
+ }
+ }
+
+ if( use_dev )
+ {
+ int fd;
+
+ /* At least on Linux, /dev/random can block if there's not
+ enough entropy. We really don't want that, so if it can't
+ give anything, use /dev/urandom instead. */
+ if( ( fd = open( "/dev/random", O_RDONLY | O_NONBLOCK ) ) >= 0 )
+ if( read( fd, buf, count ) == count )
+ {
+ close( fd );
+ return;
+ }
+ close( fd );
+
+ /* urandom isn't supposed to block at all, but just to be
+ sure. If it blocks, we'll disable use_dev and use the libc
+ randomizer instead. */
+ if( ( fd = open( "/dev/urandom", O_RDONLY | O_NONBLOCK ) ) >= 0 )
+ if( read( fd, buf, count ) == count )
+ {
+ close( fd );
+ return;
+ }
+ close( fd );
+
+ /* If /dev/random blocks once, we'll still try to use it
+ again next time. If /dev/urandom also fails for some
+ reason, stick with libc during this session. */
+
+ use_dev = 0;
+ srand( ( getpid() << 16 ) ^ time( NULL ) );
+ }
+
+ if( !use_dev )
+ {
+ int i;
+
+ /* Possibly the LSB of rand() isn't very random on some
+ platforms. Seems okay on at least Linux and OSX though. */
+ for( i = 0; i < count; i ++ )
+ buf[i] = rand() & 0xff;
+ }
+}
+
+int is_bool( char *value )
+{
+ if( *value == 0 )
+ return 0;
+
+ if( ( g_strcasecmp( value, "true" ) == 0 ) || ( g_strcasecmp( value, "yes" ) == 0 ) || ( g_strcasecmp( value, "on" ) == 0 ) )
+ return 1;
+ if( ( g_strcasecmp( value, "false" ) == 0 ) || ( g_strcasecmp( value, "no" ) == 0 ) || ( g_strcasecmp( value, "off" ) == 0 ) )
+ return 1;
+
+ while( *value )
+ if( !isdigit( *value ) )
+ return 0;
+ else
+ value ++;
+
+ return 1;
+}
+
+int bool2int( char *value )
+{
+ int i;
+
+ if( ( g_strcasecmp( value, "true" ) == 0 ) || ( g_strcasecmp( value, "yes" ) == 0 ) || ( g_strcasecmp( value, "on" ) == 0 ) )
+ return 1;
+ if( ( g_strcasecmp( value, "false" ) == 0 ) || ( g_strcasecmp( value, "no" ) == 0 ) || ( g_strcasecmp( value, "off" ) == 0 ) )
+ return 0;
+
+ if( sscanf( value, "%d", &i ) == 1 )
+ return i;
+
+ return 0;
+}
+
+struct ns_srv_reply *srv_lookup( char *service, char *protocol, char *domain )
+{
+ struct ns_srv_reply *reply = NULL;
+#ifdef HAVE_RESOLV_A
+ char name[1024];
+ unsigned char querybuf[1024];
+ const unsigned char *buf;
+ ns_msg nsh;
+ ns_rr rr;
+ int i, len, size;
+
+ g_snprintf( name, sizeof( name ), "_%s._%s.%s", service, protocol, domain );
+
+ if( ( size = res_query( name, ns_c_in, ns_t_srv, querybuf, sizeof( querybuf ) ) ) <= 0 )
+ return NULL;
+
+ if( ns_initparse( querybuf, size, &nsh ) != 0 )
+ return NULL;
+
+ if( ns_parserr( &nsh, ns_s_an, 0, &rr ) != 0 )
+ return NULL;
+
+ size = ns_rr_rdlen( rr );
+ buf = ns_rr_rdata( rr );
+
+ len = 0;
+ for( i = 6; i < size && buf[i]; i += buf[i] + 1 )
+ len += buf[i] + 1;
+
+ if( i > size )
+ return NULL;
+
+ reply = g_malloc( sizeof( struct ns_srv_reply ) + len );
+ memcpy( reply->name, buf + 7, len );
+
+ for( i = buf[6]; i < len && buf[7+i]; i += buf[7+i] + 1 )
+ reply->name[i] = '.';
+
+ if( i > len )
+ {
+ g_free( reply );
+ return NULL;
+ }
+
+ reply->prio = ( buf[0] << 8 ) | buf[1];
+ reply->weight = ( buf[2] << 8 ) | buf[3];
+ reply->port = ( buf[4] << 8 ) | buf[5];
+#endif
+
+ return reply;
+}
+
+/* Word wrapping. Yes, I know this isn't UTF-8 clean. I'm willing to take the risk. */
+char *word_wrap( char *msg, int line_len )
+{
+ GString *ret = g_string_sized_new( strlen( msg ) + 16 );
+
+ while( strlen( msg ) > line_len )
+ {
+ int i;
+
+ /* First try to find out if there's a newline already. Don't
+ want to add more splits than necessary. */
+ for( i = line_len; i > 0 && msg[i] != '\n'; i -- );
+ if( msg[i] == '\n' )
+ {
+ g_string_append_len( ret, msg, i + 1 );
+ msg += i + 1;
+ continue;
+ }
+
+ for( i = line_len; i > 0; i -- )
+ {
+ if( msg[i] == '-' )
+ {
+ g_string_append_len( ret, msg, i + 1 );
+ g_string_append_c( ret, '\n' );
+ msg += i + 1;
+ break;
+ }
+ else if( msg[i] == ' ' )
+ {
+ g_string_append_len( ret, msg, i );
+ g_string_append_c( ret, '\n' );
+ msg += i + 1;
+ break;
+ }
+ }
+ if( i == 0 )
+ {
+ g_string_append_len( ret, msg, line_len );
+ g_string_append_c( ret, '\n' );
+ msg += line_len;
+ }
+ }
+ g_string_append( ret, msg );
+
+ return g_string_free( ret, FALSE );
+}
+
+gboolean ssl_sockerr_again( void *ssl )
+{
+ if( ssl )
+ return ssl_errno == SSL_AGAIN;
+ else
+ return sockerr_again();
+}
+
+/* Returns values: -1 == Failure (base64-decoded to something unexpected)
+ 0 == Okay
+ 1 == Password doesn't match the hash. */
+int md5_verify_password( char *password, char *hash )
+{
+ md5_byte_t *pass_dec = NULL;
+ md5_byte_t pass_md5[16];
+ md5_state_t md5_state;
+ int ret, i;
+
+ if( base64_decode( hash, &pass_dec ) != 21 )
+ {
+ ret = -1;
+ }
+ else
+ {
+ md5_init( &md5_state );
+ md5_append( &md5_state, (md5_byte_t*) password, strlen( password ) );
+ md5_append( &md5_state, (md5_byte_t*) pass_dec + 16, 5 ); /* Hmmm, salt! */
+ md5_finish( &md5_state, pass_md5 );
+
+ for( i = 0; i < 16; i ++ )
+ {
+ if( pass_dec[i] != pass_md5[i] )
+ {
+ ret = 1;
+ break;
+ }
+ }
+
+ /* If we reached the end of the loop, it was a match! */
+ if( i == 16 )
+ ret = 0;
+ }
+
+ g_free( pass_dec );
+
+ return ret;
+}