summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar default2022-09-19 20:41:11 +0200
committerGravatar default2022-09-19 20:41:11 +0200
commit67288a763b8bceadbb128d2cf5bdc431ba8a686f (patch)
tree0a0bc1922016bbd3d3cd2ec3c80a033970280a9f
parentProject started. (diff)
downloadsnac2-67288a763b8bceadbb128d2cf5bdc431ba8a686f.tar.gz
snac2-67288a763b8bceadbb128d2cf5bdc431ba8a686f.tar.xz
snac2-67288a763b8bceadbb128d2cf5bdc431ba8a686f.zip
Imported xs.
-rw-r--r--xs.h714
-rw-r--r--xs_curl.h177
-rw-r--r--xs_encdec.h180
-rw-r--r--xs_httpd.h184
-rw-r--r--xs_io.h86
-rw-r--r--xs_json.h501
-rw-r--r--xs_openssl.h159
-rw-r--r--xs_socket.h95
8 files changed, 2096 insertions, 0 deletions
diff --git a/xs.h b/xs.h
new file mode 100644
index 0000000..40df89f
--- /dev/null
+++ b/xs.h
@@ -0,0 +1,714 @@
1/* copyright (c) 2022 grunfink - MIT license */
2
3#ifndef _XS_H
4
5#define _XS_H
6
7#include <stdio.h>
8#include <string.h>
9#include <stdlib.h>
10#include <ctype.h>
11#include <unistd.h>
12#include <stdarg.h>
13#include <signal.h>
14#include <errno.h>
15
16typedef enum {
17 XSTYPE_NULL = 0x18,
18 XSTYPE_TRUE = 0x06,
19 XSTYPE_FALSE = 0x15,
20 XSTYPE_SOL = 0x11,
21 XSTYPE_LITEM = 0x1f,
22 XSTYPE_EOL = 0x12,
23 XSTYPE_SOD = 0x13,
24 XSTYPE_DITEM = 0x1e,
25 XSTYPE_EOD = 0x14,
26 XSTYPE_NUMBER = 0x17,
27 XSTYPE_STRING = 0x02
28} xstype;
29
30
31/* dynamic strings */
32typedef char d_char;
33
34/* auto-destroyable strings */
35#define xs __attribute__ ((__cleanup__ (_xs_destroy))) d_char
36
37#define _XS_BLK_SIZE 16
38#define _xs_blk_size(sz) ((((sz) + _XS_BLK_SIZE) / _XS_BLK_SIZE) * _XS_BLK_SIZE)
39
40#define xs_debug() kill(getpid(), 5)
41xstype xs_type(const char *data);
42int xs_size(const char *data);
43d_char *xs_dup(const char *data);
44d_char *xs_expand(d_char *data, int offset, int size);
45d_char *xs_collapse(d_char *data, int offset, int size);
46d_char *xs_insert_m(d_char *data, int offset, const char *mem, int size);
47#define xs_insert(data, offset, data2) xs_insert_m(data, offset, data2, xs_size(data2))
48#define xs_append_m(data, mem, size) xs_insert_m(data, xs_size(data) - 1, mem, size)
49d_char *xs_str_new(const char *str);
50#define xs_str_cat(str1, str2) xs_insert(str1, xs_size(str1) - 1, str2)
51d_char *xs_replace(const char *str, const char *sfrom, const char *sto);
52d_char *xs_fmt(const char *fmt, ...);
53int xs_str_in(char *haystack, char *needle);
54int xs_startswith(char *str, char *prefix);
55int xs_endswith(char *str, char *postfix);
56d_char *xs_crop(d_char *str, int start, int end);
57d_char *xs_strip(d_char *str);
58d_char *xs_tolower(d_char *str);
59d_char *xs_list_new(void);
60d_char *xs_list_append_m(d_char *list, const char *mem, int dsz);
61#define xs_list_append(list, data) xs_list_append_m(list, data, xs_size(data))
62int xs_list_iter(char **list, char **value);
63int xs_list_len(char *list);
64char *xs_list_get(char *list, int num);
65int xs_list_in(char *list, char *val);
66d_char *xs_join(char *list, const char *sep);
67d_char *xs_splitn(const char *str, const char *sep, int times);
68#define xs_split(str, sep) xs_splitn(str, sep, 0xfffffff)
69d_char *xs_dict_new(void);
70d_char *xs_dict_append_m(d_char *dict, const char *key, const char *mem, int dsz);
71#define xs_dict_append(dict, key, data) xs_dict_append_m(dict, key, data, xs_size(data))
72int xs_dict_iter(char **dict, char **key, char **value);
73char *xs_dict_get(char *dict, const char *key);
74d_char *xs_dict_del(d_char *dict, const char *key);
75d_char *xs_dict_set(d_char *dict, const char *key, const char *data);
76d_char *xs_val_new(xstype t);
77d_char *xs_number_new(float f);
78float xs_get_number(char *v);
79
80extern int _xs_debug;
81
82
83#ifdef XS_IMPLEMENTATION
84
85int _xs_debug = 0;
86
87void _xs_destroy(char **var)
88{
89 if (_xs_debug)
90 printf("_xs_destroy %p\n", var);
91
92 free(*var);
93}
94
95xstype xs_type(const char *data)
96/* return the type of data */
97{
98 xstype t;
99
100 switch (data[0]) {
101 case XSTYPE_NULL:
102 case XSTYPE_TRUE:
103 case XSTYPE_FALSE:
104 case XSTYPE_SOL:
105 case XSTYPE_EOL:
106 case XSTYPE_SOD:
107 case XSTYPE_EOD:
108 case XSTYPE_LITEM:
109 case XSTYPE_DITEM:
110 case XSTYPE_NUMBER:
111 t = data[0];
112 break;
113 default:
114 t = XSTYPE_STRING;
115 break;
116 }
117
118 return t;
119}
120
121
122int xs_size(const char *data)
123/* returns the size of data in bytes */
124{
125 int len = 0;
126 int c = 0;
127 const char *p;
128
129 if (data == NULL)
130 return 0;
131
132 switch (xs_type(data)) {
133 case XSTYPE_STRING:
134 len = strlen(data) + 1;
135 break;
136
137 case XSTYPE_SOL:
138 /* look for a balanced EOL */
139 do {
140 c += data[len] == XSTYPE_SOL ? 1 : data[len] == XSTYPE_EOL ? -1 : 0;
141 len++;
142 } while (c);
143
144 break;
145
146 case XSTYPE_SOD:
147 /* look for a balanced EOD */
148 do {
149 c += data[len] == XSTYPE_SOD ? 1 : data[len] == XSTYPE_EOD ? -1 : 0;
150 len++;
151 } while (c);
152
153 break;
154
155 case XSTYPE_DITEM:
156 /* calculate the size of the key and the value */
157 p = data + 1;
158 p += xs_size(p);
159 p += xs_size(p);
160
161 len = p - data;
162
163 break;
164
165 case XSTYPE_LITEM:
166 /* it's the size of the item + 1 */
167 p = data + 1;
168 p += xs_size(p);
169
170 len = p - data;
171
172 break;
173
174 case XSTYPE_NUMBER:
175 len = sizeof(float) + 1;
176
177 break;
178
179 default:
180 len = 1;
181 }
182
183 return len;
184}
185
186
187d_char *xs_dup(const char *data)
188/* creates a duplicate of data */
189{
190 int sz = xs_size(data);
191 d_char *s = malloc(_xs_blk_size(sz));
192
193 memcpy(s, data, sz);
194
195 return s;
196}
197
198
199d_char *xs_expand(d_char *data, int offset, int size)
200/* opens a hole in data */
201{
202 int sz = xs_size(data);
203 int n;
204
205 /* open room */
206 if (sz == 0 || _xs_blk_size(sz) != _xs_blk_size(sz + size))
207 data = realloc(data, _xs_blk_size(sz + size));
208
209 /* move up the rest of the data */
210 for (n = sz + size - 1; n >= offset + size; n--)
211 data[n] = data[n - size];
212
213 return data;
214}
215
216
217d_char *xs_collapse(d_char *data, int offset, int size)
218/* shrinks data */
219{
220 int sz = xs_size(data);
221 int n;
222
223 /* don't try to delete beyond the limit */
224 if (offset + size > sz)
225 size = sz - offset;
226
227 /* shrink total size */
228 sz -= size;
229
230 for (n = offset; n < sz; n++)
231 data[n] = data[n + size];
232
233 return realloc(data, _xs_blk_size(sz));
234}
235
236
237d_char *xs_insert_m(d_char *data, int offset, const char *mem, int size)
238/* inserts a memory block */
239{
240 data = xs_expand(data, offset, size);
241 memcpy(data + offset, mem, size);
242
243 return data;
244}
245
246
247/** strings **/
248
249d_char *xs_str_new(const char *str)
250/* creates a new string */
251{
252 return xs_insert(NULL, 0, str ? str : "");
253}
254
255
256d_char *xs_replace(const char *str, const char *sfrom, const char *sto)
257/* replaces all occurrences of sfrom with sto in str */
258{
259 d_char *s;
260 char *ss;
261 int sfsz;
262
263 /* cache the sizes */
264 sfsz = strlen(sfrom);
265
266 /* create the new string */
267 s = xs_str_new(NULL);
268
269 while ((ss = strstr(str, sfrom)) != NULL) {
270 /* copy the first part */
271 s = xs_append_m(s, str, ss - str);
272
273 /* copy sto */
274 s = xs_str_cat(s, sto);
275
276 /* move forward */
277 str = ss + sfsz;
278 }
279
280 /* copy the rest */
281 s = xs_str_cat(s, str);
282
283 return s;
284}
285
286
287d_char *xs_fmt(const char *fmt, ...)
288/* formats a string with printf()-like marks */
289{
290 int n;
291 d_char *s = NULL;
292 va_list ap;
293
294 va_start(ap, fmt);
295 n = vsnprintf(s, 0, fmt, ap);
296 va_end(ap);
297
298 if (n > 0) {
299 n = _xs_blk_size(n + 1);
300 s = calloc(n, 1);
301
302 va_start(ap, fmt);
303 n = vsnprintf(s, n, fmt, ap);
304 va_end(ap);
305 }
306
307 return s;
308}
309
310
311int xs_str_in(char *haystack, char *needle)
312/* finds needle in haystack and returns the offset or -1 */
313{
314 char *s;
315 int r = -1;
316
317 if ((s = strstr(haystack, needle)) != NULL)
318 r = s - haystack;
319
320 return r;
321}
322
323
324int xs_startswith(char *str, char *prefix)
325/* returns true if str starts with prefix */
326{
327 return !!(xs_str_in(str, prefix) == 0);
328}
329
330
331int xs_endswith(char *str, char *postfix)
332/* returns true if str ends with postfix */
333{
334 int ssz = strlen(str);
335 int psz = strlen(postfix);
336
337 return !!(ssz >= psz && memcmp(postfix, str + ssz - psz, psz) == 0);
338}
339
340
341d_char *xs_crop(d_char *str, int start, int end)
342/* crops the d_char to be only from start to end */
343{
344 int sz = strlen(str);
345
346 if (end <= 0)
347 end = sz + end;
348
349 /* crop from the top */
350 str[end] = '\0';
351
352 /* crop from the bottom */
353 str = xs_collapse(str, 0, start);
354
355 return str;
356}
357
358
359d_char *xs_strip(d_char *str)
360/* strips the string of blanks from the start and the end */
361{
362 int s, e;
363
364 for (s = 0; isspace(str[s]); s++);
365 for (e = strlen(str); e > 0 && isspace(str[e - 1]); e--);
366
367 return xs_crop(str, s, e);
368}
369
370
371d_char *xs_tolower(d_char *str)
372/* convert to lowercase */
373{
374 int n;
375
376 for (n = 0; str[n]; n++)
377 str[n] = tolower(str[n]);
378
379 return str;
380}
381
382
383/** lists **/
384
385d_char *xs_list_new(void)
386/* creates a new list */
387{
388 d_char *list;
389
390 list = malloc(_xs_blk_size(2));
391 list[0] = XSTYPE_SOL;
392 list[1] = XSTYPE_EOL;
393
394 return list;
395}
396
397
398d_char *xs_list_append_m(d_char *list, const char *mem, int dsz)
399/* adds a memory block to the list */
400{
401 char c = XSTYPE_LITEM;
402 int lsz = xs_size(list);
403
404 list = xs_insert_m(list, lsz - 1, &c, 1);
405 list = xs_insert_m(list, lsz, mem, dsz);
406
407 return list;
408}
409
410
411int xs_list_iter(char **list, char **value)
412/* iterates a list value */
413{
414 int goon = 1;
415 char *p;
416
417 if (list == NULL || *list == NULL)
418 return 0;
419
420 p = *list;
421
422 /* skip a possible start of the list */
423 if (*p == XSTYPE_SOL)
424 p++;
425
426 /* an element? */
427 if (*p == XSTYPE_LITEM) {
428 p++;
429
430 *value = p;
431
432 p += xs_size(*value);
433 }
434 else {
435 /* end of list */
436 p++;
437 goon = 0;
438 }
439
440 /* store back the pointer */
441 *list = p;
442
443 return goon;
444}
445
446
447int xs_list_len(char *list)
448/* returns the number of elements in the list */
449{
450 int c = 0;
451 char *v;
452
453 while (xs_list_iter(&list, &v))
454 c++;
455
456 return c;
457}
458
459
460char *xs_list_get(char *list, int num)
461/* returns the element #num */
462{
463 char *v, *r = NULL;
464 int c = 0;
465
466 if (num < 0)
467 num = xs_list_len(list) + num;
468
469 while (xs_list_iter(&list, &v)) {
470 if (c == num) {
471 r = v;
472 break;
473 }
474 c++;
475 }
476
477 return r;
478}
479
480
481int xs_list_in(char *list, char *val)
482/* returns the position of val in list or -1 */
483{
484 int n = 0;
485 int r = -1;
486 char *v;
487 int sz = xs_size(val);
488
489 while (r == -1 && xs_list_iter(&list, &v)) {
490 int vsz = xs_size(v);
491
492 if (sz == vsz && memcmp(val, v, sz) == 0)
493 r = n;
494
495 n++;
496 }
497
498 return r;
499}
500
501
502d_char *xs_join(char *list, const char *sep)
503/* joins a list into a string */
504{
505 d_char *s;
506 char *v;
507 int c = 0;
508
509 s = xs_str_new(NULL);
510
511 while (xs_list_iter(&list, &v)) {
512 /* refuse to join non-string values */
513 if (xs_type(v) == XSTYPE_STRING) {
514 /* add the separator */
515 if (c != 0)
516 s = xs_str_cat(s, sep);
517
518 /* add the element */
519 s = xs_str_cat(s, v);
520
521 c++;
522 }
523 }
524
525 return s;
526}
527
528
529d_char *xs_splitn(const char *str, const char *sep, int times)
530/* splits a string into a list upto n times */
531{
532 int sz = strlen(sep);
533 char *ss;
534 d_char *list;
535
536 list = xs_list_new();
537
538 while (times > 0 && (ss = strstr(str, sep)) != NULL) {
539 /* add the first part (without the asciiz) */
540 list = xs_list_append_m(list, str, ss - str);
541
542 /* add the asciiz */
543 list = xs_str_cat(list, "");
544
545 /* skip past the separator */
546 str = ss + sz;
547
548 times--;
549 }
550
551 /* add the rest of the string */
552 list = xs_list_append(list, str);
553
554 return list;
555}
556
557
558/** dicts **/
559
560d_char *xs_dict_new(void)
561/* creates a new dict */
562{
563 d_char *dict;
564
565 dict = malloc(_xs_blk_size(2));
566 dict[0] = XSTYPE_SOD;
567 dict[1] = XSTYPE_EOD;
568
569 return dict;
570}
571
572
573d_char *xs_dict_append_m(d_char *dict, const char *key, const char *mem, int dsz)
574/* adds a memory block to the dict */
575{
576 char c = XSTYPE_DITEM;
577 int sz = xs_size(dict);
578 int ksz = xs_size(key);
579
580 dict = xs_insert_m(dict, sz - 1, &c, 1);
581 dict = xs_insert_m(dict, sz, key, ksz);
582 dict = xs_insert_m(dict, sz + ksz, mem, dsz);
583
584 return dict;
585}
586
587
588int xs_dict_iter(char **dict, char **key, char **value)
589/* iterates a dict value */
590{
591 int goon = 1;
592 char *p;
593
594 if (dict == NULL || *dict == NULL)
595 return 0;
596
597 p = *dict;
598
599 /* skip a possible start of the list */
600 if (*p == XSTYPE_SOD)
601 p++;
602
603 /* an element? */
604 if (*p == XSTYPE_DITEM) {
605 p++;
606
607 *key = p;
608 p += xs_size(*key);
609
610 *value = p;
611 p += xs_size(*value);
612 }
613 else {
614 /* end of list */
615 p++;
616 goon = 0;
617 }
618
619 /* store back the pointer */
620 *dict = p;
621
622 return goon;
623}
624
625
626char *xs_dict_get(char *dict, const char *key)
627/* returns the value directed by key */
628{
629 char *k, *v, *r = NULL;
630
631 while (xs_dict_iter(&dict, &k, &v)) {
632 if (strcmp(k, key) == 0) {
633 r = v;
634 break;
635 }
636 }
637
638 return r;
639}
640
641
642d_char *xs_dict_del(d_char *dict, const char *key)
643/* deletes a key */
644{
645 char *k, *v;
646 char *p = dict;
647
648 while (xs_dict_iter(&p, &k, &v)) {
649 if (strcmp(k, key) == 0) {
650 /* the address of the item is just behind the key */
651 char *i = k - 1;
652
653 dict = xs_collapse(dict, i - dict, xs_size(i));
654 break;
655 }
656 }
657
658 return dict;
659}
660
661
662d_char *xs_dict_set(d_char *dict, const char *key, const char *data)
663/* sets (replaces) a key */
664{
665 /* delete the possibly existing key */
666 dict = xs_dict_del(dict, key);
667
668 /* append the data */
669 dict = xs_dict_append(dict, key, data);
670
671 return dict;
672}
673
674
675/** other values **/
676
677d_char *xs_val_new(xstype t)
678/* adds a new special value */
679{
680 d_char *v = malloc(_xs_blk_size(1));
681
682 v[0] = t;
683
684 return v;
685}
686
687
688d_char *xs_number_new(float f)
689/* adds a new number value */
690{
691 d_char *v = malloc(_xs_blk_size(1 + sizeof(float)));
692
693 v[0] = XSTYPE_NUMBER;
694 memcpy(&v[1], &f, sizeof(float));
695
696 return v;
697}
698
699
700float xs_get_number(char *v)
701/* gets the number as a float */
702{
703 float f = 0.0;
704
705 if (v[0] == XSTYPE_NUMBER)
706 memcpy(&f, &v[1], sizeof(float));
707
708 return f;
709}
710
711
712#endif /* XS_IMPLEMENTATION */
713
714#endif /* _XS_H */
diff --git a/xs_curl.h b/xs_curl.h
new file mode 100644
index 0000000..01d9311
--- /dev/null
+++ b/xs_curl.h
@@ -0,0 +1,177 @@
1/* copyright (c) 2022 grunfink - MIT license */
2
3#ifndef _XS_CURL_H
4
5#define _XS_CURL_H
6
7d_char *xs_http_request(char *method, char *url, d_char *headers,
8 d_char *body, int b_size,
9 int *status, d_char **payload, int *p_size);
10
11#ifdef XS_IMPLEMENTATION
12
13#include <curl/curl.h>
14
15static size_t _header_callback(char *buffer, size_t size,
16 size_t nitems, d_char **userdata)
17{
18 d_char *headers = *userdata;
19 xs *l;
20
21 /* get the line */
22 l = xs_str_new(NULL);
23 l = xs_append_m(l, buffer, size * nitems);
24 l = xs_strip(l);
25
26 /* only the HTTP/x 200 line and the last one doesn't have ': ' */
27 if (xs_str_in(l, ": ") != -1) {
28 xs *knv = xs_splitn(l, ": ", 1);
29
30 headers = xs_dict_append(headers, xs_list_get(knv, 0), xs_list_get(knv, 1));
31 }
32 else
33 if (xs_startswith(l, "HTTP/"))
34 headers = xs_dict_append(headers, "_proto", l);
35
36 *userdata = headers;
37
38 return nitems * size;
39}
40
41
42struct _payload_data {
43 char *data;
44 int size;
45 int offset;
46};
47
48static int _data_callback(void *buffer, size_t size,
49 size_t nitems, struct _payload_data *pd)
50{
51 int sz = size * nitems;
52
53 /* open space */
54 pd->size += sz;
55 pd->data = realloc(pd->data, pd->size + 1);
56
57 /* copy data */
58 memcpy(pd->data + pd->offset, buffer, sz);
59 pd->offset += sz;
60
61 return sz;
62}
63
64
65static int _post_callback(char *buffer, size_t size,
66 size_t nitems, struct _payload_data *pd)
67{
68 /* size of data left */
69 int sz = pd->size - pd->offset;
70
71 /* if it's still bigger than the provided space, trim */
72 if (sz > size * nitems)
73 sz = size * nitems;
74
75 memcpy(buffer, pd->data + pd->offset, sz);
76
77 /* skip sent data */
78 pd->offset += sz;
79
80 return sz;
81}
82
83
84d_char *xs_http_request(char *method, char *url, d_char *headers,
85 d_char *body, int b_size,
86 int *status, d_char **payload, int *p_size)
87/* does an HTTP request */
88{
89 d_char *response;
90 CURL *curl;
91 struct curl_slist *list = NULL;
92 char *k, *v, *p;
93 long lstatus;
94
95 response = xs_dict_new();
96
97 curl = curl_easy_init();
98
99 curl_easy_setopt(curl, CURLOPT_URL, url);
100
101 curl_easy_setopt(curl, CURLOPT_TIMEOUT, 5L);
102
103 /* force HTTP/1.1 */
104 curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
105
106 /* obey redirections */
107 curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
108
109 /* store response headers here */
110 curl_easy_setopt(curl, CURLOPT_HEADERDATA, &response);
111 curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, _header_callback);
112
113 struct _payload_data ipd = { NULL, 0, 0 };
114 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ipd);
115 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, _data_callback);
116
117 if (strcmp(method, "POST") == 0) {
118 curl_easy_setopt(curl, CURLOPT_POST, 1L);
119
120 if (body != NULL) {
121 if (b_size <= 0)
122 b_size = xs_size(body);
123
124 /* add the content-length header */
125 char tmp[32];
126 sprintf(tmp, "content-length: %d", b_size);
127 list = curl_slist_append(list, tmp);
128
129 struct _payload_data pd = { body, b_size, 0 };
130
131 curl_easy_setopt(curl, CURLOPT_READDATA, &pd);
132 curl_easy_setopt(curl, CURLOPT_READFUNCTION, _post_callback);
133 }
134 }
135
136 /* fill the request headers */
137 p = headers;
138 while (xs_dict_iter(&p, &k, &v)) {
139 xs *h;
140
141 const char *args[] = { k, v, NULL };
142 h = xs_fmt("%s: %s", args);
143
144 list = curl_slist_append(list, h);
145 }
146
147 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
148
149 /* do it */
150 curl_easy_perform(curl);
151
152 curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &lstatus);
153
154 curl_easy_cleanup(curl);
155
156 if (status != NULL)
157 *status = (int) lstatus;
158
159 if (p_size != NULL)
160 *p_size = ipd.size;
161
162 if (payload != NULL) {
163 *payload = ipd.data;
164
165 /* add an asciiz just in case (but not touching p_size) */
166 if (ipd.data != NULL)
167 ipd.data[ipd.size] = '\0';
168 }
169 else
170 free(ipd.data);
171
172 return response;
173}
174
175#endif /* XS_IMPLEMENTATION */
176
177#endif /* _XS_CURL_H */
diff --git a/xs_encdec.h b/xs_encdec.h
new file mode 100644
index 0000000..e336218
--- /dev/null
+++ b/xs_encdec.h
@@ -0,0 +1,180 @@
1/* copyright (c) 2022 grunfink - MIT license */
2
3#ifndef _XS_ENCDEC_H
4
5#define _XS_ENCDEC_H
6
7d_char *xs_hex_enc(const char *data, int size);
8d_char *xs_hex_dec(const char *hex);
9d_char *xs_base64_enc(const char *data, int sz);
10d_char *xs_base64_dec(const char *data, int *size);
11d_char *xs_utf8_enc(d_char *str, unsigned int cpoint);
12
13
14#ifdef XS_IMPLEMENTATION
15
16d_char *xs_hex_enc(const char *data, int size)
17/* returns an hexdump of data */
18{
19 d_char *s;
20 char *p;
21 int n;
22
23 p = s = calloc(size * 2 + 1, 1);
24
25 for (n = 0; n < size; n++) {
26 sprintf(p, "%02x", (unsigned char)data[n]);
27 p += 2;
28 }
29
30 return s;
31}
32
33
34d_char *xs_hex_dec(const char *hex)
35/* decodes an hexdump into data */
36{
37 int sz = strlen(hex);
38 d_char *s;
39 char *p;
40 int n;
41
42 p = s = calloc(sz / 2, 1);
43
44 for (n = 0; n < sz; n += 2) {
45 int i;
46 if (sscanf(&hex[n], "%02x", &i) == 0) {
47 /* decoding error */
48 free(s);
49 s = NULL;
50 }
51 else
52 *p = i;
53
54 p++;
55 }
56
57 return s;
58}
59
60
61d_char *xs_base64_enc(const char *data, int sz)
62/* encodes data to base64 */
63{
64 d_char *s;
65 unsigned char *p;
66 int n;
67 static char *b64_tbl = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
68 "abcdefghijklmnopqrstuvwxyz"
69 "0123456789+/";
70
71 s = xs_str_new(NULL);
72 p = (unsigned char *)data;
73
74 for (n = 0; n < sz; n += 3) {
75 int l = sz - n;
76
77 if (l == 1) {
78 s = xs_append_m(s, &b64_tbl[(p[n] >> 2) & 0x3f], 1);
79 s = xs_append_m(s, &b64_tbl[(p[n] << 4) & 0x3f], 1);
80 s = xs_append_m(s, "==", 2);
81 }
82 else
83 if (l == 2) {
84 s = xs_append_m(s, &b64_tbl[(p[n] >> 2) & 0x3f], 1);
85 s = xs_append_m(s, &b64_tbl[(p[n] << 4 | p[n + 1] >> 4) & 0x3f], 1);
86 s = xs_append_m(s, &b64_tbl[(p[n + 1] << 2) & 0x3f], 1);
87 s = xs_append_m(s, "=", 1);
88 }
89 else {
90 s = xs_append_m(s, &b64_tbl[(p[n] >> 2) & 0x3f], 1);
91 s = xs_append_m(s, &b64_tbl[(p[n] << 4 | p[n + 1] >> 4) & 0x3f], 1);
92 s = xs_append_m(s, &b64_tbl[(p[n + 1] << 2 | p[n + 2] >> 6) & 0x3f], 1);
93 s = xs_append_m(s, &b64_tbl[(p[n + 2]) & 0x3f], 1);
94 }
95 }
96
97 return s;
98}
99
100
101d_char *xs_base64_dec(const char *data, int *size)
102/* decodes data from base64 */
103{
104 d_char *s = NULL;
105 int sz = 0;
106 char *p;
107 static char *b64_tbl = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
108 "abcdefghijklmnopqrstuvwxyz"
109 "0123456789+/=";
110
111 p = (char *)data;
112
113 for (p = (char *)data; *p; p += 4) {
114 int cs[4];
115 int n;
116 unsigned char tmp[3];
117
118 for (n = 0; n < 4; n++) {
119 char *ss = strchr(b64_tbl, p[n]);
120
121 if (ss == NULL) {
122 /* not a base64 char */
123 free(s);
124 return NULL;
125 }
126
127 cs[n] = ss - b64_tbl;
128 }
129
130 n = 0;
131
132 /* first byte */
133 tmp[n++] = cs[0] << 2 | ((cs[1] >> 4) & 0x0f);
134
135 /* second byte */
136 if (cs[2] != 64)
137 tmp[n++] = cs[1] << 4 | ((cs[2] >> 2) & 0x3f);
138
139 /* third byte */
140 if (cs[3] != 64)
141 tmp[n++] = cs[2] << 6 | (cs[3] & 0x3f);
142
143 /* must be done manually because data can be pure binary */
144 s = realloc(s, sz + n);
145 memcpy(s + sz, tmp, n);
146 sz += n;
147 }
148
149 *size = sz;
150
151 return s;
152}
153
154
155d_char *xs_utf8_enc(d_char *str, unsigned int cpoint)
156/* encodes an Unicode codepoint to utf8 */
157{
158 unsigned char tmp[4];
159 int n = 0;
160
161 if (cpoint < 0x80)
162 tmp[n++] = cpoint & 0xff;
163 else
164 if (cpoint < 0x800) {
165 tmp[n++] = 0xc0 | (cpoint >> 6);
166 tmp[n++] = 0x80 | (cpoint & 0x3f);
167 }
168 else
169 if (cpoint < 0x10000) {
170 tmp[n++] = 0xe0 | (cpoint >> 12);
171 tmp[n++] = 0x80 | ((cpoint >> 6) & 0x3f);
172 tmp[n++] = 0x80 | (cpoint & 0x3f);
173 }
174
175 return xs_append_m(str, (char *)tmp, n);
176}
177
178#endif /* XS_IMPLEMENTATION */
179
180#endif /* _XS_ENCDEC_H */
diff --git a/xs_httpd.h b/xs_httpd.h
new file mode 100644
index 0000000..3d520ec
--- /dev/null
+++ b/xs_httpd.h
@@ -0,0 +1,184 @@
1/* copyright (c) 2022 grunfink - MIT license */
2
3#ifndef _XS_HTTPD_H
4
5#define _XS_HTTPD_H
6
7d_char *xs_url_dec(char *str);
8d_char *xs_url_vars(char *str);
9d_char *xs_httpd_request(FILE *f);
10void xs_httpd_response(FILE *f, int status, d_char *headers, char *body, int b_size);
11
12
13#ifdef XS_IMPLEMENTATION
14
15d_char *xs_url_dec(char *str)
16/* decodes an URL */
17{
18 d_char *s = xs_str_new(NULL);
19
20 while (*str) {
21 if (*str == '%') {
22 int i;
23
24 if (sscanf(str + 1, "%02x", &i) == 1) {
25 unsigned char uc = i;
26
27 s = xs_append_m(s, (char *)&uc, 1);
28 str += 2;
29 }
30 }
31 else
32 if (*str == '+')
33 s = xs_append_m(s, " ", 1);
34 else
35 s = xs_append_m(s, str, 1);
36
37 str++;
38 }
39
40 return s;
41}
42
43
44d_char *xs_url_vars(char *str)
45/* parse url variables */
46{
47 d_char *vars;
48
49 vars = xs_dict_new();
50
51 if (str != NULL) {
52 char *v, *l;
53 xs *args;
54
55 /* split by arguments */
56 args = xs_split(str, "&");
57
58 l = args;
59 while (xs_list_iter(&l, &v)) {
60 xs *kv = xs_splitn(v, "=", 2);
61
62 if (xs_list_len(kv) == 2)
63 vars = xs_dict_append(vars,
64 xs_list_get(kv, 0), xs_list_get(kv, 1));
65 }
66 }
67
68 return vars;
69}
70
71
72d_char *xs_httpd_request(FILE *f)
73/* processes an httpd connection */
74{
75 xs *headers = NULL;
76 xs *q_vars = NULL;
77 xs *p_vars = NULL;
78 d_char *req = NULL;
79 xs *l1, *l2;
80 char *v;
81
82 xs_socket_timeout(fileno(f), 2.0, 0.0);
83
84 /* read the first line and split it */
85 l1 = xs_strip(xs_readline(f));
86 l2 = xs_split(l1, " ");
87
88 if (xs_list_len(l2) != 3) {
89 /* error or timeout */
90 return NULL;
91 }
92
93 headers = xs_dict_new();
94
95 headers = xs_dict_append(headers, "method", xs_list_get(l2, 0));
96 headers = xs_dict_append(headers, "proto", xs_list_get(l2, 2));
97
98 {
99 /* split the path with its optional variables */
100 xs *udp = xs_url_dec(xs_list_get(l2, 1));
101 xs *pnv = xs_splitn(udp, "?", 1);
102
103 /* store the path */
104 headers = xs_dict_append(headers, "path", xs_list_get(pnv, 0));
105
106 /* get the variables */
107 q_vars = xs_url_vars(xs_list_get(pnv, 1));
108 }
109
110 /* read the headers */
111 for (;;) {
112 xs *l, *p = NULL;
113
114 l = xs_strip(xs_readline(f));
115
116 /* done with the header? */
117 if (strcmp(l, "") == 0)
118 break;
119
120 /* split header and content */
121 p = xs_splitn(l, ": ", 1);
122
123 if (xs_list_len(p) == 2)
124 headers = xs_dict_append(headers,
125 xs_tolower(xs_list_get(p, 0)), xs_list_get(p, 1));
126 }
127
128 xs_socket_timeout(fileno(f), 5.0, 0.0);
129
130 /* does it have a payload with form urlencoded variables? */
131 v = xs_dict_get(headers, "content-type");
132
133 if (v && strcmp(v, "application/x-www-form-urlencoded") == 0) {
134 if ((v = xs_dict_get(headers, "content-length")) != NULL) {
135 int cl = atoi(v);
136 xs *payload;
137
138 if ((payload = xs_read(f, cl)) != NULL) {
139 xs *upl = xs_url_dec(payload);
140 p_vars = xs_url_vars(upl);
141 }
142 }
143 }
144 else
145 p_vars = xs_dict_new();
146
147 if (errno == 0) {
148 req = xs_dict_new();
149 req = xs_dict_append(req, "headers", headers);
150 req = xs_dict_append(req, "q_vars", q_vars);
151 req = xs_dict_append(req, "p_vars", p_vars);
152 }
153
154 return req;
155}
156
157
158void xs_httpd_response(FILE *f, int status, d_char *headers, char *body, int b_size)
159/* sends an httpd response */
160{
161 xs *proto;
162 char *p, *k, *v;
163
164 proto = xs_fmt("HTTP/1.1 %d", status);
165 fprintf(f, "%s\r\n", proto);
166
167 p = headers;
168 while (xs_dict_iter(&p, &k, &v)) {
169 fprintf(f, "%s: %s\r\n", k, v);
170 }
171
172 if (b_size != 0)
173 fprintf(f, "content-length: %d\r\n", b_size);
174
175 fprintf(f, "\r\n");
176
177 if (body != NULL && b_size != 0)
178 fwrite(body, b_size, 1, f);
179}
180
181
182#endif /* XS_IMPLEMENTATION */
183
184#endif /* XS_HTTPD_H */
diff --git a/xs_io.h b/xs_io.h
new file mode 100644
index 0000000..523d207
--- /dev/null
+++ b/xs_io.h
@@ -0,0 +1,86 @@
1/* copyright (c) 2022 grunfink - MIT license */
2
3#ifndef _XS_IO_H
4
5#define _XS_IO_H
6
7d_char *xs_readall(FILE *f);
8d_char *xs_readline(FILE *f);
9d_char *xs_read(FILE *f, int size);
10
11
12#ifdef XS_IMPLEMENTATION
13
14d_char *xs_readall(FILE *f)
15/* reads the rest of the file into a string */
16{
17 d_char *s;
18 char tmp[1024];
19
20 errno = 0;
21
22 /* create the new string */
23 s = xs_str_new(NULL);
24
25 while (fgets(tmp, sizeof(tmp), f))
26 s = xs_str_cat(s, tmp);
27
28 return s;
29}
30
31
32d_char *xs_readline(FILE *f)
33/* reads a line from a file */
34{
35 d_char *s = NULL;
36
37 errno = 0;
38
39 /* don't even try on eof */
40 if (!feof(f)) {
41 int c;
42
43 s = xs_str_new(NULL);
44
45 while ((c = fgetc(f)) != EOF) {
46 unsigned char rc = c;
47
48 s = xs_append_m(s, (char *)&rc, 1);
49
50 if (c == '\n')
51 break;
52 }
53 }
54
55 return s;
56}
57
58
59d_char *xs_read(FILE *f, int size)
60/* reads up to size bytes from f */
61{
62 d_char *s;
63
64 errno = 0;
65
66 s = xs_str_new(NULL);
67
68 while (size != 0 && !feof(f)) {
69 char tmp[2048];
70 int n, r;
71
72 if ((n = sizeof(tmp)) > size)
73 n = size;
74
75 r = fread(tmp, 1, n, f);
76 s = xs_append_m(s, tmp, r);
77
78 size -= r;
79 }
80
81 return s;
82}
83
84#endif /* XS_IMPLEMENTATION */
85
86#endif /* _XS_IO_H */
diff --git a/xs_json.h b/xs_json.h
new file mode 100644
index 0000000..4bf73d5
--- /dev/null
+++ b/xs_json.h
@@ -0,0 +1,501 @@
1/* copyright (c) 2022 grunfink - MIT license */
2
3#ifndef _XS_JSON_H
4
5#define _XS_JSON_H
6
7d_char *xs_json_dumps_pp(char *data, int indent);
8#define xs_json_dumps(data) xs_json_dumps_pp(data, 0)
9d_char *xs_json_loads(const char *json);
10
11
12#ifdef XS_IMPLEMENTATION
13
14/** IMPLEMENTATION **/
15
16/** JSON dumps **/
17
18d_char *_xs_json_dumps_str(d_char *s, char *data)
19/* dumps a string in JSON format */
20{
21 unsigned char c;
22 s = xs_str_cat(s, "\"");
23
24 while ((c = *data)) {
25 if (c == '\n')
26 s = xs_str_cat(s, "\\n");
27 else
28 if (c == '\r')
29 s = xs_str_cat(s, "\\r");
30 else
31 if (c == '\t')
32 s = xs_str_cat(s, "\\t");
33 else
34 if (c == '\\')
35 s = xs_str_cat(s, "\\\\");
36 else
37 if (c == '"')
38 s = xs_str_cat(s, "\\\"");
39 else
40 if (c < 32) {
41 char tmp[10];
42
43 sprintf(tmp, "\\u%04x", (unsigned int) c);
44 s = xs_str_cat(s, tmp);
45 }
46 else
47 s = xs_append_m(s, data, 1);
48
49 data++;
50 }
51
52 s = xs_str_cat(s, "\"");
53
54 return s;
55}
56
57
58d_char *_xs_json_indent(d_char *s, int level, int indent)
59/* adds indentation */
60{
61 if (indent) {
62 int n;
63
64 s = xs_str_cat(s, "\n");
65
66 for (n = 0; n < level * indent; n++)
67 s = xs_str_cat(s, " ");
68 }
69
70 return s;
71}
72
73
74d_char *_xs_json_dumps(d_char *s, char *data, int level, int indent)
75/* dumps partial data as JSON */
76{
77 char *k, *v;
78 int c = 0;
79
80 switch (xs_type(data)) {
81 case XSTYPE_NULL:
82 s = xs_str_cat(s, "null");
83 break;
84
85 case XSTYPE_TRUE:
86 s = xs_str_cat(s, "true");
87 break;
88
89 case XSTYPE_FALSE:
90 s = xs_str_cat(s, "false");
91 break;
92
93 case XSTYPE_NUMBER:
94 {
95 char tmp[32];
96 snprintf(tmp, sizeof(tmp), "%g", xs_get_number(data));
97 s = xs_str_cat(s, tmp);
98 }
99 break;
100
101 case XSTYPE_SOL:
102 s = xs_str_cat(s, "[");
103
104 while (xs_list_iter(&data, &v)) {
105 if (c != 0)
106 s = xs_str_cat(s, ",");
107
108 s = _xs_json_indent(s, level + 1, indent);
109 s = _xs_json_dumps(s, v, level + 1, indent);
110
111 c++;
112 }
113
114 s = _xs_json_indent(s, level, indent);
115 s = xs_str_cat(s, "]");
116
117 break;
118
119 case XSTYPE_SOD:
120 s = xs_str_cat(s, "{");
121
122 while (xs_dict_iter(&data, &k, &v)) {
123 if (c != 0)
124 s = xs_str_cat(s, ",");
125
126 s = _xs_json_indent(s, level + 1, indent);
127
128 s = _xs_json_dumps_str(s, k);
129 s = xs_str_cat(s, ":");
130
131 if (indent)
132 s = xs_str_cat(s, " ");
133
134 s = _xs_json_dumps(s, v, level + 1, indent);
135
136 c++;
137 }
138
139 s = _xs_json_indent(s, level, indent);
140 s = xs_str_cat(s, "}");
141 break;
142
143 case XSTYPE_STRING:
144 s = _xs_json_dumps_str(s, data);
145 break;
146
147 default:
148 break;
149 }
150
151 return s;
152}
153
154
155d_char *xs_json_dumps_pp(char *data, int indent)
156/* dumps a piece of data as JSON */
157{
158 xstype t = xs_type(data);
159 d_char *s = NULL;
160
161 if (t == XSTYPE_SOL || t == XSTYPE_SOD) {
162 s = xs_str_new(NULL);
163 s = _xs_json_dumps(s, data, 0, indent);
164 }
165
166 return s;
167}
168
169
170/** JSON loads **/
171
172/* this code comes mostly from the Minimum Profit Text Editor (MPDM) */
173
174typedef enum {
175 JS_ERROR = -1,
176 JS_INCOMPLETE,
177 JS_OCURLY,
178 JS_OBRACK,
179 JS_CCURLY,
180 JS_CBRACK,
181 JS_COMMA,
182 JS_COLON,
183 JS_VALUE,
184 JS_STRING,
185 JS_INTEGER,
186 JS_REAL,
187 JS_TRUE,
188 JS_FALSE,
189 JS_NULL,
190 JS_ARRAY,
191 JS_OBJECT
192} js_type;
193
194
195d_char *_xs_json_loads_lexer(const char **json, js_type *t)
196{
197 char c;
198 const char *s = *json;
199 d_char *v = NULL;
200
201 /* skip blanks */
202 while (*s == L' ' || *s == L'\t' || *s == L'\n' || *s == L'\r')
203 s++;
204
205 c = *s++;
206
207 if (c == '{')
208 *t = JS_OCURLY;
209 else
210 if (c == '}')
211 *t = JS_CCURLY;
212 else
213 if (c == '[')
214 *t = JS_OBRACK;
215 else
216 if (c == ']')
217 *t = JS_CBRACK;
218 else
219 if (c == ',')
220 *t = JS_COMMA;
221 else
222 if (c == ':')
223 *t = JS_COLON;
224 else
225 if (c == '"') {
226 *t = JS_STRING;
227
228 v = xs_str_new(NULL);
229
230 while ((c = *s) != '"' && c != '\0') {
231 char tmp[5];
232 int i;
233
234 if (c == '\\') {
235 s++;
236 c = *s;
237 switch (c) {
238 case 'n': c = '\n'; break;
239 case 'r': c = '\r'; break;
240 case 't': c = '\t'; break;
241 case 'u': /* Unicode codepoint as an hex char */
242 s++;
243 tmp[0] = (char)*s; s++;
244 tmp[1] = (char)*s; s++;
245 tmp[2] = (char)*s; s++;
246 tmp[3] = (char)*s;
247 tmp[4] = '\0';
248
249 sscanf(tmp, "%04x", &i);
250
251 v = xs_utf8_enc(v, i);
252 c = '\0';
253
254 break;
255 }
256 }
257
258 if (c)
259 v = xs_append_m(v, &c, 1);
260
261 s++;
262 }
263
264 if (c != '\0')
265 s++;
266 }
267 else
268 if (c == '-' || (c >= '0' && c <= '9') || c == '.') {
269 xs *vn = NULL;
270
271 *t = JS_INTEGER;
272
273 vn = xs_str_new(NULL);
274 vn = xs_append_m(vn, &c, 1);
275
276 while (((c = *s) >= '0' && c <= '9') || c == '.') {
277 if (c == '.')
278 *t = JS_REAL;
279
280 vn = xs_append_m(vn, &c, 1);
281 s++;
282 }
283
284 /* convert to XSTYPE_NUMBER */
285 v = xs_number_new(atof(vn));
286 }
287 else
288 if (c == 't' && strncmp(s, "rue", 3) == 0) {
289 s += 3;
290 *t = JS_TRUE;
291
292 v = xs_val_new(XSTYPE_TRUE);
293 }
294 else
295 if (c == 'f' && strncmp(s, "alse", 4) == 0) {
296 s += 4;
297 *t = JS_FALSE;
298
299 v = xs_val_new(XSTYPE_FALSE);
300 }
301 else
302 if (c == 'n' && strncmp(s, "ull", 3) == 0) {
303 s += 3;
304 *t = JS_NULL;
305
306 v = xs_val_new(XSTYPE_NULL);
307 }
308 else
309 *t = JS_ERROR;
310
311 *json = s;
312
313 return v;
314}
315
316
317d_char *_xs_json_loads_array(const char **json, js_type *t);
318d_char *_xs_json_loads_object(const char **json, js_type *t);
319
320d_char *_xs_json_loads_value(const char **json, js_type *t, d_char *v)
321/* parses a JSON value */
322{
323 if (*t == JS_OBRACK)
324 v = _xs_json_loads_array(json, t);
325 else
326 if (*t == JS_OCURLY)
327 v = _xs_json_loads_object(json, t);
328
329 if (*t >= JS_VALUE)
330 *t = JS_VALUE;
331 else
332 *t = JS_ERROR;
333
334 return v;
335}
336
337
338d_char *_xs_json_loads_array(const char **json, js_type *t)
339/* parses a JSON array */
340{
341 const char *s = *json;
342 xs *v;
343 d_char *l;
344 js_type tt;
345
346 l = xs_list_new();
347
348 *t = JS_INCOMPLETE;
349
350 v = _xs_json_loads_lexer(&s, &tt);
351
352 if (tt == JS_CBRACK)
353 *t = JS_ARRAY;
354 else {
355 v = _xs_json_loads_value(&s, &tt, v);
356
357 if (tt == JS_VALUE) {
358 l = xs_list_append(l, v);
359
360 while (*t == JS_INCOMPLETE) {
361 _xs_json_loads_lexer(&s, &tt);
362
363 if (tt == JS_CBRACK)
364 *t = JS_ARRAY;
365 else
366 if (tt == JS_COMMA) {
367 xs *v2;
368
369 v2 = _xs_json_loads_lexer(&s, &tt);
370 v2 = _xs_json_loads_value(&s, &tt, v2);
371
372 if (tt == JS_VALUE)
373 l = xs_list_append(l, v2);
374 else
375 *t = JS_ERROR;
376 }
377 else
378 *t = JS_ERROR;
379 }
380 }
381 else
382 *t = JS_ERROR;
383 }
384
385 if (*t == JS_ERROR) {
386 free(l);
387 l = NULL;
388 }
389
390 *json = s;
391
392 return l;
393}
394
395
396d_char *_xs_json_loads_object(const char **json, js_type *t)
397/* parses a JSON object */
398{
399 const char *s = *json;
400 xs *k1;
401 d_char *d;
402 js_type tt;
403
404 d = xs_dict_new();
405
406 *t = JS_INCOMPLETE;
407
408 k1 = _xs_json_loads_lexer(&s, &tt);
409
410 if (tt == JS_CCURLY)
411 *t = JS_OBJECT;
412 else
413 if (tt == JS_STRING) {
414 _xs_json_loads_lexer(&s, &tt);
415
416 if (tt == JS_COLON) {
417 xs *v1;
418
419 v1 = _xs_json_loads_lexer(&s, &tt);
420 v1 = _xs_json_loads_value(&s, &tt, v1);
421
422 if (tt == JS_VALUE) {
423 d = xs_dict_append(d, k1, v1);
424
425 while (*t == JS_INCOMPLETE) {
426 _xs_json_loads_lexer(&s, &tt);
427
428 if (tt == JS_CCURLY)
429 *t = JS_OBJECT;
430 else
431 if (tt == JS_COMMA) {
432 xs *k;
433
434 k = _xs_json_loads_lexer(&s, &tt);
435
436 if (tt == JS_STRING) {
437 _xs_json_loads_lexer(&s, &tt);
438
439 if (tt == JS_COLON) {
440 xs *v;
441
442 v = _xs_json_loads_lexer(&s, &tt);
443 v = _xs_json_loads_value(&s, &tt, v);
444
445 if (tt == JS_VALUE)
446 d = xs_dict_append(d, k, v);
447 else
448 *t = JS_ERROR;
449 }
450 else
451 *t = JS_ERROR;
452 }
453 else
454 *t = JS_ERROR;
455 }
456 else
457 *t = JS_ERROR;
458 }
459 }
460 else
461 *t = JS_ERROR;
462 }
463 else
464 *t = JS_ERROR;
465 }
466 else
467 *t = JS_ERROR;
468
469 if (*t == JS_ERROR) {
470 free(d);
471 d = NULL;
472 }
473
474 *json = s;
475
476 return d;
477}
478
479
480d_char *xs_json_loads(const char *json)
481/* loads a string in JSON format and converts to a multiple data */
482{
483 d_char *v = NULL;
484 js_type t;
485
486 _xs_json_loads_lexer(&json, &t);
487
488 if (t == JS_OBRACK)
489 v = _xs_json_loads_array(&json, &t);
490 else
491 if (t == JS_OCURLY)
492 v = _xs_json_loads_object(&json, &t);
493 else
494 t = JS_ERROR;
495
496 return v;
497}
498
499#endif /* XS_IMPLEMENTATION */
500
501#endif /* _XS_JSON_H */
diff --git a/xs_openssl.h b/xs_openssl.h
new file mode 100644
index 0000000..a6a17b1
--- /dev/null
+++ b/xs_openssl.h
@@ -0,0 +1,159 @@
1/* copyright (c) 2022 grunfink - MIT license */
2
3#ifndef _XS_OPENSSL_H
4
5#define _XS_OPENSSL_H
6
7d_char *xs_md5_hex(const void *input, int size);
8d_char *xs_sha1_hex(const void *input, int size);
9d_char *xs_sha256_hex(const void *input, int size);
10d_char *xs_rsa_genkey(int bits);
11d_char *xs_rsa_sign(char *secret, char *mem, int size);
12int xs_rsa_verify(char *pubkey, char *mem, int size, char *b64sig);
13
14
15#ifdef XS_IMPLEMENTATION
16
17#include "openssl/md5.h"
18#include "openssl/sha.h"
19#include "openssl/rsa.h"
20#include "openssl/pem.h"
21
22d_char *xs_md5_hex(const void *input, int size)
23{
24 unsigned char md5[16];
25 MD5_CTX ctx;
26
27 MD5_Init(&ctx);
28 MD5_Update(&ctx, input, size);
29 MD5_Final(md5, &ctx);
30
31 return xs_hex_enc((char *)md5, sizeof(md5));
32}
33
34
35d_char *xs_sha1_hex(const void *input, int size)
36{
37 unsigned char sha1[20];
38 SHA_CTX ctx;
39
40 SHA1_Init(&ctx);
41 SHA1_Update(&ctx, input, size);
42 SHA1_Final(sha1, &ctx);
43
44 return xs_hex_enc((char *)sha1, sizeof(sha1));
45}
46
47
48d_char *xs_sha256_hex(const void *input, int size)
49{
50 unsigned char sha256[32];
51 SHA256_CTX ctx;
52
53 SHA256_Init(&ctx);
54 SHA256_Update(&ctx, input, size);
55 SHA256_Final(sha256, &ctx);
56
57 return xs_hex_enc((char *)sha256, sizeof(sha256));
58}
59
60
61d_char *xs_rsa_genkey(int bits)
62/* generates an RSA keypair */
63{
64 BIGNUM *bne;
65 RSA *rsa;
66 d_char *keypair = NULL;
67
68 if ((bne = BN_new()) != NULL) {
69 if (BN_set_word(bne, RSA_F4) == 1) {
70 if ((rsa = RSA_new()) != NULL) {
71 if (RSA_generate_key_ex(rsa, bits, bne, NULL) == 1) {
72 BIO *bs = BIO_new(BIO_s_mem());
73 BIO *bp = BIO_new(BIO_s_mem());
74 BUF_MEM *sptr;
75 BUF_MEM *pptr;
76
77 PEM_write_bio_RSAPrivateKey(bs, rsa, NULL, NULL, 0, 0, NULL);
78 BIO_get_mem_ptr(bs, &sptr);
79
80 PEM_write_bio_RSA_PUBKEY(bp, rsa);
81 BIO_get_mem_ptr(bp, &pptr);
82
83 keypair = xs_dict_new();
84
85 keypair = xs_dict_append(keypair, "secret", sptr->data);
86 keypair = xs_dict_append(keypair, "public", pptr->data);
87
88 BIO_free(bs);
89 BIO_free(bp);
90 }
91 }
92 }
93 }
94
95 return keypair;
96}
97
98
99d_char *xs_rsa_sign(char *secret, char *mem, int size)
100/* signs a memory block (secret is in PEM format) */
101{
102 d_char *signature = NULL;
103 BIO *b;
104 RSA *rsa;
105 unsigned char *sig;
106 unsigned int sig_len;
107
108 /* un-PEM the key */
109 b = BIO_new_mem_buf(secret, strlen(secret));
110 rsa = PEM_read_bio_RSAPrivateKey(b, NULL, NULL, NULL);
111
112 /* alloc space */
113 sig = malloc(RSA_size(rsa));
114
115 if (RSA_sign(NID_sha256, (unsigned char *)mem, size, sig, &sig_len, rsa) == 1)
116 signature = xs_base64_enc((char *)sig, sig_len);
117
118 BIO_free(b);
119 RSA_free(rsa);
120 free(sig);
121
122 return signature;
123}
124
125
126int xs_rsa_verify(char *pubkey, char *mem, int size, char *b64sig)
127/* verifies a base64 block, returns non-zero on ok */
128{
129 int r = 0;
130 BIO *b;
131 RSA *rsa;
132
133 /* un-PEM the key */
134 b = BIO_new_mem_buf(pubkey, strlen(pubkey));
135 rsa = PEM_read_bio_RSA_PUBKEY(b, NULL, NULL, NULL);
136
137 if (rsa != NULL) {
138 d_char *sig = NULL;
139 int s_size;
140
141 /* de-base64 */
142 sig = xs_base64_dec(b64sig, &s_size);
143
144 if (sig != NULL)
145 r = RSA_verify(NID_sha256, (unsigned char *)mem, size,
146 (unsigned char *)sig, s_size, rsa);
147
148 free(sig);
149 }
150
151 BIO_free(b);
152 RSA_free(rsa);
153
154 return r;
155}
156
157#endif /* XS_IMPLEMENTATION */
158
159#endif /* _XS_OPENSSL_H */
diff --git a/xs_socket.h b/xs_socket.h
new file mode 100644
index 0000000..c5eab80
--- /dev/null
+++ b/xs_socket.h
@@ -0,0 +1,95 @@
1/* copyright (c) 2022 grunfink - MIT license */
2
3#ifndef _XS_SOCKET_H
4
5#define _XS_SOCKET_H
6
7int xs_socket_timeout(int s, float rto, float sto);
8int xs_socket_server(char *addr, int port);
9FILE *xs_socket_accept(int rs);
10
11
12#ifdef XS_IMPLEMENTATION
13
14#include <sys/socket.h>
15#include <netdb.h>
16#include <netinet/in.h>
17
18
19int xs_socket_timeout(int s, float rto, float sto)
20/* sets the socket timeout in seconds */
21{
22 struct timeval tv;
23 int ret = 0;
24
25 if (rto > 0.0) {
26 tv.tv_sec = (int)rto;
27 tv.tv_usec = (int)((rto - (float)(int)rto) * 1000000.0);
28
29 ret = setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv));
30 }
31
32 if (sto > 0.0) {
33 tv.tv_sec = (int)sto;
34 tv.tv_usec = (int)((sto - (float)(int)sto) * 1000000.0);
35
36 ret = setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv, sizeof(tv));
37 }
38
39 return ret;
40}
41
42
43int xs_socket_server(char *addr, int port)
44/* opens a server socket */
45{
46 int rs = -1;
47 struct sockaddr_in host;
48
49 memset(&host, '\0', sizeof(host));
50
51 if (addr != NULL) {
52 struct hostent *he;
53
54 if ((he = gethostbyname(addr)) != NULL)
55 memcpy(&host.sin_addr, he->h_addr_list[0], he->h_length);
56 else
57 goto end;
58 }
59
60 host.sin_family = AF_INET;
61 host.sin_port = htons(port);
62
63 if ((rs = socket(AF_INET, SOCK_STREAM, 0)) != -1) {
64 /* reuse addr */
65 int i = 1;
66 setsockopt(rs, SOL_SOCKET, SO_REUSEADDR, (char *)&i, sizeof(i));
67
68 if (bind(rs, (struct sockaddr *)&host, sizeof(host)) == -1) {
69 close(rs);
70 rs = -1;
71 }
72 else
73 listen(rs, SOMAXCONN);
74 }
75
76end:
77 return rs;
78}
79
80
81FILE *xs_socket_accept(int rs)
82/* accepts an incoming connection */
83{
84 int cs = -1;
85 struct sockaddr_in host;
86 socklen_t l = sizeof(host);
87
88 cs = accept(rs, (struct sockaddr *)&host, &l);
89
90 return cs == -1 ? NULL : fdopen(cs, "r+");
91}
92
93#endif /* XS_IMPLEMENTATION */
94
95#endif /* _XS_SOCKET_H */ \ No newline at end of file