1 /*****************************************************************************
2  * access.c
3  *****************************************************************************
4  * Copyright (C) 1999-2008 VLC authors and VideoLAN
5  * $Id: 2e7c1aea1da5cdcb2ea22daf3e08ce3cee6844e7 $
6  *
7  * Author: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23 
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27 
28 #include <assert.h>
29 #include <stdlib.h>
30 #include <string.h>
31 
32 #include <vlc_common.h>
33 #include <vlc_url.h>
34 #include <vlc_modules.h>
35 #include <vlc_interrupt.h>
36 
37 #include <libvlc.h>
38 #include "stream.h"
39 #include "input_internal.h"
40 
41 /* Decode URL (which has had its scheme stripped earlier) to a file path. */
get_path(const char * location)42 char *get_path(const char *location)
43 {
44     char *url, *path;
45 
46     /* Prepending "file://" is a bit hackish. But then again, we do not want
47      * to hard-code the list of schemes that use file paths in vlc_uri2path().
48      */
49     if (asprintf(&url, "file://%s", location) == -1)
50         return NULL;
51 
52     path = vlc_uri2path (url);
53     free (url);
54     return path;
55 }
56 
vlc_access_Destroy(stream_t * access)57 static void vlc_access_Destroy(stream_t *access)
58 {
59     module_unneed(access, access->p_module);
60     free(access->psz_filepath);
61     free(access->psz_name);
62 }
63 
64 #define MAX_REDIR 5
65 
66 /*****************************************************************************
67  * access_New:
68  *****************************************************************************/
access_New(vlc_object_t * parent,input_thread_t * input,bool preparsing,const char * mrl)69 static stream_t *access_New(vlc_object_t *parent, input_thread_t *input,
70                             bool preparsing, const char *mrl)
71 {
72     char *redirv[MAX_REDIR];
73     unsigned redirc = 0;
74 
75     stream_t *access = vlc_stream_CommonNew(parent, vlc_access_Destroy);
76     if (unlikely(access == NULL))
77         return NULL;
78 
79     access->p_input = input;
80     access->psz_name = NULL;
81     access->psz_url = strdup(mrl);
82     access->psz_filepath = NULL;
83     access->b_preparsing = preparsing;
84 
85     if (unlikely(access->psz_url == NULL))
86         goto error;
87 
88     while (redirc < MAX_REDIR)
89     {
90         char *url = access->psz_url;
91         msg_Dbg(access, "creating access: %s", url);
92 
93         const char *p = strstr(url, "://");
94         if (p == NULL)
95             goto error;
96 
97         access->psz_name = strndup(url, p - url);
98         if (unlikely(access->psz_name == NULL))
99             goto error;
100 
101         access->psz_location = p + 3;
102         access->psz_filepath = get_path(access->psz_location);
103         if (access->psz_filepath != NULL)
104             msg_Dbg(access, " (path: %s)", access->psz_filepath);
105 
106         access->p_module = module_need(access, "access", access->psz_name,
107                                        true);
108         if (access->p_module != NULL) /* success */
109         {
110             while (redirc > 0)
111                 free(redirv[--redirc]);
112 
113             assert(access->pf_control != NULL);
114             return access;
115         }
116 
117         if (access->psz_url == url) /* failure (no redirection) */
118             goto error;
119 
120         /* redirection */
121         msg_Dbg(access, "redirecting to: %s", access->psz_url);
122         redirv[redirc++] = url;
123 
124         for (unsigned j = 0; j < redirc; j++)
125             if (!strcmp(redirv[j], access->psz_url))
126             {
127                 msg_Err(access, "redirection loop");
128                 goto error;
129             }
130 
131         free(access->psz_filepath);
132         free(access->psz_name);
133         access->psz_filepath = access->psz_name = NULL;
134     }
135 
136     msg_Err(access, "too many redirections");
137 error:
138     while (redirc > 0)
139         free(redirv[--redirc]);
140     free(access->psz_filepath);
141     free(access->psz_name);
142     stream_CommonDelete(access);
143     return NULL;
144 }
145 
vlc_access_NewMRL(vlc_object_t * parent,const char * mrl)146 stream_t *vlc_access_NewMRL(vlc_object_t *parent, const char *mrl)
147 {
148     return access_New(parent, NULL, false, mrl);
149 }
150 
151 /*****************************************************************************
152  * access_vaDirectoryControlHelper:
153  *****************************************************************************/
access_vaDirectoryControlHelper(stream_t * p_access,int i_query,va_list args)154 int access_vaDirectoryControlHelper( stream_t *p_access, int i_query, va_list args )
155 {
156     VLC_UNUSED( p_access );
157 
158     switch( i_query )
159     {
160         case STREAM_CAN_SEEK:
161         case STREAM_CAN_FASTSEEK:
162         case STREAM_CAN_PAUSE:
163         case STREAM_CAN_CONTROL_PACE:
164             *va_arg( args, bool* ) = false;
165             break;
166         case STREAM_GET_PTS_DELAY:
167             *va_arg( args, int64_t * ) = 0;
168             break;
169         case STREAM_IS_DIRECTORY:
170             break;
171         default:
172             return VLC_EGENERIC;
173      }
174      return VLC_SUCCESS;
175 }
176 
AStreamNoReadDir(stream_t * s,input_item_node_t * p_node)177 static int AStreamNoReadDir(stream_t *s, input_item_node_t *p_node)
178 {
179     (void) s; (void) p_node;
180     return VLC_EGENERIC;;
181 }
182 
183 /* Block access */
AStreamReadBlock(stream_t * s,bool * restrict eof)184 static block_t *AStreamReadBlock(stream_t *s, bool *restrict eof)
185 {
186     stream_t *access = s->p_sys;
187     input_thread_t *input = s->p_input;
188     block_t * block;
189 
190     if (vlc_stream_Eof(access))
191     {
192         *eof = true;
193         return NULL;
194     }
195     if (vlc_killed())
196         return NULL;
197 
198     block = vlc_stream_ReadBlock(access);
199 
200     if (block != NULL && input != NULL)
201     {
202         uint64_t total;
203 
204         vlc_mutex_lock(&input_priv(input)->counters.counters_lock);
205         stats_Update(input_priv(input)->counters.p_read_bytes,
206                      block->i_buffer, &total);
207         stats_Update(input_priv(input)->counters.p_input_bitrate, total, NULL);
208         stats_Update(input_priv(input)->counters.p_read_packets, 1, NULL);
209         vlc_mutex_unlock(&input_priv(input)->counters.counters_lock);
210     }
211 
212     return block;
213 }
214 
215 /* Read access */
AStreamReadStream(stream_t * s,void * buf,size_t len)216 static ssize_t AStreamReadStream(stream_t *s, void *buf, size_t len)
217 {
218     stream_t *access = s->p_sys;
219     input_thread_t *input = s->p_input;
220 
221     if (vlc_stream_Eof(access))
222         return 0;
223     if (vlc_killed())
224         return -1;
225 
226     ssize_t val = vlc_stream_ReadPartial(access, buf, len);
227 
228     if (val > 0 && input != NULL)
229     {
230         uint64_t total;
231 
232         vlc_mutex_lock(&input_priv(input)->counters.counters_lock);
233         stats_Update(input_priv(input)->counters.p_read_bytes, val, &total);
234         stats_Update(input_priv(input)->counters.p_input_bitrate, total, NULL);
235         stats_Update(input_priv(input)->counters.p_read_packets, 1, NULL);
236         vlc_mutex_unlock(&input_priv(input)->counters.counters_lock);
237     }
238 
239     return val;
240 }
241 
242 /* Directory */
AStreamReadDir(stream_t * s,input_item_node_t * p_node)243 static int AStreamReadDir(stream_t *s, input_item_node_t *p_node)
244 {
245     stream_t *access = s->p_sys;
246 
247     return access->pf_readdir(access, p_node);
248 }
249 
250 /* Common */
AStreamSeek(stream_t * s,uint64_t offset)251 static int AStreamSeek(stream_t *s, uint64_t offset)
252 {
253     stream_t *access = s->p_sys;
254 
255     return vlc_stream_Seek(access, offset);
256 }
257 
AStreamControl(stream_t * s,int cmd,va_list args)258 static int AStreamControl(stream_t *s, int cmd, va_list args)
259 {
260     stream_t *access = s->p_sys;
261 
262     return vlc_stream_vaControl(access, cmd, args);
263 }
264 
AStreamDestroy(stream_t * s)265 static void AStreamDestroy(stream_t *s)
266 {
267     stream_t *access = s->p_sys;
268 
269     vlc_stream_Delete(access);
270 }
271 
stream_AccessNew(vlc_object_t * parent,input_thread_t * input,bool preparsing,const char * url)272 stream_t *stream_AccessNew(vlc_object_t *parent, input_thread_t *input,
273                            bool preparsing, const char *url)
274 {
275     stream_t *s = vlc_stream_CommonNew(parent, AStreamDestroy);
276     if (unlikely(s == NULL))
277         return NULL;
278 
279     stream_t *access = access_New(VLC_OBJECT(s), input, preparsing, url);
280     if (access == NULL)
281     {
282         stream_CommonDelete(s);
283         return NULL;
284     }
285 
286     s->p_input = input;
287     s->psz_url = strdup(access->psz_url);
288 
289     const char *cachename;
290 
291     if (access->pf_block != NULL)
292     {
293         s->pf_block = AStreamReadBlock;
294         cachename = "prefetch,cache_block";
295     }
296     else
297     if (access->pf_read != NULL)
298     {
299         s->pf_read = AStreamReadStream;
300         cachename = "prefetch,cache_read";
301     }
302     else
303     {
304         cachename = NULL;
305     }
306 
307     if (access->pf_readdir != NULL)
308         s->pf_readdir = AStreamReadDir;
309     else
310         s->pf_readdir = AStreamNoReadDir;
311 
312     s->pf_seek    = AStreamSeek;
313     s->pf_control = AStreamControl;
314     s->p_sys      = access;
315 
316     if (cachename != NULL)
317         s = stream_FilterChainNew(s, cachename);
318     return stream_FilterAutoNew(s);
319 }
320