a3805d0d426a473e73c8961480845364507652a0
[vlc.git] / modules / access / http / access.c
1 /*****************************************************************************
2  * access.c: HTTP/TLS VLC access plug-in
3  *****************************************************************************
4  * Copyright © 2015 Rémi Denis-Courmont
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation; either version 2.1 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19  *****************************************************************************/
20
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
24
25 #undef MODULE_STRING
26 #define MODULE_STRING "http"
27
28 #include <assert.h>
29 #include <stdint.h>
30 #include <string.h>
31 #include <stdlib.h>
32
33 #include <vlc_common.h>
34 #include <vlc_access.h>
35 #include <vlc_keystore.h>
36 #include <vlc_plugin.h>
37 #include <vlc_url.h>
38
39 #include "connmgr.h"
40 #include "resource.h"
41 #include "file.h"
42 #include "live.h"
43
44 typedef struct
45 {
46     struct vlc_http_mgr *manager;
47     struct vlc_http_resource *resource;
48 } access_sys_t;
49
50 static block_t *FileRead(stream_t *access, bool *restrict eof)
51 {
52     access_sys_t *sys = access->p_sys;
53
54     block_t *b = vlc_http_file_read(sys->resource);
55     if (b == NULL)
56         *eof = true;
57     return b;
58 }
59
60 static int FileSeek(stream_t *access, uint64_t pos)
61 {
62     access_sys_t *sys = access->p_sys;
63
64     if (vlc_http_file_seek(sys->resource, pos))
65         return VLC_EGENERIC;
66     return VLC_SUCCESS;
67 }
68
69 static int FileControl(stream_t *access, int query, va_list args)
70 {
71     access_sys_t *sys = access->p_sys;
72
73     switch (query)
74     {
75         case STREAM_CAN_SEEK:
76             *va_arg(args, bool *) = vlc_http_file_can_seek(sys->resource);
77             break;
78
79         case STREAM_CAN_FASTSEEK:
80             *va_arg(args, bool *) = false;
81             break;
82
83         case STREAM_CAN_PAUSE:
84         case STREAM_CAN_CONTROL_PACE:
85             *va_arg(args, bool *) = true;
86             break;
87
88         case STREAM_GET_SIZE:
89         {
90             uintmax_t val = vlc_http_file_get_size(sys->resource);
91             if (val >= UINT64_MAX)
92                 return VLC_EGENERIC;
93
94             *va_arg(args, uint64_t *) = val;
95             break;
96         }
97
98         case STREAM_GET_PTS_DELAY:
99             *va_arg(args, vlc_tick_t *) = VLC_TICK_FROM_MS(
100                 var_InheritInteger(access, "network-caching") );
101             break;
102
103         case STREAM_GET_CONTENT_TYPE:
104             *va_arg(args, char **) = vlc_http_file_get_type(sys->resource);
105             break;
106
107         case STREAM_SET_PAUSE_STATE:
108             break;
109
110         default:
111             return VLC_EGENERIC;
112     }
113     return VLC_SUCCESS;
114 }
115
116 static block_t *LiveRead(stream_t *access, bool *restrict eof)
117 {
118     access_sys_t *sys = access->p_sys;
119
120     block_t *b = vlc_http_live_read(sys->resource);
121     if (b == NULL) /* TODO: loop instead of EOF, see vlc_http_live_read() */
122         *eof = true;
123     return b;
124 }
125
126 static int LiveControl(stream_t *access, int query, va_list args)
127 {
128     access_sys_t *sys = access->p_sys;
129
130     switch (query)
131     {
132         case STREAM_CAN_SEEK:
133         case STREAM_CAN_FASTSEEK:
134         case STREAM_CAN_PAUSE:
135         case STREAM_CAN_CONTROL_PACE:
136             *va_arg(args, bool *) = false;
137             break;
138
139         case STREAM_GET_PTS_DELAY:
140             *va_arg(args, vlc_tick_t *) = VLC_TICK_FROM_MS(
141                 var_InheritInteger(access, "network-caching") );
142             break;
143
144         case STREAM_GET_CONTENT_TYPE:
145             *va_arg(args, char **) = vlc_http_live_get_type(sys->resource);
146             break;
147
148         default:
149             return VLC_EGENERIC;
150     }
151     return VLC_SUCCESS;
152 }
153
154 static int Open(vlc_object_t *obj)
155 {
156     stream_t *access = (stream_t *)obj;
157     access_sys_t *sys = malloc(sizeof (*sys));
158     int ret = VLC_ENOMEM;
159
160     if (unlikely(sys == NULL))
161         return VLC_ENOMEM;
162
163     sys->manager = NULL;
164     sys->resource = NULL;
165
166     void *jar = NULL;
167     if (var_InheritBool(obj, "http-forward-cookies"))
168         jar = var_InheritAddress(obj, "http-cookies");
169
170     struct vlc_credential crd;
171     struct vlc_url_t crd_url;
172     char *psz_realm = NULL;
173
174     vlc_UrlParse(&crd_url, access->psz_url);
175     vlc_credential_init(&crd, &crd_url);
176
177     sys->manager = vlc_http_mgr_create(obj, jar);
178     if (sys->manager == NULL)
179         goto error;
180
181     char *ua = var_InheritString(obj, "http-user-agent");
182     char *referer = var_InheritString(obj, "http-referrer");
183     bool live = var_InheritBool(obj, "http-continuous");
184
185     sys->resource = (live ? vlc_http_live_create : vlc_http_file_create)(
186         sys->manager, access->psz_url, ua, referer);
187     free(referer);
188     free(ua);
189
190     if (sys->resource == NULL)
191         goto error;
192
193     if (vlc_credential_get(&crd, obj, NULL, NULL, NULL, NULL))
194         vlc_http_res_set_login(sys->resource,
195                                crd.psz_username, crd.psz_password);
196
197     ret = VLC_EGENERIC;
198
199     int status = vlc_http_res_get_status(sys->resource);
200
201     while (status == 401) /* authentication */
202     {
203         crd.psz_authtype = "Basic";
204         free(psz_realm);
205         psz_realm = vlc_http_res_get_basic_realm(sys->resource);
206
207         if (psz_realm == NULL)
208             break;
209         crd.psz_realm = psz_realm;
210         if (!vlc_credential_get(&crd, obj, NULL, NULL, _("HTTP authentication"),
211                                 _("Please enter a valid login name and "
212                                   "a password for realm %s."), crd.psz_realm))
213             break;
214
215         vlc_http_res_set_login(sys->resource,
216                                crd.psz_username, crd.psz_password);
217         status = vlc_http_res_get_status(sys->resource);
218     }
219
220     if (status < 0)
221     {
222         msg_Err(access, "HTTP connection failure");
223         goto error;
224     }
225
226     char *redir = vlc_http_res_get_redirect(sys->resource);
227     if (redir != NULL)
228     {
229         access->psz_url = redir;
230         ret = VLC_ACCESS_REDIRECT;
231         goto error;
232     }
233
234     if (status >= 300)
235     {
236         msg_Err(access, "HTTP %d error", status);
237         goto error;
238     }
239
240     vlc_credential_store(&crd, obj);
241     free(psz_realm);
242     vlc_credential_clean(&crd);
243     vlc_UrlClean(&crd_url);
244
245     access->pf_read = NULL;
246     if (live)
247     {
248         access->pf_block = LiveRead;
249         access->pf_seek = NULL;
250         access->pf_control = LiveControl;
251     }
252     else
253     {
254         access->pf_block = FileRead;
255         access->pf_seek = FileSeek;
256         access->pf_control = FileControl;
257     }
258     access->p_sys = sys;
259     return VLC_SUCCESS;
260
261 error:
262     if (sys->resource != NULL)
263         vlc_http_res_destroy(sys->resource);
264     if (sys->manager != NULL)
265         vlc_http_mgr_destroy(sys->manager);
266     free(psz_realm);
267     vlc_credential_clean(&crd);
268     vlc_UrlClean(&crd_url);
269     free(sys);
270     return ret;
271 }
272
273 static void Close(vlc_object_t *obj)
274 {
275     stream_t *access = (stream_t *)obj;
276     access_sys_t *sys = access->p_sys;
277
278     vlc_http_res_destroy(sys->resource);
279     vlc_http_mgr_destroy(sys->manager);
280     free(sys);
281 }
282
283 vlc_module_begin()
284     set_description(N_("HTTPS input"))
285     set_shortname(N_("HTTPS"))
286     set_category(CAT_INPUT)
287     set_subcategory(SUBCAT_INPUT_ACCESS)
288     set_capability("access", 2)
289     add_shortcut("https", "http")
290     set_callbacks(Open, Close)
291
292     add_bool("http-continuous", false, N_("Continuous stream"),
293              N_("Keep reading a resource that keeps being updated."), true)
294         change_volatile()
295     add_bool("http-forward-cookies", true, N_("Cookies forwarding"),
296              N_("Forward cookies across HTTP redirections."), true)
297     add_string("http-referrer", NULL, N_("Referrer"),
298                N_("Provide the referral URL, i.e. HTTP \"Referer\" (sic)."),
299                true)
300         change_safe()
301         change_volatile()
302     add_string("http-user-agent", NULL, N_("User agent"),
303                N_("Override the name and version of the application as "
304                   "provided to the HTTP server, i.e. the HTTP \"User-Agent\". "
305                   "Name and version must be separated by a forward slash, "
306                   "e.g. \"FooBar/1.2.3\"."), true)
307         change_safe()
308         change_private()
309 vlc_module_end()