d998ebbedc27b6dc005531d54284c52b1ebacad5
[silc.git] / lib / silcutil / silcstrutil.c
1 /*
2
3   silcstrutil.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2002 - 2004 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 "silcincludes.h"
22 #include "silcstrutil.h"
23
24 static unsigned char pem_enc[64] =
25 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
26
27 /* Encodes data into PEM encoding. Returns NULL terminated PEM encoded
28    data string. */
29
30 char *silc_pem_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_pem_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_pem_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 PEM into data. Returns the decoded data. */
105
106 unsigned char *silc_pem_decode(unsigned char *pem, SilcUInt32 pem_len,
107                                SilcUInt32 *ret_len)
108 {
109   int i, j;
110   SilcUInt32 len, c, char_count, bits;
111   unsigned char *data;
112   static char ialpha[256], decoder[256];
113
114   for (i = 64 - 1; i >= 0; i--) {
115     ialpha[pem_enc[i]] = 1;
116     decoder[pem_enc[i]] = i;
117   }
118
119   char_count = 0;
120   bits = 0;
121   j = 0;
122
123   if (!pem_len)
124     len = strlen(pem);
125   else
126     len = pem_len;
127
128   data = silc_calloc(((len * 6) / 8), sizeof(*data));
129
130   for (i = 0; i < len; i++) {
131     c = pem[i];
132
133     if (c == '=')
134       break;
135
136     if (c > 127 || !ialpha[c])
137       continue;
138
139     bits += decoder[c];
140     char_count++;
141
142     if (char_count == 4) {
143       data[j++] = bits >> 16;
144       data[j++] = (bits >> 8) & 0xff;
145       data[j++] = bits & 0xff;
146       bits = 0;
147       char_count = 0;
148     } else {
149       bits <<= 6;
150     }
151   }
152
153   switch(char_count) {
154   case 1:
155     silc_free(data);
156     return NULL;
157     break;
158   case 2:
159     data[j++] = bits >> 10;
160     break;
161   case 3:
162     data[j++] = bits >> 16;
163     data[j++] = (bits >> 8) & 0xff;
164     break;
165   }
166
167   if (ret_len)
168     *ret_len = j;
169
170   return data;
171 }
172
173 /* Mime constants and macros */
174 #define MIME_VERSION "MIME-Version: "
175 #define MIME_VERSION_LEN 14
176 #define MIME_CONTENT_TYPE "Content-Type: "
177 #define MIME_CONTENT_TYPE_LEN 14
178 #define MIME_TRANSFER_ENCODING "Content-Transfer-Encoding: "
179 #define MIME_TRANSFER_ENCODING_LEN 27
180
181 #define MIME_GET_FIELD(mime, mime_len, field, field_len,                \
182                        dest, dest_size)                                 \
183 do {                                                                    \
184   if (dest) {                                                           \
185     char *f = strstr(mime, field);                                      \
186     if (f) {                                                            \
187       int parse_len;                                                    \
188       f += field_len;                                                   \
189       parse_len = (mime_len - (f - (char *)mime));                      \
190       for (i = 0; i < parse_len; i++) {                                 \
191         if ((i == dest_size) ||                                         \
192             ((f[i] == '\n') &&                                          \
193                ((i == parse_len - 1) ||                                 \
194                   ((f[i+1] != ' ') && (f[i+1] != '\t')))) ||            \
195             ((f[i] == '\r') &&                                          \
196                ((i == parse_len - 1) || (f[i+1] == '\n')) &&            \
197                ((i >= parse_len - 2) ||                                 \
198                   ((f[i+2] != ' ') && (f[i+2] != '\t')))))              \
199           break;                                                        \
200         dest[i] = f[i];                                                 \
201       }                                                                 \
202     }                                                                   \
203   }                                                                     \
204 } while(0)
205
206 /* Parses MIME object and MIME header in it. */
207
208 bool
209 silc_mime_parse(const unsigned char *mime, SilcUInt32 mime_len,
210                 char *version, SilcUInt32 version_size,
211                 char *content_type, SilcUInt32 content_type_size,
212                 char *transfer_encoding, SilcUInt32 transfer_encoding_size,
213                 unsigned char **mime_data_ptr, SilcUInt32 *mime_data_len)
214 {
215   int i;
216   unsigned char *tmp;
217
218   /* Get the pointer to the data area in the object */
219   for (i = 0; i < mime_len; i++) {
220     if ((mime_len >= i + 4 &&
221          mime[i    ] == '\r' && mime[i + 1] == '\n' &&
222          mime[i + 2] == '\r' && mime[i + 3] == '\n') ||
223         (mime_len >= i + 2 &&
224          mime[i    ] == '\n' && mime[i + 1] == '\n'))
225       break;
226   }
227   if (i >= mime_len)
228     return FALSE;
229
230   if (mime_data_ptr)
231     *mime_data_ptr = (unsigned char *)mime + i +
232             (mime[i] == '\n' ? 2 : 4);
233   if (mime_data_len)
234     *mime_data_len = mime_len - (i + (mime[i] == '\n' ? 2 : 4));
235
236   /* Check for mandatory Content-Type field */
237   tmp = strstr(mime, MIME_CONTENT_TYPE);
238   if (!tmp || (tmp - mime) >= i)
239     return FALSE;
240
241   /* Get MIME version, Content-Type and Transfer Encoding fields */
242   MIME_GET_FIELD(mime, mime_len,
243                  MIME_VERSION, MIME_VERSION_LEN,
244                  version, version_size);
245   MIME_GET_FIELD(mime, mime_len,
246                  MIME_CONTENT_TYPE, MIME_CONTENT_TYPE_LEN,
247                  content_type, content_type_size);
248   MIME_GET_FIELD(mime, mime_len,
249                  MIME_TRANSFER_ENCODING, MIME_TRANSFER_ENCODING_LEN,
250                  transfer_encoding, transfer_encoding_size);
251
252   return TRUE;
253 }
254
255 /* Concatenates the `src' into `dest'.  If `src_len' is more than the
256    size of the `dest' (minus NULL at the end) the `src' will be
257    truncated to fit. */
258
259 char *silc_strncat(char *dest, SilcUInt32 dest_size,
260                    const char *src, SilcUInt32 src_len)
261 {
262   int len;
263
264   dest[dest_size - 1] = '\0';
265
266   len = dest_size - 1 - strlen(dest);
267   if (len < src_len) {
268     if (len > 0)
269       strncat(dest, src, len);
270   } else {
271     strncat(dest, src, src_len);
272   }
273
274   return dest;
275 }