Merge branch 'topic/mm-fixes' of git://208.110.73.182/silc into silc.1.1.branch
[silc.git] / lib / silcutil / silcstrutil.c
1 /*
2
3   silcstrutil.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2002 - 2007 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19 /* $Id$ */
20
21 #include "silc.h"
22 #include "silcstrutil.h"
23
24 static unsigned char pem_enc[64] =
25 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
26
27 /* Encodes data into Base 64 encoding. Returns NULL terminated base 64 encoded
28    data string. */
29
30 char *silc_base64_encode(unsigned char *data, SilcUInt32 len)
31 {
32   int i, j;
33   SilcUInt32 bits, c, char_count;
34   char *pem;
35
36   char_count = 0;
37   bits = 0;
38   j = 0;
39
40   pem = silc_calloc(((len * 8 + 5) / 6) + 5, sizeof(*pem));
41
42   for (i = 0; i < len; i++) {
43     c = data[i];
44     bits += c;
45     char_count++;
46
47     if (char_count == 3) {
48       pem[j++] = pem_enc[bits  >> 18];
49       pem[j++] = pem_enc[(bits >> 12) & 0x3f];
50       pem[j++] = pem_enc[(bits >> 6)  & 0x3f];
51       pem[j++] = pem_enc[bits & 0x3f];
52       bits = 0;
53       char_count = 0;
54     } else {
55       bits <<= 8;
56     }
57   }
58
59   if (char_count != 0) {
60     bits <<= 16 - (8 * char_count);
61     pem[j++] = pem_enc[bits >> 18];
62     pem[j++] = pem_enc[(bits >> 12) & 0x3f];
63
64     if (char_count == 1) {
65       pem[j++] = '=';
66       pem[j] = '=';
67     } else {
68       pem[j++] = pem_enc[(bits >> 6) & 0x3f];
69       pem[j] = '=';
70     }
71   }
72
73   return pem;
74 }
75
76 /* Same as above but puts newline ('\n') every 72 characters. */
77
78 char *silc_base64_encode_file(unsigned char *data, SilcUInt32 data_len)
79 {
80   int i, j;
81   SilcUInt32 len, cols;
82   char *pem, *pem2;
83
84   pem = silc_base64_encode(data, data_len);
85   len = strlen(pem);
86
87   pem2 = silc_calloc(len + (len / 72) + 1, sizeof(*pem2));
88
89   for (i = 0, j = 0, cols = 1; i < len; i++, cols++) {
90     if (cols == 72) {
91       pem2[i] = '\n';
92       cols = 0;
93       len++;
94       continue;
95     }
96
97     pem2[i] = pem[j++];
98   }
99
100   silc_free(pem);
101   return pem2;
102 }
103
104 /* Decodes Base 64 into data. Returns the decoded data. */
105
106 unsigned char *silc_base64_decode(unsigned char *base64,
107                                   SilcUInt32 base64_len,
108                                   SilcUInt32 *ret_len)
109 {
110   int i, j;
111   SilcUInt32 len, c, char_count, bits;
112   unsigned char *data;
113   static char ialpha[256], decoder[256];
114
115   for (i = 64 - 1; i >= 0; i--) {
116     ialpha[pem_enc[i]] = 1;
117     decoder[pem_enc[i]] = i;
118   }
119
120   char_count = 0;
121   bits = 0;
122   j = 0;
123
124   if (!base64_len)
125     len = strlen(base64);
126   else
127     len = base64_len;
128
129   data = silc_calloc(((len * 6) / 8), sizeof(*data));
130
131   for (i = 0; i < len; i++) {
132     c = base64[i];
133
134     if (c == '=')
135       break;
136
137     if (c > 127 || !ialpha[c])
138       continue;
139
140     bits += decoder[c];
141     char_count++;
142
143     if (char_count == 4) {
144       data[j++] = bits >> 16;
145       data[j++] = (bits >> 8) & 0xff;
146       data[j++] = bits & 0xff;
147       bits = 0;
148       char_count = 0;
149     } else {
150       bits <<= 6;
151     }
152   }
153
154   switch(char_count) {
155   case 1:
156     silc_free(data);
157     return NULL;
158     break;
159   case 2:
160     data[j++] = bits >> 10;
161     break;
162   case 3:
163     data[j++] = bits >> 16;
164     data[j++] = (bits >> 8) & 0xff;
165     break;
166   }
167
168   if (ret_len)
169     *ret_len = j;
170
171   return data;
172 }
173
174 /* Concatenates the `src' into `dest'.  If `src_len' is more than the
175    size of the `dest' (minus NULL at the end) the `src' will be
176    truncated to fit. */
177
178 char *silc_strncat(char *dest, SilcUInt32 dest_size,
179                    const char *src, SilcUInt32 src_len)
180 {
181   int len;
182
183   dest[dest_size - 1] = '\0';
184
185   len = dest_size - 1 - strlen(dest);
186   if (len < src_len) {
187     if (len > 0)
188       strncat(dest, src, len);
189   } else {
190     strncat(dest, src, src_len);
191   }
192
193   return dest;
194 }
195
196 /* Compares two strings. Strings may include wildcards '*' and '?'.
197    Returns TRUE if strings match. */
198
199 int silc_string_compare(char *string1, char *string2)
200 {
201   int i;
202   int slen1;
203   int slen2;
204   char *tmpstr1, *tmpstr2;
205
206   if (!string1 || !string2)
207     return FALSE;
208
209   slen1 = strlen(string1);
210   slen2 = strlen(string2);
211
212   /* See if they are same already */
213   if (!strncmp(string1, string2, slen2) && slen2 == slen1)
214     return TRUE;
215
216   if (slen2 < slen1)
217     if (!strchr(string1, '*'))
218       return FALSE;
219
220   /* Take copies of the original strings as we will change them */
221   tmpstr1 = silc_calloc(slen1 + 1, sizeof(char));
222   memcpy(tmpstr1, string1, slen1);
223   tmpstr2 = silc_calloc(slen2 + 1, sizeof(char));
224   memcpy(tmpstr2, string2, slen2);
225
226   for (i = 0; i < slen1; i++) {
227
228     /* * wildcard. Only one * wildcard is possible. */
229     if (tmpstr1[i] == '*')
230       if (!strncmp(tmpstr1, tmpstr2, i)) {
231         memset(tmpstr2, 0, slen2);
232         strncpy(tmpstr2, tmpstr1, i);
233         break;
234       }
235
236     /* ? wildcard */
237     if (tmpstr1[i] == '?') {
238       if (!strncmp(tmpstr1, tmpstr2, i)) {
239         if (!(slen1 < i + 1))
240           if (tmpstr1[i + 1] != '?' &&
241               tmpstr1[i + 1] != tmpstr2[i + 1])
242             continue;
243
244         if (!(slen1 < slen2))
245           tmpstr2[i] = '?';
246       }
247     }
248   }
249
250   /* if using *, remove it */
251   if (strchr(tmpstr1, '*'))
252     *strchr(tmpstr1, '*') = 0;
253
254   if (!strcmp(tmpstr1, tmpstr2)) {
255     memset(tmpstr1, 0, slen1);
256     memset(tmpstr2, 0, slen2);
257     silc_free(tmpstr1);
258     silc_free(tmpstr2);
259     return TRUE;
260   }
261
262   memset(tmpstr1, 0, slen1);
263   memset(tmpstr2, 0, slen2);
264   silc_free(tmpstr1);
265   silc_free(tmpstr2);
266   return FALSE;
267 }
268
269 /* Splits a string containing separator `ch' and returns an array of the
270    splitted strings. */
271
272 char **silc_string_split(const char *string, char ch, int *ret_count)
273 {
274   char **splitted = NULL, sep[1], *item, *cp;
275   int i = 0, len;
276
277   if (!string)
278     return NULL;
279   if (!ret_count)
280     return NULL;
281
282   splitted = silc_calloc(1, sizeof(*splitted));
283   if (!splitted)
284     return NULL;
285
286   if (!strchr(string, ch)) {
287     splitted[0] = silc_memdup(string, strlen(string));
288     *ret_count = 1;
289     return splitted;
290   }
291
292   sep[0] = ch;
293   cp = (char *)string;
294   while(cp) {
295     len = strcspn(cp, sep);
296     item = silc_memdup(cp, len);
297     if (!item) {
298       silc_free(splitted);
299       return NULL;
300     }
301
302     cp += len;
303     if (strlen(cp) == 0)
304       cp = NULL;
305     else
306       cp++;
307
308     splitted = silc_realloc(splitted, (i + 1) * sizeof(*splitted));
309     if (!splitted)
310       return NULL;
311     splitted[i++] = item;
312   }
313   *ret_count = i;
314
315   return splitted;
316 }
317
318 /* Inspects the `string' for wildcards and returns regex string that can
319    be used by the GNU regex library. A comma (`,') in the `string' means
320    that the string is list. */
321
322 char *silc_string_regexify(const char *string)
323 {
324   int i, len, count;
325   char *regex;
326
327   if (!string)
328     return NULL;
329
330   len = strlen(string);
331   count = 4;
332   for (i = 0; i < len; i++) {
333     if (string[i] == '*' || string[i] == '?')
334       count++;                  /* Will add '.' */
335     if (string[i] == ',')
336       count += 2;               /* Will add '|' and '^' */
337   }
338
339   regex = silc_calloc(len + count + 1, sizeof(*regex));
340   if (!regex)
341     return NULL;
342
343   count = 0;
344   regex[count++] = '(';
345   regex[count++] = '^';
346
347   for (i = 0; i < len; i++) {
348     if (string[i] == '*' || string[i] == '?') {
349       regex[count] = '.';
350       count++;
351     } else if (string[i] == ',') {
352       if (i + 2 == len)
353         continue;
354       regex[count++] = '|';
355       regex[count++] = '^';
356       continue;
357     }
358
359     regex[count++] = string[i];
360   }
361
362   regex[count++] = ')';
363   regex[count] = '$';
364
365   return regex;
366 }
367
368 /* Combines two regex strings into one regex string so that they can be
369    used as one by the GNU regex library. The `string2' is combine into
370    the `string1'. */
371
372 char *silc_string_regex_combine(const char *string1, const char *string2)
373 {
374   char *tmp;
375   int len1, len2;
376
377   if (!string1 || !string2)
378     return NULL;
379
380   len1 = strlen(string1);
381   len2 = strlen(string2);
382
383   tmp = silc_calloc(2 + len1 + len2, sizeof(*tmp));
384   strncat(tmp, string1, len1 - 2);
385   strncat(tmp, "|", 1);
386   strncat(tmp, string2 + 1, len2 - 1);
387
388   return tmp;
389 }
390
391 /* Matches the two strings and returns TRUE if the strings match. */
392
393 int silc_string_regex_match(const char *regex, const char *string)
394 {
395   regex_t preg;
396   int ret = FALSE;
397
398   if (regcomp(&preg, regex, REG_NOSUB | REG_EXTENDED) != 0)
399     return FALSE;
400
401   if (regexec(&preg, string, 0, NULL, 0) == 0)
402     ret = TRUE;
403
404   regfree(&preg);
405
406   return ret;
407 }
408
409 /* Do regex match to the two strings `string1' and `string2'. If the
410    `string2' matches the `string1' this returns TRUE. */
411
412 int silc_string_match(const char *string1, const char *string2)
413 {
414   char *s1;
415   int ret = FALSE;
416
417   if (!string1 || !string2)
418     return ret;
419
420   s1 = silc_string_regexify(string1);
421   ret = silc_string_regex_match(s1, string2);
422   silc_free(s1);
423
424   return ret;
425 }