[vlc-commits] [Git][videolan/vlc][master] 3 commits: access/http: expose vlc_http_mktime

Felix Paul Kühne (@fkuehne) gitlab at videolan.org
Fri May 1 13:35:45 UTC 2026



Felix Paul Kühne pushed to branch master at VideoLAN / VLC


Commits:
52ccf40c by Felix Paul Kühne at 2026-05-01T15:04:10+02:00
access/http: expose vlc_http_mktime

- - - - -
760a5865 by Felix Paul Kühne at 2026-05-01T15:04:10+02:00
access: add webdav browsing module

It uses the existing HTTP stack for connections and deploys our XML
reader to parse the answers.

Handles dav://, davs://, webdav:// and webdavs:// URL schemes

Basic authentication is supported via the credentials keystore

- - - - -
37927522 by Felix Paul Kühne at 2026-05-01T15:04:10+02:00
services_discovery/zeroconf: discover webdav servers

- - - - -


9 changed files:

- modules/access/Makefile.am
- modules/access/http/meson.build
- modules/access/http/message.c
- modules/access/http/message.h
- + modules/access/webdav.c
- modules/services_discovery/avahi.c
- modules/services_discovery/bonjour.m
- modules/services_discovery/microdns.c
- po/POTFILES.in


Changes:

=====================================
modules/access/Makefile.am
=====================================
@@ -367,6 +367,10 @@ libhttp_plugin_la_SOURCES = access/http.c
 libhttp_plugin_la_LIBADD = $(SOCKET_LIBS)
 access_PLUGINS += libhttp_plugin.la
 
+libwebdav_plugin_la_SOURCES = access/webdav.c
+libwebdav_plugin_la_LIBADD = libvlc_http.la
+access_PLUGINS += libwebdav_plugin.la
+
 liblive555_plugin_la_SOURCES = access/live555.cpp access/mms/asf.c access/mms/buffer.c \
                                access/live555_dtsgen.h codec/opus_header.c codec/opus_header.h
 liblive555_plugin_la_CXXFLAGS = $(AM_CXXFLAGS) $(CXXFLAGS_live555)


=====================================
modules/access/http/meson.build
=====================================
@@ -95,3 +95,12 @@ vlc_modules += {
     'sources' : files('access.c'),
     'link_with' : [vlc_http_lib]
 }
+
+#
+# WebDAV browsing module
+#
+vlc_modules += {
+    'name' : 'webdav',
+    'sources' : files('../webdav.c'),
+    'link_with' : [vlc_http_lib]
+}


=====================================
modules/access/http/message.c
=====================================
@@ -831,7 +831,7 @@ int vlc_http_msg_add_atime(struct vlc_http_msg *m)
     return vlc_http_msg_add_time(m, "Date", &now);
 }
 
