]> git.sesse.net Git - vlc/blob - modules/access/rar/access.c
access: rar: fix memory leaks of unused naming scheme
[vlc] / modules / access / rar / access.c
1 /*****************************************************************************
2  * access.c: uncompressed RAR access
3  *****************************************************************************
4  * Copyright (C) 2008-2010 Laurent Aimar
5  * $Id$
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 <vlc_common.h>
29 #include <vlc_plugin.h>
30 #include <vlc_access.h>
31 #include <vlc_stream.h>
32 #include <vlc_url.h>
33
34 #include <assert.h>
35 #include <limits.h>
36
37 #include "rar.h"
38
39 struct access_sys_t {
40     stream_t               *s;
41     rar_file_t             *file;
42     const rar_file_chunk_t *chunk;
43 };
44
45 static int Seek(access_t *access, uint64_t position)
46 {
47     access_sys_t *sys = access->p_sys;
48     const rar_file_t *file = sys->file;
49
50     if (position > file->real_size)
51         position = file->real_size;
52
53     /* Search the chunk */
54     const rar_file_chunk_t *old_chunk = sys->chunk;
55     for (int i = 0; i < file->chunk_count; i++) {
56         sys->chunk = file->chunk[i];
57         if (position < sys->chunk->cummulated_size + sys->chunk->size)
58             break;
59     }
60     access->info.i_pos = position;
61     access->info.b_eof = false;
62
63     const uint64_t offset = sys->chunk->offset +
64                             (position - sys->chunk->cummulated_size);
65
66     if (strcmp(old_chunk->mrl, sys->chunk->mrl)) {
67         if (sys->s)
68             stream_Delete(sys->s);
69         sys->s = stream_UrlNew(access, sys->chunk->mrl);
70     }
71     return sys->s ? stream_Seek(sys->s, offset) : VLC_EGENERIC;
72 }
73
74 static ssize_t Read(access_t *access, uint8_t *data, size_t size)
75 {
76     access_sys_t *sys = access->p_sys;
77
78     size_t total = 0;
79     while (total < size) {
80         const uint64_t chunk_end = sys->chunk->cummulated_size + sys->chunk->size;
81         int max = __MIN(__MIN((int64_t)(size - total), (int64_t)(chunk_end - access->info.i_pos)), INT_MAX);
82         if (max <= 0)
83             break;
84
85         int r = sys->s ? stream_Read(sys->s, data, max) : -1;
86         if (r <= 0)
87             break;
88
89         total += r;
90         if( data )
91             data += r;
92         access->info.i_pos += r;
93         if (access->info.i_pos >= chunk_end &&
94             Seek(access, access->info.i_pos))
95             break;
96     }
97     if (size > 0 && total <= 0)
98         access->info.b_eof = true;
99     return total;
100
101 }
102
103 static int Control(access_t *access, int query, va_list args)
104 {
105     access_sys_t *sys = access->p_sys;
106     stream_t *s = sys->s;
107     if (!s)
108         return VLC_EGENERIC;
109
110     switch (query) {
111     case ACCESS_CAN_SEEK: {
112         bool *b = va_arg(args, bool *);
113         return stream_Control(s, STREAM_CAN_SEEK, b);
114     }
115     case ACCESS_CAN_FASTSEEK: {
116         bool *b = va_arg(args, bool *);
117         return stream_Control(s, STREAM_CAN_FASTSEEK, b);
118     }
119     /* FIXME the following request should ask the underlying access object */
120     case ACCESS_CAN_PAUSE:
121     case ACCESS_CAN_CONTROL_PACE: {
122         bool *b = va_arg(args, bool *);
123         *b = true;
124         return VLC_SUCCESS;
125     }
126     case ACCESS_GET_SIZE:
127         *va_arg(args, uint64_t *) = sys->file->size;
128         return VLC_SUCCESS;
129     case ACCESS_GET_PTS_DELAY: {
130         int64_t *delay = va_arg(args, int64_t *);
131         *delay = DEFAULT_PTS_DELAY;
132         return VLC_SUCCESS;
133     }
134     case ACCESS_SET_PAUSE_STATE:
135         return VLC_SUCCESS;
136
137     default:
138         return VLC_EGENERIC;
139     }
140 }
141
142 int RarAccessOpen(vlc_object_t *object)
143 {
144     access_t *access = (access_t*)object;
145
146     if (!strchr(access->psz_location, '|'))
147         return VLC_EGENERIC;
148
149     char *base = strdup(access->psz_location);
150     if (!base)
151         return VLC_EGENERIC;
152     char *name = strchr(base, '|');
153     *name++ = '\0';
154     decode_URI(base);
155
156     stream_t *s = stream_UrlNew(access, base);
157     if (!s || RarProbe(s))
158         goto error;
159
160     struct
161     {
162         int filescount;
163         rar_file_t **files;
164         unsigned int i_nbvols;
165     } newscheme = { 0, NULL, 0 }, oldscheme = { 0, NULL, 0 }, *p_scheme;
166
167     if (RarParse(s, &newscheme.filescount, &newscheme.files, &newscheme.i_nbvols, false)
168             || newscheme.filescount < 1 || newscheme.i_nbvols < 2 )
169     {
170         /* We might want to lookup old naming scheme, could be a part1.rar,part1.r00 */
171         stream_Seek(s, 0);
172         RarParse(s, &oldscheme.filescount, &oldscheme.files, &oldscheme.i_nbvols, true);
173     }
174
175     if (oldscheme.filescount >= newscheme.filescount && oldscheme.i_nbvols > newscheme.i_nbvols)
176     {
177         for (int i = 0; i < newscheme.filescount; i++)
178             RarFileDelete(newscheme.files[i]);
179         free(newscheme.files);
180         p_scheme = &oldscheme;
181         msg_Dbg(s, "using rar old naming for %d files nbvols %u", p_scheme->filescount, oldscheme.i_nbvols);
182     }
183     else if (newscheme.filescount)
184     {
185         for (int i = 0; i < oldscheme.filescount; i++)
186             RarFileDelete(oldscheme.files[i]);
187         free(oldscheme.files);
188         p_scheme = &newscheme;
189         msg_Dbg(s, "using rar new naming for %d files nbvols %u", p_scheme->filescount, oldscheme.i_nbvols);
190     }
191     else
192     {
193         msg_Info(s, "Invalid or unsupported RAR archive");
194         for (int i = 0; i < oldscheme.filescount; i++)
195             RarFileDelete(oldscheme.files[i]);
196         free(oldscheme.files);
197         for (int i = 0; i < newscheme.filescount; i++)
198             RarFileDelete(newscheme.files[i]);
199         free(newscheme.files);
200         goto error;
201     }
202
203     rar_file_t *file = NULL;
204     for (int i = 0; i < p_scheme->filescount; i++) {
205         if (!file && !strcmp(p_scheme->files[i]->name, name))
206             file = p_scheme->files[i];
207         else
208             RarFileDelete(p_scheme->files[i]);
209     }
210     free(p_scheme->files);
211     if (!file)
212         goto error;
213
214     access_sys_t *sys = access->p_sys = malloc(sizeof(*sys));
215     sys->s    = s;
216     sys->file = file;
217
218     access->pf_read    = Read;
219     access->pf_block   = NULL;
220     access->pf_control = Control;
221     access->pf_seek    = Seek;
222
223     access_InitFields(access);
224
225     rar_file_chunk_t dummy = {
226         .mrl = base,
227     };
228     sys->chunk = &dummy;
229     Seek(access, 0);
230
231     free(base);
232     return VLC_SUCCESS;
233
234 error:
235     if (s)
236         stream_Delete(s);
237     free(base);
238     return VLC_EGENERIC;
239 }
240
241 void RarAccessClose(vlc_object_t *object)
242 {
243     access_t *access = (access_t*)object;
244     access_sys_t *sys = access->p_sys;
245
246     if (sys->s)
247         stream_Delete(sys->s);
248     RarFileDelete(sys->file);
249     free(sys);
250 }