implement base64 data in-place decoding - ics2txt - convert icalendar .ics file to plain text HTML git clone git://bitreich.org/ics2txt git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/ics2txt DIR Log DIR Files DIR Refs DIR Tags DIR README --- DIR commit b72092250747c7443e20fee06bee232b236f441e DIR parent 917f5b056d0b1241f0816bfd41276a36b5727fb1 HTML Author: Josuah Demangeon <me@josuah.net> Date: Sun, 13 Jun 2021 13:47:25 +0200 implement base64 data in-place decoding This is not done implicitly in case base64 decoding is not needed every time, but instead available as a ical_get_value() function that decodes the content if it is base64 data. Diffstat: M Makefile | 4 ++-- A base64.c | 107 +++++++++++++++++++++++++++++++ A base64.h | 19 +++++++++++++++++++ M ical.c | 22 +++++++++++++++++++++- M ical.h | 9 +++++---- M ics2tree.c | 5 ++++- D ics2tsv.c | 59 ------------------------------- M util.c | 2 +- 8 files changed, 159 insertions(+), 68 deletions(-) --- DIR diff --git a/Makefile b/Makefile @@ -7,8 +7,8 @@ CFLAGS = $D $W -g PREFIX = /usr/local MANPREFIX = ${PREFIX}/man -SRC = ical.c util.c -HDR = ical.h util.h +SRC = ical.c base64.c util.c +HDR = ical.h base64.h util.h OBJ = ${SRC:.c=.o} BIN = ics2tree MAN1 = ics2txt.1 DIR diff --git a/base64.c b/base64.c @@ -0,0 +1,107 @@ +#include "base64.h" + +#include <assert.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> + +#include <stdio.h> + +static char encode_map[64] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +void +base64_encode(char const *s, size_t slen, char *d, size_t *dlen) +{ + char const *sbeg = s, *send = s + slen, *dbeg = d; + unsigned char x; + + while (s < send) { + switch ((s - sbeg) % 3) { + case 0: /* AAAAAABB bbbbcccc ccdddddd */ + assert((size_t)(d - dbeg) + 1 < *dlen); + *d++ = encode_map[*s >> 2]; + x = *s << 4 & 0x3f; + break; + case 1: /* aaaaaabb BBBBCCCC ccdddddd */ + assert((size_t)(d - dbeg) + 1 < *dlen); + *d++ = encode_map[x | (*s >> 4)]; + x = (*s << 2) & 0x3f; + break; + case 2: /* aaaaaabb bbbbcccc CCDDDDDD */ + assert((size_t)(d - dbeg) + 2 < *dlen); + *d++ = encode_map[x | (*s >> 6)]; + *d++ = encode_map[*s & 0x3f]; + break; + } + s++; + } + + /* flush extra content in 'x' */ + assert((size_t)(d - dbeg) + 1 < *dlen); + if ((s - sbeg) % 3 != 2) + *d++ = encode_map[x]; + + /* pad the end with '=' */ + while ((d - dbeg) % 4 != 0) { + assert((size_t)(d - dbeg) + 1 < *dlen); + *d++ = '='; + } + + *dlen = d - dbeg; +} + +static int8_t decode_map[256] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, 0, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +}; + +int +base64_decode(char const *s, size_t *slen, char *d, size_t *dlen) +{ + char const *sbeg = s, *send = sbeg + *slen, *dbeg = d; + + for (; s + 3 < send; s += 4) { + int8_t x0 = decode_map[(unsigned)s[0]]; + int8_t x1 = decode_map[(unsigned)s[1]]; + int8_t x2 = decode_map[(unsigned)s[2]]; + int8_t x3 = decode_map[(unsigned)s[3]]; + uint32_t x = (x0 << 18) | (x1 << 12) | (x2 << 6) | (x3 << 0); + + assert((size_t)(d - dbeg) + 3 < *dlen); + *d++ = x >> 16; + *d++ = x >> 8 & 0xff; + *d++ = x & 0xff; + + /* only "xxxx" or "xxx=" or "xx==" allowed */ + if (s[0] == '=' || s[1] == '=' || (s[2] == '=' && s[3] != '=')) + return -2; + if (s[2] == '=') + d--; + if (s[3] == '=') { + d--; + break; + } + + if (x0 < 0 || x1 < 0 || x2 < 0 || x3 < 0) + return -1; + } + + *slen = s - sbeg; + *dlen = d - dbeg; + return 0; +} DIR diff --git a/base64.h b/base64.h @@ -0,0 +1,19 @@ +#ifndef BASE64_H +#define BASE64_H + +#include <stddef.h> + +void base64_encode(char const *, size_t, char *, size_t *); + +/* + * It is possible to use the same variables for both source and + * destination. Then the base64 will overwrite the source buffer + * with the destination data. + * + * If the same pointer is passed as both source and destination + * size, the source size will be inaccurate but the destination + * will be correct. + */ +int base64_decode(char const *, size_t *, char *, size_t *); + +#endif DIR diff --git a/ical.c b/ical.c @@ -9,14 +9,31 @@ #include <strings.h> #include "util.h" +#include "base64.h" -static int +int ical_error(IcalParser *p, char const *msg) { p->errmsg = msg; return -1; } +int +ical_get_value(IcalParser *p, char *s, size_t *len) +{ + *len = strlen(s); + if (p->base64) + if (base64_decode(s, len, s, len) < 0) + return ical_error(p, "invalid base64 data"); + return 0; +} + +int +ical_get_time(IcalParser *p, char *s, time_t *t) +{ + return -1; +} + #define CALL(p, fn, ...) ((p)->fn ? (p)->fn((p), __VA_ARGS__) : 0) static int @@ -40,6 +57,8 @@ ical_parse_value(IcalParser *p, char **sp, char *name) c = *s, *s = '\0'; if ((err = CALL(p, fn_param_value, name, val)) != 0) return err; + if (strcasecmp(name, "ENCODING") == 0) + p->base64 = (strcasecmp(val, "BASE64") == 0); *s = c; *sp = s; @@ -90,6 +109,7 @@ ical_parse_contentline(IcalParser *p, char *line) *s = c; end = s; + p->base64 = 0; while (*s == ';') { s++; if ((err = ical_parse_param(p, &s)) != 0) DIR diff --git a/ical.h b/ical.h @@ -15,7 +15,7 @@ struct IcalParser { int (*fn_block_end)(IcalParser *, char *); /* if returning non-zero then halt the parser */ - int base64encoded; + int base64; char const *errmsg; size_t line; @@ -24,8 +24,9 @@ struct IcalParser { char stack[1024]; }; -int ical_parse(IcalParser *, FILE *); -//TODO: char *ical_get_time(char *); -//TODO: char *ical_get_value(IcalCtx *, char *); +int ical_parse(IcalParser *, FILE *); +int ical_get_time(IcalParser *, char *, time_t *); +int ical_get_value(IcalParser *, char *, size_t *); +int ical_error(IcalParser *, char const *); #endif DIR diff --git a/ics2tree.c b/ics2tree.c @@ -39,8 +39,11 @@ fn_param_value(IcalParser *p, char *name, char *value) static int fn_entry_value(IcalParser *p, char *name, char *value) { + size_t len; (void)name; + if (ical_get_value(p, value, &len) < 0) + return -1; print_ruler(p->level + 1); printf("value %s\n", value); return 0; @@ -59,7 +62,7 @@ main(int argc, char **argv) if (*argv == NULL) { if (ical_parse(&p, stdin) < 0) - err("parsing stdin:%d %s", p.line, p.errmsg); + err("parsing stdin:%d: %s", p.line, p.errmsg); } for (; *argv != NULL; argv++, argc--) { DIR diff --git a/ics2tsv.c b/ics2tsv.c @@ -1,59 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "ical.h" -#include "log.h" -#include "util.h" - -int -print_ical_tsv(FILE *fp) -{ - struct ical_vcalendar vcal; - int e; - - if ((e = ical_read_vcalendar(&vcal, fp)) < 0) - die("reading ical file: %s", ical_strerror(e)); - - ical_free_vcalendar(&vcal); - return 0; -} - -void -print_header(void) -{ - char *fields[] = { "", NULL }; - - printf("%s\t%s", "beg", "end"); - - for (char **f = fields; *f != NULL; f++) { - fprintf(stdout, "\t%s", *f); - } - fprintf(stdout, "\n"); -} - -int -main(int argc, char **argv) -{ - print_header(); - - log_arg0 = *argv++; - - if (*argv == NULL) { - if (print_ical_tsv(stdin) < 0) - die("converting stdin"); - } - - for (; *argv != NULL; argv++, argc--) { - FILE *fp; - - info("converting \"%s\"", *argv); - if ((fp = fopen(*argv, "r")) == NULL) - die("opening %s", *argv); - if (print_ical_tsv(fp) < 0) - die("converting %s", *argv); - fclose(fp); - } - - return 0; -} DIR diff --git a/util.c b/util.c @@ -49,7 +49,7 @@ debug(char const *fmt, ...) va_list va; if (verbose < 0) - verbose = (getenv("DEBUG") == NULL); + verbose = (getenv("DEBUG") != NULL); if (!verbose) return; va_start(va, fmt);