source: lib/misc.c @ df67b48

Last change on this file since df67b48 was c39cd8e, checked in by Wilmer van der Gaast <wilmer@…>, at 2017-04-02T18:50:49Z

Add reverse_lookup() function which does a more proper (verified) DNS
reverse lookup. (Code copied from OpenSSH instead of redoing it
poorly.)

  • Property mode set to 100644
File size: 20.5 KB
RevLine 
[5ebff60]1/********************************************************************\
[b7d3cc34]2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
[0e788f5]4  * Copyright 2002-2012 Wilmer van der Gaast and others                *
[b7d3cc34]5  \********************************************************************/
6
7/*
[c88999c]8 * Various utility functions. Some are copied from Gaim to support the
9 * IM-modules, most are from BitlBee.
[b7d3cc34]10 *
11 * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
12 *                          (and possibly other members of the Gaim team)
[0e788f5]13 * Copyright 2002-2012 Wilmer van der Gaast <wilmer@gaast.net>
[b7d3cc34]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;
[6f10697]29  if not, write to the Free Software Foundation, Inc., 51 Franklin St.,
30  Fifth Floor, Boston, MA  02110-1301  USA
[b7d3cc34]31*/
32
33#define BITLBEE_CORE
34#include "nogaim.h"
[4e8db1c]35#include "base64.h"
[c6ca3ee]36#include "md5.h"
[b7d3cc34]37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
[dd8d4c5]40#include <ctype.h>
[b7d3cc34]41#include <glib.h>
42#include <time.h>
43
[36e9f62]44#ifdef HAVE_RESOLV_A
45#include <arpa/nameser.h>
46#include <resolv.h>
47#endif
48
[4cf80bb]49#include "md5.h"
[d52111a]50#include "ssl_client.h"
51
[b7d3cc34]52void strip_linefeed(gchar *text)
53{
54        int i, j;
55        gchar *text2 = g_malloc(strlen(text) + 1);
56
[5ebff60]57        for (i = 0, j = 0; text[i]; i++) {
58                if (text[i] != '\r') {
[b7d3cc34]59                        text2[j++] = text[i];
[5ebff60]60                }
61        }
[b7d3cc34]62        text2[j] = '\0';
63
64        strcpy(text, text2);
65        g_free(text2);
66}
67
68time_t get_time(int year, int month, int day, int hour, int min, int sec)
69{
70        struct tm tm;
71
[613cc55]72        memset(&tm, 0, sizeof(struct tm));
[b7d3cc34]73        tm.tm_year = year - 1900;
74        tm.tm_mon = month - 1;
75        tm.tm_mday = day;
76        tm.tm_hour = hour;
77        tm.tm_min = min;
78        tm.tm_sec = sec >= 0 ? sec : time(NULL) % 60;
[5ebff60]79
[b7d3cc34]80        return mktime(&tm);
81}
82
[5ebff60]83time_t mktime_utc(struct tm *tp)
[2e3a857]84{
85        struct tm utc;
86        time_t res, tres;
[5ebff60]87
[2e3a857]88        tp->tm_isdst = -1;
[5ebff60]89        res = mktime(tp);
[2e3a857]90        /* Problem is, mktime() just gave us the GMT timestamp for the
91           given local time... While the given time WAS NOT local. So
92           we should fix this now.
[5ebff60]93
[2e3a857]94           Now I could choose between messing with environment variables
95           (kludgy) or using timegm() (not portable)... Or doing the
96           following, which I actually prefer...
[5ebff60]97
[2e3a857]98           tzset() may also work but in other places I actually want to
99           use local time.
[5ebff60]100
[2e3a857]101           FFFFFFFFFFFFFFFFFFFFFUUUUUUUUUUUUUUUUUUUU!! */
[5ebff60]102        gmtime_r(&res, &utc);
[2e3a857]103        utc.tm_isdst = -1;
[5ebff60]104        if (utc.tm_hour == tp->tm_hour && utc.tm_min == tp->tm_min) {
[2e3a857]105                /* Sweet! We're in UTC right now... */
106                return res;
[5ebff60]107        }
108
109        tres = mktime(&utc);
[2e3a857]110        res += res - tres;
[5ebff60]111
[2e3a857]112        /* Yes, this is a hack. And it will go wrong around DST changes.
113           BUT this is more likely to be threadsafe than messing with
114           environment variables, and possibly more portable... */
[5ebff60]115
[2e3a857]116        return res;
117}
118
[5ebff60]119typedef struct htmlentity {
[51fdc45]120        char code[7];
121        char is[3];
[b7d3cc34]122} htmlentity_t;
123
[39cc341]124static const htmlentity_t ent[] =
[b7d3cc34]125{
[39cc341]126        { "lt",     "<" },
127        { "gt",     ">" },
128        { "amp",    "&" },
[b52e478]129        { "apos",   "'" },
[39cc341]130        { "quot",   "\"" },
131        { "aacute", "á" },
132        { "eacute", "é" },
133        { "iacute", "é" },
134        { "oacute", "ó" },
135        { "uacute", "ú" },
136        { "agrave", "à" },
137        { "egrave", "è" },
138        { "igrave", "ì" },
139        { "ograve", "ò" },
140        { "ugrave", "ù" },
141        { "acirc",  "â" },
142        { "ecirc",  "ê" },
143        { "icirc",  "î" },
144        { "ocirc",  "ô" },
145        { "ucirc",  "û" },
146        { "auml",   "ä" },
147        { "euml",   "ë" },
148        { "iuml",   "ï" },
149        { "ouml",   "ö" },
150        { "uuml",   "ü" },
151        { "nbsp",   " " },
152        { "",        ""  }
[b7d3cc34]153};
154
[5ebff60]155void strip_html(char *in)
[b7d3cc34]156{
157        char *start = in;
[5ebff60]158        char out[strlen(in) + 1];
[b7d3cc34]159        char *s = out, *cs;
160        int i, matched;
[5a71d9c]161        int taglen;
[5ebff60]162
163        memset(out, 0, sizeof(out));
164
165        while (*in) {
166                if (*in == '<' && (g_ascii_isalpha(*(in + 1)) || *(in + 1) == '/')) {
[b7d3cc34]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. */
[5ebff60]170
[b7d3cc34]171                        cs = in;
[5ebff60]172
173                        while (*in && *in != '>') {
174                                in++;
175                        }
176
[be999a5]177                        taglen = in - cs - 1;   /* not <0 because the above loop runs at least once */
[5ebff60]178                        if (*in) {
179                                if (g_strncasecmp(cs + 1, "b", taglen) == 0) {
[5a71d9c]180                                        *(s++) = '\x02';
[5ebff60]181                                } else if (g_strncasecmp(cs + 1, "/b", taglen) == 0) {
[5a71d9c]182                                        *(s++) = '\x02';
[5ebff60]183                                } else if (g_strncasecmp(cs + 1, "i", taglen) == 0) {
[5a71d9c]184                                        *(s++) = '\x1f';
[5ebff60]185                                } else if (g_strncasecmp(cs + 1, "/i", taglen) == 0) {
[5a71d9c]186                                        *(s++) = '\x1f';
[5ebff60]187                                } else if (g_strncasecmp(cs + 1, "br", taglen) == 0) {
[b7d3cc34]188                                        *(s++) = '\n';
[098b430]189                                } else if (g_strncasecmp(cs + 1, "br/", taglen) == 0) {
190                                        *(s++) = '\n';
191                                } else if (g_strncasecmp(cs + 1, "br /", taglen) == 0) {
192                                        *(s++) = '\n';
[5ebff60]193                                }
194                                in++;
195                        } else {
[b7d3cc34]196                                in = cs;
197                                *(s++) = *(in++);
198                        }
[5ebff60]199                } else if (*in == '&') {
[b7d3cc34]200                        cs = ++in;
[5ebff60]201                        while (*in && g_ascii_isalpha(*in)) {
202                                in++;
203                        }
204
205                        if (*in == ';') {
206                                in++;
207                        }
[b7d3cc34]208                        matched = 0;
[5ebff60]209
210                        for (i = 0; *ent[i].code; i++) {
211                                if (g_strncasecmp(ent[i].code, cs, strlen(ent[i].code)) == 0) {
[39cc341]212                                        int j;
[5ebff60]213
214                                        for (j = 0; ent[i].is[j]; j++) {
[39cc341]215                                                *(s++) = ent[i].is[j];
[5ebff60]216                                        }
217
[b7d3cc34]218                                        matched = 1;
219                                        break;
220                                }
[5ebff60]221                        }
[b7d3cc34]222
223                        /* None of the entities were matched, so return the string */
[5ebff60]224                        if (!matched) {
[b7d3cc34]225                                in = cs - 1;
226                                *(s++) = *(in++);
227                        }
[5ebff60]228                } else {
[b7d3cc34]229                        *(s++) = *(in++);
230                }
231        }
[5ebff60]232
233        strcpy(start, out);
[b7d3cc34]234}
235
[5ebff60]236char *escape_html(const char *html)
[b7d3cc34]237{
238        const char *c = html;
239        GString *ret;
240        char *str;
[5ebff60]241
242        if (html == NULL) {
243                return(NULL);
244        }
245
246        ret = g_string_new("");
247
248        while (*c) {
249                switch (*c) {
250                case '&':
251                        ret = g_string_append(ret, "&amp;");
252                        break;
253                case '<':
254                        ret = g_string_append(ret, "&lt;");
255                        break;
256                case '>':
257                        ret = g_string_append(ret, "&gt;");
258                        break;
259                case '"':
260                        ret = g_string_append(ret, "&quot;");
261                        break;
262                default:
263                        ret = g_string_append_c(ret, *c);
[b7d3cc34]264                }
[5ebff60]265                c++;
[b7d3cc34]266        }
[5ebff60]267
[b7d3cc34]268        str = ret->str;
[5ebff60]269        g_string_free(ret, FALSE);
270        return(str);
[b7d3cc34]271}
272
[c88999c]273/* Decode%20a%20file%20name                                             */
[5ebff60]274void http_decode(char *s)
[c88999c]275{
276        char *t;
277        int i, j, k;
[5ebff60]278
279        t = g_new(char, strlen(s) + 1);
280
281        for (i = j = 0; s[i]; i++, j++) {
282                if (s[i] == '%') {
283                        if (sscanf(s + i + 1, "%2x", &k)) {
[c88999c]284                                t[j] = k;
285                                i += 2;
[5ebff60]286                        } else {
[c88999c]287                                *t = 0;
288                                break;
289                        }
[5ebff60]290                } else {
[c88999c]291                        t[j] = s[i];
292                }
293        }
294        t[j] = 0;
[5ebff60]295
296        strcpy(s, t);
297        g_free(t);
[c88999c]298}
299
300/* Warning: This one explodes the string. Worst-cases can make the string 3x its original size! */
[e88fe7da]301/* This function is safe, but make sure you call it safely as well! */
[5ebff60]302void http_encode(char *s)
[c88999c]303{
[5ebff60]304        char t[strlen(s) + 1];
[c88999c]305        int i, j;
[5ebff60]306
307        strcpy(t, s);
308        for (i = j = 0; t[i]; i++, j++) {
[6b13103]309                /* Warning: g_ascii_isalnum() is locale-aware, so don't use it here! */
[5ebff60]310                if ((t[i] >= 'A' && t[i] <= 'Z') ||
311                    (t[i] >= 'a' && t[i] <= 'z') ||
312                    (t[i] >= '0' && t[i] <= '9') ||
313                    strchr("._-~", t[i])) {
[b40e60d]314                        s[j] = t[i];
[5ebff60]315                } else {
316                        sprintf(s + j, "%%%02X", ((unsigned char *) t)[i]);
[b40e60d]317                        j += 2;
[c88999c]318                }
319        }
320        s[j] = 0;
321}
322
[5ebff60]323/* Strip newlines from a string. Modifies the string passed to it. */
324char *strip_newlines(char *source)
[c88999c]325{
[5ebff60]326        int i;
[c88999c]327
[5ebff60]328        for (i = 0; source[i] != '\0'; i++) {
329                if (source[i] == '\n' || source[i] == '\r') {
[c88999c]330                        source[i] = ' ';
[5ebff60]331                }
332        }
333
[c88999c]334        return source;
335}
[2a6ca4f]336
337/* Wrap an IPv4 address into IPv6 space. Not thread-safe... */
[5ebff60]338char *ipv6_wrap(char *src)
[2a6ca4f]339{
340        static char dst[64];
341        int i;
[5ebff60]342
343        for (i = 0; src[i]; i++) {
344                if ((src[i] < '0' || src[i] > '9') && src[i] != '.') {
[2a6ca4f]345                        break;
[5ebff60]346                }
347        }
348
[2a6ca4f]349        /* Hmm, it's not even an IP... */
[5ebff60]350        if (src[i]) {
[2a6ca4f]351                return src;
[5ebff60]352        }
353
354        g_snprintf(dst, sizeof(dst), "::ffff:%s", src);
355
[2a6ca4f]356        return dst;
357}
358
359/* Unwrap an IPv4 address into IPv6 space. Thread-safe, because it's very simple. :-) */
[5ebff60]360char *ipv6_unwrap(char *src)
[2a6ca4f]361{
362        int i;
[5ebff60]363
364        if (g_strncasecmp(src, "::ffff:", 7) != 0) {
[2a6ca4f]365                return src;
[5ebff60]366        }
367
368        for (i = 7; src[i]; i++) {
369                if ((src[i] < '0' || src[i] > '9') && src[i] != '.') {
[2a6ca4f]370                        break;
[5ebff60]371                }
372        }
373
[2a6ca4f]374        /* Hmm, it's not even an IP... */
[5ebff60]375        if (src[i]) {
[2a6ca4f]376                return src;
[5ebff60]377        }
378
379        return (src + 7);
[2a6ca4f]380}
[e27661d]381
382/* Convert from one charset to another.
[5ebff60]383
[e27661d]384   from_cs, to_cs: Source and destination charsets
385   src, dst: Source and destination strings
386   size: Size if src. 0 == use strlen(). strlen() is not reliable for UNICODE/UTF16 strings though.
387   maxbuf: Maximum number of bytes to write to dst
[5ebff60]388
[e27661d]389   Returns the number of bytes written to maxbuf or -1 on an error.
390*/
[5ebff60]391signed int do_iconv(char *from_cs, char *to_cs, char *src, char *dst, size_t size, size_t maxbuf)
[e27661d]392{
[574af7e]393        GIConv cd;
[e27661d]394        size_t res;
395        size_t inbytesleft, outbytesleft;
396        char *inbuf = src;
397        char *outbuf = dst;
[5ebff60]398
399        cd = g_iconv_open(to_cs, from_cs);
400        if (cd == (GIConv) - 1) {
[f5da476]401                return -1;
[5ebff60]402        }
403
404        inbytesleft = size ? size : strlen(src);
[e27661d]405        outbytesleft = maxbuf - 1;
[5ebff60]406        res = g_iconv(cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft);
[e27661d]407        *outbuf = '\0';
[5ebff60]408        g_iconv_close(cd);
409
410        if (res != 0) {
[f5da476]411                return -1;
[5ebff60]412        } else {
[f5da476]413                return outbuf - dst;
[5ebff60]414        }
[e27661d]415}
416
[5eab298f]417/* A wrapper for /dev/urandom.
418 * If /dev/urandom is not present or not usable, it calls abort()
419 * to prevent bitlbee from working without a decent entropy source */
[5ebff60]420void random_bytes(unsigned char *buf, int count)
[7f49a86]421{
[5eab298f]422        int fd;
[5ebff60]423
424        if (((fd = open("/dev/urandom", O_RDONLY)) == -1) ||
425            (read(fd, buf, count) == -1)) {
426                log_message(LOGLVL_ERROR, "/dev/urandom not present - aborting");
[5eab298f]427                abort();
[7f49a86]428        }
[5eab298f]429
[5ebff60]430        close(fd);
[7f49a86]431}
432
[5ebff60]433int is_bool(char *value)
[5100caa]434{
[5ebff60]435        if (*value == 0) {
[5100caa]436                return 0;
[5ebff60]437        }
438
439        if ((g_strcasecmp(value,
440                          "true") == 0) || (g_strcasecmp(value, "yes") == 0) || (g_strcasecmp(value, "on") == 0)) {
[5100caa]441                return 1;
[5ebff60]442        }
443        if ((g_strcasecmp(value,
444                          "false") == 0) || (g_strcasecmp(value, "no") == 0) || (g_strcasecmp(value, "off") == 0)) {
[5100caa]445                return 1;
[5ebff60]446        }
447
448        while (*value) {
449                if (!g_ascii_isdigit(*value)) {
[5100caa]450                        return 0;
[5ebff60]451                } else {
452                        value++;
453                }
454        }
455
[5100caa]456        return 1;
457}
458
[5ebff60]459int bool2int(char *value)
[5100caa]460{
461        int i;
[5ebff60]462
463        if ((g_strcasecmp(value,
464                          "true") == 0) || (g_strcasecmp(value, "yes") == 0) || (g_strcasecmp(value, "on") == 0)) {
[5100caa]465                return 1;
[5ebff60]466        }
467        if ((g_strcasecmp(value,
468                          "false") == 0) || (g_strcasecmp(value, "no") == 0) || (g_strcasecmp(value, "off") == 0)) {
[5100caa]469                return 0;
[5ebff60]470        }
471
472        if (sscanf(value, "%d", &i) == 1) {
[5100caa]473                return i;
[5ebff60]474        }
475
[5100caa]476        return 0;
477}
[36e9f62]478
[5ebff60]479struct ns_srv_reply **srv_lookup(char *service, char *protocol, char *domain)
480{
[ffdf2e7]481        struct ns_srv_reply **replies = NULL;
[5ebff60]482
[36e9f62]483#ifdef HAVE_RESOLV_A
[ffdf2e7]484        struct ns_srv_reply *reply = NULL;
[36e9f62]485        char name[1024];
486        unsigned char querybuf[1024];
487        const unsigned char *buf;
488        ns_msg nsh;
489        ns_rr rr;
[632627e]490        int n, len, size;
[5ebff60]491
492        g_snprintf(name, sizeof(name), "_%s._%s.%s", service, protocol, domain);
493
494        if ((size = res_query(name, ns_c_in, ns_t_srv, querybuf, sizeof(querybuf))) <= 0) {
[36e9f62]495                return NULL;
[5ebff60]496        }
497
498        if (ns_initparse(querybuf, size, &nsh) != 0) {
[36e9f62]499                return NULL;
[5ebff60]500        }
501
[ffdf2e7]502        n = 0;
[5ebff60]503        while (ns_parserr(&nsh, ns_s_an, n, &rr) == 0) {
[632627e]504                char name[NS_MAXDNAME];
505
[5ebff60]506                if (ns_rr_rdlen(rr) < 7) {
507                        break;
508                }
[632627e]509
[5ebff60]510                buf = ns_rr_rdata(rr);
511
512                if (dn_expand(querybuf, querybuf + size, &buf[6], name, NS_MAXDNAME) == -1) {
[ffdf2e7]513                        break;
[5ebff60]514                }
[632627e]515
516                len = strlen(name) + 1;
[5ebff60]517
518                reply = g_malloc(sizeof(struct ns_srv_reply) + len);
519                memcpy(reply->name, name, len);
520
521                reply->prio = (buf[0] << 8) | buf[1];
522                reply->weight = (buf[2] << 8) | buf[3];
523                reply->port = (buf[4] << 8) | buf[5];
524
525                n++;
526                replies = g_renew(struct ns_srv_reply *, replies, n + 1);
527                replies[n - 1] = reply;
528        }
529        if (replies) {
[ffdf2e7]530                replies[n] = NULL;
[5ebff60]531        }
[36e9f62]532#endif
[5ebff60]533
[ffdf2e7]534        return replies;
535}
536
[5ebff60]537void srv_free(struct ns_srv_reply **srv)
[ffdf2e7]538{
539        int i;
[5ebff60]540
541        if (srv == NULL) {
[ffdf2e7]542                return;
[5ebff60]543        }
544
545        for (i = 0; srv[i]; i++) {
546                g_free(srv[i]);
547        }
548        g_free(srv);
[36e9f62]549}
[d444c09]550
[c39cd8e]551/* From OpenSSH 7.4p1 canohost.c" */
552char *reverse_lookup(const struct sockaddr *from_, const socklen_t fromlen_)
553{
554        struct sockaddr_storage from;
555        socklen_t fromlen;
556        struct addrinfo hints, *ai, *aitop;
557        char name[NI_MAXHOST], ntop2[NI_MAXHOST];
558        char ntop[INET6_ADDRSTRLEN];
559
560        fromlen = sizeof(from);
561        memset(&from, 0, sizeof(from));
562        memcpy(&from, from_, fromlen_);
563        ipv64_normalise_mapped(&from, &fromlen);
564        if (from.ss_family == AF_INET6) {
565                fromlen = sizeof(struct sockaddr_in6);
566        }
567
568        if (getnameinfo((struct sockaddr *)&from, fromlen, ntop, sizeof(ntop),
569            NULL, 0, NI_NUMERICHOST) != 0) {
570                return NULL;
571        }
572
573        /* Map the IP address to a host name. */
574        if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name),
575            NULL, 0, NI_NAMEREQD) != 0) {
576                /* Host name not found.  Use ip address. */
577                return g_strdup(ntop);
578        }
579
580        /*
581         * if reverse lookup result looks like a numeric hostname,
582         * someone is trying to trick us by PTR record like following:
583         *      1.1.1.10.in-addr.arpa.  IN PTR  2.3.4.5
584         */
585        memset(&hints, 0, sizeof(hints));
586        hints.ai_socktype = SOCK_DGRAM; /*dummy*/
587        hints.ai_flags = AI_NUMERICHOST;
588        if (getaddrinfo(name, NULL, &hints, &ai) == 0) {
589                freeaddrinfo(ai);
590                return g_strdup(ntop);
591        }
592
593        /* Names are stored in lowercase. */
594        char *tolower = g_utf8_strdown(name, -1);
595        g_snprintf(name, sizeof(name), "%s", tolower);
596        g_free(tolower);
597
598        /*
599         * Map it back to an IP address and check that the given
600         * address actually is an address of this host.  This is
601         * necessary because anyone with access to a name server can
602         * define arbitrary names for an IP address. Mapping from
603         * name to IP address can be trusted better (but can still be
604         * fooled if the intruder has access to the name server of
605         * the domain).
606         */
607        memset(&hints, 0, sizeof(hints));
608        hints.ai_family = from.ss_family;
609        hints.ai_socktype = SOCK_STREAM;
610        if (getaddrinfo(name, NULL, &hints, &aitop) != 0) {
611                return g_strdup(ntop);
612        }
613        /* Look for the address from the list of addresses. */
614        for (ai = aitop; ai; ai = ai->ai_next) {
615                if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2,
616                    sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 &&
617                    (strcmp(ntop, ntop2) == 0))
618                        break;
619        }
620        freeaddrinfo(aitop);
621        /* If we reached the end of the list, the address was not there. */
622        if (ai == NULL) {
623                /* Address not found for the host name. */
624                return g_strdup(ntop);
625        }
626        return g_strdup(name);
627}
628
629void
630ipv64_normalise_mapped(struct sockaddr_storage *addr, socklen_t *len)
631{
632        struct sockaddr_in6 *a6 = (struct sockaddr_in6 *)addr;
633        struct sockaddr_in *a4 = (struct sockaddr_in *)addr;
634        struct in_addr inaddr;
635        u_int16_t port;
636
637        if (addr->ss_family != AF_INET6 ||
638            !IN6_IS_ADDR_V4MAPPED(&a6->sin6_addr))
639                return;
640
641        memcpy(&inaddr, ((char *)&a6->sin6_addr) + 12, sizeof(inaddr));
642        port = a6->sin6_port;
643
644        memset(a4, 0, sizeof(*a4));
645
646        a4->sin_family = AF_INET;
647        *len = sizeof(*a4);
648        memcpy(&a4->sin_addr, &inaddr, sizeof(inaddr));
649        a4->sin_port = port;
650}
651
[5ebff60]652char *word_wrap(const char *msg, int line_len)
[d444c09]653{
[5ebff60]654        GString *ret = g_string_sized_new(strlen(msg) + 16);
655
656        while (strlen(msg) > line_len) {
[d444c09]657                int i;
[5ebff60]658
[d444c09]659                /* First try to find out if there's a newline already. Don't
660                   want to add more splits than necessary. */
[5ebff60]661                for (i = line_len; i > 0 && msg[i] != '\n'; i--) {
662                        ;
663                }
664                if (msg[i] == '\n') {
665                        g_string_append_len(ret, msg, i + 1);
[d444c09]666                        msg += i + 1;
667                        continue;
668                }
[5ebff60]669
670                for (i = line_len; i > 0; i--) {
671                        if (msg[i] == '-') {
672                                g_string_append_len(ret, msg, i + 1);
673                                g_string_append_c(ret, '\n');
[d444c09]674                                msg += i + 1;
675                                break;
[5ebff60]676                        } else if (msg[i] == ' ') {
677                                g_string_append_len(ret, msg, i);
678                                g_string_append_c(ret, '\n');
[d444c09]679                                msg += i + 1;
680                                break;
681                        }
682                }
[5ebff60]683                if (i == 0) {
[fca4683]684                        const char *end;
685                        size_t len;
686
687                        g_utf8_validate(msg, line_len, &end);
688
689                        len = (end != msg) ? end - msg : line_len;
690
691                        g_string_append_len(ret, msg, len);
[5ebff60]692                        g_string_append_c(ret, '\n');
[fca4683]693                        msg += len;
[d444c09]694                }
695        }
[5ebff60]696        g_string_append(ret, msg);
697
698        return g_string_free(ret, FALSE);
[d444c09]699}
[d52111a]700
[5ebff60]701gboolean ssl_sockerr_again(void *ssl)
[d52111a]702{
[5ebff60]703        if (ssl) {
[d52111a]704                return ssl_errno == SSL_AGAIN;
[5ebff60]705        } else {
[d52111a]706                return sockerr_again();
[5ebff60]707        }
[d52111a]708}
[4e8db1c]709
710/* Returns values: -1 == Failure (base64-decoded to something unexpected)
711                    0 == Okay
712                    1 == Password doesn't match the hash. */
[5ebff60]713int md5_verify_password(char *password, char *hash)
[4e8db1c]714{
715        md5_byte_t *pass_dec = NULL;
716        md5_byte_t pass_md5[16];
717        md5_state_t md5_state;
[6a78c0e]718        int ret = -1, i;
[5ebff60]719
720        if (base64_decode(hash, &pass_dec) == 21) {
721                md5_init(&md5_state);
722                md5_append(&md5_state, (md5_byte_t *) password, strlen(password));
723                md5_append(&md5_state, (md5_byte_t *) pass_dec + 16, 5);  /* Hmmm, salt! */
724                md5_finish(&md5_state, pass_md5);
725
726                for (i = 0; i < 16; i++) {
727                        if (pass_dec[i] != pass_md5[i]) {
[4e8db1c]728                                ret = 1;
729                                break;
730                        }
731                }
[5ebff60]732
[4e8db1c]733                /* If we reached the end of the loop, it was a match! */
[5ebff60]734                if (i == 16) {
[4e8db1c]735                        ret = 0;
[5ebff60]736                }
[4e8db1c]737        }
[5ebff60]738
739        g_free(pass_dec);
[4e8db1c]740
741        return ret;
742}
[24b8bbb]743
[7b87539]744/* Split commands (root-style, *not* IRC-style). Handles "quoting of"
745   white\ space in 'various ways'. Returns a NULL-terminated static
746   char** so watch out with nested use! Definitely not thread-safe. */
[5ebff60]747char **split_command_parts(char *command, int limit)
[24b8bbb]748{
[5ebff60]749        static char *cmd[IRC_MAX_ARGS + 1];
[24b8bbb]750        char *s, q = 0;
751        int k;
[5ebff60]752
753        memset(cmd, 0, sizeof(cmd));
[24b8bbb]754        cmd[0] = command;
755        k = 1;
[5ebff60]756        for (s = command; *s && k < IRC_MAX_ARGS; s++) {
757                if (*s == ' ' && !q) {
[24b8bbb]758                        *s = 0;
[5ebff60]759                        while (*++s == ' ') {
760                                ;
761                        }
762                        if (k != limit && (*s == '"' || *s == '\'')) {
[24b8bbb]763                                q = *s;
[5ebff60]764                                s++;
[24b8bbb]765                        }
[5ebff60]766                        if (*s) {
[24b8bbb]767                                cmd[k++] = s;
[269580c]768                                if (limit && k > limit) {
769                                        break;
770                                }
[5ebff60]771                                s--;
772                        } else {
[24b8bbb]773                                break;
774                        }
[5ebff60]775                } else if (*s == '\\' && ((!q && s[1]) || (q && q == s[1]))) {
[24b8bbb]776                        char *cpy;
[5ebff60]777
778                        for (cpy = s; *cpy; cpy++) {
[24b8bbb]779                                cpy[0] = cpy[1];
[5ebff60]780                        }
781                } else if (*s == q) {
[24b8bbb]782                        q = *s = 0;
783                }
[269580c]784        }
[5ebff60]785
[89c11e7]786        /* Full zero-padding for easier argc checking. */
[5ebff60]787        while (k <= IRC_MAX_ARGS) {
[89c11e7]788                cmd[k++] = NULL;
[5ebff60]789        }
790
[24b8bbb]791        return cmd;
792}
[9b0ad7e]793
[5ebff60]794char *get_rfc822_header(const char *text, const char *header, int len)
[9b0ad7e]795{
[5ebff60]796        int hlen = strlen(header), i;
[55ccc9a0]797        const char *ret;
[5ebff60]798
799        if (text == NULL) {
[f9789d4]800                return NULL;
[5ebff60]801        }
802
803        if (len == 0) {
804                len = strlen(text);
805        }
806
[9b0ad7e]807        i = 0;
[5ebff60]808        while ((i + hlen) < len) {
[9b0ad7e]809                /* Maybe this is a bit over-commented, but I just hate this part... */
[5ebff60]810                if (g_strncasecmp(text + i, header, hlen) == 0) {
[9b0ad7e]811                        /* Skip to the (probable) end of the header */
812                        i += hlen;
[5ebff60]813
[9b0ad7e]814                        /* Find the first non-[: \t] character */
[5ebff60]815                        while (i < len && (text[i] == ':' || text[i] == ' ' || text[i] == '\t')) {
816                                i++;
817                        }
818
[9b0ad7e]819                        /* Make sure we're still inside the string */
[5ebff60]820                        if (i >= len) {
821                                return(NULL);
822                        }
823
[9b0ad7e]824                        /* Save the position */
825                        ret = text + i;
[5ebff60]826
[9b0ad7e]827                        /* Search for the end of this line */
[5ebff60]828                        while (i < len && text[i] != '\r' && text[i] != '\n') {
829                                i++;
830                        }
831
[9b0ad7e]832                        /* Copy the found data */
[5ebff60]833                        return(g_strndup(ret, text + i - ret));
[9b0ad7e]834                }
[5ebff60]835
[9b0ad7e]836                /* This wasn't the header we were looking for, skip to the next line. */
[5ebff60]837                while (i < len && (text[i] != '\r' && text[i] != '\n')) {
838                        i++;
839                }
840                while (i < len && (text[i] == '\r' || text[i] == '\n')) {
841                        i++;
842                }
843
[9b0ad7e]844                /* End of headers? */
[5ebff60]845                if ((i >= 4 && strncmp(text + i - 4, "\r\n\r\n", 4) == 0) ||
846                    (i >= 2 && (strncmp(text + i - 2, "\n\n", 2) == 0 ||
847                                strncmp(text + i - 2, "\r\r", 2) == 0))) {
[9b0ad7e]848                        break;
849                }
850        }
[5ebff60]851
[f9789d4]852        return NULL;
[9b0ad7e]853}
[fed4f76]854
855/* Takes a string, truncates it where it's safe, returns the new length */
[5ebff60]856int truncate_utf8(char *string, int maxlen)
[fed4f76]857{
858        char *end;
[5ebff60]859
860        g_utf8_validate((const gchar *) string, maxlen, (const gchar **) &end);
[fed4f76]861        *end = '\0';
862        return end - string;
863}
[73f0a01]864
865/* Parses a guint64 from string, returns TRUE on success */
866gboolean parse_int64(char *string, int base, guint64 *number)
867{
868        guint64 parsed;
869        char *endptr;
870
871        errno = 0;
872        parsed = g_ascii_strtoull(string, &endptr, base);
873        if (errno || endptr == string || *endptr != '\0') {
874                return FALSE;
875        }
876        *number = parsed;
877        return TRUE;
878}
879
[47ab9a9]880/* Filters all the characters in 'blacklist' replacing them with 'replacement'.
881 * Modifies the string in-place and returns the string itself.
882 * For the opposite, use g_strcanon() */
883char *str_reject_chars(char *string, const char *reject, char replacement)
884{
885        char *c = string;
886
887        while (*c) {
888                c += strcspn(c, reject);
889                if (*c) {
890                        *c = replacement;
891                }
892        }
893
894        return string;
895}
[4466e3e]896
897/* Returns a string that is exactly 'char_len' utf8 characters long (not bytes),
898 * padded to the right with spaces or truncated with the 'ellipsis' parameter
899 * if specified (can be NULL).
900 * Returns a newly allocated string, or NULL on invalid parameters. */
901char *str_pad_and_truncate(const char *string, long char_len, const char *ellipsis)
902{
903        size_t string_len = strlen(string);
904        size_t ellipsis_len = (ellipsis) ? strlen(ellipsis) : 0;
905        long orig_len = g_utf8_strlen(string, -1);
906
907        g_return_val_if_fail(char_len > ellipsis_len, NULL);
908
909        if (orig_len > char_len) {
910                char *ret = g_malloc(string_len + 1);
911                g_utf8_strncpy(ret, string, char_len - ellipsis_len);
912                if (ellipsis) {
913                        g_strlcat(ret, ellipsis, string_len);
914                }
915                return ret;
916        } else if (orig_len < char_len) {
917                return g_strdup_printf("%s%*s", string, (int) (char_len - orig_len), "");
918        } else {
919                return g_strdup(string);
920        }
921}
Note: See TracBrowser for help on using the repository browser.