-static time_t vlc_http_mktime(const char *str)
+time_t vlc_http_mktime(const char *str)
 {   /* IETF RFC7231 §7.1.1.1 */
     struct tm tm;
     char mon[4];


=====================================
modules/access/http/message.h
=====================================
@@ -107,6 +107,17 @@ w* @param msg the response to get the timestamp header from
  */
 time_t vlc_http_msg_get_time(const struct vlc_http_msg *msg, const char *name);
 
+/**
+ * Parses an HTTP-date string.
+ *
+ * Accepts the three formats defined by RFC 7231 §7.1.1.1 (IMF-fixdate,
+ * obsolete RFC 850, and ANSI C asctime()).
+ *
+ * @param str NULL-terminated date string
+ * @return a timestamp value, or -1 on error.
+ */
+time_t vlc_http_mktime(const char *str);
+
 /**
  * Adds a timestamp header field.
  *


=====================================
modules/access/webdav.c
=====================================
@@ -0,0 +1,683 @@
+/*****************************************************************************
+ * webdav.c: WebDAV directory browsing access plug-in
+ *****************************************************************************
+ * Copyright (C) 2026 VLC authors and VideoLAN
+ *
+ * Authors: Felix Paul Kühne <fkuehne -at- videolan.org>
+ *
+ * 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 <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <vlc_common.h>
+#include <vlc_access.h>
+#include <vlc_block.h>
+#include <vlc_input_item.h>
+#include <vlc_keystore.h>
+#include <vlc_plugin.h>
+#include <vlc_stream.h>
+#include <vlc_strings.h>
+#include <vlc_url.h>
+#include <vlc_vector.h>
+#include <vlc_xml.h>
+
+#include "http/connmgr.h"
+#include "http/message.h"
+
+struct dav_entry
+{
+    char    *href;
+    char    *name;
+    bool     is_collection;
+    bool     has_size;
+    uint64_t size;
+    bool     has_mtime;
+    time_t   mtime;
+};
+
+typedef struct VLC_VECTOR(struct dav_entry) dav_entry_vector_t;
+
+typedef struct
+{
+    struct vlc_http_mgr *manager;
+    vlc_url_t            url;
+    bool                 secure;
+    unsigned             port;
+    char                *host;
+    char                *authority;
+    char                *path;      /* absolute, includes any ?query */
+    dav_entry_vector_t   entries;
+} access_sys_t;
+
+static int  Open(vlc_object_t *);
+static void Close(vlc_object_t *);
+
+vlc_module_begin()
+    set_shortname("WebDAV")
+    set_description(N_("WebDAV input"))
+    set_capability("access", 0)
+    set_subcategory(SUBCAT_INPUT_ACCESS)
+    add_shortcut("dav", "davs", "webdav", "webdavs")
+    set_callbacks(Open, Close)
+vlc_module_end()
+
+static char *BuildChildUrl(access_sys_t *sys, const char *href)
+{
+    vlc_url_t base = {
+        .psz_protocol = sys->secure ? (char *)"webdavs" : (char *)"webdav",
+        .psz_host     = sys->host,
+        .i_port       = sys->port,
+        .psz_path     = sys->path,
+    };
+    char *base_uri = vlc_uri_compose(&base);
+    if (base_uri == NULL)
+        return NULL;
+
+    char *abs = vlc_uri_resolve(base_uri, href);
+    free(base_uri);
+    return abs;
+}
+
+static bool IsSelfHref(const access_sys_t *sys, const char *href)
+{
+    vlc_url_t u;
+    if (vlc_UrlParse(&u, href) != 0 || u.psz_path == NULL)
+    {
+        vlc_UrlClean(&u);
+        return false;
+    }
+
+    /* Compare the href path against our request path (without any query). */
+    size_t hlen = strlen(u.psz_path);
+    size_t slen = strcspn(sys->path, "?");
+    bool match = hlen == slen && memcmp(u.psz_path, sys->path, hlen) == 0;
+    vlc_UrlClean(&u);
+    return match;
+}
+
+static char *LeafName(const char *href)
+{
+    size_t len = strcspn(href, "?");
+    while (len > 0 && href[len - 1] == '/')
+        len--;
+    if (len == 0)
+        return NULL;
+
+    size_t start = len;
+    while (start > 0 && href[start - 1] != '/')
+        start--;
+
+    char *name = strndup(href + start, len - start);
+    if (name != NULL)
+        vlc_uri_decode(name);
+    return name;
+}
+
+/* Wrap an in-flight vlc_http_msg as a stream_t so the XML reader can
+ * pull blocks lazily without buffering the full body. */
+static void HttpRespDestroy(stream_t *s)
+{
+    vlc_http_msg_destroy(s->p_sys);
+}
+
+static block_t *HttpRespBlock(stream_t *s, bool *eof)
+{
+    struct vlc_http_msg *resp = s->p_sys;
+    block_t *b = vlc_http_msg_read(resp);
+    if (b == vlc_http_error)
+        return NULL;
+    if (b == NULL)
+        *eof = true;
+    return b;
+}
+
+static int HttpRespControl(stream_t *s, int query, va_list ap)
+{
+    (void)s;
+    switch (query)
+    {
+        case STREAM_CAN_SEEK:
+        case STREAM_CAN_FASTSEEK:
+        case STREAM_CAN_PAUSE:
+        case STREAM_CAN_CONTROL_PACE:
+            *va_arg(ap, bool *) = false;
+            return VLC_SUCCESS;
+        case STREAM_GET_PTS_DELAY:
+            *va_arg(ap, vlc_tick_t *) = DEFAULT_PTS_DELAY;
+            return VLC_SUCCESS;
+        default:
+            return VLC_EGENERIC;
+    }
+}
+
+static stream_t *HttpRespStream(vlc_object_t *parent, struct vlc_http_msg *resp)
+{
+    stream_t *s = vlc_stream_CommonNew(parent, HttpRespDestroy);
+    if (s == NULL)
+    {
+        vlc_http_msg_destroy(resp);
+        return NULL;
+    }
+    s->pf_block = HttpRespBlock;
+    s->pf_control = HttpRespControl;
+    s->p_sys = resp;
+    return s;
+}
+
+static struct vlc_http_msg *DoPropfind(stream_t *access, const char *user,
+                                       const char *pass, int *status)
+{
+    access_sys_t *sys = access->p_sys;
+
+    struct vlc_http_msg *req = vlc_http_req_create("PROPFIND",
+        sys->secure ? "https" : "http", sys->authority, sys->path);
+    if (req == NULL)
+        return NULL;
+
+    vlc_http_msg_add_header(req, "Depth", "1");
+    vlc_http_msg_add_header(req, "Content-Length", "0");
+    vlc_http_msg_add_header(req, "Accept", "application/xml, text/xml");
+
+    char *ua = var_InheritString(access, "http-user-agent");
+    if (ua != NULL)
+    {
+        vlc_http_msg_add_agent(req, ua);
+        free(ua);
+    }
+
+    if (user != NULL && pass != NULL)
+        vlc_http_msg_add_creds_basic(req, false, user, pass);
+
+    struct vlc_http_msg *resp = vlc_http_mgr_request(sys->manager, sys->secure,
+                                                     sys->host, sys->port,
+                                                     req, true, false);
+    vlc_http_msg_destroy(req);
+
+    resp = vlc_http_msg_get_final(resp);
+    if (resp == NULL)
+    {
+        *status = -1;
+        return NULL;
+    }
+    *status = vlc_http_msg_get_status(resp);
+    return resp;
+}
+
+static bool IsDavNamespace(const char *ns)
+{
+    return ns != NULL && !strcmp(ns, "DAV:");
+}
+
+static char *DupXmlText(const char *s)
+{
+    static const char xml_ws[] = " \t\r\n";
+    s += strspn(s, xml_ws);
+    size_t n = strlen(s);
+    while (n > 0 && strchr(xml_ws, s[n - 1]) != NULL)
+        n--;
+    return strndup(s, n);
+}
+
+static void EmitEntry(stream_t *access, struct vlc_readdir_helper *rdh,
+                      const struct dav_entry *e)
+{
+    access_sys_t *sys = access->p_sys;
+
+    char *url = BuildChildUrl(sys, e->href);
+    if (url == NULL)
+        return;
+
+    const char *name = e->name;
+    char *leaf = NULL;
+    if (name == NULL || *name == '\0')
+    {
+        leaf = LeafName(e->href);
+        name = leaf;
+    }
+    if (name == NULL)
+    {
+        free(url);
+        return;
+    }
+
+    int type = e->is_collection ? ITEM_TYPE_DIRECTORY : ITEM_TYPE_FILE;
+    input_item_t *item = NULL;
+    vlc_readdir_helper_additem(rdh, url, NULL, name, type, ITEM_NET, &item);
+
+    if (item != NULL && !e->is_collection)
+    {
+        if (e->has_size)
+            input_item_AddStat(item, "size", e->size);
+        if (e->has_mtime)
+            input_item_AddStat(item, "mtime", e->mtime);
+    }
+
+    free(leaf);
+    free(url);
+}
+
+static void ResetEntry(struct dav_entry *e)
+{
+    free(e->href);
+    free(e->name);
+    memset(e, 0, sizeof(*e));
+}
+
+/*
+ * Parse a WebDAV PROPFIND multistatus response (RFC 4918 §14.16):
+ *
+ *   <D:multistatus xmlns:D="DAV:">
+ *     <D:response>
+ *       <D:href>/path/to/resource</D:href>
+ *       <D:propstat>
+ *         <D:prop>
+ *           <D:resourcetype><D:collection/></D:resourcetype>     (optional)
+ *           <D:getcontentlength>1234</D:getcontentlength>        (optional)
+ *           <D:getlastmodified>RFC1123 date</D:getlastmodified>  (optional)
+ *           <D:displayname>name</D:displayname>                  (optional)
+ *         </D:prop>
+ *       </D:propstat>
+ *     </D:response>
+ *     ... one <D:response> per child resource ...
+ *   </D:multistatus>
+ *
+ * The DAV: namespace prefix varies between servers, so we match on the
+ * resolved URI. With Depth: 1 the first <response> is the request target
+ * itself, which lets us report self_is_collection_out from the same parse.
+ */
+static int ParseMultistatus(stream_t *access, stream_t *body,
+                            bool *self_is_collection_out)
+{
+    access_sys_t *sys = access->p_sys;
+    xml_reader_t *reader = xml_ReaderCreate(access, body);
+    if (reader == NULL)
+        return VLC_EGENERIC;
+
+    struct dav_entry cur = { 0 };
+    bool in_response = false;
+    bool in_prop = false;
+    bool in_resourcetype = false;
+    enum { FIELD_NONE, FIELD_HREF, FIELD_NAME,
+           FIELD_SIZE, FIELD_MTIME } field = FIELD_NONE;
+
+    const char *node, *ns;
+    int type;
+
+    while ((type = xml_ReaderNextNodeNS(reader, &node, &ns)) > 0)
+    {
+        if (type == XML_READER_STARTELEM)
+        {
+            bool empty = xml_ReaderIsEmptyElement(reader) == 1;
+            field = FIELD_NONE;
+
+            if (!IsDavNamespace(ns))
+                continue;
+
+            /* we get the prefixed name, e.g. "D:href" */
+            const char *local = strchr(node, ':');
+            local = local ? local + 1 : node;
+
+            if (!in_response)
+            {
+                if (!strcmp(local, "response"))
+                {
+                    in_response = true;
+                    ResetEntry(&cur);
+                }
+                continue;
+            }
+
+            if (!in_prop)
+            {
+                if (!strcmp(local, "href"))
+                    field = FIELD_HREF;
+                else if (!strcmp(local, "prop"))
+                    in_prop = !empty;
+            }
+            else if (in_resourcetype)
+            {
+                if (!strcmp(local, "collection"))
+                    cur.is_collection = true;
+            }
+            else if (!strcmp(local, "resourcetype"))
+                in_resourcetype = !empty;
+            else if (!strcmp(local, "getcontentlength"))
+                field = FIELD_SIZE;
+            else if (!strcmp(local, "getlastmodified"))
+                field = FIELD_MTIME;
+            else if (!strcmp(local, "displayname"))
+                field = FIELD_NAME;
+        }
+        else if (type == XML_READER_TEXT)
+        {
+            if (!in_response || field == FIELD_NONE)
+                continue;
+
+            char *value = DupXmlText(node);
+            if (value == NULL)
+                continue;
+
+            switch (field)
+            {
+                case FIELD_HREF:
+                    if (cur.href == NULL)
+                        cur.href = value;
+                    else
+                        free(value);
+                    break;
+                case FIELD_NAME:
+                    if (cur.name == NULL)
+                        cur.name = value;
+                    else
+                        free(value);
+                    break;
+                case FIELD_SIZE:
+                {
+                    char *end;
+                    unsigned long long v = strtoull(value, &end, 10);
+                    if (end != value)
+                    {
+                        cur.size = v;
+                        cur.has_size = true;
+                    }
+                    free(value);
+                    break;
+                }
+                case FIELD_MTIME:
+                {
+                    time_t t = vlc_http_mktime(value);
+                    if (t != (time_t)-1)
+                    {
+                        cur.mtime = t;
+                        cur.has_mtime = true;
+                    }
+                    free(value);
+                    break;
+                }
+                default:
+                    free(value);
+                    break;
+            }
+            field = FIELD_NONE;
+        }
+        else if (type == XML_READER_ENDELEM)
+        {
+            field = FIELD_NONE;
+            if (!IsDavNamespace(ns))
+                continue;
+
+            const char *local = strchr(node, ':');
+            local = local ? local + 1 : node;
+
+            if (!strcmp(local, "resourcetype"))
+                in_resourcetype = false;
+            else if (!strcmp(local, "prop"))
+            {
+                in_prop = false;
+                in_resourcetype = false;
+            }
+            else if (!strcmp(local, "response"))
+            {
+                /* memset on successful push transfers href/name ownership
+                 * to the vector; ResetEntry frees them otherwise. */
+                if (cur.href != NULL && IsSelfHref(sys, cur.href))
+                {
+                    if (self_is_collection_out != NULL)
+                        *self_is_collection_out = cur.is_collection;
+                    ResetEntry(&cur);
+                }
+                else if (cur.href == NULL
+                      || !vlc_vector_push(&sys->entries, cur))
+                    ResetEntry(&cur);
+                else
+                    memset(&cur, 0, sizeof(cur));
+                in_response = false;
+                in_prop = false;
+                in_resourcetype = false;
+            }
+        }
+    }
+
+    ResetEntry(&cur);
+    xml_ReaderDelete(reader);
+    return VLC_SUCCESS;
+}
+
+static int DirRead(stream_t *access, input_item_node_t *node)
+{
+    access_sys_t *sys = access->p_sys;
+
+    struct vlc_readdir_helper rdh;
+    vlc_readdir_helper_init(&rdh, access, node);
+    const struct dav_entry *e;
+    vlc_vector_foreach_ref(e, &sys->entries)
+        EmitEntry(access, &rdh, e);
+    vlc_readdir_helper_finish(&rdh, true);
+    return VLC_SUCCESS;
+}
+
+static int Open(vlc_object_t *obj)
+{
+    stream_t *access = (stream_t *)obj;
+    access_sys_t *sys = vlc_obj_calloc(obj, 1, sizeof(*sys));
+    if (unlikely(sys == NULL))
+        return VLC_ENOMEM;
+    access->p_sys = sys;
+    int ret = VLC_EGENERIC;
+
+    if (vlc_UrlParseFixup(&sys->url, access->psz_url) != 0
+     || sys->url.psz_host == NULL || sys->url.psz_protocol == NULL)
+        goto clean;
+
+    const char *proto = sys->url.psz_protocol;
+    if (!vlc_ascii_strcasecmp(proto, "dav")
+     || !vlc_ascii_strcasecmp(proto, "webdav"))
+        sys->secure = false;
+    else if (!vlc_ascii_strcasecmp(proto, "davs")
+          || !vlc_ascii_strcasecmp(proto, "webdavs"))
+        sys->secure = true;
+    else
+        goto clean;
+
+    sys->host = strdup(sys->url.psz_host);
+    sys->port = sys->url.i_port;
+    sys->authority = vlc_http_authority(sys->url.psz_host, sys->url.i_port);
+    if (sys->host == NULL || sys->authority == NULL)
+        goto clean;
+
+    const char *path = sys->url.psz_path ? sys->url.psz_path : "/";
+    if (sys->url.psz_option != NULL)
+    {
+        if (asprintf(&sys->path, "%s?%s", path, sys->url.psz_option) < 0)
+            sys->path = NULL;
+    }
+    else
+        sys->path = strdup(path);
+    if (sys->path == NULL)
+        goto clean;
+
+    sys->manager = vlc_http_mgr_create(obj, NULL);
+    if (sys->manager == NULL)
+        goto clean;
+
+    vlc_credential crd;
+    vlc_credential_init(&crd, &sys->url);
+
+    const char *user = sys->url.psz_username;
+    const char *pass = sys->url.psz_password;
+    if (vlc_credential_get(&crd, obj, NULL, NULL, NULL, NULL) == 0)
+    {
+        user = crd.psz_username;
+        pass = crd.psz_password;
+    }
+
+    struct vlc_http_msg *resp = NULL;
+    int status = -1;
+
+    for (;;)
+    {
+        if (resp != NULL)
+            vlc_http_msg_destroy(resp);
+        resp = DoPropfind(access, user, pass, &status);
+        if (resp == NULL)
+        {
+            vlc_credential_clean(&crd);
+            goto clean;
+        }
+
+        if (status != 401)
+            break;
+
+        char *realm = vlc_http_msg_get_basic_realm(resp);
+        if (realm == NULL)
+            break;
+
+        crd.psz_realm = realm;
+        crd.psz_authtype = "Basic";
+        int ok = vlc_credential_get(&crd, obj, NULL, NULL,
+                                    _("HTTP authentication"),
+                                    _("Please enter a valid login name and "
+                                      "a password for realm %s."), realm);
+        free(realm);
+        if (ok != 0)
+            break;
+        user = crd.psz_username;
+        pass = crd.psz_password;
+    }
+
+    if (status == 401 || status < 0)
+    {
+        msg_Err(access, "PROPFIND failed (status %d)", status);
+        vlc_http_msg_destroy(resp);
+        vlc_credential_clean(&crd);
+        goto clean;
+    }
+
+    /* Follow Location on redirects. */
+    if (status == 301 || status == 302 || status == 303
+     || status == 307 || status == 308)
+    {
+        const char *loc = vlc_http_msg_get_header(resp, "Location");
+        if (loc != NULL)
+        {
+            char *abs = vlc_uri_resolve(access->psz_url, loc);
+            if (abs != NULL)
+            {
+                access->psz_url = abs;
+                vlc_http_msg_destroy(resp);
+                vlc_credential_clean(&crd);
+                ret = VLC_ACCESS_REDIRECT;
+                goto clean;
+            }
+        }
+        msg_Err(access, "redirect without usable Location");
+        vlc_http_msg_destroy(resp);
+        vlc_credential_clean(&crd);
+        goto clean;
+    }
+
+    if (status != 207)
+    {
+        /* Not a multistatus: redirect to http(s) so the regular HTTP access
+         * can play the target as a single file. */
+        char *abs;
+        if (asprintf(&abs, "%s://%s%s", sys->secure ? "https" : "http",
+                     sys->authority, sys->path) >= 0)
+        {
+            access->psz_url = abs;
+            ret = VLC_ACCESS_REDIRECT;
+        }
+        vlc_http_msg_destroy(resp);
+        vlc_credential_clean(&crd);
+        goto clean;
+    }
+
+    vlc_credential_store(&crd, obj);
+    vlc_credential_clean(&crd);
+
+    bool self_is_collection = false;
+    stream_t *body = HttpRespStream(obj, resp);
+    if (body != NULL)
+    {
+        ParseMultistatus(access, body, &self_is_collection);
+        vlc_stream_Delete(body);
+    }
+
+    if (!self_is_collection)
+    {
+        /* item is not a collection, so it's a file that we want to play
+         * --> generate a http/s URL to allow playback */
+        char *abs;
+        if (asprintf(&abs, "%s://%s%s", sys->secure ? "https" : "http",
+                     sys->authority, sys->path) >= 0)
+        {
+            access->psz_url = abs;
+            ret = VLC_ACCESS_REDIRECT;
+            goto clean;
+        }
+        msg_Err(access, "failed to build redirect URL");
+        goto clean;
+    }
+
+    access->pf_readdir = DirRead;
+    access->pf_control = access_vaDirectoryControlHelper;
+    return VLC_SUCCESS;
+
+clean:
+    if (sys->manager != NULL)
+        vlc_http_mgr_destroy(sys->manager);
+    struct dav_entry *e;
+    vlc_vector_foreach_ref(e, &sys->entries)
+    {
+        free(e->href);
+        free(e->name);
+    }
+    vlc_vector_destroy(&sys->entries);
+    free(sys->path);
+    free(sys->authority);
+    free(sys->host);
+    vlc_UrlClean(&sys->url);
+    return ret;
+}
+
+static void Close(vlc_object_t *obj)
+{
+    stream_t *access = (stream_t *)obj;
+    access_sys_t *sys = access->p_sys;
+
+    if (sys->manager != NULL)
+        vlc_http_mgr_destroy(sys->manager);
+    struct dav_entry *e;
+    vlc_vector_foreach_ref(e, &sys->entries)
+    {
+        free(e->href);
+        free(e->name);
+    }
+    vlc_vector_destroy(&sys->entries);
+    free(sys->path);
+    free(sys->authority);
+    free(sys->host);
+    vlc_UrlClean(&sys->url);
+}


