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