1 /*
2 Copyright (C) 2014-2017,2018 John E. Davis
3
4 This file is part of the S-Lang Library.
5
6 The S-Lang Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License as
8 published by the Free Software Foundation; either version 2 of the
9 License, or (at your option) any later version.
10
11 The S-Lang Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this library; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
19 USA.
20 */
21 #include <stdio.h>
22 #include <string.h>
23 #include <ctype.h>
24 #include <slang.h>
25
26 SLANG_MODULE(base64);
27
28 static int Base64_Type_Id = 0;
29 typedef struct _Base64_Type Base64_Type;
30 struct _Base64_Type
31 {
32 int type;
33 #define B64_TYPE_ENCODER 1
34 #define B64_TYPE_DECODER 2
35 SLang_Name_Type *callback;
36 SLang_Any_Type *callback_data;
37 unsigned char *buffer; /* malloced buffer_size + overhead */
38 #define B64_ENCODE_BUFFER_SIZE 76 /* multiple of 4 */
39 #define B64_DECODE_BUFFER_SIZE 512
40 unsigned int buffer_size;
41 unsigned int num_buffered;
42 unsigned char smallbuf[4];
43 unsigned int smallbuf_len;
44 #define B64_CLOSED 0x1
45 #define B64_INVALID 0x2
46 int flags;
47 };
48
check_b64_type(Base64_Type * b64,int type,int err)49 static int check_b64_type (Base64_Type *b64, int type, int err)
50 {
51 if (type && (b64->type != type))
52 {
53 if (err)
54 SLang_verror (SL_InvalidParm_Error, "Expected a base64 %s type",
55 (type == B64_TYPE_ENCODER) ? "encoder" : "decoder");
56 return -1;
57 }
58
59 if (b64->flags & (B64_INVALID|B64_CLOSED))
60 {
61 if (err)
62 SLang_verror (SL_InvalidParm_Error, "Base64 encoder is invalid or closed");
63 return -1;
64 }
65 return 0;
66 }
67
b64_partial_free(Base64_Type * b64)68 static void b64_partial_free (Base64_Type *b64)
69 {
70 if (b64->callback_data != NULL) SLang_free_anytype (b64->callback_data);
71 b64->callback_data = NULL;
72 if (b64->callback != NULL) SLang_free_function (b64->callback);
73 b64->callback = NULL;
74 if (b64->buffer != NULL) SLfree ((char *)b64->buffer);
75 b64->buffer = NULL;
76 b64->flags |= B64_INVALID;
77 }
78
create_b64_buffer(Base64_Type * b64)79 static int create_b64_buffer (Base64_Type *b64)
80 {
81 b64->num_buffered = 0;
82 if (NULL == (b64->buffer = (unsigned char *)SLmalloc (b64->buffer_size + 1)))
83 return -1;
84 return 0;
85 }
86
execute_callback(Base64_Type * b64)87 static int execute_callback (Base64_Type *b64)
88 {
89 SLang_BString_Type *b;
90
91 if (NULL == (b = SLbstring_create_malloced (b64->buffer, b64->num_buffered, 0)))
92 return -1;
93
94 if (-1 == create_b64_buffer (b64))
95 {
96 SLbstring_free (b);
97 return -1;
98 }
99
100 if ((-1 == SLang_start_arg_list ())
101 || (-1 == SLang_push_anytype (b64->callback_data))
102 || (-1 == SLang_push_bstring (b))
103 || (-1 == SLang_end_arg_list ())
104 || (-1 == SLexecute_function (b64->callback)))
105 {
106 b64->flags |= B64_INVALID;
107 SLbstring_free (b);
108 return -1;
109 }
110 SLbstring_free (b);
111 return 0;
112 }
113
free_b64_type(Base64_Type * b64)114 static void free_b64_type (Base64_Type *b64)
115 {
116 if (b64 == NULL)
117 return;
118 b64_partial_free (b64);
119 SLfree ((char *)b64);
120 }
121
122 /* rfc1521:
123 *
124 * The encoding process represents 24-bit groups of input bits as output
125 * strings of 4 encoded characters. Proceeding from left to right, a
126 * 24-bit input group is formed by concatenating 3 8-bit input groups.
127 * These 24 bits are then treated as 4 concatenated 6-bit groups, each
128 * of which is translated into a single digit in the base64 alphabet.
129 * When encoding a bit stream via the base64 encoding, the bit stream
130 * must be presumed to be ordered with the most-significant-bit first.
131 */
132
133 static char Base64_Bit_Mapping[64] =
134 {
135 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
136 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
137 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
138 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/',
139 };
140
141 /* The output stream (encoded bytes) must be represented in lines of no
142 * more than 76 characters each. All line breaks or other characters
143 * not found in Table 1 must be ignored by decoding software. In base64
144 * data, characters other than those in Table 1, line breaks, and other
145 * white space probably indicate a transmission error, about which a
146 * warning message or even a message rejection might be appropriate
147 * under some circumstances.
148 */
149
b64_encode_triplet(Base64_Type * b64,unsigned char * str)150 static int b64_encode_triplet (Base64_Type *b64, unsigned char *str)
151 {
152 unsigned char *encode_buf;
153 unsigned char ch0, ch1, ch2;
154
155 encode_buf = b64->buffer + b64->num_buffered;
156
157 ch0 = str[0];
158 ch1 = str[1];
159 ch2 = str[2];
160 encode_buf[0] = Base64_Bit_Mapping[ch0>>2];
161 encode_buf[1] = Base64_Bit_Mapping[((ch0&0x3)<<4) | (ch1>>4)];
162 encode_buf[2] = Base64_Bit_Mapping[((ch1&0xF)<<2) | (ch2>>6)];
163 encode_buf[3] = Base64_Bit_Mapping[ch2&0x3F];
164 b64->num_buffered += 4;
165 if (b64->num_buffered < b64->buffer_size)
166 return 0;
167 encode_buf[4] = 0;
168 return execute_callback (b64);
169 }
170
b64_encode_accumulate(Base64_Type * b64,unsigned char * line,unsigned int len)171 static int b64_encode_accumulate (Base64_Type *b64, unsigned char *line, unsigned int len)
172 {
173 unsigned char *linemax;
174 unsigned int i;
175
176 linemax = line + len;
177
178 i = b64->smallbuf_len;
179 if (i && (i < 3))
180 {
181 if (line < linemax)
182 b64->smallbuf[i++] = *line++;
183 if ((i < 3) && (line < linemax))
184 b64->smallbuf[i++] = *line++;
185
186 if (i < 3)
187 {
188 b64->smallbuf_len = i;
189 return 0;
190 }
191 if (-1 == b64_encode_triplet (b64, b64->smallbuf))
192 return -1;
193 b64->smallbuf_len = 0;
194 }
195
196 while (line + 3 <= linemax)
197 {
198 if (-1 == b64_encode_triplet (b64, line))
199 return -1;
200 line += 3;
201 }
202
203 i = 0;
204 while (line < linemax)
205 b64->smallbuf[i++] = *line++;
206 b64->smallbuf_len = i;
207 return 0;
208 }
209
b64_encoder_accumulate_intrin(Base64_Type * b64,SLang_BString_Type * bstr)210 static void b64_encoder_accumulate_intrin (Base64_Type *b64, SLang_BString_Type *bstr)
211 {
212 unsigned char *data;
213 SLstrlen_Type len;
214
215 if (-1 == check_b64_type (b64, B64_TYPE_ENCODER, 1))
216 return;
217
218 if (NULL == (data = SLbstring_get_pointer (bstr, &len)))
219 return;
220
221 (void) b64_encode_accumulate (b64, data, len);
222 }
223
b64_encoder_close_intrin(Base64_Type * b64)224 static void b64_encoder_close_intrin (Base64_Type *b64)
225 {
226 if (-1 == check_b64_type (b64, B64_TYPE_ENCODER, 0))
227 goto close_encoder;
228
229 /* Handle the padding */
230 if (b64->smallbuf_len)
231 {
232 unsigned char *encode_buf = b64->buffer + b64->num_buffered;
233 unsigned char ch0;
234
235 ch0 = b64->smallbuf[0];
236 encode_buf[0] = Base64_Bit_Mapping[ch0>>2];
237 if (b64->smallbuf_len > 1)
238 {
239 unsigned char ch1 = b64->smallbuf[1];
240 encode_buf[1] = Base64_Bit_Mapping[((ch0&0x3)<<4) | (ch1>>4)];
241 encode_buf[2] = Base64_Bit_Mapping[((ch1&0xF)<<2)];
242 }
243 else
244 {
245 encode_buf[1] = Base64_Bit_Mapping[((ch0&0x3)<<4)];
246 encode_buf[2] = '=';
247 }
248 encode_buf[3] = '=';
249 b64->num_buffered += 4;
250 b64->smallbuf_len = 0;
251 if (b64->num_buffered >= b64->buffer_size)
252 (void) execute_callback (b64);
253 }
254
255 if (b64->num_buffered)
256 (void) execute_callback (b64);
257
258 close_encoder:
259 b64_partial_free (b64);
260 b64->flags |= B64_CLOSED;
261 }
262
263 static unsigned char Base64_Decode_Map [256] =
264 {
265 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
266 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
267 255,255,255,255,255,255,255,255,255,255,255, 62,255,255,255, 63,
268 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255,255,255,255,
269 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
270 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255,
271 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
272 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255,
273 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
274 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
275 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
276 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
277 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
278 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
279 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
280 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255
281 };
282
b64_decode_quartet(Base64_Type * b64,unsigned char * str4)283 static int b64_decode_quartet (Base64_Type *b64, unsigned char *str4)
284 {
285 unsigned char b0, b1, b2, b3;
286 unsigned char bytes_buf[3], *bytes;
287 unsigned int bad;
288 unsigned int n;
289
290 if (0xFF == (b0 = Base64_Decode_Map[str4[0]]))
291 {
292 bad = 0;
293 goto return_error;
294 }
295 if (0xFF == (b1 = Base64_Decode_Map[str4[1]]))
296 {
297 bad = 1;
298 goto return_error;
299 }
300
301 n = 3;
302
303 b2 = Base64_Decode_Map[str4[2]];
304 b3 = Base64_Decode_Map[str4[3]];
305 if ((b2 == 0xFF) || (b3 == 0xFF))
306 {
307 if (b2 == 0xFF)
308 {
309 if ('=' != str4[2])
310 {
311 bad = 2;
312 goto return_error;
313 }
314 n = 1;
315 }
316 else n = 2;
317
318 if (str4[3] != '=')
319 {
320 SLang_verror (SL_Data_Error, "Illegally padded base64 sequence seen");
321 return -1;
322 }
323 }
324
325 if (b64->num_buffered + n < b64->buffer_size)
326 bytes = b64->buffer + b64->num_buffered;
327 else
328 bytes = bytes_buf;
329
330 bytes[0] = (b0 << 2) | (b1>>4);
331 if (n > 1)
332 {
333 bytes[1] = (b1 << 4) | (b2 >> 2);
334 if (n > 2)
335 bytes[2] = (b2 << 6) | b3;
336 }
337
338 if (bytes != bytes_buf)
339 {
340 b64->num_buffered += n;
341 return 0;
342 }
343
344 while (n && (b64->num_buffered < b64->buffer_size))
345 {
346 b64->buffer[b64->num_buffered++] = *bytes++;
347 n--;
348 }
349 if (b64->num_buffered == b64->buffer_size)
350 {
351 if (-1 == execute_callback (b64))
352 return -1;
353 }
354 while (n)
355 {
356 b64->buffer[b64->num_buffered++] = *bytes++;
357 n--;
358 }
359 return 0;
360
361 return_error:
362 SLang_verror (SL_Data_Error, "Invalid character (0x%X) found in base64-encoded stream", str4[bad]);
363 return -1;
364 }
365
b64_decoder_accumulate_intrin(Base64_Type * b64,SLFUTURE_CONST char * str)366 static void b64_decoder_accumulate_intrin (Base64_Type *b64, SLFUTURE_CONST char *str)
367 {
368 unsigned int i;
369 unsigned char ch;
370 unsigned char *buf4;
371 if (-1 == check_b64_type (b64, B64_TYPE_DECODER, 1))
372 return;
373
374 #define NEXT_CHAR \
375 while (isspace ((unsigned char)*str)) str++; ch = *str++
376
377 NEXT_CHAR;
378 if (ch == 0)
379 return;
380
381 i = b64->smallbuf_len;
382 buf4 = b64->smallbuf;
383 if (i && (i < 4))
384 {
385 buf4[i++] = ch;
386 NEXT_CHAR;
387
388 if ((i < 4) && (ch != 0))
389 {
390 buf4[i++] = ch;
391 NEXT_CHAR;
392 }
393 if ((i < 4) && (ch != 0))
394 {
395 buf4[i++] = ch;
396 NEXT_CHAR;
397 }
398 if (i < 4)
399 {
400 b64->smallbuf_len = i;
401 return;
402 }
403 if (-1 == b64_decode_quartet (b64, buf4))
404 return;
405
406 b64->smallbuf_len = 0;
407 }
408
409 while (1)
410 {
411 if (ch == 0)
412 {
413 i = 0;
414 break;
415 }
416 buf4[0] = ch;
417 NEXT_CHAR;
418 if (ch == 0)
419 {
420 i = 1;
421 break;
422 }
423 buf4[1] = ch;
424 NEXT_CHAR;
425 if (ch == 0)
426 {
427 i = 2;
428 break;
429 }
430 buf4[2] = ch;
431 NEXT_CHAR;
432 if (ch == 0)
433 {
434 i = 3;
435 break;
436 }
437 buf4[3] = ch;
438 if (-1 == b64_decode_quartet (b64, buf4))
439 return;
440 NEXT_CHAR;
441 }
442
443 b64->smallbuf_len = i;
444 }
445
b64_decoder_close_intrin(Base64_Type * b64)446 static void b64_decoder_close_intrin (Base64_Type *b64)
447 {
448 SLFUTURE_CONST char *pad = "====";
449
450 if (-1 == check_b64_type (b64, B64_TYPE_DECODER, 0))
451 goto close_decoder;
452
453 /* silently add pad characters if necessary */
454 if (b64->smallbuf_len)
455 (void) b64_decoder_accumulate_intrin (b64, pad + b64->smallbuf_len);
456
457 if (b64->num_buffered)
458 (void) execute_callback (b64);
459
460 close_decoder:
461 b64_partial_free (b64);
462 b64->flags |= B64_CLOSED;
463 }
464
new_b64_type(int type)465 static void new_b64_type (int type)
466 {
467 Base64_Type *b64;
468 SLang_MMT_Type *mmt;
469
470 if (NULL == (b64 = (Base64_Type *)SLmalloc(sizeof(Base64_Type))))
471 return;
472 memset ((char *)b64, 0, sizeof(Base64_Type));
473
474 b64->type = type;
475 if (type == B64_TYPE_ENCODER)
476 b64->buffer_size = B64_ENCODE_BUFFER_SIZE;
477 else
478 b64->buffer_size = B64_DECODE_BUFFER_SIZE;
479
480 if (-1 == create_b64_buffer(b64))
481 {
482 SLfree ((char *)b64);
483 return;
484 }
485
486 if ((-1 == SLang_pop_anytype (&b64->callback_data))
487 || (NULL == (b64->callback = SLang_pop_function ()))
488 || (NULL == (mmt = SLang_create_mmt (Base64_Type_Id, (VOID_STAR)b64))))
489 {
490 free_b64_type (b64);
491 return;
492 }
493
494 if (-1 == SLang_push_mmt (mmt))
495 SLang_free_mmt (mmt);
496 }
497
new_b64_encoder_intrin(void)498 static void new_b64_encoder_intrin (void)
499 {
500 new_b64_type (B64_TYPE_ENCODER);
501 }
502
new_b64_decoder_intrin(void)503 static void new_b64_decoder_intrin (void)
504 {
505 new_b64_type (B64_TYPE_DECODER);
506 }
507
508 #define DUMMY_B64_TYPE ((SLtype)-1)
509 static SLang_Intrin_Fun_Type Module_Intrinsics [] =
510 {
511 MAKE_INTRINSIC_0("_base64_encoder_new", new_b64_encoder_intrin, SLANG_VOID_TYPE),
512 MAKE_INTRINSIC_2("_base64_encoder_accumulate", b64_encoder_accumulate_intrin, SLANG_VOID_TYPE, DUMMY_B64_TYPE, SLANG_BSTRING_TYPE),
513 MAKE_INTRINSIC_1("_base64_encoder_close", b64_encoder_close_intrin, SLANG_VOID_TYPE, DUMMY_B64_TYPE),
514 MAKE_INTRINSIC_0("_base64_decoder_new", new_b64_decoder_intrin, SLANG_VOID_TYPE),
515 MAKE_INTRINSIC_2("_base64_decoder_accumulate", b64_decoder_accumulate_intrin, SLANG_VOID_TYPE, DUMMY_B64_TYPE, SLANG_STRING_TYPE),
516 MAKE_INTRINSIC_1("_base64_decoder_close", b64_decoder_close_intrin, SLANG_VOID_TYPE, DUMMY_B64_TYPE),
517 SLANG_END_INTRIN_FUN_TABLE
518 };
519
destroy_b64(SLtype type,VOID_STAR f)520 static void destroy_b64 (SLtype type, VOID_STAR f)
521 {
522 (void) type;
523 free_b64_type ((Base64_Type *)f);
524 }
525
register_b64_type(void)526 static int register_b64_type (void)
527 {
528 SLang_Class_Type *cl;
529
530 if (Base64_Type_Id != 0)
531 return 0;
532
533 if (NULL == (cl = SLclass_allocate_class ("Base64_Type")))
534 return -1;
535
536 if (-1 == SLclass_set_destroy_function (cl, destroy_b64))
537 return -1;
538
539 /* By registering as SLANG_VOID_TYPE, slang will dynamically allocate a
540 * type.
541 */
542 if (-1 == SLclass_register_class (cl, SLANG_VOID_TYPE, sizeof (Base64_Type), SLANG_CLASS_TYPE_MMT))
543 return -1;
544
545 Base64_Type_Id = SLclass_get_class_id (cl);
546 if (-1 == SLclass_patch_intrin_fun_table1 (Module_Intrinsics, DUMMY_B64_TYPE, Base64_Type_Id))
547 return -1;
548
549 return 0;
550 }
551
init_base64_module_ns(char * ns_name)552 int init_base64_module_ns (char *ns_name)
553 {
554 SLang_NameSpace_Type *ns = SLns_create_namespace (ns_name);
555 if (ns == NULL)
556 return -1;
557
558 if (-1 == register_b64_type ())
559 return -1;
560
561 if (-1 == SLns_add_intrin_fun_table (ns, Module_Intrinsics, NULL))
562 return -1;
563
564 return 0;
565 }
566
567 /* This function is optional */
deinit_base64_module(void)568 void deinit_base64_module (void)
569 {
570 }
571