Author: Bleep <helveytw@home.com>
[ircu2.10.12-pk.git] / ircd / ircd_string.c
1 /*
2  * IRC - Internet Relay Chat, ircd/ircd_string.c
3  * Copyright (C) 1999 Thomas Helvey
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 1, or (at your option)
8  * any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  *
19  * $Id$
20  */
21 #include "ircd_string.h"
22 #include "ircd_defs.h"
23 #include "ircd_chattr.h"
24 #include "ircd_log.h"
25 #include <assert.h>
26 #include <string.h>
27 #include <regex.h>
28 /*
29  * include the character attribute tables here
30  */
31 #include "chattr.tab.c"
32
33
34 /*
35  * Disallow a hostname label to contain anything but a [-a-zA-Z0-9].
36  * It may not start or end on a '.'.
37  * A label may not end on a '-', the maximum length of a label is
38  * 63 characters.
39  * On top of that (which seems to be the RFC) we demand that the
40  * top domain does not contain any digits.
41  */
42 static const char* hostExpr = "^([-0-9A-Za-z]*[0-9A-Za-z]\\.)+[A-Za-z]+$";
43 static regex_t hostRegex;
44
45 static const char* addrExpr =
46     "^((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\\.){1,3}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])$";
47 static regex_t addrRegex;
48
49 int init_string(void)
50 {
51   /*
52    * initialize matching expressions
53    * XXX - expressions MUST be correct, don't change expressions
54    * without testing them. Might be a good idea to exit if these fail,
55    * important code depends on them.
56    * TODO: use regerror for an error message
57    */
58   if (regcomp(&hostRegex, hostExpr, REG_EXTENDED | REG_NOSUB))
59     return 0;
60
61   if (regcomp(&addrRegex, addrExpr, REG_EXTENDED | REG_NOSUB))
62     return 0;
63   return 1;
64 }
65
66 int string_is_hostname(const char* str)
67 {
68   assert(0 != str);
69   return (strlen(str) <= HOSTLEN && 0 == regexec(&hostRegex, str, 0, 0, 0));
70 }
71
72 int string_is_address(const char* str)
73 {
74   assert(0 != str);
75   return (0 == regexec(&addrRegex, str, 0, 0, 0));
76 }
77
78 int string_has_wildcards(const char* str)
79 {
80   assert(0 != str);
81   for ( ; *str; ++str) {
82     if ('\\' == *str) {
83       if ('\0' == *++str)
84         break;
85     }
86     else if ('*' == *str || '?' == *str)
87       return 1;
88   }
89   return 0;
90 }
91
92 /*
93  * strtoken.c
94  *
95  * Walk through a string of tokens, using a set of separators.
96  * -argv 9/90
97  */
98 char* ircd_strtok(char **save, char *str, char *fs)
99 {
100   char *pos = *save;            /* keep last position across calls */
101   char *tmp;
102
103   if (str)
104     pos = str;                  /* new string scan */
105
106   while (pos && *pos && strchr(fs, *pos) != NULL)
107     pos++;                      /* skip leading separators */
108
109   if (!pos || !*pos)
110     return (pos = *save = NULL);        /* string contains only sep's */
111
112   tmp = pos;                    /* now, keep position of the token */
113
114   while (*pos && strchr(fs, *pos) == NULL)
115     pos++;                      /* skip content of the token */
116
117   if (*pos)
118     *pos++ = '\0';              /* remove first sep after the token */
119   else
120     pos = NULL;                 /* end of string */
121
122   *save = pos;
123   return (tmp);
124 }
125
126 /*
127  * canonize
128  *
129  * reduce a string of duplicate list entries to contain only the unique
130  * items.  Unavoidably O(n^2).
131  */
132 char* canonize(char* buffer)
133 {
134   static char cbuf[BUFSIZE];
135   char*       s;
136   char*       t;
137   char*       cp = cbuf;
138   int         l = 0;
139   char*       p = NULL;
140   char*       p2;
141
142   *cp = '\0';
143
144   for (s = ircd_strtok(&p, buffer, ","); s; s = ircd_strtok(&p, NULL, ","))
145   {
146     if (l)
147     {
148       p2 = NULL;
149       for (t = ircd_strtok(&p2, cbuf, ","); t; t = ircd_strtok(&p2, NULL, ","))
150         if (0 == ircd_strcmp(s, t))
151           break;
152         else if (p2)
153           p2[-1] = ',';
154     }
155     else
156       t = NULL;
157     if (!t)
158     {
159       if (l)
160         *(cp - 1) = ',';
161       else
162         l = 1;
163       strcpy(cp, s);
164       if (p)
165         cp += (p - s);
166     }
167     else if (p2)
168       p2[-1] = ',';
169   }
170   return cbuf;
171 }
172
173 /*
174  * ircd_strncpy - optimized strncpy
175  * This may not look like it would be the fastest possible way to do it,
176  * but it generally outperforms everything else on many platforms,
177  * including asm library versions and memcpy, if compiled with the
178  * optimizer on. (-O2 for gcc) --Bleep
179  */
180 char* ircd_strncpy(char* s1, const char* s2, size_t n)
181 {
182   char* endp = s1 + n;
183   char* s = s1;
184
185   assert(0 != s1);
186   assert(0 != s2);
187
188   while (s < endp && (*s++ = *s2++))
189     ;
190   return s1;
191 }
192
193
194 #ifndef FORCEINLINE
195 NTL_HDR_strChattr { NTL_SRC_strChattr }
196 NTL_HDR_strCasediff { NTL_SRC_strCasediff }
197 #endif /* !FORCEINLINE */
198
199 /*
200  * Other functions visible externally
201  */
202
203 int strnChattr(const char *s, size_t n)
204 {
205   const char *rs = s;
206   unsigned int x = ~0;
207   int r = n;
208   while (*rs && r--)
209     x &= IRCD_CharAttrTab[*rs++ - CHAR_MIN];
210   return x;
211 }
212
213 /*
214  * ircd_strcmp - case insensitive comparison of 2 strings
215  * NOTE: see ircd_chattr.h for notes on case mapping.
216  */
217 int ircd_strcmp(const char *a, const char *b)
218 {
219   const char* ra = a;
220   const char* rb = b;
221   while (ToLower(*ra) == ToLower(*rb)) {
222     if (!*ra++)
223       return 0;
224     else
225       ++rb;
226   }
227   return (*ra - *rb);
228 }
229
230 /*
231  * ircd_strncmp - counted case insensitive comparison of 2 strings
232  * NOTE: see ircd_chattr.h for notes on case mapping.
233  */
234 int ircd_strncmp(const char *a, const char *b, size_t n)
235 {
236   const char* ra = a;
237   const char* rb = b;
238   int left = n;
239   if (!left--)
240     return 0;
241   while (ToLower(*ra) == ToLower(*rb)) {
242     if (!*ra++ || !left--)
243       return 0;
244     else
245       ++rb;
246   }
247   return (*ra - *rb);
248 }
249
250 /*
251  * unique_name_vector - create a unique vector of names from
252  * a token separated list
253  * list   - [in]  a token delimited null terminated character array
254  * token  - [in]  the token to replace 
255  * vector - [out] vector of strings to be returned
256  * size   - [in]  maximum number of elements to place in vector
257  * Returns count of elements placed into the vector, if the list
258  * is an empty string { '\0' } 0 is returned.
259  * list, and vector must be non-null and size must be > 0 
260  * Empty strings <token><token> are not placed in the vector or counted.
261  * This function ignores all subsequent tokens when count == size
262  *
263  * NOTE: this function destroys it's input, do not use list after it
264  * is passed to this function
265  */
266 int unique_name_vector(char* list, char token, char** vector, int size)
267 {
268   int   i;
269   int   count = 0;
270   char* start = list;
271   char* end;
272
273   assert(0 != list);
274   assert(0 != vector);
275   assert(0 < size);
276  
277   /*
278    * ignore spurious tokens
279    */
280   while (token == *start)
281     ++start;
282
283   for (end = strchr(start, token); end; end = strchr(start, token)) {
284     *end++ = '\0';
285     /*
286      * ignore spurious tokens
287      */
288     while (token == *end)
289       ++end;
290     for (i = 0; i < count; ++i) {
291       if (0 == ircd_strcmp(vector[i], start))
292         break;
293     }
294     if (i == count) {
295       vector[count++] = start;
296       if (count == size)
297         return count;
298     }
299     start = end;
300   }
301   if (*start)
302     vector[count++] = start;
303
304   return count;
305 }
306
307 /*
308  * token_vector - create a vector of tokens from
309  * a token separated list
310  * list   - [in]  a token delimited null terminated character array
311  * token  - [in]  the token to replace 
312  * vector - [out] vector of strings to be returned
313  * size   - [in]  maximum number of elements to place in vector
314  * returns count of elements placed into the vector, if the list
315  * is an empty string { '\0' } 0 is returned.
316  * list, and vector must be non-null and size must be > 1 
317  * Empty tokens are counted and placed in the list
318  *
319  * NOTE: this function destroys it's input, do not use list after it
320  * is passed to this function
321  */
322 int token_vector(char* list, char token, char** vector, int size)
323 {
324   int   count = 0;
325   char* start = list;
326   char* end;
327
328   assert(0 != list);
329   assert(0 != vector);
330   assert(1 < size);
331  
332   vector[count++] = start;
333   for (end = strchr(start, token); end; end = strchr(start, token)) {
334     *end++ = '\0';
335     start = end;
336     if (*start) {
337       vector[count++] = start;
338       if (count < size)
339         continue;
340     }
341     break;
342   }
343   return count;
344
345
346 /*
347  * host_from_uh - get the host.domain part of a user@host.domain string
348  * ripped from get_sockhost
349  */
350 char* host_from_uh(char* host, const char* userhost, size_t n)
351 {
352   const char* s;
353
354   assert(0 != host);
355   assert(0 != userhost);
356
357   if ((s = strchr(userhost, '@')))
358     ++s;
359   else
360     s = userhost;
361   ircd_strncpy(host, s, n);
362   host[n] = '\0';
363   return host;
364 }
365
366 /* 
367  * this new faster inet_ntoa was ripped from:
368  * From: Thomas Helvey <tomh@inxpress.net>
369  */
370 static const char* IpQuadTab[] =
371 {
372     "0",   "1",   "2",   "3",   "4",   "5",   "6",   "7",   "8",   "9",
373    "10",  "11",  "12",  "13",  "14",  "15",  "16",  "17",  "18",  "19",
374    "20",  "21",  "22",  "23",  "24",  "25",  "26",  "27",  "28",  "29",
375    "30",  "31",  "32",  "33",  "34",  "35",  "36",  "37",  "38",  "39",
376    "40",  "41",  "42",  "43",  "44",  "45",  "46",  "47",  "48",  "49",
377    "50",  "51",  "52",  "53",  "54",  "55",  "56",  "57",  "58",  "59",
378    "60",  "61",  "62",  "63",  "64",  "65",  "66",  "67",  "68",  "69",
379    "70",  "71",  "72",  "73",  "74",  "75",  "76",  "77",  "78",  "79",
380    "80",  "81",  "82",  "83",  "84",  "85",  "86",  "87",  "88",  "89",
381    "90",  "91",  "92",  "93",  "94",  "95",  "96",  "97",  "98",  "99",
382   "100", "101", "102", "103", "104", "105", "106", "107", "108", "109",
383   "110", "111", "112", "113", "114", "115", "116", "117", "118", "119",
384   "120", "121", "122", "123", "124", "125", "126", "127", "128", "129",
385   "130", "131", "132", "133", "134", "135", "136", "137", "138", "139",
386   "140", "141", "142", "143", "144", "145", "146", "147", "148", "149",
387   "150", "151", "152", "153", "154", "155", "156", "157", "158", "159",
388   "160", "161", "162", "163", "164", "165", "166", "167", "168", "169",
389   "170", "171", "172", "173", "174", "175", "176", "177", "178", "179",
390   "180", "181", "182", "183", "184", "185", "186", "187", "188", "189",
391   "190", "191", "192", "193", "194", "195", "196", "197", "198", "199",
392   "200", "201", "202", "203", "204", "205", "206", "207", "208", "209",
393   "210", "211", "212", "213", "214", "215", "216", "217", "218", "219",
394   "220", "221", "222", "223", "224", "225", "226", "227", "228", "229",
395   "230", "231", "232", "233", "234", "235", "236", "237", "238", "239",
396   "240", "241", "242", "243", "244", "245", "246", "247", "248", "249",
397   "250", "251", "252", "253", "254", "255"
398 };
399
400 /*
401  * ircd_ntoa - rewrote and renamed yet again :) --Bleep
402  * inetntoa - in_addr to string
403  *      changed name to remove collision possibility and
404  *      so behaviour is guaranteed to take a pointer arg.
405  *      -avalon 23/11/92
406  *  inet_ntoa --  returned the dotted notation of a given
407  *      internet number
408  *      argv 11/90).
409  *  inet_ntoa --  its broken on some Ultrix/Dynix too. -avalon
410  */
411 const char* ircd_ntoa(const char* in)
412 {
413   static char buf[20];
414   return ircd_ntoa_r(buf, in);
415 }
416
417 /*
418  * reentrant version of above
419  */
420 const char* ircd_ntoa_r(char* buf, const char* in)
421 {
422   char*                p = buf;
423   const unsigned char* a = (const unsigned char*)in;
424   const char*          n;
425
426   assert(0 != buf);
427   assert(0 != in);
428
429   n = IpQuadTab[*a++];
430   while ((*p = *n++))
431     ++p;
432   *p++ = '.';
433   n = IpQuadTab[*a++];
434   while ((*p = *n++))
435     ++p;
436   *p++ = '.';
437   n = IpQuadTab[*a++];
438   while ((*p = *n++))
439     ++p;
440   *p++ = '.';
441   n = IpQuadTab[*a];
442   while ((*p = *n++))
443     ++p;
444   return buf;
445 }
446
447