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
strip_linefeed(gchar * text)52 void 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
get_time(int year,int month,int day,int hour,int min,int sec)68 time_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
mktime_utc(struct tm * tp)83 time_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
119 typedef struct htmlentity {
120 char code[7];
121 char is[3];
122 } htmlentity_t;
123
124 static 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
strip_html(char * in)155 void 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
escape_html(const char * html)236 char *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, "&");
252 break;
253 case '<':
254 ret = g_string_append(ret, "<");
255 break;
256 case '>':
257 ret = g_string_append(ret, ">");
258 break;
259 case '"':
260 ret = g_string_append(ret, """);
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 */
http_decode(char * s)274 void 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! */
http_encode(char * s)302 void 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. */
strip_newlines(char * source)324 char *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 */
do_iconv(char * from_cs,char * to_cs,char * src,char * dst,size_t size,size_t maxbuf)346 signed 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 */
random_bytes(unsigned char * buf,int count)375 void 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
is_bool(char * value)388 int 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
bool2int(char * value)414 int 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
srv_lookup(char * service,char * protocol,char * domain)434 struct 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
srv_free(struct ns_srv_reply ** srv)492 void 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
word_wrap(const char * msg,int line_len)506 char *word_wrap(const char *msg, int line_len)
507 {
508 GString *ret = g_string_sized_new(strlen(msg) + 16);
509
510 while (strlen(msg) > line_len) {
511 int i;
512
513 /* First try to find out if there's a newline already. Don't
514 want to add more splits than necessary. */
515 for (i = line_len; i > 0 && msg[i] != '\n'; i--) {
516 ;
517 }
518 if (msg[i] == '\n') {
519 g_string_append_len(ret, msg, i + 1);
520 msg += i + 1;
521 continue;
522 }
523
524 for (i = line_len; i > 0; i--) {
525 if (msg[i] == '-') {
526 g_string_append_len(ret, msg, i + 1);
527 g_string_append_c(ret, '\n');
528 msg += i + 1;
529 break;
530 } else if (msg[i] == ' ') {
531 g_string_append_len(ret, msg, i);
532 g_string_append_c(ret, '\n');
533 msg += i + 1;
534 break;
535 }
536 }
537 if (i == 0) {
538 const char *end;
539 size_t len;
540
541 g_utf8_validate(msg, line_len, &end);
542
543 len = (end != msg) ? end - msg : line_len;
544
545 g_string_append_len(ret, msg, len);
546 g_string_append_c(ret, '\n');
547 msg += len;
548 }
549 }
550 g_string_append(ret, msg);
551
552 return g_string_free(ret, FALSE);
553 }
554
ssl_sockerr_again(void * ssl)555 gboolean ssl_sockerr_again(void *ssl)
556 {
557 if (ssl) {
558 return ssl_errno == SSL_AGAIN;
559 } else {
560 return sockerr_again();
561 }
562 }
563
564 /* Returns values: -1 == Failure (base64-decoded to something unexpected)
565 0 == Okay
566 1 == Password doesn't match the hash. */
md5_verify_password(char * password,char * hash)567 int md5_verify_password(char *password, char *hash)
568 {
569 md5_byte_t *pass_dec = NULL;
570 md5_byte_t pass_md5[16];
571 md5_state_t md5_state;
572 int ret = -1, i;
573
574 if (base64_decode(hash, &pass_dec) == 21) {
575 md5_init(&md5_state);
576 md5_append(&md5_state, (md5_byte_t *) password, strlen(password));
577 md5_append(&md5_state, (md5_byte_t *) pass_dec + 16, 5); /* Hmmm, salt! */
578 md5_finish(&md5_state, pass_md5);
579
580 for (i = 0; i < 16; i++) {
581 if (pass_dec[i] != pass_md5[i]) {
582 ret = 1;
583 break;
584 }
585 }
586
587 /* If we reached the end of the loop, it was a match! */
588 if (i == 16) {
589 ret = 0;
590 }
591 }
592
593 g_free(pass_dec);
594
595 return ret;
596 }
597
598 /* Split commands (root-style, *not* IRC-style). Handles "quoting of"
599 white\ space in 'various ways'. Returns a NULL-terminated static
600 char** so watch out with nested use! Definitely not thread-safe. */
split_command_parts(char * command,int limit)601 char **split_command_parts(char *command, int limit)
602 {
603 static char *cmd[IRC_MAX_ARGS + 1];
604 char *s, q = 0;
605 int k;
606
607 memset(cmd, 0, sizeof(cmd));
608 cmd[0] = command;
609 k = 1;
610 for (s = command; *s && k < IRC_MAX_ARGS; s++) {
611 if (*s == ' ' && !q) {
612 *s = 0;
613 while (*++s == ' ') {
614 ;
615 }
616 if (k != limit && (*s == '"' || *s == '\'')) {
617 q = *s;
618 s++;
619 }
620 if (*s) {
621 cmd[k++] = s;
622 if (limit && k > limit) {
623 break;
624 }
625 s--;
626 } else {
627 break;
628 }
629 } else if (*s == '\\' && ((!q && s[1]) || (q && q == s[1]))) {
630 char *cpy;
631
632 for (cpy = s; *cpy; cpy++) {
633 cpy[0] = cpy[1];
634 }
635 } else if (*s == q) {
636 q = *s = 0;
637 }
638 }
639
640 /* Full zero-padding for easier argc checking. */
641 while (k <= IRC_MAX_ARGS) {
642 cmd[k++] = NULL;
643 }
644
645 return cmd;
646 }
647
get_rfc822_header(const char * text,const char * header,int len)648 char *get_rfc822_header(const char *text, const char *header, int len)
649 {
650 int hlen = strlen(header), i;
651 const char *ret;
652
653 if (text == NULL) {
654 return NULL;
655 }
656
657 if (len == 0) {
658 len = strlen(text);
659 }
660
661 i = 0;
662 while ((i + hlen) < len) {
663 /* Maybe this is a bit over-commented, but I just hate this part... */
664 if (g_strncasecmp(text + i, header, hlen) == 0) {
665 /* Skip to the (probable) end of the header */
666 i += hlen;
667
668 /* Find the first non-[: \t] character */
669 while (i < len && (text[i] == ':' || text[i] == ' ' || text[i] == '\t')) {
670 i++;
671 }
672
673 /* Make sure we're still inside the string */
674 if (i >= len) {
675 return(NULL);
676 }
677
678 /* Save the position */
679 ret = text + i;
680
681 /* Search for the end of this line */
682 while (i < len && text[i] != '\r' && text[i] != '\n') {
683 i++;
684 }
685
686 /* Copy the found data */
687 return(g_strndup(ret, text + i - ret));
688 }
689
690 /* This wasn't the header we were looking for, skip to the next line. */
691 while (i < len && (text[i] != '\r' && text[i] != '\n')) {
692 i++;
693 }
694 while (i < len && (text[i] == '\r' || text[i] == '\n')) {
695 i++;
696 }
697
698 /* End of headers? */
699 if ((i >= 4 && strncmp(text + i - 4, "\r\n\r\n", 4) == 0) ||
700 (i >= 2 && (strncmp(text + i - 2, "\n\n", 2) == 0 ||
701 strncmp(text + i - 2, "\r\r", 2) == 0))) {
702 break;
703 }
704 }
705
706 return NULL;
707 }
708
709 /* Takes a string, truncates it where it's safe, returns the new length */
truncate_utf8(char * string,int maxlen)710 int truncate_utf8(char *string, int maxlen)
711 {
712 char *end;
713
714 g_utf8_validate((const gchar *) string, maxlen, (const gchar **) &end);
715 *end = '\0';
716 return end - string;
717 }
718
719 /* Parses a guint64 from string, returns TRUE on success */
parse_int64(char * string,int base,guint64 * number)720 gboolean parse_int64(char *string, int base, guint64 *number)
721 {
722 guint64 parsed;
723 char *endptr;
724
725 errno = 0;
726 parsed = g_ascii_strtoull(string, &endptr, base);
727 if (errno || endptr == string || *endptr != '\0') {
728 return FALSE;
729 }
730 *number = parsed;
731 return TRUE;
732 }
733
734 /* Filters all the characters in 'blacklist' replacing them with 'replacement'.
735 * Modifies the string in-place and returns the string itself.
736 * For the opposite, use g_strcanon() */
str_reject_chars(char * string,const char * reject,char replacement)737 char *str_reject_chars(char *string, const char *reject, char replacement)
738 {
739 char *c = string;
740
741 while (*c) {
742 c += strcspn(c, reject);
743 if (*c) {
744 *c = replacement;
745 }
746 }
747
748 return string;
749 }
750
751 /* Returns a string that is exactly 'char_len' utf8 characters long (not bytes),
752 * padded to the right with spaces or truncated with the 'ellipsis' parameter
753 * if specified (can be NULL).
754 * Returns a newly allocated string, or NULL on invalid parameters. */
str_pad_and_truncate(const char * string,long char_len,const char * ellipsis)755 char *str_pad_and_truncate(const char *string, long char_len, const char *ellipsis)
756 {
757 size_t string_len = strlen(string);
758 size_t ellipsis_len = (ellipsis) ? strlen(ellipsis) : 0;
759 long orig_len = g_utf8_strlen(string, -1);
760
761 g_return_val_if_fail(char_len > ellipsis_len, NULL);
762
763 if (orig_len > char_len) {
764 char *ret = g_malloc(string_len + 1);
765 g_utf8_strncpy(ret, string, char_len - ellipsis_len);
766 if (ellipsis) {
767 g_strlcat(ret, ellipsis, string_len);
768 }
769 return ret;
770 } else if (orig_len < char_len) {
771 return g_strdup_printf("%s%*s", string, (int) (char_len - orig_len), "");
772 } else {
773 return g_strdup(string);
774 }
775 }
776
777 /* copied from irssi's misc.c, by timo sirainen */
b_istr_equal(gconstpointer v,gconstpointer v2)778 int b_istr_equal(gconstpointer v, gconstpointer v2)
779 {
780 return g_ascii_strcasecmp((const char *) v, (const char *) v2) == 0;
781 }
782
783 /* copied from irssi's misc.c, by lemonboy */
b_istr_hash(gconstpointer v)784 guint b_istr_hash(gconstpointer v)
785 {
786 const signed char *p;
787 guint32 h = 5381;
788
789 for (p = v; *p != '\0'; p++) {
790 h = (h << 5) + h + g_ascii_toupper(*p);
791 }
792
793 return h;
794 }
795