[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