2 * URL utility functions
3 * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
5 * This file is part of FFmpeg.
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
30 #include "libavutil/avassert.h"
31 #include "libavutil/avstring.h"
32 #include "libavutil/error.h"
33 #include "libavutil/mem.h"
37 * URL utility functions.
40 int ff_url_join(char *str
, int size
, const char *proto
,
41 const char *authorization
, const char *hostname
,
42 int port
, const char *fmt
, ...)
45 struct addrinfo hints
= { 0 }, *ai
;
50 av_strlcatf(str
, size
, "%s://", proto
);
51 if (authorization
&& authorization
[0])
52 av_strlcatf(str
, size
, "%s@", authorization
);
53 #if CONFIG_NETWORK && defined(AF_INET6)
54 /* Determine if hostname is a numerical IPv6 address,
55 * properly escape it within [] in that case. */
56 hints
.ai_flags
= AI_NUMERICHOST
;
57 if (!getaddrinfo(hostname
, NULL
, &hints
, &ai
)) {
58 if (ai
->ai_family
== AF_INET6
) {
59 av_strlcat(str
, "[", size
);
60 av_strlcat(str
, hostname
, size
);
61 av_strlcat(str
, "]", size
);
63 av_strlcat(str
, hostname
, size
);
68 /* Not an IPv6 address, just output the plain string. */
69 av_strlcat(str
, hostname
, size
);
72 av_strlcatf(str
, size
, ":%d", port
);
75 size_t len
= strlen(str
);
78 vsnprintf(str
+ len
, size
> len
? size
- len
: 0, fmt
, vl
);
84 static const char *find_delim(const char *delim
, const char *cur
, const char *end
)
86 while (cur
< end
&& !strchr(delim
, *cur
))
91 int ff_url_decompose(URLComponents
*uc
, const char *url
, const char *end
)
93 const char *cur
, *aend
, *p
;
97 end
= url
+ strlen(url
);
102 p
= find_delim(":/?#", cur
, end
); /* lavf "schemes" can contain options but not some RFC 3986 delimiters */
108 if (end
- cur
>= 2 && cur
[0] == '/' && cur
[1] == '/') {
110 aend
= find_delim("/?#", cur
, end
);
114 p
= find_delim("@", cur
, aend
);
120 if (*cur
== '[') { /* hello IPv6, thanks for using colons! */
121 p
= find_delim("]", cur
, aend
);
123 return AVERROR(EINVAL
);
124 if (p
+ 1 < aend
&& p
[1] != ':')
125 return AVERROR(EINVAL
);
128 cur
= find_delim(":", cur
, aend
);
135 uc
->userinfo
= uc
->host
= uc
->port
= cur
;
140 cur
= find_delim("?#", cur
, end
);
145 cur
= find_delim("#", cur
, end
);
154 static int is_fq_dos_path(const char *path
)
156 if ((path
[0] >= 'a' && path
[0] <= 'z' || path
[0] >= 'A' && path
[0] <= 'Z') &&
158 (path
[2] == '/' || path
[2] == '\\'))
160 if ((path
[0] == '/' || path
[0] == '\\') &&
161 (path
[1] == '/' || path
[1] == '\\'))
166 static int append_path(char *root
, char *out_end
, char **rout
,
167 const char *in
, const char *in_end
)
170 const char *d
, *next
;
172 if (in
< in_end
&& *in
== '/')
173 in
++; /* already taken care of */
174 while (in
< in_end
) {
175 d
= find_delim("/", in
, in_end
);
176 next
= d
+ (d
< in_end
&& *d
== '/');
177 if (d
- in
== 1 && in
[0] == '.') {
179 } else if (d
- in
== 2 && in
[0] == '.' && in
[1] == '.') {
180 av_assert1(out
[-1] == '/');
182 while (out
> root
&& (--out
)[-1] != '/');
184 if (out_end
- out
< next
- in
)
185 return AVERROR(ENOMEM
);
186 memmove(out
, in
, next
- in
);
195 int ff_make_absolute_url2(char *buf
, int size
, const char *base
,
196 const char *rel
, int handle_dos_paths
)
198 URLComponents ub
, uc
;
199 char *out
, *out_end
, *path
;
200 const char *keep
, *base_path_end
;
201 int use_base_path
, simplify_path
= 0, ret
;
202 const char *base_separators
= "/";
205 For HTTP, http://server/site/page + ../media/file
206 should resolve into http://server/media/file
207 but for filesystem access, dir/playlist + ../media/file
208 should resolve into dir/../media/file
209 because dir could be a symlink, and .. points to
210 the actual parent of the target directory.
212 We'll consider that URLs with an actual scheme and authority,
213 i.e. starting with scheme://, need parent dir simplification,
214 while bare paths or pseudo-URLs starting with proto: without
215 the double slash do not.
217 For real URLs, the processing is similar to the algorithm described
219 https://tools.ietf.org/html/rfc3986#section-5
223 return AVERROR(ENOMEM
);
225 out_end
= buf
+ size
- 1;
229 if (handle_dos_paths
) {
230 if ((ret
= ff_url_decompose(&ub
, base
, NULL
)) < 0)
232 if (is_fq_dos_path(base
) || av_strstart(base
, "file:", NULL
) || ub
.path
== ub
.url
) {
233 base_separators
= "/\\";
234 if (is_fq_dos_path(rel
))
238 if ((ret
= ff_url_decompose(&ub
, base
, NULL
)) < 0 ||
239 (ret
= ff_url_decompose(&uc
, rel
, NULL
)) < 0)
243 #define KEEP(component, also) do { \
244 if (uc.url_component_end_##component == uc.url && \
245 ub.url_component_end_##component > keep) { \
246 keep = ub.url_component_end_##component; \
251 KEEP(authority_full
, simplify_path
= 1;);
256 #define COPY(start, end) do { \
257 size_t len = end - start; \
258 if (len > out_end - out) { \
259 ret = AVERROR(ENOMEM); \
262 memmove(out, start, len); \
266 COPY(uc
.url
, uc
.path
);
268 use_base_path
= URL_COMPONENT_HAVE(ub
, path
) && keep
<= ub
.path
;
269 if (uc
.path
> uc
.url
)
271 if (URL_COMPONENT_HAVE(uc
, path
) && uc
.path
[0] == '/')
274 base_path_end
= ub
.url_component_end_path
;
275 if (URL_COMPONENT_HAVE(uc
, path
))
276 while (base_path_end
> ub
.path
&& !strchr(base_separators
, base_path_end
[-1]))
281 if (URL_COMPONENT_HAVE(uc
, scheme
))
283 if (URL_COMPONENT_HAVE(uc
, authority
))
285 /* No path at all, leave it */
286 if (!use_base_path
&& !URL_COMPONENT_HAVE(uc
, path
))
290 const char *root
= "/";
291 COPY(root
, root
+ 1);
294 ret
= append_path(path
, out_end
, &out
, ub
.path
, base_path_end
);
298 if (URL_COMPONENT_HAVE(uc
, path
)) {
299 ret
= append_path(path
, out_end
, &out
, uc
.path
, uc
.url_component_end_path
);
305 COPY(ub
.path
, base_path_end
);
306 COPY(uc
.path
, uc
.url_component_end_path
);
309 COPY(uc
.url_component_end_path
, uc
.end
);
315 snprintf(buf
, size
, "invalid:%s",
316 ret
== AVERROR(ENOMEM
) ? "truncated" :
317 ret
== AVERROR(EINVAL
) ? "syntax_error" : "");
321 int ff_make_absolute_url(char *buf
, int size
, const char *base
,
324 return ff_make_absolute_url2(buf
, size
, base
, rel
, HAVE_DOS_PATHS
);
327 AVIODirEntry
*ff_alloc_dir_entry(void)
329 AVIODirEntry
*entry
= av_mallocz(sizeof(AVIODirEntry
));
331 entry
->type
= AVIO_ENTRY_UNKNOWN
;
333 entry
->modification_timestamp
= -1;
334 entry
->access_timestamp
= -1;
335 entry
->status_change_timestamp
= -1;
337 entry
->group_id
= -1;
338 entry
->filemode
= -1;