=====================================
modules/services_discovery/avahi.c
=====================================
@@ -97,6 +97,8 @@ static const struct
     { "smb", "_smb._tcp", false },
     { "nfs", "_nfs._tcp", false },
     { "sftp", "_sftp-ssh._tcp", false },
+    { "webdav", "_webdav._tcp", false },
+    { "webdavs", "_webdavs._tcp", false },
     { "rtsp", "_rtsp._tcp", false },
     { "chromecast", "_googlecast._tcp", true },
 };


=====================================
modules/services_discovery/bonjour.m
=====================================
@@ -201,6 +201,16 @@ static NSString * ipAddressAsStringForData(NSData * data)
                                        VLCBonjourIsRenderer         : @(NO),
                                        VLCBonjourProtocolModules     : @[@"sftp"]
                                        };
+    NSDictionary *VLCWebDavProtocol = @{ VLCBonjourProtocolName       : @"webdav",
+                                         VLCBonjourProtocolServiceName: @"_webdav._tcp.",
+                                         VLCBonjourIsRenderer         : @(NO),
+                                         VLCBonjourProtocolModules    : @[@"webdav"]
+                                         };
+    NSDictionary *VLCWebDavsProtocol = @{ VLCBonjourProtocolName       : @"webdavs",
+                                          VLCBonjourProtocolServiceName: @"_webdavs._tcp.",
+                                          VLCBonjourIsRenderer         : @(NO),
+                                          VLCBonjourProtocolModules    : @[@"webdavs"]
+                                          };
     NSDictionary *VLCCastProtocol = @{ VLCBonjourProtocolName       : @"chromecast",
                                        VLCBonjourProtocolServiceName: @"_googlecast._tcp.",
                                        VLCBonjourIsRenderer         : @(YES),
@@ -212,6 +222,8 @@ static NSString * ipAddressAsStringForData(NSData * data)
                                       VLCSmbProtocol,
                                       VLCNfsProtocol,
                                       VLCSftpProtocol,
+                                      VLCWebDavProtocol,
+                                      VLCWebDavsProtocol,
                                       VLCCastProtocol];
 
     _rawNetServices = [[NSMutableArray alloc] init];


