From ba662d032a4812b20ad9d9430f245bd4b7484516 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?R=C3=A9mi=20Denis-Courmont?= <remi@remlab.net>
Date: Sat, 25 Sep 2021 23:13:37 +0300
Subject: [PATCH] data: add access for data URI scheme

As defined in RFC2397.
---
 modules/access/Makefile.am |   3 +
 modules/access/data.c      | 168 +++++++++++++++++++++++++++++++++++++
 po/POTFILES.in             |   1 +
 3 files changed, 172 insertions(+)
 create mode 100644 modules/access/data.c

diff --git a/modules/access/Makefile.am b/modules/access/Makefile.am
index fc227a6e4361..cf1825b1a699 100644
--- a/modules/access/Makefile.am
+++ b/modules/access/Makefile.am
@@ -5,6 +5,9 @@ AM_CPPFLAGS += -I$(srcdir)/access
 
 ### Files ###
 
+libdata_plugin_la_SOURCES = access/data.c
+access_LTLIBRARIES += libdata_plugin.la
+
 libdcp_plugin_la_SOURCES = access/dcp/dcpparser.h access/dcp/dcp.cpp access/dcp/dcpparser.cpp access/dcp/dcpdecrypt.cpp
 if HAVE_ASDCP
 libdcp_plugin_la_CPPFLAGS = $(AM_CPPFLAGS) $(ASDCP_CFLAGS)
diff --git a/modules/access/data.c b/modules/access/data.c
new file mode 100644
index 000000000000..f656df9e03e7
--- /dev/null
+++ b/modules/access/data.c
@@ -0,0 +1,168 @@
+/*****************************************************************************
+ * data.c: data URL scheme
+ *****************************************************************************
+ * Copyright (C) 2021 Rémi Denis-Courmont
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_access.h>
+#include <vlc_strings.h>
+#include <vlc_url.h>
+
+struct access_data {
+    size_t type_length;
+    size_t length;
+    size_t offset;
+    char *data;
+};
+
+static ssize_t Read(stream_t *access, void *buf, size_t len)
+{
+    struct access_data *sys = access->p_sys;
+    size_t avail = sys->length - sys->offset;
+
+    assert(sys->offset <= sys->length);
+
+    if (len > avail)
+        len = avail;
+
+    memcpy(buf, sys->data + sys->offset, len);
+    sys->offset += len;
+    return len;
+}
+
+static int Seek(stream_t *access, uint64_t position)
+{
+    struct access_data *sys = access->p_sys;
+
+    sys->offset = (position <= sys->length) ? position : sys->length;
+    return VLC_SUCCESS;
+}
+
+static int Control(stream_t *access, int query, va_list args)
+{
+    struct access_data *sys = access->p_sys;
+
+    switch (query)
+    {
+        case STREAM_CAN_SEEK:
+        case STREAM_CAN_FASTSEEK:
+        case STREAM_CAN_PAUSE:
+        case STREAM_CAN_CONTROL_PACE:
+            *va_arg(args, bool *) = true;
+            return VLC_SUCCESS;
+
+        case STREAM_GET_SIZE:
+            *va_arg(args, uint64_t *) = sys->length;
+            return VLC_SUCCESS;
+
+        case STREAM_GET_PTS_DELAY:
+            *va_arg(args, vlc_tick_t *) =  DEFAULT_PTS_DELAY;
+            return VLC_SUCCESS;
+
+        case STREAM_GET_CONTENT_TYPE: {
+            char *type = strndup(access->psz_url + 5, sys->type_length);
+
+            *va_arg(args, char **) = type;
+            return likely(type != NULL) ? VLC_SUCCESS : VLC_ENOMEM;
+        }
+    
+        case STREAM_SET_PAUSE_STATE:
+            return VLC_SUCCESS;
+    }
+    return VLC_ENOTSUP;
+}
+
+static int Open(vlc_object_t *obj)
+{
+    stream_t *access = (stream_t *)obj;
+    const char *url = access->psz_url;
+
+    if (strncmp(url, "data:", 5))
+        return VLC_ENOTSUP;
+
+    url += 5;
+
+    struct access_data *sys = vlc_obj_malloc(obj, sizeof (*sys));
+    if (unlikely(sys == NULL))
+        return VLC_ENOMEM;
+
+    const char *data = strchr(url, ',');
+    if (unlikely(data == NULL)) {
+        msg_Err(obj, "invalid data URL");
+        return VLC_EINVAL;
+    }
+
+    sys->data = vlc_uri_decode_duplicate(data + 1);
+    if (unlikely(sys->data == NULL))
+        return -errno;
+
+    sys->type_length = data - url;
+    sys->length = strlen(sys->data);
+    sys->offset = 0;
+
+    if ((data - url) >= 7 && strncmp(data - 7, ";base64", 7) == 0) {
+        sys->type_length -= 7; /* ";base64" is not part of the type */
+
+        size_t size = sys->length - (sys->length / 4);
+        char *d = malloc(size);
+
+        if (likely(d != NULL))
+            sys->length = vlc_b64_decode_binary_to_buffer(d, size, sys->data);
+        free(sys->data);
+        sys->data = d;
+
+        if (unlikely(d == NULL))
+            return VLC_ENOMEM;
+    }
+
+    access->pf_read = Read;
+    access->pf_block = NULL;
+    access->pf_seek = Seek;
+    access->pf_control = Control;
+    access->p_sys = sys;
+    return VLC_SUCCESS;
+}
+
+static void Close(vlc_object_t *obj)
+{
+    stream_t *access = (stream_t *)obj;
+    struct access_data *sys = access->p_sys;
+
+    free(sys->data);
+}
+
+vlc_module_begin()
+    set_shortname(N_("data"))
+    set_description(N_("data URI scheme"))
+    set_category(CAT_INPUT)
+    set_subcategory(SUBCAT_INPUT_ACCESS)
+
+    set_capability("access", 0)
+    set_callbacks(Open, Close)
+    add_shortcut("data")
+vlc_module_end()
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 73ee32bff713..3f95e51a08d6 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -157,6 +157,7 @@ modules/access/avio.h
 modules/access/bluray.c
 modules/access/cdda.c
 modules/access/concat.c
+modules/access/data.c
 modules/access/dc1394.c
 modules/access/dcp/dcp.cpp
 modules/access/decklink.cpp
-- 
GitLab