You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

334 lines
9.4KB

  1. /*
  2. * URL utility functions
  3. * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
  4. *
  5. * This file is part of FFmpeg.
  6. *
  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.
  11. *
  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.
  16. *
  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
  20. */
  21. #include "avformat.h"
  22. #include "internal.h"
  23. #include "config.h"
  24. #include "url.h"
  25. #if CONFIG_NETWORK
  26. #include "network.h"
  27. #endif
  28. #include "libavutil/avassert.h"
  29. #include "libavutil/avstring.h"
  30. /**
  31. * @file
  32. * URL utility functions.
  33. */
  34. int ff_url_join(char *str, int size, const char *proto,
  35. const char *authorization, const char *hostname,
  36. int port, const char *fmt, ...)
  37. {
  38. #if CONFIG_NETWORK
  39. struct addrinfo hints = { 0 }, *ai;
  40. #endif
  41. str[0] = '\0';
  42. if (proto)
  43. av_strlcatf(str, size, "%s://", proto);
  44. if (authorization && authorization[0])
  45. av_strlcatf(str, size, "%s@", authorization);
  46. #if CONFIG_NETWORK && defined(AF_INET6)
  47. /* Determine if hostname is a numerical IPv6 address,
  48. * properly escape it within [] in that case. */
  49. hints.ai_flags = AI_NUMERICHOST;
  50. if (!getaddrinfo(hostname, NULL, &hints, &ai)) {
  51. if (ai->ai_family == AF_INET6) {
  52. av_strlcat(str, "[", size);
  53. av_strlcat(str, hostname, size);
  54. av_strlcat(str, "]", size);
  55. } else {
  56. av_strlcat(str, hostname, size);
  57. }
  58. freeaddrinfo(ai);
  59. } else
  60. #endif
  61. /* Not an IPv6 address, just output the plain string. */
  62. av_strlcat(str, hostname, size);
  63. if (port >= 0)
  64. av_strlcatf(str, size, ":%d", port);
  65. if (fmt) {
  66. va_list vl;
  67. size_t len = strlen(str);
  68. va_start(vl, fmt);
  69. vsnprintf(str + len, size > len ? size - len : 0, fmt, vl);
  70. va_end(vl);
  71. }
  72. return strlen(str);
  73. }
  74. static const char *find_delim(const char *delim, const char *cur, const char *end)
  75. {
  76. while (cur < end && !strchr(delim, *cur))
  77. cur++;
  78. return cur;
  79. }
  80. int ff_url_decompose(URLComponents *uc, const char *url, const char *end)
  81. {
  82. const char *cur, *aend, *p;
  83. av_assert0(url);
  84. if (!end)
  85. end = url + strlen(url);
  86. cur = uc->url = url;
  87. /* scheme */
  88. uc->scheme = cur;
  89. p = find_delim(":/?#", cur, end); /* lavf "schemes" can contain options but not some RFC 3986 delimiters */
  90. if (*p == ':')
  91. cur = p + 1;
  92. /* authority */
  93. uc->authority = cur;
  94. if (end - cur >= 2 && cur[0] == '/' && cur[1] == '/') {
  95. cur += 2;
  96. aend = find_delim("/?#", cur, end);
  97. /* userinfo */
  98. uc->userinfo = cur;
  99. p = find_delim("@", cur, aend);
  100. if (*p == '@')
  101. cur = p + 1;
  102. /* host */
  103. uc->host = cur;
  104. if (*cur == '[') { /* hello IPv6, thanks for using colons! */
  105. p = find_delim("]", cur, aend);
  106. if (*p != ']')
  107. return AVERROR(EINVAL);
  108. if (p + 1 < aend && p[1] != ':')
  109. return AVERROR(EINVAL);
  110. cur = p + 1;
  111. } else {
  112. cur = find_delim(":", cur, aend);
  113. }
  114. /* port */
  115. uc->port = cur;
  116. cur = aend;
  117. } else {
  118. uc->userinfo = uc->host = uc->port = cur;
  119. }
  120. /* path */
  121. uc->path = cur;
  122. cur = find_delim("?#", cur, end);
  123. /* query */
  124. uc->query = cur;
  125. if (*cur == '?')
  126. cur = find_delim("#", cur, end);
  127. /* fragment */
  128. uc->fragment = cur;
  129. uc->end = end;
  130. return 0;
  131. }
  132. static int is_fq_dos_path(const char *path)
  133. {
  134. if ((path[0] >= 'a' && path[0] <= 'z' || path[0] >= 'A' && path[0] <= 'Z') &&
  135. path[1] == ':' &&
  136. (path[2] == '/' || path[2] == '\\'))
  137. return 1;
  138. if ((path[0] == '/' || path[0] == '\\') &&
  139. (path[1] == '/' || path[1] == '\\'))
  140. return 1;
  141. return 0;
  142. }
  143. static int append_path(char *root, char *out_end, char **rout,
  144. const char *in, const char *in_end)
  145. {
  146. char *out = *rout;
  147. const char *d, *next;
  148. if (in < in_end && *in == '/')
  149. in++; /* already taken care of */
  150. while (in < in_end) {
  151. d = find_delim("/", in, in_end);
  152. next = d + (d < in_end && *d == '/');
  153. if (d - in == 1 && in[0] == '.') {
  154. /* skip */
  155. } else if (d - in == 2 && in[0] == '.' && in[1] == '.') {
  156. av_assert1(out[-1] == '/');
  157. if (out - root > 1)
  158. while (out > root && (--out)[-1] != '/');
  159. } else {
  160. if (out_end - out < next - in)
  161. return AVERROR(ENOMEM);
  162. memmove(out, in, next - in);
  163. out += next - in;
  164. }
  165. in = next;
  166. }
  167. *rout = out;
  168. return 0;
  169. }
  170. int ff_make_absolute_url(char *buf, int size, const char *base,
  171. const char *rel)
  172. {
  173. URLComponents ub, uc;
  174. char *out, *out_end, *path;
  175. const char *keep, *base_path_end;
  176. int use_base_path, simplify_path = 0, ret;
  177. const char *base_separators = "/";
  178. /* This is tricky.
  179. For HTTP, http://server/site/page + ../media/file
  180. should resolve into http://server/media/file
  181. but for filesystem access, dir/playlist + ../media/file
  182. should resolve into dir/../media/file
  183. because dir could be a symlink, and .. points to
  184. the actual parent of the target directory.
  185. We'll consider that URLs with an actual scheme and authority,
  186. i.e. starting with scheme://, need parent dir simplification,
  187. while bare paths or pseudo-URLs starting with proto: without
  188. the double slash do not.
  189. For real URLs, the processing is similar to the algorithm described
  190. here:
  191. https://tools.ietf.org/html/rfc3986#section-5
  192. */
  193. if (!size)
  194. return AVERROR(ENOMEM);
  195. out = buf;
  196. out_end = buf + size - 1;
  197. if (!base)
  198. base = "";
  199. if (HAVE_DOS_PATHS) {
  200. if ((ret = ff_url_decompose(&ub, base, NULL)) < 0)
  201. goto error;
  202. if (is_fq_dos_path(base) || av_strstart(base, "file:", NULL) || ub.path == ub.url) {
  203. base_separators = "/\\";
  204. if (is_fq_dos_path(rel))
  205. base = "";
  206. }
  207. }
  208. if ((ret = ff_url_decompose(&ub, base, NULL)) < 0 ||
  209. (ret = ff_url_decompose(&uc, rel, NULL)) < 0)
  210. goto error;
  211. keep = ub.url;
  212. #define KEEP(component, also) do { \
  213. if (uc.url_component_end_##component == uc.url && \
  214. ub.url_component_end_##component > keep) { \
  215. keep = ub.url_component_end_##component; \
  216. also \
  217. } \
  218. } while (0)
  219. KEEP(scheme, );
  220. KEEP(authority_full, simplify_path = 1;);
  221. KEEP(path,);
  222. KEEP(query,);
  223. KEEP(fragment,);
  224. #undef KEEP
  225. #define COPY(start, end) do { \
  226. size_t len = end - start; \
  227. if (len > out_end - out) { \
  228. ret = AVERROR(ENOMEM); \
  229. goto error; \
  230. } \
  231. memmove(out, start, len); \
  232. out += len; \
  233. } while (0)
  234. COPY(ub.url, keep);
  235. COPY(uc.url, uc.path);
  236. use_base_path = URL_COMPONENT_HAVE(ub, path) && keep <= ub.path;
  237. if (uc.path > uc.url)
  238. use_base_path = 0;
  239. if (URL_COMPONENT_HAVE(uc, path) && uc.path[0] == '/')
  240. use_base_path = 0;
  241. if (use_base_path) {
  242. base_path_end = ub.url_component_end_path;
  243. if (URL_COMPONENT_HAVE(uc, path))
  244. while (base_path_end > ub.path && !strchr(base_separators, base_path_end[-1]))
  245. base_path_end--;
  246. }
  247. if (keep > ub.path)
  248. simplify_path = 0;
  249. if (URL_COMPONENT_HAVE(uc, scheme))
  250. simplify_path = 0;
  251. if (URL_COMPONENT_HAVE(uc, authority))
  252. simplify_path = 1;
  253. /* No path at all, leave it */
  254. if (!use_base_path && !URL_COMPONENT_HAVE(uc, path))
  255. simplify_path = 0;
  256. if (simplify_path) {
  257. const char *root = "/";
  258. COPY(root, root + 1);
  259. path = out;
  260. if (use_base_path) {
  261. ret = append_path(path, out_end, &out, ub.path, base_path_end);
  262. if (ret < 0)
  263. goto error;
  264. }
  265. if (URL_COMPONENT_HAVE(uc, path)) {
  266. ret = append_path(path, out_end, &out, uc.path, uc.url_component_end_path);
  267. if (ret < 0)
  268. goto error;
  269. }
  270. } else {
  271. if (use_base_path)
  272. COPY(ub.path, base_path_end);
  273. COPY(uc.path, uc.url_component_end_path);
  274. }
  275. COPY(uc.url_component_end_path, uc.end);
  276. #undef COPY
  277. *out = 0;
  278. return 0;
  279. error:
  280. snprintf(buf, size, "invalid:%s",
  281. ret == AVERROR(ENOMEM) ? "truncated" :
  282. ret == AVERROR(EINVAL) ? "syntax_error" : "");
  283. return ret;
  284. }
  285. AVIODirEntry *ff_alloc_dir_entry(void)
  286. {
  287. AVIODirEntry *entry = av_mallocz(sizeof(AVIODirEntry));
  288. if (entry) {
  289. entry->type = AVIO_ENTRY_UNKNOWN;
  290. entry->size = -1;
  291. entry->modification_timestamp = -1;
  292. entry->access_timestamp = -1;
  293. entry->status_change_timestamp = -1;
  294. entry->user_id = -1;
  295. entry->group_id = -1;
  296. entry->filemode = -1;
  297. }
  298. return entry;
  299. }