=====================================
modules/services_discovery/microdns.c
=====================================
@@ -82,6 +82,8 @@ static const struct
     { "smb", "_smb._tcp.local", false, 0 },
     { "nfs", "_nfs._tcp.local", false, 0 },
     { "sftp", "_sftp-ssh._tcp.local", false, 0 },
+    { "webdav", "_webdav._tcp.local", false, 0 },
+    { "webdavs", "_webdavs._tcp.local", false, 0 },
     { "rtsp", "_rtsp._tcp.local", false, 0 },
     { "chromecast", "_googlecast._tcp.local", true, VLC_RENDERER_CAN_AUDIO },
 };


=====================================
po/POTFILES.in
=====================================
@@ -209,6 +209,7 @@ modules/access/vcd/vcd.c
 modules/access/vdr.c
 modules/access/vnc.c
 modules/access/wasapi.c
+modules/access/webdav.c
 modules/access_output/dummy.c
 modules/access_output/file.c
 modules/access_output/http-put.c



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/1e7e99a838e91a483430aac9a749d75fd601871a...379275229e068ebb3a990fe4c586e0f91e319c98

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/1e7e99a838e91a483430aac9a749d75fd601871a...379275229e068ebb3a990fe4c586e0f91e319c98
You're receiving this email because of your account on code.videolan.org.




More information about the vlc-commits mailing list