1 /*
2 
3   silcstrutil.c
4 
5   Author: Pekka Riikonen <priikone@silcnet.org>
6 
7   Copyright (C) 2002 - 2007 Pekka Riikonen
8 
9   The contents of this file are subject to one of the Licenses specified
10   in the COPYING file;  You may not use this file except in compliance
11   with the License.
12 
13   The software distributed under the License is distributed on an "AS IS"
14   basis, in the hope that it will be useful, but WITHOUT WARRANTY OF ANY
15   KIND, either expressed or implied.  See the COPYING file for more
16   information.
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 
silc_base64_encode(unsigned char * data,SilcUInt32 len)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 
silc_base64_encode_file(unsigned char * data,SilcUInt32 data_len)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 
silc_base64_decode(unsigned char * base64,SilcUInt32 base64_len,SilcUInt32 * ret_len)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 
silc_strncat(char * dest,SilcUInt32 dest_size,const char * src,SilcUInt32 src_len)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 
silc_string_compare(char * string1,char * string2)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 
silc_string_split(const char * string,char ch,int * ret_count)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 
silc_string_regexify(const char * string)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 
silc_string_regex_combine(const char * string1,const char * string2)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 
silc_string_regex_match(const char * regex,const char * string)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 
silc_string_match(const char * string1,const char * string2)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 }
426