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 #include <assert.h>
26 #include <stdint.h>
27 #include <string.h>
28 #include <stdlib.h>
29 
30 #include <vlc_common.h>
31 #include <vlc_access.h>
32 #include <vlc_keystore.h>
33 #include <vlc_plugin.h>
34 #include <vlc_url.h>
35 
36 #include "connmgr.h"
37 #include "resource.h"
38 #include "file.h"
39 #include "live.h"
40 
41 struct access_sys_t
42 {
43     struct vlc_http_mgr *manager;
44     struct vlc_http_resource *resource;
45 };
46 
FileRead(stream_t * access,bool * restrict eof)47 static block_t *FileRead(stream_t *access, bool *restrict eof)
48 {
49     access_sys_t *sys = access->p_sys;
50 
51     block_t *b = vlc_http_file_read(sys->resource);
52     if (b == NULL)
53         *eof = true;
54     return b;
55 }
56 
FileSeek(stream_t * access,uint64_t pos)57 static int FileSeek(stream_t *access, uint64_t pos)
58 {
59     access_sys_t *sys = access->p_sys;
60 
61     if (vlc_http_file_seek(sys->resource, pos))
62         return VLC_EGENERIC;
63     return VLC_SUCCESS;
64 }
65 
FileControl(stream_t * access,int query,va_list args)66 static int FileControl(stream_t *access, int query, va_list args)
67 {
68     access_sys_t *sys = access->p_sys;
69 
70     switch (query)
71     {
72         case STREAM_CAN_SEEK:
73             *va_arg(args, bool *) = vlc_http_file_can_seek(sys->resource);
74             break;
75 
76         case STREAM_CAN_FASTSEEK:
77             *va_arg(args, bool *) = false;
78             break;
79 
80         case STREAM_CAN_PAUSE:
81         case STREAM_CAN_CONTROL_PACE:
82             *va_arg(args, bool *) = true;
83             break;
84 
85         case STREAM_GET_SIZE:
86         {
87             uintmax_t val = vlc_http_file_get_size(sys->resource);
88             if (val >= UINT64_MAX)
89                 return VLC_EGENERIC;
90 
91             *va_arg(args, uint64_t *) = val;
92             break;
93         }
94 
95         case STREAM_GET_PTS_DELAY:
96             *va_arg(args, int64_t *) = INT64_C(1000) *
97                 var_InheritInteger(access, "network-caching");
98             break;
99 
100         case STREAM_GET_CONTENT_TYPE:
101             *va_arg(args, char **) = vlc_http_file_get_type(sys->resource);
102             break;
103 
104         case STREAM_SET_PAUSE_STATE:
105             break;
106 
107         default:
108             return VLC_EGENERIC;
109     }
110     return VLC_SUCCESS;
111 }
112 
LiveRead(stream_t * access,bool * restrict eof)113 static block_t *LiveRead(stream_t *access, bool *restrict eof)
114 {
115     access_sys_t *sys = access->p_sys;
116 
117     block_t *b = vlc_http_live_read(sys->resource);
118     if (b == NULL) /* TODO: loop instead of EOF, see vlc_http_live_read() */
119         *eof = true;
120     return b;
121 }
122 
NoSeek(stream_t * access,uint64_t pos)123 static int NoSeek(stream_t *access, uint64_t pos)
124 {
125     (void) access;
126     (void) pos;
127     return VLC_EGENERIC;
128 }
129 
LiveControl(stream_t * access,int query,va_list args)130 static int LiveControl(stream_t *access, int query, va_list args)
131 {
132     access_sys_t *sys = access->p_sys;
133 
134     switch (query)
135     {
136         case STREAM_CAN_SEEK:
137         case STREAM_CAN_FASTSEEK:
138         case STREAM_CAN_PAUSE:
139         case STREAM_CAN_CONTROL_PACE:
140             *va_arg(args, bool *) = false;
141             break;
142 
143         case STREAM_GET_PTS_DELAY:
144             *va_arg(args, int64_t *) = INT64_C(1000) *
145                 var_InheritInteger(access, "network-caching");
146             break;
147 
148         case STREAM_GET_CONTENT_TYPE:
149             *va_arg(args, char **) = vlc_http_live_get_type(sys->resource);
150             break;
151 
152         default:
153             return VLC_EGENERIC;
154     }
155     return VLC_SUCCESS;
156 }
157 
Open(vlc_object_t * obj)158 static int Open(vlc_object_t *obj)
159 {
160     stream_t *access = (stream_t *)obj;
161     access_sys_t *sys = malloc(sizeof (*sys));
162     int ret = VLC_ENOMEM;
163 
164     if (unlikely(sys == NULL))
165         return VLC_ENOMEM;
166 
167     sys->manager = NULL;
168     sys->resource = NULL;
169 
170     void *jar = NULL;
171     if (var_InheritBool(obj, "http-forward-cookies"))
172         jar = var_InheritAddress(obj, "http-cookies");
173 
174     struct vlc_credential crd;
175     struct vlc_url_t crd_url;
176     char *psz_realm = NULL;
177 
178     vlc_UrlParse(&crd_url, access->psz_url);
179     vlc_credential_init(&crd, &crd_url);
180 
181     sys->manager = vlc_http_mgr_create(obj, jar);
182     if (sys->manager == NULL)
183         goto error;
184 
185     char *ua = var_InheritString(obj, "http-user-agent");
186     char *referer = var_InheritString(obj, "http-referrer");
187     bool live = var_InheritBool(obj, "http-continuous");
188 
189     sys->resource = (live ? vlc_http_live_create : vlc_http_file_create)(
190         sys->manager, access->psz_url, ua, referer);
191     free(referer);
192     free(ua);
193 
194     if (sys->resource == NULL)
195         goto error;
196 
197     if (vlc_credential_get(&crd, obj, NULL, NULL, NULL, NULL))
198         vlc_http_res_set_login(sys->resource,
199                                crd.psz_username, crd.psz_password);
200 
201     ret = VLC_EGENERIC;
202 
203     int status = vlc_http_res_get_status(sys->resource);
204 
205     while (status == 401) /* authentication */
206     {
207         crd.psz_authtype = "Basic";
208         free(psz_realm);
209         psz_realm = vlc_http_res_get_basic_realm(sys->resource);
210 
211         if (psz_realm == NULL)
212             break;
213         crd.psz_realm = psz_realm;
214         if (!vlc_credential_get(&crd, obj, NULL, NULL, _("HTTP authentication"),
215                                 _("Please enter a valid login name and "
216                                   "a password for realm %s."), crd.psz_realm))
217             break;
218 
219         vlc_http_res_set_login(sys->resource,
220                                crd.psz_username, crd.psz_password);
221         status = vlc_http_res_get_status(sys->resource);
222     }
223 
224     if (status < 0)
225     {
226         msg_Err(access, "HTTP connection failure");
227         goto error;
228     }
229 
230     char *redir = vlc_http_res_get_redirect(sys->resource);
231     if (redir != NULL)
232     {
233         access->psz_url = redir;
234         ret = VLC_ACCESS_REDIRECT;
235         goto error;
236     }
237 
238     if (status >= 300)
239     {
240         msg_Err(access, "HTTP %d error", status);
241         goto error;
242     }
243 
244     vlc_credential_store(&crd, obj);
245     free(psz_realm);
246     vlc_credential_clean(&crd);
247     vlc_UrlClean(&crd_url);
248 
249     access->pf_read = NULL;
250     if (live)
251     {
252         access->pf_block = LiveRead;
253         access->pf_seek = NoSeek;
254         access->pf_control = LiveControl;
255     }
256     else
257     {
258         access->pf_block = FileRead;
259         access->pf_seek = FileSeek;
260         access->pf_control = FileControl;
261     }
262     access->p_sys = sys;
263     return VLC_SUCCESS;
264 
265 error:
266     if (sys->resource != NULL)
267         vlc_http_res_destroy(sys->resource);
268     if (sys->manager != NULL)
269         vlc_http_mgr_destroy(sys->manager);
270     free(psz_realm);
271     vlc_credential_clean(&crd);
272     vlc_UrlClean(&crd_url);
273     free(sys);
274     return ret;
275 }
276 
Close(vlc_object_t * obj)277 static void Close(vlc_object_t *obj)
278 {
279     stream_t *access = (stream_t *)obj;
280     access_sys_t *sys = access->p_sys;
281 
282     vlc_http_res_destroy(sys->resource);
283     vlc_http_mgr_destroy(sys->manager);
284     free(sys);
285 }
286 
287 vlc_module_begin()
288     set_description(N_("HTTPS input"))
289     set_shortname(N_("HTTPS"))
290     set_category(CAT_INPUT)
291     set_subcategory(SUBCAT_INPUT_ACCESS)
292     set_capability("access", 2)
293     add_shortcut("https", "http")
294     set_callbacks(Open, Close)
295 
296     add_bool("http-continuous", false, N_("Continuous stream"),
297              N_("Keep reading a resource that keeps being updated."), true)
298         change_volatile()
299     add_bool("http-forward-cookies", true, N_("Cookies forwarding"),
300              N_("Forward cookies across HTTP redirections."), true)
301     add_string("http-referrer", NULL, N_("Referrer"),
302                N_("Provide the referral URL, i.e. HTTP \"Referer\" (sic)."),
303                true)
304         change_safe()
305         change_volatile()
306     add_string("http-user-agent", NULL, N_("User agent"),
307                N_("Override the name and version of the application as "
308                   "provided to the HTTP server, i.e. the HTTP \"User-Agent\". "
309                   "Name and version must be separated by a forward slash, "
310                   "e.g. \"FooBar/1.2.3\"."), true)
311         change_safe()
312         change_private()
313 vlc_module_end()
314