configure: fix execution with dash
[rofl0r-ixchat.git] / src / common / util.c
blob09868d6fe7ee4c460de01c05f6f28f880e3f53db
1 /* X-Chat
2 * Copyright (C) 1998 Peter Zelezny.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19 #define __APPLE_API_STRICT_CONFORMANCE
21 #define _FILE_OFFSET_BITS 64
22 #include <stdio.h>
23 #include <unistd.h>
24 #include <string.h>
25 #include <stdlib.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <sys/types.h>
29 #include <pwd.h>
30 #include <sys/time.h>
31 #include <sys/utsname.h>
32 #include <fcntl.h>
33 #include <dirent.h>
34 #include <errno.h>
35 #include "xchat.h"
36 #include "xchatc.h"
37 #include <glib.h>
38 #include <ctype.h>
39 #include "util.h"
41 #define WANTSOCKET
42 #include "inet.h"
44 #if defined (USING_FREEBSD) || defined (__APPLE__)
45 #include <sys/sysctl.h>
46 #endif
47 #ifdef SOCKS
48 #include <socks.h>
49 #endif
51 /* SASL mechanisms */
52 #ifdef USE_OPENSSL
53 #include <openssl/bn.h>
54 #include <openssl/rand.h>
55 #include <openssl/blowfish.h>
56 #include <openssl/aes.h>
57 #ifndef WIN32
58 #include <netinet/in.h>
59 #endif
60 #endif
62 #ifdef USE_DEBUG
64 #undef free
65 #undef malloc
66 #undef realloc
67 #undef strdup
69 int current_mem_usage;
71 struct mem_block
73 char *file;
74 void *buf;
75 int size;
76 int line;
77 int total;
78 struct mem_block *next;
81 struct mem_block *mroot = NULL;
83 void *
84 xchat_malloc (int size, char *file, int line)
86 void *ret;
87 struct mem_block *new;
89 current_mem_usage += size;
90 ret = malloc (size);
91 if (!ret)
93 printf ("Out of memory! (%d)\n", current_mem_usage);
94 exit (255);
97 new = malloc (sizeof (struct mem_block));
98 new->buf = ret;
99 new->size = size;
100 new->next = mroot;
101 new->line = line;
102 new->file = strdup (file);
103 mroot = new;
105 printf ("%s:%d Malloc'ed %d bytes, now \033[35m%d\033[m\n", file, line,
106 size, current_mem_usage);
108 return ret;
111 void *
112 xchat_realloc (char *old, int len, char *file, int line)
114 char *ret;
116 ret = xchat_malloc (len, file, line);
117 if (ret)
119 strcpy (ret, old);
120 xchat_dfree (old, file, line);
122 return ret;
125 void *
126 xchat_strdup (char *str, char *file, int line)
128 void *ret;
129 struct mem_block *new;
130 int size;
132 size = strlen (str) + 1;
133 current_mem_usage += size;
134 ret = malloc (size);
135 if (!ret)
137 printf ("Out of memory! (%d)\n", current_mem_usage);
138 exit (255);
140 strcpy (ret, str);
142 new = malloc (sizeof (struct mem_block));
143 new->buf = ret;
144 new->size = size;
145 new->next = mroot;
146 new->line = line;
147 new->file = strdup (file);
148 mroot = new;
150 printf ("%s:%d strdup (\"%-.40s\") size: %d, total: \033[35m%d\033[m\n",
151 file, line, str, size, current_mem_usage);
153 return ret;
156 void
157 xchat_mem_list (void)
159 struct mem_block *cur, *p;
160 GSList *totals = 0;
161 GSList *list;
163 cur = mroot;
164 while (cur)
166 list = totals;
167 while (list)
169 p = list->data;
170 if (p->line == cur->line &&
171 strcmp (p->file, cur->file) == 0)
173 p->total += p->size;
174 break;
176 list = list->next;
178 if (!list)
180 cur->total = cur->size;
181 totals = g_slist_prepend (totals, cur);
183 cur = cur->next;
186 fprintf (stderr, "file line size num total\n");
187 list = totals;
188 while (list)
190 cur = list->data;
191 fprintf (stderr, "%-15.15s %6d %6d %6d %6d\n", cur->file, cur->line,
192 cur->size, cur->total/cur->size, cur->total);
193 list = list->next;
197 void
198 xchat_dfree (void *buf, char *file, int line)
200 struct mem_block *cur, *last;
202 if (buf == NULL)
204 printf ("%s:%d \033[33mTried to free NULL\033[m\n", file, line);
205 return;
208 last = NULL;
209 cur = mroot;
210 while (cur)
212 if (buf == cur->buf)
213 break;
214 last = cur;
215 cur = cur->next;
217 if (cur == NULL)
219 printf ("%s:%d \033[31mTried to free unknown block %lx!\033[m\n",
220 file, line, (unsigned long) buf);
221 /* abort(); */
222 free (buf);
223 return;
225 current_mem_usage -= cur->size;
226 printf ("%s:%d Free'ed %d bytes, usage now \033[35m%d\033[m\n",
227 file, line, cur->size, current_mem_usage);
228 if (last)
229 last->next = cur->next;
230 else
231 mroot = cur->next;
232 free (cur->file);
233 free (cur);
236 #define malloc(n) xchat_malloc(n, __FILE__, __LINE__)
237 #define realloc(n, m) xchat_realloc(n, m, __FILE__, __LINE__)
238 #define free(n) xchat_dfree(n, __FILE__, __LINE__)
239 #define strdup(n) xchat_strdup(n, __FILE__, __LINE__)
241 #endif /* MEMORY_DEBUG */
243 char *
244 file_part (char *file)
246 char *filepart = file;
247 if (!file)
248 return "";
249 while (1)
251 switch (*file)
253 case 0:
254 return (filepart);
255 case '/':
256 filepart = file + 1;
257 break;
259 file++;
263 void
264 path_part (char *file, char *path, int pathlen)
266 unsigned char t;
267 char *filepart = file_part (file);
268 t = *filepart;
269 *filepart = 0;
270 safe_strcpy (path, file, pathlen);
271 *filepart = t;
274 char * /* like strstr(), but nocase */
275 nocasestrstr (const char *s, const char *wanted)
277 register const int len = strlen (wanted);
279 if (len == 0)
280 return (char *)s;
281 while (rfc_tolower(*s) != rfc_tolower(*wanted) || strncasecmp (s, wanted, len))
282 if (*s++ == '\0')
283 return (char *)NULL;
284 return (char *)s;
287 char *
288 errorstring (int err)
290 switch (err)
292 case -1:
293 return "";
294 case 0:
295 return _("Remote host closed socket");
298 return strerror (err);
302 waitline (int sok, char *buf, int bufsize, int use_recv)
304 int i = 0;
306 while (1)
308 if (use_recv)
310 if (recv (sok, &buf[i], 1, 0) < 1)
311 return -1;
312 } else
314 if (read (sok, &buf[i], 1) < 1)
315 return -1;
317 if (buf[i] == '\n' || bufsize == i + 1)
319 buf[i] = 0;
320 return i;
322 i++;
326 /* checks for "~" in a file and expands */
328 char *
329 expand_homedir (char *file)
331 char *ret, *user;
332 struct passwd *pw;
334 if (*file == '~')
336 if (file[1] != '\0' && file[1] != '/')
338 user = strdup(file);
339 if (strchr(user,'/') != NULL)
340 *(strchr(user,'/')) = '\0';
341 if ((pw = getpwnam(user + 1)) == NULL)
343 free(user);
344 return strdup(file);
346 free(user);
347 user = strchr(file, '/') != NULL ? strchr(file,'/') : file;
348 ret = malloc(strlen(user) + strlen(pw->pw_dir) + 1);
349 strcpy(ret, pw->pw_dir);
350 strcat(ret, user);
352 else
354 ret = malloc (strlen (file) + strlen (g_get_home_dir ()) + 1);
355 sprintf (ret, "%s%s", g_get_home_dir (), file + 1);
357 return ret;
359 return strdup (file);
362 gchar *
363 strip_color (const char *text, int len, int flags)
365 char *new_str;
367 if (len == -1)
368 len = strlen (text);
370 new_str = g_malloc (len + 2);
371 strip_color2 (text, len, new_str, flags);
373 if (flags & STRIP_ESCMARKUP)
375 char *esc = g_markup_escape_text (new_str, -1);
376 g_free (new_str);
377 return esc;
380 return new_str;
383 /* CL: strip_color2 strips src and writes the output at dst; pass the same pointer
384 in both arguments to strip in place. */
386 strip_color2 (const char *src, int len, char *dst, int flags)
388 int rcol = 0, bgcol = 0;
389 char *start = dst;
391 if (len == -1) len = strlen (src);
392 while (len-- > 0)
394 if (rcol > 0 && (isdigit ((unsigned char)*src) ||
395 (*src == ',' && isdigit ((unsigned char)src[1]) && !bgcol)))
397 if (src[1] != ',') rcol--;
398 if (*src == ',')
400 rcol = 2;
401 bgcol = 1;
403 } else
405 rcol = bgcol = 0;
406 switch (*src)
408 case '\003': /*ATTR_COLOR: */
409 if (!(flags & STRIP_COLOR)) goto pass_char;
410 rcol = 2;
411 break;
412 case HIDDEN_CHAR: /* CL: invisible text (for event formats only) */ /* this takes care of the topic */
413 if (!(flags & STRIP_HIDDEN)) goto pass_char;
414 break;
415 case '\007': /*ATTR_BEEP: */
416 case '\017': /*ATTR_RESET: */
417 case '\026': /*ATTR_REVERSE: */
418 case '\002': /*ATTR_BOLD: */
419 case '\037': /*ATTR_UNDERLINE: */
420 case '\035': /*ATTR_ITALICS: */
421 if (!(flags & STRIP_ATTRIB)) goto pass_char;
422 break;
423 default:
424 pass_char:
425 *dst++ = *src;
428 src++;
430 *dst = 0;
432 return (int) (dst - start);
436 strip_hidden_attribute (char *src, char *dst)
438 int len = 0;
439 while (*src != '\000')
441 if (*src != HIDDEN_CHAR)
443 *dst++ = *src;
444 len++;
446 src++;
448 return len;
451 #if defined (USING_LINUX) || defined (USING_FREEBSD) || defined (__APPLE__)
453 static void
454 get_cpu_info (double *mhz, int *cpus)
457 #ifdef USING_LINUX
459 char buf[256];
460 int fh;
462 *mhz = 0;
463 *cpus = 0;
465 fh = open ("/proc/cpuinfo", O_RDONLY); /* linux 2.2+ only */
466 if (fh == -1)
468 *cpus = 1;
469 return;
472 while (1)
474 if (waitline (fh, buf, sizeof buf, FALSE) < 0)
475 break;
476 if (!strncmp (buf, "cycle frequency [Hz]\t:", 22)) /* alpha */
478 *mhz = atoi (buf + 23) / 1000000;
479 } else if (!strncmp (buf, "cpu MHz\t\t:", 10)) /* i386 */
481 *mhz = atof (buf + 11) + 0.5;
482 } else if (!strncmp (buf, "clock\t\t:", 8)) /* PPC */
484 *mhz = atoi (buf + 9);
485 } else if (!strncmp (buf, "processor\t", 10))
487 (*cpus)++;
490 close (fh);
491 if (!*cpus)
492 *cpus = 1;
494 #endif
495 #ifdef USING_FREEBSD
497 int mib[2], ncpu;
498 u_long freq;
499 size_t len;
501 freq = 0;
502 *mhz = 0;
503 *cpus = 0;
505 mib[0] = CTL_HW;
506 mib[1] = HW_NCPU;
508 len = sizeof(ncpu);
509 sysctl(mib, 2, &ncpu, &len, NULL, 0);
511 len = sizeof(freq);
512 sysctlbyname("machdep.tsc_freq", &freq, &len, NULL, 0);
514 *cpus = ncpu;
515 *mhz = (freq / 1000000);
517 #endif
518 #ifdef __APPLE__
520 int mib[2], ncpu;
521 unsigned long long freq;
522 size_t len;
524 freq = 0;
525 *mhz = 0;
526 *cpus = 0;
528 mib[0] = CTL_HW;
529 mib[1] = HW_NCPU;
531 len = sizeof(ncpu);
532 sysctl(mib, 2, &ncpu, &len, NULL, 0);
534 len = sizeof(freq);
535 sysctlbyname("hw.cpufrequency", &freq, &len, NULL, 0);
537 *cpus = ncpu;
538 *mhz = (freq / 1000000);
540 #endif
543 #endif
545 char *
546 get_cpu_str (void)
548 #if defined (USING_LINUX) || defined (USING_FREEBSD) || defined (__APPLE__)
549 double mhz;
550 #endif
551 int cpus = 1;
552 struct utsname un;
553 static char *buf = NULL;
555 if (buf)
556 return buf;
558 buf = malloc (128);
560 uname (&un);
562 #if defined (USING_LINUX) || defined (USING_FREEBSD) || defined (__APPLE__)
563 get_cpu_info (&mhz, &cpus);
564 if (mhz)
566 double cpuspeed = ( mhz > 1000 ) ? mhz / 1000 : mhz;
567 const char *cpuspeedstr = ( mhz > 1000 ) ? "GHz" : "MHz";
568 snprintf (buf, 128,
569 (cpus == 1) ? "%s %s [%s/%.2f%s]" : "%s %s [%s/%.2f%s/SMP]",
570 un.sysname, un.release, un.machine,
571 cpuspeed, cpuspeedstr);
572 } else
573 #endif
574 snprintf (buf, 128,
575 (cpus == 1) ? "%s %s [%s]" : "%s %s [%s/SMP]",
576 un.sysname, un.release, un.machine);
578 return buf;
582 buf_get_line (char *ibuf, char **buf, int *position, int len)
584 int pos = *position, spos = pos;
586 if (pos == len)
587 return 0;
589 while (ibuf[pos++] != '\n')
591 if (pos == len)
592 return 0;
594 pos--;
595 ibuf[pos] = 0;
596 *buf = &ibuf[spos];
597 pos++;
598 *position = pos;
599 return 1;
602 int match(const char *mask, const char *string)
604 register const char *m = mask, *s = string;
605 register char ch;
606 const char *bm, *bs; /* Will be reg anyway on a decent CPU/compiler */
608 /* Process the "head" of the mask, if any */
609 while ((ch = *m++) && (ch != '*'))
610 switch (ch)
612 case '\\':
613 if (*m == '?' || *m == '*')
614 ch = *m++;
615 default:
616 if (rfc_tolower(*s) != rfc_tolower(ch))
617 return 0;
618 case '?':
619 if (!*s++)
620 return 0;
622 if (!ch)
623 return !(*s);
625 /* We got a star: quickly find if/where we match the next char */
626 got_star:
627 bm = m; /* Next try rollback here */
628 while ((ch = *m++))
629 switch (ch)
631 case '?':
632 if (!*s++)
633 return 0;
634 case '*':
635 bm = m;
636 continue; /* while */
637 case '\\':
638 if (*m == '?' || *m == '*')
639 ch = *m++;
640 default:
641 goto break_while; /* C is structured ? */
643 break_while:
644 if (!ch)
645 return 1; /* mask ends with '*', we got it */
646 ch = rfc_tolower(ch);
647 while (rfc_tolower(*s++) != ch)
648 if (!*s)
649 return 0;
650 bs = s; /* Next try start from here */
652 /* Check the rest of the "chunk" */
653 while ((ch = *m++))
655 switch (ch)
657 case '*':
658 goto got_star;
659 case '\\':
660 if (*m == '?' || *m == '*')
661 ch = *m++;
662 default:
663 if (rfc_tolower(*s) != rfc_tolower(ch))
665 if (!*s)
666 return 0;
667 m = bm;
668 s = bs;
669 goto got_star;
671 case '?':
672 if (!*s++)
673 return 0;
676 if (*s)
678 m = bm;
679 s = bs;
680 goto got_star;
682 return 1;
685 void
686 for_files (char *dirname, char *mask, void callback (char *file))
688 DIR *dir;
689 struct dirent *ent;
690 char *buf;
692 dir = opendir (dirname);
693 if (dir)
695 while ((ent = readdir (dir)))
697 if (strcmp (ent->d_name, ".") && strcmp (ent->d_name, ".."))
699 if (match (mask, ent->d_name))
701 buf = malloc (strlen (dirname) + strlen (ent->d_name) + 2);
702 sprintf (buf, "%s/%s", dirname, ent->d_name);
703 callback (buf);
704 free (buf);
708 closedir (dir);
712 /*void
713 tolowerStr (char *str)
715 while (*str)
717 *str = rfc_tolower (*str);
718 str++;
722 typedef struct
724 char *code, *country;
725 } domain_t;
727 static int
728 country_compare (const void *a, const void *b)
730 return strcasecmp (a, ((domain_t *)b)->code);
733 static const domain_t domain[] =
735 {"AC", N_("Ascension Island") },
736 {"AD", N_("Andorra") },
737 {"AE", N_("United Arab Emirates") },
738 {"AF", N_("Afghanistan") },
739 {"AG", N_("Antigua and Barbuda") },
740 {"AI", N_("Anguilla") },
741 {"AL", N_("Albania") },
742 {"AM", N_("Armenia") },
743 {"AN", N_("Netherlands Antilles") },
744 {"AO", N_("Angola") },
745 {"AQ", N_("Antarctica") },
746 {"AR", N_("Argentina") },
747 {"ARPA", N_("Reverse DNS") },
748 {"AS", N_("American Samoa") },
749 {"AT", N_("Austria") },
750 {"ATO", N_("Nato Fiel") },
751 {"AU", N_("Australia") },
752 {"AW", N_("Aruba") },
753 {"AX", N_("Aland Islands") },
754 {"AZ", N_("Azerbaijan") },
755 {"BA", N_("Bosnia and Herzegovina") },
756 {"BB", N_("Barbados") },
757 {"BD", N_("Bangladesh") },
758 {"BE", N_("Belgium") },
759 {"BF", N_("Burkina Faso") },
760 {"BG", N_("Bulgaria") },
761 {"BH", N_("Bahrain") },
762 {"BI", N_("Burundi") },
763 {"BIZ", N_("Businesses"), },
764 {"BJ", N_("Benin") },
765 {"BM", N_("Bermuda") },
766 {"BN", N_("Brunei Darussalam") },
767 {"BO", N_("Bolivia") },
768 {"BR", N_("Brazil") },
769 {"BS", N_("Bahamas") },
770 {"BT", N_("Bhutan") },
771 {"BV", N_("Bouvet Island") },
772 {"BW", N_("Botswana") },
773 {"BY", N_("Belarus") },
774 {"BZ", N_("Belize") },
775 {"CA", N_("Canada") },
776 {"CC", N_("Cocos Islands") },
777 {"CD", N_("Democratic Republic of Congo") },
778 {"CF", N_("Central African Republic") },
779 {"CG", N_("Congo") },
780 {"CH", N_("Switzerland") },
781 {"CI", N_("Cote d'Ivoire") },
782 {"CK", N_("Cook Islands") },
783 {"CL", N_("Chile") },
784 {"CM", N_("Cameroon") },
785 {"CN", N_("China") },
786 {"CO", N_("Colombia") },
787 {"COM", N_("Internic Commercial") },
788 {"CR", N_("Costa Rica") },
789 {"CS", N_("Serbia and Montenegro") },
790 {"CU", N_("Cuba") },
791 {"CV", N_("Cape Verde") },
792 {"CX", N_("Christmas Island") },
793 {"CY", N_("Cyprus") },
794 {"CZ", N_("Czech Republic") },
795 {"DE", N_("Germany") },
796 {"DJ", N_("Djibouti") },
797 {"DK", N_("Denmark") },
798 {"DM", N_("Dominica") },
799 {"DO", N_("Dominican Republic") },
800 {"DZ", N_("Algeria") },
801 {"EC", N_("Ecuador") },
802 {"EDU", N_("Educational Institution") },
803 {"EE", N_("Estonia") },
804 {"EG", N_("Egypt") },
805 {"EH", N_("Western Sahara") },
806 {"ER", N_("Eritrea") },
807 {"ES", N_("Spain") },
808 {"ET", N_("Ethiopia") },
809 {"EU", N_("European Union") },
810 {"FI", N_("Finland") },
811 {"FJ", N_("Fiji") },
812 {"FK", N_("Falkland Islands") },
813 {"FM", N_("Micronesia") },
814 {"FO", N_("Faroe Islands") },
815 {"FR", N_("France") },
816 {"GA", N_("Gabon") },
817 {"GB", N_("Great Britain") },
818 {"GD", N_("Grenada") },
819 {"GE", N_("Georgia") },
820 {"GF", N_("French Guiana") },
821 {"GG", N_("British Channel Isles") },
822 {"GH", N_("Ghana") },
823 {"GI", N_("Gibraltar") },
824 {"GL", N_("Greenland") },
825 {"GM", N_("Gambia") },
826 {"GN", N_("Guinea") },
827 {"GOV", N_("Government") },
828 {"GP", N_("Guadeloupe") },
829 {"GQ", N_("Equatorial Guinea") },
830 {"GR", N_("Greece") },
831 {"GS", N_("S. Georgia and S. Sandwich Isles") },
832 {"GT", N_("Guatemala") },
833 {"GU", N_("Guam") },
834 {"GW", N_("Guinea-Bissau") },
835 {"GY", N_("Guyana") },
836 {"HK", N_("Hong Kong") },
837 {"HM", N_("Heard and McDonald Islands") },
838 {"HN", N_("Honduras") },
839 {"HR", N_("Croatia") },
840 {"HT", N_("Haiti") },
841 {"HU", N_("Hungary") },
842 {"ID", N_("Indonesia") },
843 {"IE", N_("Ireland") },
844 {"IL", N_("Israel") },
845 {"IM", N_("Isle of Man") },
846 {"IN", N_("India") },
847 {"INFO", N_("Informational") },
848 {"INT", N_("International") },
849 {"IO", N_("British Indian Ocean Territory") },
850 {"IQ", N_("Iraq") },
851 {"IR", N_("Iran") },
852 {"IS", N_("Iceland") },
853 {"IT", N_("Italy") },
854 {"JE", N_("Jersey") },
855 {"JM", N_("Jamaica") },
856 {"JO", N_("Jordan") },
857 {"JP", N_("Japan") },
858 {"KE", N_("Kenya") },
859 {"KG", N_("Kyrgyzstan") },
860 {"KH", N_("Cambodia") },
861 {"KI", N_("Kiribati") },
862 {"KM", N_("Comoros") },
863 {"KN", N_("St. Kitts and Nevis") },
864 {"KP", N_("North Korea") },
865 {"KR", N_("South Korea") },
866 {"KW", N_("Kuwait") },
867 {"KY", N_("Cayman Islands") },
868 {"KZ", N_("Kazakhstan") },
869 {"LA", N_("Laos") },
870 {"LB", N_("Lebanon") },
871 {"LC", N_("Saint Lucia") },
872 {"LI", N_("Liechtenstein") },
873 {"LK", N_("Sri Lanka") },
874 {"LR", N_("Liberia") },
875 {"LS", N_("Lesotho") },
876 {"LT", N_("Lithuania") },
877 {"LU", N_("Luxembourg") },
878 {"LV", N_("Latvia") },
879 {"LY", N_("Libya") },
880 {"MA", N_("Morocco") },
881 {"MC", N_("Monaco") },
882 {"MD", N_("Moldova") },
883 {"ME", N_("Montenegro") },
884 {"MED", N_("United States Medical") },
885 {"MG", N_("Madagascar") },
886 {"MH", N_("Marshall Islands") },
887 {"MIL", N_("Military") },
888 {"MK", N_("Macedonia") },
889 {"ML", N_("Mali") },
890 {"MM", N_("Myanmar") },
891 {"MN", N_("Mongolia") },
892 {"MO", N_("Macau") },
893 {"MP", N_("Northern Mariana Islands") },
894 {"MQ", N_("Martinique") },
895 {"MR", N_("Mauritania") },
896 {"MS", N_("Montserrat") },
897 {"MT", N_("Malta") },
898 {"MU", N_("Mauritius") },
899 {"MV", N_("Maldives") },
900 {"MW", N_("Malawi") },
901 {"MX", N_("Mexico") },
902 {"MY", N_("Malaysia") },
903 {"MZ", N_("Mozambique") },
904 {"NA", N_("Namibia") },
905 {"NC", N_("New Caledonia") },
906 {"NE", N_("Niger") },
907 {"NET", N_("Internic Network") },
908 {"NF", N_("Norfolk Island") },
909 {"NG", N_("Nigeria") },
910 {"NI", N_("Nicaragua") },
911 {"NL", N_("Netherlands") },
912 {"NO", N_("Norway") },
913 {"NP", N_("Nepal") },
914 {"NR", N_("Nauru") },
915 {"NU", N_("Niue") },
916 {"NZ", N_("New Zealand") },
917 {"OM", N_("Oman") },
918 {"ORG", N_("Internic Non-Profit Organization") },
919 {"PA", N_("Panama") },
920 {"PE", N_("Peru") },
921 {"PF", N_("French Polynesia") },
922 {"PG", N_("Papua New Guinea") },
923 {"PH", N_("Philippines") },
924 {"PK", N_("Pakistan") },
925 {"PL", N_("Poland") },
926 {"PM", N_("St. Pierre and Miquelon") },
927 {"PN", N_("Pitcairn") },
928 {"PR", N_("Puerto Rico") },
929 {"PS", N_("Palestinian Territory") },
930 {"PT", N_("Portugal") },
931 {"PW", N_("Palau") },
932 {"PY", N_("Paraguay") },
933 {"QA", N_("Qatar") },
934 {"RE", N_("Reunion") },
935 {"RO", N_("Romania") },
936 {"RPA", N_("Old School ARPAnet") },
937 {"RS", N_("Serbia") },
938 {"RU", N_("Russian Federation") },
939 {"RW", N_("Rwanda") },
940 {"SA", N_("Saudi Arabia") },
941 {"SB", N_("Solomon Islands") },
942 {"SC", N_("Seychelles") },
943 {"SD", N_("Sudan") },
944 {"SE", N_("Sweden") },
945 {"SG", N_("Singapore") },
946 {"SH", N_("St. Helena") },
947 {"SI", N_("Slovenia") },
948 {"SJ", N_("Svalbard and Jan Mayen Islands") },
949 {"SK", N_("Slovakia") },
950 {"SL", N_("Sierra Leone") },
951 {"SM", N_("San Marino") },
952 {"SN", N_("Senegal") },
953 {"SO", N_("Somalia") },
954 {"SR", N_("Suriname") },
955 {"ST", N_("Sao Tome and Principe") },
956 {"SU", N_("Former USSR") },
957 {"SV", N_("El Salvador") },
958 {"SY", N_("Syria") },
959 {"SZ", N_("Swaziland") },
960 {"TC", N_("Turks and Caicos Islands") },
961 {"TD", N_("Chad") },
962 {"TF", N_("French Southern Territories") },
963 {"TG", N_("Togo") },
964 {"TH", N_("Thailand") },
965 {"TJ", N_("Tajikistan") },
966 {"TK", N_("Tokelau") },
967 {"TL", N_("East Timor") },
968 {"TM", N_("Turkmenistan") },
969 {"TN", N_("Tunisia") },
970 {"TO", N_("Tonga") },
971 {"TP", N_("East Timor") },
972 {"TR", N_("Turkey") },
973 {"TT", N_("Trinidad and Tobago") },
974 {"TV", N_("Tuvalu") },
975 {"TW", N_("Taiwan") },
976 {"TZ", N_("Tanzania") },
977 {"UA", N_("Ukraine") },
978 {"UG", N_("Uganda") },
979 {"UK", N_("United Kingdom") },
980 {"US", N_("United States of America") },
981 {"UY", N_("Uruguay") },
982 {"UZ", N_("Uzbekistan") },
983 {"VA", N_("Vatican City State") },
984 {"VC", N_("St. Vincent and the Grenadines") },
985 {"VE", N_("Venezuela") },
986 {"VG", N_("British Virgin Islands") },
987 {"VI", N_("US Virgin Islands") },
988 {"VN", N_("Vietnam") },
989 {"VU", N_("Vanuatu") },
990 {"WF", N_("Wallis and Futuna Islands") },
991 {"WS", N_("Samoa") },
992 {"YE", N_("Yemen") },
993 {"YT", N_("Mayotte") },
994 {"YU", N_("Yugoslavia") },
995 {"ZA", N_("South Africa") },
996 {"ZM", N_("Zambia") },
997 {"ZW", N_("Zimbabwe") },
1000 char *
1001 country (char *hostname)
1003 char *p;
1004 domain_t *dom;
1006 if (!hostname || !*hostname || isdigit ((unsigned char) hostname[strlen (hostname) - 1]))
1007 return _("Unknown");
1008 if ((p = strrchr (hostname, '.')))
1009 p++;
1010 else
1011 p = hostname;
1013 dom = bsearch (p, domain, sizeof (domain) / sizeof (domain_t),
1014 sizeof (domain_t), country_compare);
1016 if (!dom)
1017 return _("Unknown");
1019 return _(dom->country);
1022 void
1023 country_search (char *pattern, void *ud, void (*print)(void *, char *, ...))
1025 const domain_t *dom;
1026 int i;
1028 for (i = 0; i < sizeof (domain) / sizeof (domain_t); i++)
1030 dom = &domain[i];
1031 if (match (pattern, dom->country) || match (pattern, _(dom->country)))
1033 print (ud, "%s = %s\n", dom->code, _(dom->country));
1038 /* I think gnome1.0.x isn't necessarily linked against popt, ah well! */
1039 /* !!! For now use this inlined function, or it would break fe-text building */
1040 /* .... will find a better solution later. */
1041 /*#ifndef USE_GNOME*/
1043 /* this is taken from gnome-libs 1.2.4 */
1044 #define POPT_ARGV_ARRAY_GROW_DELTA 5
1046 int my_poptParseArgvString(const char * s, int * argcPtr, char *** argvPtr) {
1047 char * buf, * bufStart, * dst;
1048 const char * src;
1049 char quote = '\0';
1050 int argvAlloced = POPT_ARGV_ARRAY_GROW_DELTA;
1051 char ** argv = malloc(sizeof(*argv) * argvAlloced);
1052 const char ** argv2;
1053 int argc = 0;
1054 int i, buflen;
1056 buflen = strlen(s) + 1;
1057 /* bufStart = buf = alloca(buflen);*/
1058 bufStart = buf = malloc (buflen);
1059 memset(buf, '\0', buflen);
1061 src = s;
1062 argv[argc] = buf;
1064 while (*src) {
1065 if (quote == *src) {
1066 quote = '\0';
1067 } else if (quote) {
1068 if (*src == '\\') {
1069 src++;
1070 if (!*src) {
1071 free(argv);
1072 free(bufStart);
1073 return 1;
1075 if (*src != quote) *buf++ = '\\';
1077 *buf++ = *src;
1078 /*} else if (isspace((unsigned char) *src)) {*/
1079 } else if (*src == ' ') {
1080 if (*argv[argc]) {
1081 buf++, argc++;
1082 if (argc == argvAlloced) {
1083 argvAlloced += POPT_ARGV_ARRAY_GROW_DELTA;
1084 argv = realloc(argv, sizeof(*argv) * argvAlloced);
1086 argv[argc] = buf;
1088 } else switch (*src) {
1089 case '"':
1090 case '\'':
1091 quote = *src;
1092 break;
1093 case '\\':
1094 src++;
1095 if (!*src) {
1096 free(argv);
1097 free(bufStart);
1098 return 1;
1100 /* fallthrough */
1101 default:
1102 *buf++ = *src;
1105 src++;
1108 if (strlen(argv[argc])) {
1109 argc++, buf++;
1112 dst = malloc((argc + 1) * sizeof(*argv) + (buf - bufStart));
1113 argv2 = (void *) dst;
1114 dst += (argc + 1) * sizeof(*argv);
1115 memcpy((void *)argv2, argv, argc * sizeof(*argv));
1116 argv2[argc] = NULL;
1117 memcpy(dst, bufStart, buf - bufStart);
1119 for (i = 0; i < argc; i++) {
1120 argv2[i] = dst + (argv[i] - bufStart);
1123 free(argv);
1125 *argvPtr = (char **)argv2; /* XXX don't change the API */
1126 *argcPtr = argc;
1128 free (bufStart);
1130 return 0;
1134 util_exec (const char *cmd)
1136 int pid;
1137 char **argv;
1138 int argc;
1139 int fd;
1141 if (my_poptParseArgvString (cmd, &argc, &argv) != 0)
1142 return -1;
1144 pid = fork ();
1145 if (pid == -1)
1146 return -1;
1147 if (pid == 0)
1149 /* Now close all open file descriptors except stdin, stdout and stderr */
1150 for (fd = 3; fd < 1024; fd++) close(fd);
1151 execvp (argv[0], argv);
1152 _exit (0);
1153 } else
1155 free (argv);
1156 return pid;
1161 util_execv (char * const argv[])
1163 int pid, fd;
1165 pid = fork ();
1166 if (pid == -1)
1167 return -1;
1168 if (pid == 0)
1170 /* Now close all open file descriptors except stdin, stdout and stderr */
1171 for (fd = 3; fd < 1024; fd++) close(fd);
1172 execv (argv[0], argv);
1173 _exit (0);
1174 } else
1176 return pid;
1180 unsigned long
1181 make_ping_time (void)
1183 struct timeval timev;
1184 gettimeofday (&timev, 0);
1185 return (timev.tv_sec - 50000) * 1000000 + timev.tv_usec;
1189 /************************************************************************
1190 * This technique was borrowed in part from the source code to
1191 * ircd-hybrid-5.3 to implement case-insensitive string matches which
1192 * are fully compliant with Section 2.2 of RFC 1459, the copyright
1193 * of that code being (C) 1990 Jarkko Oikarinen and under the GPL.
1195 * A special thanks goes to Mr. Okarinen for being the one person who
1196 * seems to have ever noticed this section in the original RFC and
1197 * written code for it. Shame on all the rest of you (myself included).
1199 * --+ Dagmar d'Surreal
1203 rfc_casecmp (const char *s1, const char *s2)
1205 register unsigned char *str1 = (unsigned char *) s1;
1206 register unsigned char *str2 = (unsigned char *) s2;
1207 register int res;
1209 while ((res = rfc_tolower (*str1) - rfc_tolower (*str2)) == 0)
1211 if (*str1 == '\0')
1212 return 0;
1213 str1++;
1214 str2++;
1216 return (res);
1220 rfc_ncasecmp (char *str1, char *str2, int n)
1222 register unsigned char *s1 = (unsigned char *) str1;
1223 register unsigned char *s2 = (unsigned char *) str2;
1224 register int res;
1226 while ((res = rfc_tolower (*s1) - rfc_tolower (*s2)) == 0)
1228 s1++;
1229 s2++;
1230 n--;
1231 if (n == 0 || (*s1 == '\0' && *s2 == '\0'))
1232 return 0;
1234 return (res);
1237 const unsigned char rfc_tolowertab[] =
1238 { 0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa,
1239 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, 0x11, 0x12, 0x13, 0x14,
1240 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d,
1241 0x1e, 0x1f,
1242 ' ', '!', '"', '#', '$', '%', '&', 0x27, '(', ')',
1243 '*', '+', ',', '-', '.', '/',
1244 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
1245 ':', ';', '<', '=', '>', '?',
1246 '@', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
1247 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
1248 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~',
1249 '_',
1250 '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
1251 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
1252 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~',
1253 0x7f,
1254 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
1255 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
1256 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
1257 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
1258 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9,
1259 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
1260 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9,
1261 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
1262 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9,
1263 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
1264 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9,
1265 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
1266 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
1267 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
1268 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9,
1269 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
1272 /*static unsigned char touppertab[] =
1273 { 0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa,
1274 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, 0x11, 0x12, 0x13, 0x14,
1275 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d,
1276 0x1e, 0x1f,
1277 ' ', '!', '"', '#', '$', '%', '&', 0x27, '(', ')',
1278 '*', '+', ',', '-', '.', '/',
1279 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
1280 ':', ';', '<', '=', '>', '?',
1281 '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
1282 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
1283 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^',
1284 0x5f,
1285 '`', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
1286 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
1287 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^',
1288 0x7f,
1289 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
1290 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
1291 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
1292 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
1293 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9,
1294 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
1295 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9,
1296 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
1297 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9,
1298 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
1299 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9,
1300 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
1301 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
1302 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
1303 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9,
1304 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
1305 };*/
1307 /*static int
1308 rename_utf8 (char *oldname, char *newname)
1310 int sav, res;
1311 char *fso, *fsn;
1313 fso = xchat_filename_from_utf8 (oldname, -1, 0, 0, 0);
1314 if (!fso)
1315 return FALSE;
1316 fsn = xchat_filename_from_utf8 (newname, -1, 0, 0, 0);
1317 if (!fsn)
1319 g_free (fso);
1320 return FALSE;
1323 res = rename (fso, fsn);
1324 sav = errno;
1325 g_free (fso);
1326 g_free (fsn);
1327 errno = sav;
1328 return res;
1331 static int
1332 unlink_utf8 (char *fname)
1334 int res;
1335 char *fs;
1337 fs = xchat_filename_from_utf8 (fname, -1, 0, 0, 0);
1338 if (!fs)
1339 return FALSE;
1341 res = unlink (fs);
1342 g_free (fs);
1343 return res;
1346 static gboolean
1347 file_exists_utf8 (char *fname)
1349 int res;
1350 char *fs;
1352 fs = xchat_filename_from_utf8 (fname, -1, 0, 0, 0);
1353 if (!fs)
1354 return FALSE;
1356 res = access (fs, F_OK);
1357 g_free (fs);
1358 if (res == 0)
1359 return TRUE;
1360 return FALSE;
1363 static gboolean
1364 copy_file (char *dl_src, char *dl_dest, int permissions) /* FS encoding */
1366 int tmp_src, tmp_dest;
1367 gboolean ok = FALSE;
1368 char dl_tmp[4096];
1369 int return_tmp, return_tmp2;
1371 if ((tmp_src = open (dl_src, O_RDONLY | OFLAGS)) == -1)
1373 fprintf (stderr, "Unable to open() file '%s' (%s) !", dl_src,
1374 strerror (errno));
1375 return FALSE;
1378 if ((tmp_dest =
1379 open (dl_dest, O_WRONLY | O_CREAT | O_TRUNC | OFLAGS, permissions)) < 0)
1381 close (tmp_src);
1382 fprintf (stderr, "Unable to create file '%s' (%s) !", dl_src,
1383 strerror (errno));
1384 return FALSE;
1387 for (;;)
1389 return_tmp = read (tmp_src, dl_tmp, sizeof (dl_tmp));
1391 if (!return_tmp)
1393 ok = TRUE;
1394 break;
1397 if (return_tmp < 0)
1399 fprintf (stderr, "download_move_to_completed_dir(): "
1400 "error reading while moving file to save directory (%s)",
1401 strerror (errno));
1402 break;
1405 return_tmp2 = write (tmp_dest, dl_tmp, return_tmp);
1407 if (return_tmp2 < 0)
1409 fprintf (stderr, "download_move_to_completed_dir(): "
1410 "error writing while moving file to save directory (%s)",
1411 strerror (errno));
1412 break;
1415 if (return_tmp < sizeof (dl_tmp))
1417 ok = TRUE;
1418 break;
1422 close (tmp_dest);
1423 close (tmp_src);
1424 return ok;
1427 /* Takes care of moving a file from a temporary download location to a completed location. Now in UTF-8. */
1428 void
1429 move_file_utf8 (char *src_dir, char *dst_dir, char *fname, int dccpermissions)
1431 char src[4096];
1432 char dst[4096];
1433 int res, i;
1434 char *src_fs; /* FileSystem encoding */
1435 char *dst_fs;
1437 /* if dcc_dir and dcc_completed_dir are the same then we are done */
1438 if (0 == strcmp (src_dir, dst_dir) ||
1439 0 == dst_dir[0])
1440 return; /* Already in "completed dir" */
1442 snprintf (src, sizeof (src), "%s/%s", src_dir, fname);
1443 snprintf (dst, sizeof (dst), "%s/%s", dst_dir, fname);
1445 /* already exists in completed dir? Append a number */
1446 if (file_exists_utf8 (dst))
1448 for (i = 0; ; i++)
1450 snprintf (dst, sizeof (dst), "%s/%s.%d", dst_dir, fname, i);
1451 if (!file_exists_utf8 (dst))
1452 break;
1456 /* convert UTF-8 to filesystem encoding */
1457 src_fs = xchat_filename_from_utf8 (src, -1, 0, 0, 0);
1458 if (!src_fs)
1459 return;
1460 dst_fs = xchat_filename_from_utf8 (dst, -1, 0, 0, 0);
1461 if (!dst_fs)
1463 g_free (src_fs);
1464 return;
1467 /* first try a simple rename move */
1468 res = rename (src_fs, dst_fs);
1470 if (res == -1 && (errno == EXDEV || errno == EPERM))
1472 /* link failed because either the two paths aren't on the */
1473 /* same filesystem or the filesystem doesn't support hard */
1474 /* links, so we have to do a copy. */
1475 if (copy_file (src_fs, dst_fs, dccpermissions))
1476 unlink (src_fs);
1479 g_free (dst_fs);
1480 g_free (src_fs);
1484 mkdir_utf8 (char *dir)
1486 int ret;
1488 dir = xchat_filename_from_utf8 (dir, -1, 0, 0, 0);
1489 if (!dir)
1490 return -1;
1492 ret = mkdir (dir, S_IRUSR | S_IWUSR | S_IXUSR);
1493 g_free (dir);
1495 return ret;
1498 /* separates a string according to a 'sep' char, then calls the callback
1499 function for each token found */
1502 token_foreach (char *str, char sep,
1503 int (*callback) (char *str, void *ud), void *ud)
1505 char t, *start = str;
1507 while (1)
1509 if (*str == sep || *str == 0)
1511 t = *str;
1512 *str = 0;
1513 if (callback (start, ud) < 1)
1515 *str = t;
1516 return FALSE;
1518 *str = t;
1520 if (*str == 0)
1521 break;
1522 str++;
1523 start = str;
1525 } else
1527 /* chars $00-$7f can never be embedded in utf-8 */
1528 str++;
1532 return TRUE;
1535 /* 31 bit string hash functions */
1537 guint32
1538 str_hash (const char *key)
1540 const char *p = key;
1541 guint32 h = *p;
1543 if (h)
1544 for (p += 1; *p != '\0'; p++)
1545 h = (h << 5) - h + *p;
1547 return h;
1550 guint32
1551 str_ihash (const unsigned char *key)
1553 const char *p = key;
1554 guint32 h = rfc_tolowertab [(guint)*p];
1556 if (h)
1557 for (p += 1; *p != '\0'; p++)
1558 h = (h << 5) - h + rfc_tolowertab [(guint)*p];
1560 return h;
1563 /* features: 1. "src" must be valid, NULL terminated UTF-8 */
1564 /* 2. "dest" will be left with valid UTF-8 - no partial chars! */
1566 void
1567 safe_strcpy (char *dest, const char *src, int bytes_left)
1569 int mbl;
1571 while (1)
1573 mbl = g_utf8_skip[*((unsigned char *)src)];
1575 if (bytes_left < (mbl + 1)) /* can't fit with NULL? */
1577 *dest = 0;
1578 break;
1581 if (mbl == 1) /* one byte char */
1583 *dest = *src;
1584 if (*src == 0)
1585 break; /* it all fit */
1586 dest++;
1587 src++;
1588 bytes_left--;
1590 else /* multibyte char */
1592 memcpy (dest, src, mbl);
1593 dest += mbl;
1594 src += mbl;
1595 bytes_left -= mbl;
1600 char *
1601 encode_sasl_pass_plain (char *user, char *pass)
1603 int authlen;
1604 char *buffer;
1605 char *encoded;
1607 /* we can't call strlen() directly on buffer thanks to the atrocious \0 characters it requires */
1608 authlen = strlen (user) * 2 + 2 + strlen (pass);
1609 buffer = g_strdup_printf ("%s%c%s%c%s", user, '\0', user, '\0', pass);
1610 encoded = g_base64_encode ((unsigned char*) buffer, authlen);
1611 g_free (buffer);
1613 return encoded;
1616 #ifdef USE_OPENSSL
1617 /* Adapted from ZNC's SASL module */
1619 static int
1620 parse_dh (char *str, DH **dh_out, unsigned char **secret_out, int *keysize_out)
1622 DH *dh;
1623 guchar *data, *decoded_data;
1624 guchar *secret;
1625 gsize data_len;
1626 guint size;
1627 guint16 size16;
1628 BIGNUM *pubkey;
1629 gint key_size;
1631 dh = DH_new();
1632 data = decoded_data = g_base64_decode (str, &data_len);
1633 if (data_len < 2)
1634 goto fail;
1636 /* prime number */
1637 memcpy (&size16, data, sizeof(size16));
1638 size = ntohs (size16);
1639 data += 2;
1640 data_len -= 2;
1642 if (size > data_len)
1643 goto fail;
1645 dh->p = BN_bin2bn (data, size, NULL);
1646 data += size;
1648 /* Generator */
1649 if (data_len < 2)
1650 goto fail;
1652 memcpy (&size16, data, sizeof(size16));
1653 size = ntohs (size16);
1654 data += 2;
1655 data_len -= 2;
1657 if (size > data_len)
1658 goto fail;
1660 dh->g = BN_bin2bn (data, size, NULL);
1661 data += size;
1663 /* pub key */
1664 if (data_len < 2)
1665 goto fail;
1667 memcpy (&size16, data, sizeof(size16));
1668 size = ntohs(size16);
1669 data += 2;
1670 data_len -= 2;
1672 pubkey = BN_bin2bn (data, size, NULL);
1673 if (!(DH_generate_key (dh)))
1674 goto fail;
1676 secret = (unsigned char*)malloc (DH_size(dh));
1677 key_size = DH_compute_key (secret, pubkey, dh);
1678 if (key_size == -1)
1679 goto fail;
1681 g_free (decoded_data);
1683 *dh_out = dh;
1684 *secret_out = secret;
1685 *keysize_out = key_size;
1686 return 1;
1688 fail:
1689 if (decoded_data)
1690 g_free (decoded_data);
1691 return 0;
1694 char *
1695 encode_sasl_pass_blowfish (char *user, char *pass, char *data)
1697 DH *dh;
1698 char *response, *ret;
1699 unsigned char *secret;
1700 unsigned char *encrypted_pass;
1701 char *plain_pass;
1702 BF_KEY key;
1703 int key_size, length;
1704 int pass_len = strlen (pass) + (8 - (strlen (pass) % 8));
1705 int user_len = strlen (user);
1706 guint16 size16;
1707 char *in_ptr, *out_ptr;
1709 if (!parse_dh (data, &dh, &secret, &key_size))
1710 return NULL;
1711 BF_set_key (&key, key_size, secret);
1713 encrypted_pass = calloc (pass_len, 1);
1714 plain_pass = malloc (pass_len);
1715 if(!encrypted_pass || !plain_pass)
1716 return NULL;
1717 strncpy (plain_pass, pass, pass_len); /* yes, we really want strncpy here */
1718 out_ptr = (char*)encrypted_pass;
1719 in_ptr = (char*)plain_pass;
1721 for (length = pass_len; length; length -= 8, in_ptr += 8, out_ptr += 8)
1722 BF_ecb_encrypt ((unsigned char*)in_ptr, (unsigned char*)out_ptr, &key, BF_ENCRYPT);
1724 /* Create response */
1725 length = 2 + BN_num_bytes (dh->pub_key) + pass_len + user_len + 1;
1726 response = (char*)malloc (length);
1727 out_ptr = response;
1729 /* our key */
1730 size16 = htons ((guint16)BN_num_bytes (dh->pub_key));
1731 memcpy (out_ptr, &size16, sizeof(size16));
1732 out_ptr += 2;
1733 BN_bn2bin (dh->pub_key, (guchar*)out_ptr);
1734 out_ptr += BN_num_bytes (dh->pub_key);
1736 /* username */
1737 memcpy (out_ptr, user, user_len + 1);
1738 out_ptr += user_len + 1;
1740 /* pass */
1741 memcpy (out_ptr, encrypted_pass, pass_len);
1743 ret = g_base64_encode ((const guchar*)response, length);
1745 DH_free (dh);
1746 free (plain_pass);
1747 free (encrypted_pass);
1748 free (secret);
1749 free (response);
1751 return ret;
1754 char *
1755 encode_sasl_pass_aes (char *user, char *pass, char *data)
1757 DH *dh;
1758 AES_KEY key;
1759 char *response = NULL;
1760 char *out_ptr, *ret = NULL;
1761 unsigned char *secret, *ptr;
1762 unsigned char *encrypted_userpass, *plain_userpass;
1763 int key_size, length;
1764 guint16 size16;
1765 unsigned char iv[16], iv_copy[16];
1766 int user_len = strlen (user) + 1;
1767 int pass_len = strlen (pass) + 1;
1768 int len = user_len + pass_len;
1769 int padlen = 16 - (len % 16);
1770 int userpass_len = len + padlen;
1772 if (!parse_dh (data, &dh, &secret, &key_size))
1773 return NULL;
1775 encrypted_userpass = (guchar*)malloc (userpass_len);
1776 memset (encrypted_userpass, 0, userpass_len);
1777 plain_userpass = (guchar*)malloc (userpass_len);
1778 memset (plain_userpass, 0, userpass_len);
1780 /* create message */
1781 /* format of: <username>\0<password>\0<padding> */
1782 ptr = plain_userpass;
1783 memcpy (ptr, user, user_len);
1784 ptr += user_len;
1785 memcpy (ptr, pass, pass_len);
1786 ptr += pass_len;
1787 if (padlen)
1789 /* Padding */
1790 unsigned char randbytes[16];
1791 if (!RAND_bytes (randbytes, padlen))
1792 goto end;
1794 memcpy (ptr, randbytes, padlen);
1797 if (!RAND_bytes (iv, sizeof (iv)))
1798 goto end;
1800 memcpy (iv_copy, iv, sizeof(iv));
1802 /* Encrypt */
1803 AES_set_encrypt_key (secret, key_size * 8, &key);
1804 AES_cbc_encrypt(plain_userpass, encrypted_userpass, userpass_len, &key, iv_copy, AES_ENCRYPT);
1806 /* Create response */
1807 /* format of: <size pubkey><pubkey><iv (always 16 bytes)><ciphertext> */
1808 length = 2 + key_size + sizeof(iv) + userpass_len;
1809 response = (char*)malloc (length);
1810 out_ptr = response;
1812 /* our key */
1813 size16 = htons ((guint16)key_size);
1814 memcpy (out_ptr, &size16, sizeof(size16));
1815 out_ptr += 2;
1816 BN_bn2bin (dh->pub_key, (guchar*)out_ptr);
1817 out_ptr += key_size;
1819 /* iv */
1820 memcpy (out_ptr, iv, sizeof(iv));
1821 out_ptr += sizeof(iv);
1823 /* userpass */
1824 memcpy (out_ptr, encrypted_userpass, userpass_len);
1826 ret = g_base64_encode ((const guchar*)response, length);
1828 end:
1829 DH_free (dh);
1830 free (plain_userpass);
1831 free (encrypted_userpass);
1832 free (secret);
1833 if (response)
1834 free (response);
1836 return ret;
1838 #endif
1840 #ifdef USE_OPENSSL
1841 static char *
1842 str_sha256hash (char *string)
1844 int i;
1845 unsigned char hash[SHA256_DIGEST_LENGTH];
1846 char buf[SHA256_DIGEST_LENGTH * 2 + 1]; /* 64 digit hash + '\0' */
1847 SHA256_CTX sha256;
1849 SHA256_Init (&sha256);
1850 SHA256_Update (&sha256, string, strlen (string));
1851 SHA256_Final (hash, &sha256);
1853 for (i = 0; i < SHA256_DIGEST_LENGTH; i++)
1855 sprintf (buf + (i * 2), "%02x", hash[i]);
1858 buf[SHA256_DIGEST_LENGTH * 2] = 0;
1860 return g_strdup (buf);
1864 * \brief Generate CHALLENGEAUTH response for QuakeNet login.
1866 * \param username QuakeNet user name
1867 * \param password password for the user
1868 * \param challenge the CHALLENGE response we got from Q
1870 * After a successful connection to QuakeNet a CHALLENGE is requested from Q.
1871 * Generate the CHALLENGEAUTH response from this CHALLENGE and our user
1872 * credentials as per the
1873 * <a href="http://www.quakenet.org/development/challengeauth">CHALLENGEAUTH</a>
1874 * docs. As for using OpenSSL HMAC, see
1875 * <a href="http://www.askyb.com/cpp/openssl-hmac-hasing-example-in-cpp/">example 1</a>,
1876 * <a href="http://stackoverflow.com/questions/242665/understanding-engine-initialization-in-openssl">example 2</a>.
1878 char *
1879 challengeauth_response (char *username, char *password, char *challenge)
1881 int i;
1882 char *user;
1883 char *pass;
1884 char *passhash;
1885 char *key;
1886 char *keyhash;
1887 unsigned char *digest;
1888 GString *buf = g_string_new_len (NULL, SHA256_DIGEST_LENGTH * 2);
1890 user = g_strdup (username);
1891 *user = rfc_tolower (*username); /* convert username to lowercase as per the RFC*/
1893 pass = g_strndup (password, 10); /* truncate to 10 characters */
1894 passhash = str_sha256hash (pass);
1895 g_free (pass);
1897 key = g_strdup_printf ("%s:%s", user, passhash);
1898 g_free (user);
1899 g_free (passhash);
1901 keyhash = str_sha256hash (key);
1902 g_free (key);
1904 digest = HMAC (EVP_sha256 (), keyhash, strlen (keyhash), (unsigned char *) challenge, strlen (challenge), NULL, NULL);
1905 g_free (keyhash);
1907 for (i = 0; i < SHA256_DIGEST_LENGTH; i++)
1909 g_string_append_printf (buf, "%02x", (unsigned int) digest[i]);
1912 digest = (unsigned char *) g_string_free (buf, FALSE);
1914 return (char *) digest;
1916 #endif