]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
hexdecoct: optionally, line break base64 encoded data
authorLennart Poettering <lennart@poettering.net>
Tue, 22 Jun 2021 17:43:37 +0000 (19:43 +0200)
committerLennart Poettering <lennart@poettering.net>
Thu, 8 Jul 2021 07:30:03 +0000 (09:30 +0200)
src/basic/hexdecoct.c
src/basic/hexdecoct.h
src/test/test-hexdecoct.c

index da1add7c76a41fa5ffe5dc0c3e09100896bfa58c..172ae8a9218b9644bc68ca8788cd15ccc2b63737 100644 (file)
@@ -565,38 +565,79 @@ int unbase64char(char c) {
         return -EINVAL;
 }
 
-ssize_t base64mem(const void *p, size_t l, char **out) {
-        char *r, *z;
+static void maybe_line_break(char **x, char *start, size_t line_break) {
+        size_t n;
+
+        assert(x);
+        assert(*x);
+        assert(start);
+        assert(*x >= start);
+
+        if (line_break == SIZE_MAX)
+                return;
+
+        n = *x - start;
+
+        if (n % (line_break + 1) == line_break)
+                *((*x)++) = '\n';
+}
+
+ssize_t base64mem_full(
+                const void *p,
+                size_t l,
+                size_t line_break,
+                char **out) {
+
         const uint8_t *x;
+        char *r, *z;
+        size_t m;
 
         assert(p || l == 0);
         assert(out);
+        assert(line_break > 0);
 
         /* three input bytes makes four output bytes, padding is added so we must round up */
-        z = r = malloc(4 * (l + 2) / 3 + 1);
+        m = 4 * (l + 2) / 3 + 1;
+
+        if (line_break != SIZE_MAX)
+                m += m / line_break;
+
+        z = r = malloc(m);
         if (!r)
                 return -ENOMEM;
 
         for (x = p; x < (const uint8_t*) p + (l / 3) * 3; x += 3) {
                 /* x[0] == XXXXXXXX; x[1] == YYYYYYYY; x[2] == ZZZZZZZZ */
+                maybe_line_break(&z, r, line_break);
                 *(z++) = base64char(x[0] >> 2);                    /* 00XXXXXX */
+                maybe_line_break(&z, r, line_break);
                 *(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4);  /* 00XXYYYY */
+                maybe_line_break(&z, r, line_break);
                 *(z++) = base64char((x[1] & 15) << 2 | x[2] >> 6); /* 00YYYYZZ */
+                maybe_line_break(&z, r, line_break);
                 *(z++) = base64char(x[2] & 63);                    /* 00ZZZZZZ */
         }
 
         switch (l % 3) {
         case 2:
+                maybe_line_break(&z, r, line_break);
                 *(z++) = base64char(x[0] >> 2);                   /* 00XXXXXX */
+                maybe_line_break(&z, r, line_break);
                 *(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4); /* 00XXYYYY */
+                maybe_line_break(&z, r, line_break);
                 *(z++) = base64char((x[1] & 15) << 2);            /* 00YYYY00 */
+                maybe_line_break(&z, r, line_break);
                 *(z++) = '=';
 
                 break;
         case 1:
+                maybe_line_break(&z, r, line_break);
                 *(z++) = base64char(x[0] >> 2);        /* 00XXXXXX */
+                maybe_line_break(&z, r, line_break);
                 *(z++) = base64char((x[0] & 3) << 4);  /* 00XX0000 */
+                maybe_line_break(&z, r, line_break);
                 *(z++) = '=';
+                maybe_line_break(&z, r, line_break);
                 *(z++) = '=';
 
                 break;
index 4ace5b7a9957fc99a34b58e1b2ab471d5d7026a8..5218f78665a06b17bd6dbed5fadec233843ee5ea 100644 (file)
@@ -33,7 +33,11 @@ int unbase64char(char c) _const_;
 char *base32hexmem(const void *p, size_t l, bool padding);
 int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *len);
 
-ssize_t base64mem(const void *p, size_t l, char **out);
+ssize_t base64mem_full(const void *p, size_t l, size_t line_break, char **ret);
+static inline ssize_t base64mem(const void *p, size_t l, char **ret) {
+        return base64mem_full(p, l, SIZE_MAX, ret);
+}
+
 int base64_append(char **prefix, int plen,
                   const void *p, size_t l,
                   int margin, int width);
index c9d318b8d16183e3261a6506f8154b060b07c69b..a3767b47be72b64c135792eee6f2584442b9dec8 100644 (file)
@@ -5,6 +5,7 @@
 #include "alloc-util.h"
 #include "hexdecoct.h"
 #include "macro.h"
+#include "random-util.h"
 #include "string-util.h"
 
 static void test_hexchar(void) {
@@ -275,6 +276,37 @@ static void test_base64mem(void) {
         free(b64);
 }
 
+static void test_base64mem_linebreak(void) {
+        uint8_t data[4096];
+
+        for (size_t i = 0; i < 20; i++) {
+                _cleanup_free_ char *encoded = NULL;
+                _cleanup_free_ void *decoded = NULL;
+                size_t decoded_size;
+                uint64_t n, m;
+                ssize_t l;
+
+                /* Try a bunch of differently sized blobs */
+                n = random_u64_range(sizeof(data));
+                random_bytes(data, n);
+
+                /* Break at various different columns */
+                m = 1 + random_u64_range(n + 5);
+
+                l = base64mem_full(data, n, m, &encoded);
+                assert_se(l >= 0);
+                assert_se(encoded);
+                assert_se((size_t) l == strlen(encoded));
+
+                assert_se(unbase64mem(encoded, SIZE_MAX, &decoded, &decoded_size) >= 0);
+                assert_se(decoded_size == n);
+                assert_se(memcmp(data, decoded, n) == 0);
+
+                for (size_t j = 0; j < (size_t) l; j++)
+                        assert_se((encoded[j] == '\n') == (j % (m + 1) == m));
+        }
+}
+
 static void test_unbase64mem_one(const char *input, const char *output, int ret) {
         _cleanup_free_ void *buffer = NULL;
         size_t size = 0;
@@ -348,6 +380,7 @@ int main(int argc, char *argv[]) {
         test_base32hexmem();
         test_unbase32hexmem();
         test_base64mem();
+        test_base64mem_linebreak();
         test_unbase64mem();
         test_hexdump();