source: lib/misc.c

Last change on this file was 1bdc669, checked in by GitHub <noreply@…>, at 2023-02-23T23:48:10Z

Migrate internal users of md5.h to using GChecksum directly (#169)

  • Use GChecksum directly rather than md5 wrapper
  • Mark md5 functions as deprecated.
  • Migrate more users of md5.h to GChecksum
  • Property mode set to 100644
File size: 17.8 KB
RevLine 
[b097945]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"
[b7d3cc34]36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
[dd8d4c5]39#include <ctype.h>
[b7d3cc34]40#include <glib.h>
41#include <time.h>
42
[36e9f62]43#ifdef HAVE_RESOLV_A
44#include <arpa/nameser.h>
45#include <resolv.h>
46#endif
47
[d52111a]48#include "ssl_client.h"
49
[b7d3cc34]50void strip_linefeed(gchar *text)
51{
52        int i, j;
53        gchar *text2 = g_malloc(strlen(text) + 1);
54
[5ebff60]55        for (i = 0, j = 0; text[i]; i++) {
56                if (text[i] != '\r') {
[b7d3cc34]57                        text2[j++] = text[i];
[5ebff60]58                }
59        }
[b7d3cc34]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
[613cc55]70        memset(&tm, 0, sizeof(struct tm));
[b7d3cc34]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;
[5ebff60]77
[b7d3cc34]78        return mktime(&tm);
79}
80
[5ebff60]81time_t mktime_utc(struct tm *tp)
[2e3a857]82{
83        struct tm utc;
84        time_t res, tres;
[5ebff60]85
[2e3a857]86        tp->tm_isdst = -1;
[5ebff60]87        res = mktime(tp);
[2e3a857]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.
[5ebff60]91
[2e3a857]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...
[5ebff60]95
[2e3a857]96           tzset() may also work but in other places I actually want to
97           use local time.
[5ebff60]98
[2e3a857]99           FFFFFFFFFFFFFFFFFFFFFUUUUUUUUUUUUUUUUUUUU!! */
[5ebff60]100        gmtime_r(&res, &utc);
[2e3a857]101        utc.tm_isdst = -1;
[5ebff60]102        if (utc.tm_hour == tp->tm_hour && utc.tm_min == tp->tm_min) {
[2e3a857]103                /* Sweet! We're in UTC right now... */
104                return res;
[5ebff60]105        }
106
107        tres = mktime(&utc);
[2e3a857]108        res += res - tres;
[5ebff60]109
[2e3a857]110        /* Yes, this is a hack. And it will go wrong around DST changes.
111           BUT this is more likely to be threadsafe than messing with
112           environment variables, and possibly more portable... */
[5ebff60]113
[2e3a857]114        return res;
115}
116
[5ebff60]117typedef struct htmlentity {
[51fdc45]118        char code[7];
119        char is[3];
[b7d3cc34]120} htmlentity_t;
121
[39cc341]122static const htmlentity_t ent[] =
[b7d3cc34]123{
[39cc341]124        { "lt",     "<" },
125        { "gt",     ">" },
126        { "amp",    "&" },
[b52e478]127        { "apos",   "'" },
[39cc341]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",   " " },
[7e90c03]150        { "#38",    "&" },
151        { "#39",    "'" },
152        { "#60",    "<" },
153        { "#62",    ">" },
[39cc341]154        { "",        ""  }
[b7d3cc34]155};
156
[5ebff60]157void strip_html(char *in)
[b7d3cc34]158{
159        char *start = in;
[5ebff60]160        char out[strlen(in) + 1];
[b7d3cc34]161        char *s = out, *cs;
162        int i, matched;
[5a71d9c]163        int taglen;
[5ebff60]164
165        memset(out, 0, sizeof(out));
166
167        while (*in) {
168                if (*in == '<' && (g_ascii_isalpha(*(in + 1)) || *(in + 1) == '/')) {
[b7d3cc34]169                        /* If in points at a < and in+1 points at a letter or a slash, this is probably
170                           a HTML-tag. Try to find a closing > and continue there. If the > can't be
171                           found, assume that it wasn't a HTML-tag after all. */
[5ebff60]172
[b7d3cc34]173                        cs = in;
[5ebff60]174
175                        while (*in && *in != '>') {
176                                in++;
177                        }
178
[be999a5]179                        taglen = in - cs - 1;   /* not <0 because the above loop runs at least once */
[5ebff60]180                        if (*in) {
181                                if (g_strncasecmp(cs + 1, "b", taglen) == 0) {
[5a71d9c]182                                        *(s++) = '\x02';
[5ebff60]183                                } else if (g_strncasecmp(cs + 1, "/b", taglen) == 0) {
[5a71d9c]184                                        *(s++) = '\x02';
[5ebff60]185                                } else if (g_strncasecmp(cs + 1, "i", taglen) == 0) {
[5a71d9c]186                                        *(s++) = '\x1f';
[5ebff60]187                                } else if (g_strncasecmp(cs + 1, "/i", taglen) == 0) {
[5a71d9c]188                                        *(s++) = '\x1f';
[5ebff60]189                                } else if (g_strncasecmp(cs + 1, "br", taglen) == 0) {
[b7d3cc34]190                                        *(s++) = '\n';
[098b430]191                                } else if (g_strncasecmp(cs + 1, "br/", taglen) == 0) {
192                                        *(s++) = '\n';
193                                } else if (g_strncasecmp(cs + 1, "br /", taglen) == 0) {
194                                        *(s++) = '\n';
[5ebff60]195                                }
196                                in++;
197                        } else {
[b7d3cc34]198                                in = cs;
199                                *(s++) = *(in++);
200                        }
[5ebff60]201                } else if (*in == '&') {
[b7d3cc34]202                        cs = ++in;
[7e90c03]203
204                        if (*in == '#') {
205                                in++;
206                        }
207
208                        while (*in && g_ascii_isalnum(*in)) {
[5ebff60]209                                in++;
210                        }
211
212                        if (*in == ';') {
213                                in++;
214                        }
[b7d3cc34]215                        matched = 0;
[5ebff60]216
217                        for (i = 0; *ent[i].code; i++) {
218                                if (g_strncasecmp(ent[i].code, cs, strlen(ent[i].code)) == 0) {
[39cc341]219                                        int j;
[5ebff60]220
221                                        for (j = 0; ent[i].is[j]; j++) {
[39cc341]222                                                *(s++) = ent[i].is[j];
[5ebff60]223                                        }
224
[b7d3cc34]225                                        matched = 1;
226                                        break;
227                                }
[5ebff60]228                        }
[b7d3cc34]229
230                        /* None of the entities were matched, so return the string */
[5ebff60]231                        if (!matched) {
[b7d3cc34]232                                in = cs - 1;
233                                *(s++) = *(in++);
234                        }
[5ebff60]235                } else {
[b7d3cc34]236                        *(s++) = *(in++);
237                }
238        }
[5ebff60]239
240        strcpy(start, out);
[b7d3cc34]241}
242
[5ebff60]243char *escape_html(const char *html)
[b7d3cc34]244{
245        const char *c = html;
246        GString *ret;
247        char *str;
[5ebff60]248
249        if (html == NULL) {
250                return(NULL);
251        }
252
253        ret = g_string_new("");
254
255        while (*c) {
256                switch (*c) {
257                case '&':
258                        ret = g_string_append(ret, "&amp;");
259                        break;
260                case '<':
261                        ret = g_string_append(ret, "&lt;");
262                        break;
263                case '>':
264                        ret = g_string_append(ret, "&gt;");
265                        break;
266                case '"':
267                        ret = g_string_append(ret, "&quot;");
268                        break;
269                default:
270                        ret = g_string_append_c(ret, *c);
[b7d3cc34]271                }
[5ebff60]272                c++;
[b7d3cc34]273        }
[5ebff60]274
[b7d3cc34]275        str = ret->str;
[5ebff60]276        g_string_free(ret, FALSE);
277        return(str);
[b7d3cc34]278}
279
[c88999c]280/* Decode%20a%20file%20name                                             */
[5ebff60]281void http_decode(char *s)
[c88999c]282{
283        char *t;
284        int i, j, k;
[5ebff60]285
286        t = g_new(char, strlen(s) + 1);
287
288        for (i = j = 0; s[i]; i++, j++) {
289                if (s[i] == '%') {
290                        if (sscanf(s + i + 1, "%2x", &k)) {
[c88999c]291                                t[j] = k;
292                                i += 2;
[5ebff60]293                        } else {
[c88999c]294                                *t = 0;
295                                break;
296                        }
[5ebff60]297                } else {
[c88999c]298                        t[j] = s[i];
299                }
300        }
301        t[j] = 0;
[5ebff60]302
303        strcpy(s, t);
304        g_free(t);
[c88999c]305}
306
307/* Warning: This one explodes the string. Worst-cases can make the string 3x its original size! */
[e88fe7da]308/* This function is safe, but make sure you call it safely as well! */
[5ebff60]309void http_encode(char *s)
[c88999c]310{
[5ebff60]311        char t[strlen(s) + 1];
[c88999c]312        int i, j;
[5ebff60]313
314        strcpy(t, s);
315        for (i = j = 0; t[i]; i++, j++) {
[6b13103]316                /* Warning: g_ascii_isalnum() is locale-aware, so don't use it here! */
[5ebff60]317                if ((t[i] >= 'A' && t[i] <= 'Z') ||
318                    (t[i] >= 'a' && t[i] <= 'z') ||
319                    (t[i] >= '0' && t[i] <= '9') ||
320                    strchr("._-~", t[i])) {
[b40e60d]321                        s[j] = t[i];
[5ebff60]322                } else {
323                        sprintf(s + j, "%%%02X", ((unsigned char *) t)[i]);
[b40e60d]324                        j += 2;
[c88999c]325                }
326        }
327        s[j] = 0;
328}
329
[5ebff60]330/* Strip newlines from a string. Modifies the string passed to it. */
331char *strip_newlines(char *source)
[c88999c]332{
[5ebff60]333        int i;
[c88999c]334
[5ebff60]335        for (i = 0; source[i] != '\0'; i++) {
336                if (source[i] == '\n' || source[i] == '\r') {
[c88999c]337                        source[i] = ' ';
[5ebff60]338                }
339        }
340
[c88999c]341        return source;
342}
[2a6ca4f]343
[e27661d]344/* Convert from one charset to another.
[5ebff60]345
[e27661d]346   from_cs, to_cs: Source and destination charsets
347   src, dst: Source and destination strings
348   size: Size if src. 0 == use strlen(). strlen() is not reliable for UNICODE/UTF16 strings though.
349   maxbuf: Maximum number of bytes to write to dst
[5ebff60]350
[e27661d]351   Returns the number of bytes written to maxbuf or -1 on an error.
352*/
[5ebff60]353signed int do_iconv(char *from_cs, char *to_cs, char *src, char *dst, size_t size, size_t maxbuf)
[e27661d]354{
[574af7e]355        GIConv cd;
[e27661d]356        size_t res;
357        size_t inbytesleft, outbytesleft;
358        char *inbuf = src;
359        char *outbuf = dst;
[5ebff60]360
361        cd = g_iconv_open(to_cs, from_cs);
362        if (cd == (GIConv) - 1) {
[f5da476]363                return -1;
[5ebff60]364        }
365
366        inbytesleft = size ? size : strlen(src);
[e27661d]367        outbytesleft = maxbuf - 1;
[5ebff60]368        res = g_iconv(cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft);
[e27661d]369        *outbuf = '\0';
[5ebff60]370        g_iconv_close(cd);
371
372        if (res != 0) {
[f5da476]373                return -1;
[5ebff60]374        } else {
[f5da476]375                return outbuf - dst;
[5ebff60]376        }
[e27661d]377}
378
[5eab298f]379/* A wrapper for /dev/urandom.
380 * If /dev/urandom is not present or not usable, it calls abort()
381 * to prevent bitlbee from working without a decent entropy source */
[5ebff60]382void random_bytes(unsigned char *buf, int count)
[7f49a86]383{
[5eab298f]384        int fd;
[5ebff60]385
386        if (((fd = open("/dev/urandom", O_RDONLY)) == -1) ||
387            (read(fd, buf, count) == -1)) {
388                log_message(LOGLVL_ERROR, "/dev/urandom not present - aborting");
[5eab298f]389                abort();
[7f49a86]390        }
[5eab298f]391
[5ebff60]392        close(fd);
[7f49a86]393}
394
[5ebff60]395int is_bool(char *value)
[5100caa]396{
[5ebff60]397        if (*value == 0) {
[5100caa]398                return 0;
[5ebff60]399        }
400
401        if ((g_strcasecmp(value,
402                          "true") == 0) || (g_strcasecmp(value, "yes") == 0) || (g_strcasecmp(value, "on") == 0)) {
[5100caa]403                return 1;
[5ebff60]404        }
405        if ((g_strcasecmp(value,
406                          "false") == 0) || (g_strcasecmp(value, "no") == 0) || (g_strcasecmp(value, "off") == 0)) {
[5100caa]407                return 1;
[5ebff60]408        }
409
410        while (*value) {
411                if (!g_ascii_isdigit(*value)) {
[5100caa]412                        return 0;
[5ebff60]413                } else {
414                        value++;
415                }
416        }
417
[5100caa]418        return 1;
419}
420
[5ebff60]421int bool2int(char *value)
[5100caa]422{
423        int i;
[5ebff60]424
425        if ((g_strcasecmp(value,
426                          "true") == 0) || (g_strcasecmp(value, "yes") == 0) || (g_strcasecmp(value, "on") == 0)) {
[5100caa]427                return 1;
[5ebff60]428        }
429        if ((g_strcasecmp(value,
430                          "false") == 0) || (g_strcasecmp(value, "no") == 0) || (g_strcasecmp(value, "off") == 0)) {
[5100caa]431                return 0;
[5ebff60]432        }
433
434        if (sscanf(value, "%d", &i) == 1) {
[5100caa]435                return i;
[5ebff60]436        }
437
[5100caa]438        return 0;
439}
[36e9f62]440
[5ebff60]441struct ns_srv_reply **srv_lookup(char *service, char *protocol, char *domain)
442{
[ffdf2e7]443        struct ns_srv_reply **replies = NULL;
[5ebff60]444
[36e9f62]445#ifdef HAVE_RESOLV_A
[ffdf2e7]446        struct ns_srv_reply *reply = NULL;
[36e9f62]447        char name[1024];
448        unsigned char querybuf[1024];
449        const unsigned char *buf;
450        ns_msg nsh;
451        ns_rr rr;
[632627e]452        int n, len, size;
[5ebff60]453
454        g_snprintf(name, sizeof(name), "_%s._%s.%s", service, protocol, domain);
455
456        if ((size = res_query(name, ns_c_in, ns_t_srv, querybuf, sizeof(querybuf))) <= 0) {
[36e9f62]457                return NULL;
[5ebff60]458        }
459
460        if (ns_initparse(querybuf, size, &nsh) != 0) {
[36e9f62]461                return NULL;
[5ebff60]462        }
463
[ffdf2e7]464        n = 0;
[5ebff60]465        while (ns_parserr(&nsh, ns_s_an, n, &rr) == 0) {
[632627e]466                char name[NS_MAXDNAME];
467
[5ebff60]468                if (ns_rr_rdlen(rr) < 7) {
469                        break;
470                }
[632627e]471
[5ebff60]472                buf = ns_rr_rdata(rr);
473
474                if (dn_expand(querybuf, querybuf + size, &buf[6], name, NS_MAXDNAME) == -1) {
[ffdf2e7]475                        break;
[5ebff60]476                }
[632627e]477
478                len = strlen(name) + 1;
[5ebff60]479
480                reply = g_malloc(sizeof(struct ns_srv_reply) + len);
481                memcpy(reply->name, name, len);
482
483                reply->prio = (buf[0] << 8) | buf[1];
484                reply->weight = (buf[2] << 8) | buf[3];
485                reply->port = (buf[4] << 8) | buf[5];
486
487                n++;
488                replies = g_renew(struct ns_srv_reply *, replies, n + 1);
489                replies[n - 1] = reply;
490        }
491        if (replies) {
[ffdf2e7]492                replies[n] = NULL;
[5ebff60]493        }
[36e9f62]494#endif
[5ebff60]495
[ffdf2e7]496        return replies;
497}
498
[5ebff60]499void srv_free(struct ns_srv_reply **srv)
[ffdf2e7]500{
501        int i;
[5ebff60]502
503        if (srv == NULL) {
[ffdf2e7]504                return;
[5ebff60]505        }
506
507        for (i = 0; srv[i]; i++) {
508                g_free(srv[i]);
509        }
510        g_free(srv);
[36e9f62]511}
[d444c09]512
[5ebff60]513char *word_wrap(const char *msg, int line_len)
[d444c09]514{
[5ebff60]515        GString *ret = g_string_sized_new(strlen(msg) + 16);
516
517        while (strlen(msg) > line_len) {
[d444c09]518                int i;
[5ebff60]519
[d444c09]520                /* First try to find out if there's a newline already. Don't
521                   want to add more splits than necessary. */
[5ebff60]522                for (i = line_len; i > 0 && msg[i] != '\n'; i--) {
523                        ;
524                }
525                if (msg[i] == '\n') {
526                        g_string_append_len(ret, msg, i + 1);
[d444c09]527                        msg += i + 1;
528                        continue;
529                }
[5ebff60]530
531                for (i = line_len; i > 0; i--) {
532                        if (msg[i] == '-') {
533                                g_string_append_len(ret, msg, i + 1);
534                                g_string_append_c(ret, '\n');
[d444c09]535                                msg += i + 1;
536                                break;
[5ebff60]537                        } else if (msg[i] == ' ') {
538                                g_string_append_len(ret, msg, i);
539                                g_string_append_c(ret, '\n');
[d444c09]540                                msg += i + 1;
541                                break;
542                        }
543                }
[5ebff60]544                if (i == 0) {
[fca4683]545                        const char *end;
546                        size_t len;
547
548                        g_utf8_validate(msg, line_len, &end);
549
550                        len = (end != msg) ? end - msg : line_len;
551
552                        g_string_append_len(ret, msg, len);
[5ebff60]553                        g_string_append_c(ret, '\n');
[fca4683]554                        msg += len;
[d444c09]555                }
556        }
[5ebff60]557        g_string_append(ret, msg);
558
559        return g_string_free(ret, FALSE);
[d444c09]560}
[d52111a]561
[5ebff60]562gboolean ssl_sockerr_again(void *ssl)
[d52111a]563{
[5ebff60]564        if (ssl) {
[d52111a]565                return ssl_errno == SSL_AGAIN;
[5ebff60]566        } else {
[d52111a]567                return sockerr_again();
[5ebff60]568        }
[d52111a]569}
[4e8db1c]570
571/* Returns values: -1 == Failure (base64-decoded to something unexpected)
572                    0 == Okay
573                    1 == Password doesn't match the hash. */
[5ebff60]574int md5_verify_password(char *password, char *hash)
[4e8db1c]575{
[1bdc669]576        guint8 *pass_dec = NULL;
577        guint8 pass_md5[16];
578        GChecksum *md5_state;
579        gsize digest_len = MD5_HASH_SIZE;
[6a78c0e]580        int ret = -1, i;
[5ebff60]581
582        if (base64_decode(hash, &pass_dec) == 21) {
[1bdc669]583                md5_state = g_checksum_new(G_CHECKSUM_MD5);
584                g_checksum_update(md5_state, (guint8 *) password, strlen(password));
585                g_checksum_update(md5_state, (guint8 *) pass_dec + 16, 5);  /* Hmmm, salt! */
586                g_checksum_get_digest(md5_state, pass_md5, &digest_len);
587                g_checksum_free(md5_state);
[5ebff60]588
589                for (i = 0; i < 16; i++) {
590                        if (pass_dec[i] != pass_md5[i]) {
[4e8db1c]591                                ret = 1;
592                                break;
593                        }
594                }
[5ebff60]595
[4e8db1c]596                /* If we reached the end of the loop, it was a match! */
[5ebff60]597                if (i == 16) {
[4e8db1c]598                        ret = 0;
[5ebff60]599                }
[4e8db1c]600        }
[5ebff60]601
602        g_free(pass_dec);
[4e8db1c]603
604        return ret;
605}
[24b8bbb]606
[7b87539]607/* Split commands (root-style, *not* IRC-style). Handles "quoting of"
608   white\ space in 'various ways'. Returns a NULL-terminated static
609   char** so watch out with nested use! Definitely not thread-safe. */
[5ebff60]610char **split_command_parts(char *command, int limit)
[24b8bbb]611{
[5ebff60]612        static char *cmd[IRC_MAX_ARGS + 1];
[24b8bbb]613        char *s, q = 0;
614        int k;
[5ebff60]615
616        memset(cmd, 0, sizeof(cmd));
[24b8bbb]617        cmd[0] = command;
618        k = 1;
[5ebff60]619        for (s = command; *s && k < IRC_MAX_ARGS; s++) {
620                if (*s == ' ' && !q) {
[24b8bbb]621                        *s = 0;
[5ebff60]622                        while (*++s == ' ') {
623                                ;
624                        }
625                        if (k != limit && (*s == '"' || *s == '\'')) {
[24b8bbb]626                                q = *s;
[5ebff60]627                                s++;
[24b8bbb]628                        }
[5ebff60]629                        if (*s) {
[24b8bbb]630                                cmd[k++] = s;
[269580c]631                                if (limit && k > limit) {
632                                        break;
633                                }
[5ebff60]634                                s--;
635                        } else {
[24b8bbb]636                                break;
637                        }
[5ebff60]638                } else if (*s == '\\' && ((!q && s[1]) || (q && q == s[1]))) {
[24b8bbb]639                        char *cpy;
[5ebff60]640
641                        for (cpy = s; *cpy; cpy++) {
[24b8bbb]642                                cpy[0] = cpy[1];
[5ebff60]643                        }
644                } else if (*s == q) {
[24b8bbb]645                        q = *s = 0;
646                }
[269580c]647        }
[5ebff60]648
[89c11e7]649        /* Full zero-padding for easier argc checking. */
[5ebff60]650        while (k <= IRC_MAX_ARGS) {
[89c11e7]651                cmd[k++] = NULL;
[5ebff60]652        }
653
[24b8bbb]654        return cmd;
655}
[9b0ad7e]656
[5ebff60]657char *get_rfc822_header(const char *text, const char *header, int len)
[9b0ad7e]658{
[5ebff60]659        int hlen = strlen(header), i;
[55ccc9a0]660        const char *ret;
[5ebff60]661
662        if (text == NULL) {
[f9789d4]663                return NULL;
[5ebff60]664        }
665
666        if (len == 0) {
667                len = strlen(text);
668        }
669
[9b0ad7e]670        i = 0;
[5ebff60]671        while ((i + hlen) < len) {
[9b0ad7e]672                /* Maybe this is a bit over-commented, but I just hate this part... */
[5ebff60]673                if (g_strncasecmp(text + i, header, hlen) == 0) {
[9b0ad7e]674                        /* Skip to the (probable) end of the header */
675                        i += hlen;
[5ebff60]676
[9b0ad7e]677                        /* Find the first non-[: \t] character */
[5ebff60]678                        while (i < len && (text[i] == ':' || text[i] == ' ' || text[i] == '\t')) {
679                                i++;
680                        }
681
[9b0ad7e]682                        /* Make sure we're still inside the string */
[5ebff60]683                        if (i >= len) {
684                                return(NULL);
685                        }
686
[9b0ad7e]687                        /* Save the position */
688                        ret = text + i;
[5ebff60]689
[9b0ad7e]690                        /* Search for the end of this line */
[5ebff60]691                        while (i < len && text[i] != '\r' && text[i] != '\n') {
692                                i++;
693                        }
694
[9b0ad7e]695                        /* Copy the found data */
[5ebff60]696                        return(g_strndup(ret, text + i - ret));
[9b0ad7e]697                }
[5ebff60]698
[9b0ad7e]699                /* This wasn't the header we were looking for, skip to the next line. */
[5ebff60]700                while (i < len && (text[i] != '\r' && text[i] != '\n')) {
701                        i++;
702                }
703                while (i < len && (text[i] == '\r' || text[i] == '\n')) {
704                        i++;
705                }
706
[9b0ad7e]707                /* End of headers? */
[5ebff60]708                if ((i >= 4 && strncmp(text + i - 4, "\r\n\r\n", 4) == 0) ||
709                    (i >= 2 && (strncmp(text + i - 2, "\n\n", 2) == 0 ||
710                                strncmp(text + i - 2, "\r\r", 2) == 0))) {
[9b0ad7e]711                        break;
712                }
713        }
[5ebff60]714
[f9789d4]715        return NULL;
[9b0ad7e]716}
[fed4f76]717
718/* Takes a string, truncates it where it's safe, returns the new length */
[5ebff60]719int truncate_utf8(char *string, int maxlen)
[fed4f76]720{
721        char *end;
[5ebff60]722
723        g_utf8_validate((const gchar *) string, maxlen, (const gchar **) &end);
[fed4f76]724        *end = '\0';
725        return end - string;
726}
[73f0a01]727
728/* Parses a guint64 from string, returns TRUE on success */
729gboolean parse_int64(char *string, int base, guint64 *number)
730{
731        guint64 parsed;
732        char *endptr;
733
734        errno = 0;
735        parsed = g_ascii_strtoull(string, &endptr, base);
736        if (errno || endptr == string || *endptr != '\0') {
737                return FALSE;
738        }
739        *number = parsed;
740        return TRUE;
741}
742
[47ab9a9]743/* Filters all the characters in 'blacklist' replacing them with 'replacement'.
744 * Modifies the string in-place and returns the string itself.
745 * For the opposite, use g_strcanon() */
746char *str_reject_chars(char *string, const char *reject, char replacement)
747{
748        char *c = string;
749
750        while (*c) {
751                c += strcspn(c, reject);
752                if (*c) {
753                        *c = replacement;
754                }
755        }
756
757        return string;
758}
[4466e3e]759
760/* Returns a string that is exactly 'char_len' utf8 characters long (not bytes),
761 * padded to the right with spaces or truncated with the 'ellipsis' parameter
762 * if specified (can be NULL).
763 * Returns a newly allocated string, or NULL on invalid parameters. */
764char *str_pad_and_truncate(const char *string, long char_len, const char *ellipsis)
765{
766        size_t string_len = strlen(string);
767        size_t ellipsis_len = (ellipsis) ? strlen(ellipsis) : 0;
768        long orig_len = g_utf8_strlen(string, -1);
769
770        g_return_val_if_fail(char_len > ellipsis_len, NULL);
771
772        if (orig_len > char_len) {
773                char *ret = g_malloc(string_len + 1);
774                g_utf8_strncpy(ret, string, char_len - ellipsis_len);
775                if (ellipsis) {
776                        g_strlcat(ret, ellipsis, string_len);
777                }
778                return ret;
779        } else if (orig_len < char_len) {
780                return g_strdup_printf("%s%*s", string, (int) (char_len - orig_len), "");
781        } else {
782                return g_strdup(string);
783        }
784}
[9e83b15]785
786/* copied from irssi's misc.c, by timo sirainen */
787int b_istr_equal(gconstpointer v, gconstpointer v2)
788{
789        return g_ascii_strcasecmp((const char *) v, (const char *) v2) == 0;
790}
791
792/* copied from irssi's misc.c, by lemonboy */
793guint b_istr_hash(gconstpointer v)
794{
795        const signed char *p;
796        guint32 h = 5381;
797
798        for (p = v; *p != '\0'; p++) {
799                h = (h << 5) + h + g_ascii_toupper(*p);
800        }
801
802        return h;
803}
[ea3f90f]804
805#ifdef NO_STRCASESTR
806char* strcasestr(const char* haystack, const char* needle)
807{
808        size_t haystackn = strlen(haystack);
809        size_t needlen = strlen(needle);
810
811        const char *p = haystack;
812        while (haystackn >= needlen) {
813                if (g_strncasecmp(p, needle, needlen) == 0) {
814                    return (char*) p;
815                }
816                p++;
817                haystackn--;
818        }
819        return NULL;
820}
821#endif
Note: See TracBrowser for help on using the repository browser.