implement base64 data in-place decoding - ics2txt - convert icalendar .ics file to plain text
(HTM) 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
(HTM) 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);