PR #25014 opened by timrae URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25014 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25014.patch
The path from av_url_split() was passed to the SFTP server as-is, so any percent-encoded character in the URL was looked up literally. This breaks URLs produced by applications that encode paths per RFC 3986, e.g. sftp://host/My%20Movie.mkv fails with "SFTP server: No such file", while sftp://host/My Movie.mkv works. Decode the path in libssh_connect(), and the destination path in libssh_move(), so both refer to the same file the URL names. Paths without escapes are unchanged. A file whose name contains a literal '%' followed by two hex digits must now be written with the '%' escaped as %25. >From 6cb33a3fe6ab10151372b470462d8ef2f3c6b6c6 Mon Sep 17 00:00:00 2001 From: Tim Rae <[email protected]> Date: Sun, 11 Oct 2026 17:48:21 +0200 Subject: [PATCH] avformat/libssh: percent-decode the URL path The path from av_url_split() was passed to the SFTP server as-is, so any percent-encoded character in the URL was looked up literally. This breaks URLs produced by applications that encode paths per RFC 3986, e.g. sftp://host/My%20Movie.mkv fails with "SFTP server: No such file", while sftp://host/My Movie.mkv works. Decode the path in libssh_connect(), and the destination path in libssh_move(), so both refer to the same file the URL names. Paths without escapes are unchanged. A file whose name contains a literal '%' followed by two hex digits must now be written with the '%' escaped as %25. Signed-off-by: Tim Rae <[email protected]> --- libavformat/libssh.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/libavformat/libssh.c b/libavformat/libssh.c index 470175924d..eb30d0e281 100644 --- a/libavformat/libssh.c +++ b/libavformat/libssh.c @@ -29,6 +29,7 @@ #include "avformat.h" #include "internal.h" #include "url.h" +#include "urldecode.h" typedef struct { const AVClass *class; @@ -230,6 +231,16 @@ static av_cold int libssh_close(URLContext *h) return 0; } +static int libssh_decode_path(char *path, size_t path_size) +{ + char *decoded = ff_urldecode(path, 0); + if (!decoded) + return AVERROR(ENOMEM); + av_strlcpy(path, decoded, path_size); + av_free(decoded); + return 0; +} + static av_cold int libssh_connect(URLContext *h, const char *url, char *path, size_t path_size) { LIBSSHContext *libssh = h->priv_data; @@ -245,6 +256,9 @@ static av_cold int libssh_connect(URLContext *h, const char *url, char *path, si path, path_size, url); + if ((ret = libssh_decode_path(path, path_size)) < 0) + return ret; + if (!(*path)) av_strlcpy(path, "/", path_size); @@ -498,6 +512,9 @@ static int libssh_move(URLContext *h_src, URLContext *h_dst) return AVERROR(EINVAL); } + if ((ret = libssh_decode_path(path_dst, sizeof(path_dst))) < 0) + return ret; + if ((ret = libssh_connect(h_src, h_src->filename, path_src, sizeof(path_src))) < 0) goto cleanup; -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
