source: lib/misc.c @ 0156c42

Last change on this file since 0156c42 was 0156c42, checked in by Wilmer van der Gaast <github@…>, at 2017-04-06T20:25:08Z

Remove ip6_*wrap() functions which are no longer needed.

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