1cabdff1aSopenharmony_ci/*
2cabdff1aSopenharmony_ci * unbuffered I/O
3cabdff1aSopenharmony_ci * Copyright (c) 2001 Fabrice Bellard
4cabdff1aSopenharmony_ci *
5cabdff1aSopenharmony_ci * This file is part of FFmpeg.
6cabdff1aSopenharmony_ci *
7cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or
8cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public
9cabdff1aSopenharmony_ci * License as published by the Free Software Foundation; either
10cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version.
11cabdff1aSopenharmony_ci *
12cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful,
13cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
14cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15cabdff1aSopenharmony_ci * Lesser General Public License for more details.
16cabdff1aSopenharmony_ci *
17cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public
18cabdff1aSopenharmony_ci * License along with FFmpeg; if not, write to the Free Software
19cabdff1aSopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20cabdff1aSopenharmony_ci */
21cabdff1aSopenharmony_ci
22cabdff1aSopenharmony_ci#include "libavutil/avstring.h"
23cabdff1aSopenharmony_ci#include "libavutil/dict.h"
24cabdff1aSopenharmony_ci#include "libavutil/opt.h"
25cabdff1aSopenharmony_ci#include "libavutil/time.h"
26cabdff1aSopenharmony_ci#include "libavutil/avassert.h"
27cabdff1aSopenharmony_ci#include "os_support.h"
28cabdff1aSopenharmony_ci#include "avformat.h"
29cabdff1aSopenharmony_ci#include "internal.h"
30cabdff1aSopenharmony_ci#if CONFIG_NETWORK
31cabdff1aSopenharmony_ci#include "network.h"
32cabdff1aSopenharmony_ci#endif
33cabdff1aSopenharmony_ci#include "url.h"
34cabdff1aSopenharmony_ci
35cabdff1aSopenharmony_ci/** @name Logging context. */
36cabdff1aSopenharmony_ci/*@{*/
37cabdff1aSopenharmony_cistatic const char *urlcontext_to_name(void *ptr)
38cabdff1aSopenharmony_ci{
39cabdff1aSopenharmony_ci    URLContext *h = (URLContext *)ptr;
40cabdff1aSopenharmony_ci    if (h->prot)
41cabdff1aSopenharmony_ci        return h->prot->name;
42cabdff1aSopenharmony_ci    else
43cabdff1aSopenharmony_ci        return "NULL";
44cabdff1aSopenharmony_ci}
45cabdff1aSopenharmony_ci
46cabdff1aSopenharmony_cistatic void *urlcontext_child_next(void *obj, void *prev)
47cabdff1aSopenharmony_ci{
48cabdff1aSopenharmony_ci    URLContext *h = obj;
49cabdff1aSopenharmony_ci    if (!prev && h->priv_data && h->prot->priv_data_class)
50cabdff1aSopenharmony_ci        return h->priv_data;
51cabdff1aSopenharmony_ci    return NULL;
52cabdff1aSopenharmony_ci}
53cabdff1aSopenharmony_ci
54cabdff1aSopenharmony_ci#define OFFSET(x) offsetof(URLContext,x)
55cabdff1aSopenharmony_ci#define E AV_OPT_FLAG_ENCODING_PARAM
56cabdff1aSopenharmony_ci#define D AV_OPT_FLAG_DECODING_PARAM
57cabdff1aSopenharmony_cistatic const AVOption options[] = {
58cabdff1aSopenharmony_ci    {"protocol_whitelist", "List of protocols that are allowed to be used", OFFSET(protocol_whitelist), AV_OPT_TYPE_STRING, { .str = NULL },  0, 0, D },
59cabdff1aSopenharmony_ci    {"protocol_blacklist", "List of protocols that are not allowed to be used", OFFSET(protocol_blacklist), AV_OPT_TYPE_STRING, { .str = NULL },  0, 0, D },
60cabdff1aSopenharmony_ci    {"rw_timeout", "Timeout for IO operations (in microseconds)", offsetof(URLContext, rw_timeout), AV_OPT_TYPE_INT64, { .i64 = 0 }, 0, INT64_MAX, AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_DECODING_PARAM },
61cabdff1aSopenharmony_ci    { NULL }
62cabdff1aSopenharmony_ci};
63cabdff1aSopenharmony_ci
64cabdff1aSopenharmony_ciconst AVClass ffurl_context_class = {
65cabdff1aSopenharmony_ci    .class_name       = "URLContext",
66cabdff1aSopenharmony_ci    .item_name        = urlcontext_to_name,
67cabdff1aSopenharmony_ci    .option           = options,
68cabdff1aSopenharmony_ci    .version          = LIBAVUTIL_VERSION_INT,
69cabdff1aSopenharmony_ci    .child_next       = urlcontext_child_next,
70cabdff1aSopenharmony_ci    .child_class_iterate = ff_urlcontext_child_class_iterate,
71cabdff1aSopenharmony_ci};
72cabdff1aSopenharmony_ci/*@}*/
73cabdff1aSopenharmony_ci
74cabdff1aSopenharmony_cistatic int url_alloc_for_protocol(URLContext **puc, const URLProtocol *up,
75cabdff1aSopenharmony_ci                                  const char *filename, int flags,
76cabdff1aSopenharmony_ci                                  const AVIOInterruptCB *int_cb)
77cabdff1aSopenharmony_ci{
78cabdff1aSopenharmony_ci    URLContext *uc;
79cabdff1aSopenharmony_ci    int err;
80cabdff1aSopenharmony_ci
81cabdff1aSopenharmony_ci#if CONFIG_NETWORK
82cabdff1aSopenharmony_ci    if (up->flags & URL_PROTOCOL_FLAG_NETWORK && !ff_network_init())
83cabdff1aSopenharmony_ci        return AVERROR(EIO);
84cabdff1aSopenharmony_ci#endif
85cabdff1aSopenharmony_ci    if ((flags & AVIO_FLAG_READ) && !up->url_read) {
86cabdff1aSopenharmony_ci        av_log(NULL, AV_LOG_ERROR,
87cabdff1aSopenharmony_ci               "Impossible to open the '%s' protocol for reading\n", up->name);
88cabdff1aSopenharmony_ci        return AVERROR(EIO);
89cabdff1aSopenharmony_ci    }
90cabdff1aSopenharmony_ci    if ((flags & AVIO_FLAG_WRITE) && !up->url_write) {
91cabdff1aSopenharmony_ci        av_log(NULL, AV_LOG_ERROR,
92cabdff1aSopenharmony_ci               "Impossible to open the '%s' protocol for writing\n", up->name);
93cabdff1aSopenharmony_ci        return AVERROR(EIO);
94cabdff1aSopenharmony_ci    }
95cabdff1aSopenharmony_ci    uc = av_mallocz(sizeof(URLContext) + strlen(filename) + 1);
96cabdff1aSopenharmony_ci    if (!uc) {
97cabdff1aSopenharmony_ci        err = AVERROR(ENOMEM);
98cabdff1aSopenharmony_ci        goto fail;
99cabdff1aSopenharmony_ci    }
100cabdff1aSopenharmony_ci    uc->av_class = &ffurl_context_class;
101cabdff1aSopenharmony_ci    uc->filename = (char *)&uc[1];
102cabdff1aSopenharmony_ci    strcpy(uc->filename, filename);
103cabdff1aSopenharmony_ci    uc->prot            = up;
104cabdff1aSopenharmony_ci    uc->flags           = flags;
105cabdff1aSopenharmony_ci    uc->is_streamed     = 0; /* default = not streamed */
106cabdff1aSopenharmony_ci    uc->max_packet_size = 0; /* default: stream file */
107cabdff1aSopenharmony_ci    if (up->priv_data_size) {
108cabdff1aSopenharmony_ci        uc->priv_data = av_mallocz(up->priv_data_size);
109cabdff1aSopenharmony_ci        if (!uc->priv_data) {
110cabdff1aSopenharmony_ci            err = AVERROR(ENOMEM);
111cabdff1aSopenharmony_ci            goto fail;
112cabdff1aSopenharmony_ci        }
113cabdff1aSopenharmony_ci        if (up->priv_data_class) {
114cabdff1aSopenharmony_ci            char *start;
115cabdff1aSopenharmony_ci            *(const AVClass **)uc->priv_data = up->priv_data_class;
116cabdff1aSopenharmony_ci            av_opt_set_defaults(uc->priv_data);
117cabdff1aSopenharmony_ci            if (av_strstart(uc->filename, up->name, (const char**)&start) && *start == ',') {
118cabdff1aSopenharmony_ci                int ret= 0;
119cabdff1aSopenharmony_ci                char *p= start;
120cabdff1aSopenharmony_ci                char sep= *++p;
121cabdff1aSopenharmony_ci                char *key, *val;
122cabdff1aSopenharmony_ci                p++;
123cabdff1aSopenharmony_ci
124cabdff1aSopenharmony_ci                if (strcmp(up->name, "subfile"))
125cabdff1aSopenharmony_ci                    ret = AVERROR(EINVAL);
126cabdff1aSopenharmony_ci
127cabdff1aSopenharmony_ci                while(ret >= 0 && (key= strchr(p, sep)) && p<key && (val = strchr(key+1, sep))){
128cabdff1aSopenharmony_ci                    *val= *key= 0;
129cabdff1aSopenharmony_ci                    if (strcmp(p, "start") && strcmp(p, "end")) {
130cabdff1aSopenharmony_ci                        ret = AVERROR_OPTION_NOT_FOUND;
131cabdff1aSopenharmony_ci                    } else
132cabdff1aSopenharmony_ci                        ret= av_opt_set(uc->priv_data, p, key+1, 0);
133cabdff1aSopenharmony_ci                    if (ret == AVERROR_OPTION_NOT_FOUND)
134cabdff1aSopenharmony_ci                        av_log(uc, AV_LOG_ERROR, "Key '%s' not found.\n", p);
135cabdff1aSopenharmony_ci                    *val= *key= sep;
136cabdff1aSopenharmony_ci                    p= val+1;
137cabdff1aSopenharmony_ci                }
138cabdff1aSopenharmony_ci                if(ret<0 || p!=key){
139cabdff1aSopenharmony_ci                    av_log(uc, AV_LOG_ERROR, "Error parsing options string %s\n", start);
140cabdff1aSopenharmony_ci                    av_freep(&uc->priv_data);
141cabdff1aSopenharmony_ci                    av_freep(&uc);
142cabdff1aSopenharmony_ci                    err = AVERROR(EINVAL);
143cabdff1aSopenharmony_ci                    goto fail;
144cabdff1aSopenharmony_ci                }
145cabdff1aSopenharmony_ci                memmove(start, key+1, strlen(key));
146cabdff1aSopenharmony_ci            }
147cabdff1aSopenharmony_ci        }
148cabdff1aSopenharmony_ci    }
149cabdff1aSopenharmony_ci    if (int_cb)
150cabdff1aSopenharmony_ci        uc->interrupt_callback = *int_cb;
151cabdff1aSopenharmony_ci
152cabdff1aSopenharmony_ci    *puc = uc;
153cabdff1aSopenharmony_ci    return 0;
154cabdff1aSopenharmony_cifail:
155cabdff1aSopenharmony_ci    *puc = NULL;
156cabdff1aSopenharmony_ci    if (uc)
157cabdff1aSopenharmony_ci        av_freep(&uc->priv_data);
158cabdff1aSopenharmony_ci    av_freep(&uc);
159cabdff1aSopenharmony_ci#if CONFIG_NETWORK
160cabdff1aSopenharmony_ci    if (up->flags & URL_PROTOCOL_FLAG_NETWORK)
161cabdff1aSopenharmony_ci        ff_network_close();
162cabdff1aSopenharmony_ci#endif
163cabdff1aSopenharmony_ci    return err;
164cabdff1aSopenharmony_ci}
165cabdff1aSopenharmony_ci
166cabdff1aSopenharmony_ciint ffurl_connect(URLContext *uc, AVDictionary **options)
167cabdff1aSopenharmony_ci{
168cabdff1aSopenharmony_ci    int err;
169cabdff1aSopenharmony_ci    AVDictionary *tmp_opts = NULL;
170cabdff1aSopenharmony_ci    AVDictionaryEntry *e;
171cabdff1aSopenharmony_ci
172cabdff1aSopenharmony_ci    if (!options)
173cabdff1aSopenharmony_ci        options = &tmp_opts;
174cabdff1aSopenharmony_ci
175cabdff1aSopenharmony_ci    // Check that URLContext was initialized correctly and lists are matching if set
176cabdff1aSopenharmony_ci    av_assert0(!(e=av_dict_get(*options, "protocol_whitelist", NULL, 0)) ||
177cabdff1aSopenharmony_ci               (uc->protocol_whitelist && !strcmp(uc->protocol_whitelist, e->value)));
178cabdff1aSopenharmony_ci    av_assert0(!(e=av_dict_get(*options, "protocol_blacklist", NULL, 0)) ||
179cabdff1aSopenharmony_ci               (uc->protocol_blacklist && !strcmp(uc->protocol_blacklist, e->value)));
180cabdff1aSopenharmony_ci
181cabdff1aSopenharmony_ci    if (uc->protocol_whitelist && av_match_list(uc->prot->name, uc->protocol_whitelist, ',') <= 0) {
182cabdff1aSopenharmony_ci        av_log(uc, AV_LOG_ERROR, "Protocol '%s' not on whitelist '%s'!\n", uc->prot->name, uc->protocol_whitelist);
183cabdff1aSopenharmony_ci        return AVERROR(EINVAL);
184cabdff1aSopenharmony_ci    }
185cabdff1aSopenharmony_ci
186cabdff1aSopenharmony_ci    if (uc->protocol_blacklist && av_match_list(uc->prot->name, uc->protocol_blacklist, ',') > 0) {
187cabdff1aSopenharmony_ci        av_log(uc, AV_LOG_ERROR, "Protocol '%s' on blacklist '%s'!\n", uc->prot->name, uc->protocol_blacklist);
188cabdff1aSopenharmony_ci        return AVERROR(EINVAL);
189cabdff1aSopenharmony_ci    }
190cabdff1aSopenharmony_ci
191cabdff1aSopenharmony_ci    if (!uc->protocol_whitelist && uc->prot->default_whitelist) {
192cabdff1aSopenharmony_ci        av_log(uc, AV_LOG_DEBUG, "Setting default whitelist '%s'\n", uc->prot->default_whitelist);
193cabdff1aSopenharmony_ci        uc->protocol_whitelist = av_strdup(uc->prot->default_whitelist);
194cabdff1aSopenharmony_ci        if (!uc->protocol_whitelist) {
195cabdff1aSopenharmony_ci            return AVERROR(ENOMEM);
196cabdff1aSopenharmony_ci        }
197cabdff1aSopenharmony_ci    } else if (!uc->protocol_whitelist)
198cabdff1aSopenharmony_ci        av_log(uc, AV_LOG_DEBUG, "No default whitelist set\n"); // This should be an error once all declare a default whitelist
199cabdff1aSopenharmony_ci
200cabdff1aSopenharmony_ci    if ((err = av_dict_set(options, "protocol_whitelist", uc->protocol_whitelist, 0)) < 0)
201cabdff1aSopenharmony_ci        return err;
202cabdff1aSopenharmony_ci    if ((err = av_dict_set(options, "protocol_blacklist", uc->protocol_blacklist, 0)) < 0)
203cabdff1aSopenharmony_ci        return err;
204cabdff1aSopenharmony_ci
205cabdff1aSopenharmony_ci    err =
206cabdff1aSopenharmony_ci        uc->prot->url_open2 ? uc->prot->url_open2(uc,
207cabdff1aSopenharmony_ci                                                  uc->filename,
208cabdff1aSopenharmony_ci                                                  uc->flags,
209cabdff1aSopenharmony_ci                                                  options) :
210cabdff1aSopenharmony_ci        uc->prot->url_open(uc, uc->filename, uc->flags);
211cabdff1aSopenharmony_ci
212cabdff1aSopenharmony_ci    av_dict_set(options, "protocol_whitelist", NULL, 0);
213cabdff1aSopenharmony_ci    av_dict_set(options, "protocol_blacklist", NULL, 0);
214cabdff1aSopenharmony_ci
215cabdff1aSopenharmony_ci    if (err)
216cabdff1aSopenharmony_ci        return err;
217cabdff1aSopenharmony_ci    uc->is_connected = 1;
218cabdff1aSopenharmony_ci    /* We must be careful here as ffurl_seek() could be slow,
219cabdff1aSopenharmony_ci     * for example for http */
220cabdff1aSopenharmony_ci    if ((uc->flags & AVIO_FLAG_WRITE) || !strcmp(uc->prot->name, "file"))
221cabdff1aSopenharmony_ci        if (!uc->is_streamed && ffurl_seek(uc, 0, SEEK_SET) < 0)
222cabdff1aSopenharmony_ci            uc->is_streamed = 1;
223cabdff1aSopenharmony_ci    return 0;
224cabdff1aSopenharmony_ci}
225cabdff1aSopenharmony_ci
226cabdff1aSopenharmony_ciint ffurl_accept(URLContext *s, URLContext **c)
227cabdff1aSopenharmony_ci{
228cabdff1aSopenharmony_ci    av_assert0(!*c);
229cabdff1aSopenharmony_ci    if (s->prot->url_accept)
230cabdff1aSopenharmony_ci        return s->prot->url_accept(s, c);
231cabdff1aSopenharmony_ci    return AVERROR(EBADF);
232cabdff1aSopenharmony_ci}
233cabdff1aSopenharmony_ci
234cabdff1aSopenharmony_ciint ffurl_handshake(URLContext *c)
235cabdff1aSopenharmony_ci{
236cabdff1aSopenharmony_ci    int ret;
237cabdff1aSopenharmony_ci    if (c->prot->url_handshake) {
238cabdff1aSopenharmony_ci        ret = c->prot->url_handshake(c);
239cabdff1aSopenharmony_ci        if (ret)
240cabdff1aSopenharmony_ci            return ret;
241cabdff1aSopenharmony_ci    }
242cabdff1aSopenharmony_ci    c->is_connected = 1;
243cabdff1aSopenharmony_ci    return 0;
244cabdff1aSopenharmony_ci}
245cabdff1aSopenharmony_ci
246cabdff1aSopenharmony_ci#define URL_SCHEME_CHARS                        \
247cabdff1aSopenharmony_ci    "abcdefghijklmnopqrstuvwxyz"                \
248cabdff1aSopenharmony_ci    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"                \
249cabdff1aSopenharmony_ci    "0123456789+-."
250cabdff1aSopenharmony_ci
251cabdff1aSopenharmony_cistatic const struct URLProtocol *url_find_protocol(const char *filename)
252cabdff1aSopenharmony_ci{
253cabdff1aSopenharmony_ci    const URLProtocol **protocols;
254cabdff1aSopenharmony_ci    char proto_str[128], proto_nested[128], *ptr;
255cabdff1aSopenharmony_ci    size_t proto_len = strspn(filename, URL_SCHEME_CHARS);
256cabdff1aSopenharmony_ci    int i;
257cabdff1aSopenharmony_ci
258cabdff1aSopenharmony_ci    if (filename[proto_len] != ':' &&
259cabdff1aSopenharmony_ci        (strncmp(filename, "subfile,", 8) || !strchr(filename + proto_len + 1, ':')) ||
260cabdff1aSopenharmony_ci        is_dos_path(filename))
261cabdff1aSopenharmony_ci        strcpy(proto_str, "file");
262cabdff1aSopenharmony_ci    else
263cabdff1aSopenharmony_ci        av_strlcpy(proto_str, filename,
264cabdff1aSopenharmony_ci                   FFMIN(proto_len + 1, sizeof(proto_str)));
265cabdff1aSopenharmony_ci
266cabdff1aSopenharmony_ci    av_strlcpy(proto_nested, proto_str, sizeof(proto_nested));
267cabdff1aSopenharmony_ci    if ((ptr = strchr(proto_nested, '+')))
268cabdff1aSopenharmony_ci        *ptr = '\0';
269cabdff1aSopenharmony_ci
270cabdff1aSopenharmony_ci    protocols = ffurl_get_protocols(NULL, NULL);
271cabdff1aSopenharmony_ci    if (!protocols)
272cabdff1aSopenharmony_ci        return NULL;
273cabdff1aSopenharmony_ci    for (i = 0; protocols[i]; i++) {
274cabdff1aSopenharmony_ci            const URLProtocol *up = protocols[i];
275cabdff1aSopenharmony_ci        if (!strcmp(proto_str, up->name)) {
276cabdff1aSopenharmony_ci            av_freep(&protocols);
277cabdff1aSopenharmony_ci            return up;
278cabdff1aSopenharmony_ci        }
279cabdff1aSopenharmony_ci        if (up->flags & URL_PROTOCOL_FLAG_NESTED_SCHEME &&
280cabdff1aSopenharmony_ci            !strcmp(proto_nested, up->name)) {
281cabdff1aSopenharmony_ci            av_freep(&protocols);
282cabdff1aSopenharmony_ci            return up;
283cabdff1aSopenharmony_ci        }
284cabdff1aSopenharmony_ci    }
285cabdff1aSopenharmony_ci    av_freep(&protocols);
286cabdff1aSopenharmony_ci    if (av_strstart(filename, "https:", NULL) || av_strstart(filename, "tls:", NULL))
287cabdff1aSopenharmony_ci        av_log(NULL, AV_LOG_WARNING, "https protocol not found, recompile FFmpeg with "
288cabdff1aSopenharmony_ci                                     "openssl, gnutls or securetransport enabled.\n");
289cabdff1aSopenharmony_ci
290cabdff1aSopenharmony_ci    return NULL;
291cabdff1aSopenharmony_ci}
292cabdff1aSopenharmony_ci
293cabdff1aSopenharmony_ciint ffurl_alloc(URLContext **puc, const char *filename, int flags,
294cabdff1aSopenharmony_ci                const AVIOInterruptCB *int_cb)
295cabdff1aSopenharmony_ci{
296cabdff1aSopenharmony_ci    const URLProtocol *p = NULL;
297cabdff1aSopenharmony_ci
298cabdff1aSopenharmony_ci    p = url_find_protocol(filename);
299cabdff1aSopenharmony_ci    if (p)
300cabdff1aSopenharmony_ci       return url_alloc_for_protocol(puc, p, filename, flags, int_cb);
301cabdff1aSopenharmony_ci
302cabdff1aSopenharmony_ci    *puc = NULL;
303cabdff1aSopenharmony_ci    return AVERROR_PROTOCOL_NOT_FOUND;
304cabdff1aSopenharmony_ci}
305cabdff1aSopenharmony_ci
306cabdff1aSopenharmony_ciint ffurl_open_whitelist(URLContext **puc, const char *filename, int flags,
307cabdff1aSopenharmony_ci                         const AVIOInterruptCB *int_cb, AVDictionary **options,
308cabdff1aSopenharmony_ci                         const char *whitelist, const char* blacklist,
309cabdff1aSopenharmony_ci                         URLContext *parent)
310cabdff1aSopenharmony_ci{
311cabdff1aSopenharmony_ci    AVDictionary *tmp_opts = NULL;
312cabdff1aSopenharmony_ci    AVDictionaryEntry *e;
313cabdff1aSopenharmony_ci    int ret = ffurl_alloc(puc, filename, flags, int_cb);
314cabdff1aSopenharmony_ci    if (ret < 0)
315cabdff1aSopenharmony_ci        return ret;
316cabdff1aSopenharmony_ci    if (parent) {
317cabdff1aSopenharmony_ci        ret = av_opt_copy(*puc, parent);
318cabdff1aSopenharmony_ci        if (ret < 0)
319cabdff1aSopenharmony_ci            goto fail;
320cabdff1aSopenharmony_ci    }
321cabdff1aSopenharmony_ci    if (options &&
322cabdff1aSopenharmony_ci        (ret = av_opt_set_dict(*puc, options)) < 0)
323cabdff1aSopenharmony_ci        goto fail;
324cabdff1aSopenharmony_ci    if (options && (*puc)->prot->priv_data_class &&
325cabdff1aSopenharmony_ci        (ret = av_opt_set_dict((*puc)->priv_data, options)) < 0)
326cabdff1aSopenharmony_ci        goto fail;
327cabdff1aSopenharmony_ci
328cabdff1aSopenharmony_ci    if (!options)
329cabdff1aSopenharmony_ci        options = &tmp_opts;
330cabdff1aSopenharmony_ci
331cabdff1aSopenharmony_ci    av_assert0(!whitelist ||
332cabdff1aSopenharmony_ci               !(e=av_dict_get(*options, "protocol_whitelist", NULL, 0)) ||
333cabdff1aSopenharmony_ci               !strcmp(whitelist, e->value));
334cabdff1aSopenharmony_ci    av_assert0(!blacklist ||
335cabdff1aSopenharmony_ci               !(e=av_dict_get(*options, "protocol_blacklist", NULL, 0)) ||
336cabdff1aSopenharmony_ci               !strcmp(blacklist, e->value));
337cabdff1aSopenharmony_ci
338cabdff1aSopenharmony_ci    if ((ret = av_dict_set(options, "protocol_whitelist", whitelist, 0)) < 0)
339cabdff1aSopenharmony_ci        goto fail;
340cabdff1aSopenharmony_ci
341cabdff1aSopenharmony_ci    if ((ret = av_dict_set(options, "protocol_blacklist", blacklist, 0)) < 0)
342cabdff1aSopenharmony_ci        goto fail;
343cabdff1aSopenharmony_ci
344cabdff1aSopenharmony_ci    if ((ret = av_opt_set_dict(*puc, options)) < 0)
345cabdff1aSopenharmony_ci        goto fail;
346cabdff1aSopenharmony_ci
347cabdff1aSopenharmony_ci    ret = ffurl_connect(*puc, options);
348cabdff1aSopenharmony_ci
349cabdff1aSopenharmony_ci    if (!ret)
350cabdff1aSopenharmony_ci        return 0;
351cabdff1aSopenharmony_cifail:
352cabdff1aSopenharmony_ci    ffurl_closep(puc);
353cabdff1aSopenharmony_ci    return ret;
354cabdff1aSopenharmony_ci}
355cabdff1aSopenharmony_ci
356cabdff1aSopenharmony_cistatic inline int retry_transfer_wrapper(URLContext *h, uint8_t *buf,
357cabdff1aSopenharmony_ci                                         int size, int size_min,
358cabdff1aSopenharmony_ci                                         int (*transfer_func)(URLContext *h,
359cabdff1aSopenharmony_ci                                                              uint8_t *buf,
360cabdff1aSopenharmony_ci                                                              int size))
361cabdff1aSopenharmony_ci{
362cabdff1aSopenharmony_ci    int ret, len;
363cabdff1aSopenharmony_ci    int fast_retries = 5;
364cabdff1aSopenharmony_ci    int64_t wait_since = 0;
365cabdff1aSopenharmony_ci
366cabdff1aSopenharmony_ci    len = 0;
367cabdff1aSopenharmony_ci    while (len < size_min) {
368cabdff1aSopenharmony_ci        if (ff_check_interrupt(&h->interrupt_callback))
369cabdff1aSopenharmony_ci            return AVERROR_EXIT;
370cabdff1aSopenharmony_ci        ret = transfer_func(h, buf + len, size - len);
371cabdff1aSopenharmony_ci        if (ret == AVERROR(EINTR))
372cabdff1aSopenharmony_ci            continue;
373cabdff1aSopenharmony_ci        if (h->flags & AVIO_FLAG_NONBLOCK)
374cabdff1aSopenharmony_ci            return ret;
375cabdff1aSopenharmony_ci        if (ret == AVERROR(EAGAIN)) {
376cabdff1aSopenharmony_ci            ret = 0;
377cabdff1aSopenharmony_ci            if (fast_retries) {
378cabdff1aSopenharmony_ci                fast_retries--;
379cabdff1aSopenharmony_ci            } else {
380cabdff1aSopenharmony_ci                if (h->rw_timeout) {
381cabdff1aSopenharmony_ci                    if (!wait_since)
382cabdff1aSopenharmony_ci                        wait_since = av_gettime_relative();
383cabdff1aSopenharmony_ci                    else if (av_gettime_relative() > wait_since + h->rw_timeout)
384cabdff1aSopenharmony_ci                        return AVERROR(EIO);
385cabdff1aSopenharmony_ci                }
386cabdff1aSopenharmony_ci                av_usleep(1000);
387cabdff1aSopenharmony_ci            }
388cabdff1aSopenharmony_ci        } else if (ret == AVERROR_EOF)
389cabdff1aSopenharmony_ci            return (len > 0) ? len : AVERROR_EOF;
390cabdff1aSopenharmony_ci        else if (ret < 0)
391cabdff1aSopenharmony_ci            return ret;
392cabdff1aSopenharmony_ci        if (ret) {
393cabdff1aSopenharmony_ci            fast_retries = FFMAX(fast_retries, 2);
394cabdff1aSopenharmony_ci            wait_since = 0;
395cabdff1aSopenharmony_ci        }
396cabdff1aSopenharmony_ci        len += ret;
397cabdff1aSopenharmony_ci    }
398cabdff1aSopenharmony_ci    return len;
399cabdff1aSopenharmony_ci}
400cabdff1aSopenharmony_ci
401cabdff1aSopenharmony_ciint ffurl_read(URLContext *h, unsigned char *buf, int size)
402cabdff1aSopenharmony_ci{
403cabdff1aSopenharmony_ci    if (!(h->flags & AVIO_FLAG_READ))
404cabdff1aSopenharmony_ci        return AVERROR(EIO);
405cabdff1aSopenharmony_ci    return retry_transfer_wrapper(h, buf, size, 1, h->prot->url_read);
406cabdff1aSopenharmony_ci}
407cabdff1aSopenharmony_ci
408cabdff1aSopenharmony_ciint ffurl_read_complete(URLContext *h, unsigned char *buf, int size)
409cabdff1aSopenharmony_ci{
410cabdff1aSopenharmony_ci    if (!(h->flags & AVIO_FLAG_READ))
411cabdff1aSopenharmony_ci        return AVERROR(EIO);
412cabdff1aSopenharmony_ci    return retry_transfer_wrapper(h, buf, size, size, h->prot->url_read);
413cabdff1aSopenharmony_ci}
414cabdff1aSopenharmony_ci
415cabdff1aSopenharmony_ciint ffurl_write(URLContext *h, const unsigned char *buf, int size)
416cabdff1aSopenharmony_ci{
417cabdff1aSopenharmony_ci    if (!(h->flags & AVIO_FLAG_WRITE))
418cabdff1aSopenharmony_ci        return AVERROR(EIO);
419cabdff1aSopenharmony_ci    /* avoid sending too big packets */
420cabdff1aSopenharmony_ci    if (h->max_packet_size && size > h->max_packet_size)
421cabdff1aSopenharmony_ci        return AVERROR(EIO);
422cabdff1aSopenharmony_ci
423cabdff1aSopenharmony_ci    return retry_transfer_wrapper(h, (unsigned char *)buf, size, size,
424cabdff1aSopenharmony_ci                                  (int (*)(struct URLContext *, uint8_t *, int))
425cabdff1aSopenharmony_ci                                  h->prot->url_write);
426cabdff1aSopenharmony_ci}
427cabdff1aSopenharmony_ci
428cabdff1aSopenharmony_ciint64_t ffurl_seek(URLContext *h, int64_t pos, int whence)
429cabdff1aSopenharmony_ci{
430cabdff1aSopenharmony_ci    int64_t ret;
431cabdff1aSopenharmony_ci
432cabdff1aSopenharmony_ci    if (!h->prot->url_seek)
433cabdff1aSopenharmony_ci        return AVERROR(ENOSYS);
434cabdff1aSopenharmony_ci    ret = h->prot->url_seek(h, pos, whence & ~AVSEEK_FORCE);
435cabdff1aSopenharmony_ci    return ret;
436cabdff1aSopenharmony_ci}
437cabdff1aSopenharmony_ci
438cabdff1aSopenharmony_ciint ffurl_closep(URLContext **hh)
439cabdff1aSopenharmony_ci{
440cabdff1aSopenharmony_ci    URLContext *h= *hh;
441cabdff1aSopenharmony_ci    int ret = 0;
442cabdff1aSopenharmony_ci    if (!h)
443cabdff1aSopenharmony_ci        return 0;     /* can happen when ffurl_open fails */
444cabdff1aSopenharmony_ci
445cabdff1aSopenharmony_ci    if (h->is_connected && h->prot->url_close)
446cabdff1aSopenharmony_ci        ret = h->prot->url_close(h);
447cabdff1aSopenharmony_ci#if CONFIG_NETWORK
448cabdff1aSopenharmony_ci    if (h->prot->flags & URL_PROTOCOL_FLAG_NETWORK)
449cabdff1aSopenharmony_ci        ff_network_close();
450cabdff1aSopenharmony_ci#endif
451cabdff1aSopenharmony_ci    if (h->prot->priv_data_size) {
452cabdff1aSopenharmony_ci        if (h->prot->priv_data_class)
453cabdff1aSopenharmony_ci            av_opt_free(h->priv_data);
454cabdff1aSopenharmony_ci        av_freep(&h->priv_data);
455cabdff1aSopenharmony_ci    }
456cabdff1aSopenharmony_ci    av_opt_free(h);
457cabdff1aSopenharmony_ci    av_freep(hh);
458cabdff1aSopenharmony_ci    return ret;
459cabdff1aSopenharmony_ci}
460cabdff1aSopenharmony_ci
461cabdff1aSopenharmony_ciint ffurl_close(URLContext *h)
462cabdff1aSopenharmony_ci{
463cabdff1aSopenharmony_ci    return ffurl_closep(&h);
464cabdff1aSopenharmony_ci}
465cabdff1aSopenharmony_ci
466cabdff1aSopenharmony_ci
467cabdff1aSopenharmony_ciconst char *avio_find_protocol_name(const char *url)
468cabdff1aSopenharmony_ci{
469cabdff1aSopenharmony_ci    const URLProtocol *p = url_find_protocol(url);
470cabdff1aSopenharmony_ci
471cabdff1aSopenharmony_ci    return p ? p->name : NULL;
472cabdff1aSopenharmony_ci}
473cabdff1aSopenharmony_ci
474cabdff1aSopenharmony_ciint avio_check(const char *url, int flags)
475cabdff1aSopenharmony_ci{
476cabdff1aSopenharmony_ci    URLContext *h;
477cabdff1aSopenharmony_ci    int ret = ffurl_alloc(&h, url, flags, NULL);
478cabdff1aSopenharmony_ci    if (ret < 0)
479cabdff1aSopenharmony_ci        return ret;
480cabdff1aSopenharmony_ci
481cabdff1aSopenharmony_ci    if (h->prot->url_check) {
482cabdff1aSopenharmony_ci        ret = h->prot->url_check(h, flags);
483cabdff1aSopenharmony_ci    } else {
484cabdff1aSopenharmony_ci        ret = ffurl_connect(h, NULL);
485cabdff1aSopenharmony_ci        if (ret >= 0)
486cabdff1aSopenharmony_ci            ret = flags;
487cabdff1aSopenharmony_ci    }
488cabdff1aSopenharmony_ci
489cabdff1aSopenharmony_ci    ffurl_close(h);
490cabdff1aSopenharmony_ci    return ret;
491cabdff1aSopenharmony_ci}
492cabdff1aSopenharmony_ci
493cabdff1aSopenharmony_ciint ffurl_move(const char *url_src, const char *url_dst)
494cabdff1aSopenharmony_ci{
495cabdff1aSopenharmony_ci    URLContext *h_src, *h_dst;
496cabdff1aSopenharmony_ci    int ret = ffurl_alloc(&h_src, url_src, AVIO_FLAG_READ_WRITE, NULL);
497cabdff1aSopenharmony_ci    if (ret < 0)
498cabdff1aSopenharmony_ci        return ret;
499cabdff1aSopenharmony_ci    ret = ffurl_alloc(&h_dst, url_dst, AVIO_FLAG_WRITE, NULL);
500cabdff1aSopenharmony_ci    if (ret < 0) {
501cabdff1aSopenharmony_ci        ffurl_close(h_src);
502cabdff1aSopenharmony_ci        return ret;
503cabdff1aSopenharmony_ci    }
504cabdff1aSopenharmony_ci
505cabdff1aSopenharmony_ci    if (h_src->prot == h_dst->prot && h_src->prot->url_move)
506cabdff1aSopenharmony_ci        ret = h_src->prot->url_move(h_src, h_dst);
507cabdff1aSopenharmony_ci    else
508cabdff1aSopenharmony_ci        ret = AVERROR(ENOSYS);
509cabdff1aSopenharmony_ci
510cabdff1aSopenharmony_ci    ffurl_close(h_src);
511cabdff1aSopenharmony_ci    ffurl_close(h_dst);
512cabdff1aSopenharmony_ci    return ret;
513cabdff1aSopenharmony_ci}
514cabdff1aSopenharmony_ci
515cabdff1aSopenharmony_ciint ffurl_delete(const char *url)
516cabdff1aSopenharmony_ci{
517cabdff1aSopenharmony_ci    URLContext *h;
518cabdff1aSopenharmony_ci    int ret = ffurl_alloc(&h, url, AVIO_FLAG_WRITE, NULL);
519cabdff1aSopenharmony_ci    if (ret < 0)
520cabdff1aSopenharmony_ci        return ret;
521cabdff1aSopenharmony_ci
522cabdff1aSopenharmony_ci    if (h->prot->url_delete)
523cabdff1aSopenharmony_ci        ret = h->prot->url_delete(h);
524cabdff1aSopenharmony_ci    else
525cabdff1aSopenharmony_ci        ret = AVERROR(ENOSYS);
526cabdff1aSopenharmony_ci
527cabdff1aSopenharmony_ci    ffurl_close(h);
528cabdff1aSopenharmony_ci    return ret;
529cabdff1aSopenharmony_ci}
530cabdff1aSopenharmony_ci
531cabdff1aSopenharmony_ciint avio_open_dir(AVIODirContext **s, const char *url, AVDictionary **options)
532cabdff1aSopenharmony_ci{
533cabdff1aSopenharmony_ci    URLContext *h = NULL;
534cabdff1aSopenharmony_ci    AVIODirContext *ctx = NULL;
535cabdff1aSopenharmony_ci    int ret;
536cabdff1aSopenharmony_ci    av_assert0(s);
537cabdff1aSopenharmony_ci
538cabdff1aSopenharmony_ci    ctx = av_mallocz(sizeof(*ctx));
539cabdff1aSopenharmony_ci    if (!ctx) {
540cabdff1aSopenharmony_ci        ret = AVERROR(ENOMEM);
541cabdff1aSopenharmony_ci        goto fail;
542cabdff1aSopenharmony_ci    }
543cabdff1aSopenharmony_ci
544cabdff1aSopenharmony_ci    if ((ret = ffurl_alloc(&h, url, AVIO_FLAG_READ, NULL)) < 0)
545cabdff1aSopenharmony_ci        goto fail;
546cabdff1aSopenharmony_ci
547cabdff1aSopenharmony_ci    if (h->prot->url_open_dir && h->prot->url_read_dir && h->prot->url_close_dir) {
548cabdff1aSopenharmony_ci        if (options && h->prot->priv_data_class &&
549cabdff1aSopenharmony_ci            (ret = av_opt_set_dict(h->priv_data, options)) < 0)
550cabdff1aSopenharmony_ci            goto fail;
551cabdff1aSopenharmony_ci        ret = h->prot->url_open_dir(h);
552cabdff1aSopenharmony_ci    } else
553cabdff1aSopenharmony_ci        ret = AVERROR(ENOSYS);
554cabdff1aSopenharmony_ci    if (ret < 0)
555cabdff1aSopenharmony_ci        goto fail;
556cabdff1aSopenharmony_ci
557cabdff1aSopenharmony_ci    h->is_connected = 1;
558cabdff1aSopenharmony_ci    ctx->url_context = h;
559cabdff1aSopenharmony_ci    *s = ctx;
560cabdff1aSopenharmony_ci    return 0;
561cabdff1aSopenharmony_ci
562cabdff1aSopenharmony_ci  fail:
563cabdff1aSopenharmony_ci    av_free(ctx);
564cabdff1aSopenharmony_ci    *s = NULL;
565cabdff1aSopenharmony_ci    ffurl_close(h);
566cabdff1aSopenharmony_ci    return ret;
567cabdff1aSopenharmony_ci}
568cabdff1aSopenharmony_ci
569cabdff1aSopenharmony_ciint avio_read_dir(AVIODirContext *s, AVIODirEntry **next)
570cabdff1aSopenharmony_ci{
571cabdff1aSopenharmony_ci    URLContext *h;
572cabdff1aSopenharmony_ci    int ret;
573cabdff1aSopenharmony_ci
574cabdff1aSopenharmony_ci    if (!s || !s->url_context)
575cabdff1aSopenharmony_ci        return AVERROR(EINVAL);
576cabdff1aSopenharmony_ci    h = s->url_context;
577cabdff1aSopenharmony_ci    if ((ret = h->prot->url_read_dir(h, next)) < 0)
578cabdff1aSopenharmony_ci        avio_free_directory_entry(next);
579cabdff1aSopenharmony_ci    return ret;
580cabdff1aSopenharmony_ci}
581cabdff1aSopenharmony_ci
582cabdff1aSopenharmony_ciint avio_close_dir(AVIODirContext **s)
583cabdff1aSopenharmony_ci{
584cabdff1aSopenharmony_ci    URLContext *h;
585cabdff1aSopenharmony_ci
586cabdff1aSopenharmony_ci    av_assert0(s);
587cabdff1aSopenharmony_ci    if (!(*s) || !(*s)->url_context)
588cabdff1aSopenharmony_ci        return AVERROR(EINVAL);
589cabdff1aSopenharmony_ci    h = (*s)->url_context;
590cabdff1aSopenharmony_ci    h->prot->url_close_dir(h);
591cabdff1aSopenharmony_ci    ffurl_close(h);
592cabdff1aSopenharmony_ci    av_freep(s);
593cabdff1aSopenharmony_ci    *s = NULL;
594cabdff1aSopenharmony_ci    return 0;
595cabdff1aSopenharmony_ci}
596cabdff1aSopenharmony_ci
597cabdff1aSopenharmony_civoid avio_free_directory_entry(AVIODirEntry **entry)
598cabdff1aSopenharmony_ci{
599cabdff1aSopenharmony_ci    if (!entry || !*entry)
600cabdff1aSopenharmony_ci        return;
601cabdff1aSopenharmony_ci    av_free((*entry)->name);
602cabdff1aSopenharmony_ci    av_freep(entry);
603cabdff1aSopenharmony_ci}
604cabdff1aSopenharmony_ci
605cabdff1aSopenharmony_ciint64_t ffurl_size(URLContext *h)
606cabdff1aSopenharmony_ci{
607cabdff1aSopenharmony_ci    int64_t pos, size;
608cabdff1aSopenharmony_ci
609cabdff1aSopenharmony_ci    size = ffurl_seek(h, 0, AVSEEK_SIZE);
610cabdff1aSopenharmony_ci    if (size < 0) {
611cabdff1aSopenharmony_ci        pos = ffurl_seek(h, 0, SEEK_CUR);
612cabdff1aSopenharmony_ci        if ((size = ffurl_seek(h, -1, SEEK_END)) < 0)
613cabdff1aSopenharmony_ci            return size;
614cabdff1aSopenharmony_ci        size++;
615cabdff1aSopenharmony_ci        ffurl_seek(h, pos, SEEK_SET);
616cabdff1aSopenharmony_ci    }
617cabdff1aSopenharmony_ci    return size;
618cabdff1aSopenharmony_ci}
619cabdff1aSopenharmony_ci
620cabdff1aSopenharmony_ciint ffurl_get_file_handle(URLContext *h)
621cabdff1aSopenharmony_ci{
622cabdff1aSopenharmony_ci    if (!h || !h->prot || !h->prot->url_get_file_handle)
623cabdff1aSopenharmony_ci        return -1;
624cabdff1aSopenharmony_ci    return h->prot->url_get_file_handle(h);
625cabdff1aSopenharmony_ci}
626cabdff1aSopenharmony_ci
627cabdff1aSopenharmony_ciint ffurl_get_multi_file_handle(URLContext *h, int **handles, int *numhandles)
628cabdff1aSopenharmony_ci{
629cabdff1aSopenharmony_ci    if (!h || !h->prot)
630cabdff1aSopenharmony_ci        return AVERROR(ENOSYS);
631cabdff1aSopenharmony_ci    if (!h->prot->url_get_multi_file_handle) {
632cabdff1aSopenharmony_ci        if (!h->prot->url_get_file_handle)
633cabdff1aSopenharmony_ci            return AVERROR(ENOSYS);
634cabdff1aSopenharmony_ci        *handles = av_malloc(sizeof(**handles));
635cabdff1aSopenharmony_ci        if (!*handles)
636cabdff1aSopenharmony_ci            return AVERROR(ENOMEM);
637cabdff1aSopenharmony_ci        *numhandles = 1;
638cabdff1aSopenharmony_ci        *handles[0] = h->prot->url_get_file_handle(h);
639cabdff1aSopenharmony_ci        return 0;
640cabdff1aSopenharmony_ci    }
641cabdff1aSopenharmony_ci    return h->prot->url_get_multi_file_handle(h, handles, numhandles);
642cabdff1aSopenharmony_ci}
643cabdff1aSopenharmony_ci
644cabdff1aSopenharmony_ciint ffurl_get_short_seek(URLContext *h)
645cabdff1aSopenharmony_ci{
646cabdff1aSopenharmony_ci    if (!h || !h->prot || !h->prot->url_get_short_seek)
647cabdff1aSopenharmony_ci        return AVERROR(ENOSYS);
648cabdff1aSopenharmony_ci    return h->prot->url_get_short_seek(h);
649cabdff1aSopenharmony_ci}
650cabdff1aSopenharmony_ci
651cabdff1aSopenharmony_ciint ffurl_shutdown(URLContext *h, int flags)
652cabdff1aSopenharmony_ci{
653cabdff1aSopenharmony_ci    if (!h || !h->prot || !h->prot->url_shutdown)
654cabdff1aSopenharmony_ci        return AVERROR(ENOSYS);
655cabdff1aSopenharmony_ci    return h->prot->url_shutdown(h, flags);
656cabdff1aSopenharmony_ci}
657cabdff1aSopenharmony_ci
658cabdff1aSopenharmony_ciint ff_check_interrupt(AVIOInterruptCB *cb)
659cabdff1aSopenharmony_ci{
660cabdff1aSopenharmony_ci    if (cb && cb->callback)
661cabdff1aSopenharmony_ci        return cb->callback(cb->opaque);
662cabdff1aSopenharmony_ci    return 0;
663cabdff1aSopenharmony_ci}
664cabdff1aSopenharmony_ci
665cabdff1aSopenharmony_ciint ff_rename(const char *url_src, const char *url_dst, void *logctx)
666cabdff1aSopenharmony_ci{
667cabdff1aSopenharmony_ci    int ret = ffurl_move(url_src, url_dst);
668cabdff1aSopenharmony_ci    if (ret < 0)
669cabdff1aSopenharmony_ci        av_log(logctx, AV_LOG_ERROR, "failed to rename file %s to %s: %s\n", url_src, url_dst, av_err2str(ret));
670cabdff1aSopenharmony_ci    return ret;
671cabdff1aSopenharmony_ci}
672