macosx: Correct drawing of Volume slider in RTL mode
[vlc.git] / modules / stream_extractor / archive.c
1 /*****************************************************************************
2  * archive.c: libarchive based stream filter
3  *****************************************************************************
4  * Copyright (C) 2016 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Filip Roséen <filip@atch.se>
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_stream.h>
31 #include <vlc_stream_extractor.h>
32 #include <vlc_dialog.h>
33 #include <vlc_input_item.h>
34
35 #include <assert.h>
36 #include <archive.h>
37 #include <archive_entry.h>
38
39 #if ARCHIVE_VERSION_NUMBER < 3002000
40 typedef __LA_INT64_T la_int64_t;
41 typedef __LA_SSIZE_T la_ssize_t;
42 #endif
43
44 static  int ExtractorOpen( vlc_object_t* );
45 static void ExtractorClose( vlc_object_t* );
46
47 static  int DirectoryOpen( vlc_object_t* );
48 static void DirectoryClose( vlc_object_t* );
49
50 vlc_module_begin()
51     set_category( CAT_INPUT )
52     set_subcategory( SUBCAT_INPUT_STREAM_FILTER )
53     set_capability( "stream_directory", 99 )
54     set_description( N_( "libarchive based stream directory" ) )
55     set_callbacks( DirectoryOpen, DirectoryClose );
56
57     add_submodule()
58         set_description( N_( "libarchive based stream extractor" ) )
59         set_capability( "stream_extractor", 99 )
60         set_callbacks( ExtractorOpen, ExtractorClose );
61
62 vlc_module_end()
63
64 typedef struct libarchive_callback_t libarchive_callback_t;
65 typedef struct private_sys_t private_sys_t;
66 typedef struct archive libarchive_t;
67
68 struct private_sys_t
69 {
70     libarchive_t* p_archive;
71     vlc_object_t* p_obj;
72     stream_t* source;
73
74     struct archive_entry* p_entry;
75     bool b_dead;
76
77     uint64_t i_offset;
78
79     uint8_t buffer[ 8192 ];
80     bool b_seekable_source;
81     bool b_seekable_archive;
82
83     libarchive_callback_t** pp_callback_data;
84     size_t i_callback_data;
85 };
86
87 struct libarchive_callback_t {
88     private_sys_t* p_sys;
89     stream_t* p_source;
90     char* psz_url;
91 };
92
93 /* ------------------------------------------------------------------------- */
94
95 static int libarchive_exit_cb( libarchive_t* p_arc, void* p_obj )
96 {
97     VLC_UNUSED( p_arc );
98
99     libarchive_callback_t* p_cb = (libarchive_callback_t*)p_obj;
100
101     if( p_cb->p_sys->source == p_cb->p_source )
102     {  /* DO NOT CLOSE OUR MOTHER STREAM */
103         if( !p_cb->p_sys->b_dead && vlc_stream_Seek( p_cb->p_source, 0 ) )
104             return ARCHIVE_FATAL;
105     }
106     else if( p_cb->p_source )
107     {
108         vlc_stream_Delete( p_cb->p_source );
109         p_cb->p_source = NULL;
110     }
111
112     return ARCHIVE_OK;
113 }
114
115 static int libarchive_jump_cb( libarchive_t* p_arc, void* p_obj_current,
116   void* p_obj_next )
117 {
118     libarchive_callback_t* p_current = (libarchive_callback_t*)p_obj_current;
119     libarchive_callback_t* p_next    = (libarchive_callback_t*)p_obj_next;
120
121     if( libarchive_exit_cb( p_arc, p_current ) )
122         return ARCHIVE_FATAL;
123
124     if( p_next->p_source == NULL )
125         p_next->p_source = vlc_stream_NewURL( p_next->p_sys->p_obj,
126                                               p_next->psz_url );
127
128     return p_next->p_source ? ARCHIVE_OK : ARCHIVE_FATAL;
129 }
130
131
132 static la_int64_t libarchive_skip_cb( libarchive_t* p_arc, void* p_obj,
133   off_t i_request )
134 {
135     VLC_UNUSED( p_arc );
136
137     libarchive_callback_t* p_cb = (libarchive_callback_t*)p_obj;
138
139     stream_t*  p_source = p_cb->p_source;
140     private_sys_t* p_sys = p_cb->p_sys;
141
142     /* TODO: fix b_seekable_source on libarchive_callback_t */
143
144     if( p_sys->b_seekable_source )
145     {
146         if( vlc_stream_Seek( p_source, vlc_stream_Tell( p_source ) + i_request ) )
147             return ARCHIVE_FATAL;
148
149         return i_request;
150     }
151
152     ssize_t i_read = vlc_stream_Read( p_source, NULL, i_request );
153     return  i_read >= 0 ? i_read : ARCHIVE_FATAL;
154 }
155
156 static la_int64_t libarchive_seek_cb( libarchive_t* p_arc, void* p_obj,
157   la_int64_t offset, int whence )
158 {
159     VLC_UNUSED( p_arc );
160
161     libarchive_callback_t* p_cb = (libarchive_callback_t*)p_obj;
162     stream_t* p_source = p_cb->p_source;
163
164     ssize_t whence_pos;
165
166     switch( whence )
167     {
168         case SEEK_SET: whence_pos = 0;                           break;
169         case SEEK_CUR: whence_pos = vlc_stream_Tell( p_source ); break;
170         case SEEK_END: whence_pos = stream_Size( p_source ) - 1; break;
171               default: vlc_assert_unreachable();
172
173     }
174
175     if( whence_pos < 0 || vlc_stream_Seek( p_source, whence_pos + offset ) )
176         return ARCHIVE_FATAL;
177
178     return vlc_stream_Tell( p_source );
179 }
180
181 static la_ssize_t libarchive_read_cb( libarchive_t* p_arc, void* p_obj,
182   const void** pp_dst )
183 {
184     VLC_UNUSED( p_arc );
185
186     libarchive_callback_t* p_cb = (libarchive_callback_t*)p_obj;
187
188     stream_t*  p_source = p_cb->p_source;
189     private_sys_t* p_sys = p_cb->p_sys;
190
191     ssize_t i_ret = vlc_stream_Read( p_source, &p_sys->buffer,
192       sizeof( p_sys->buffer ) );
193
194     if( i_ret < 0 )
195     {
196         archive_set_error( p_sys->p_archive, ARCHIVE_FATAL,
197           "libarchive_read_cb failed = %" PRId64, i_ret );
198
199         return ARCHIVE_FATAL;
200     }
201
202     *pp_dst = &p_sys->buffer;
203     return i_ret;
204 }
205
206 /* ------------------------------------------------------------------------- */
207
208 static int archive_push_resource( private_sys_t* p_sys,
209   stream_t* p_source, char const* psz_url )
210 {
211     libarchive_callback_t** pp_callback_data;
212     libarchive_callback_t*   p_callback_data;
213
214     /* INCREASE BUFFER SIZE */
215
216     pp_callback_data = realloc( p_sys->pp_callback_data,
217       sizeof( *p_sys->pp_callback_data ) * ( p_sys->i_callback_data + 1 ) );
218
219     if( unlikely( !pp_callback_data ) )
220         goto error;
221
222     /* CREATE NEW NODE */
223
224     p_callback_data = malloc( sizeof( *p_callback_data ) );
225
226     if( unlikely( !p_callback_data ) )
227         goto error;
228
229     /* INITIALIZE AND APPEND */
230
231     p_callback_data->psz_url  = psz_url ? strdup( psz_url ) : NULL;
232     p_callback_data->p_source = p_source;
233     p_callback_data->p_sys    = p_sys;
234
235     if( unlikely( !p_callback_data->psz_url && psz_url ) )
236     {
237         free( p_callback_data );
238         goto error;
239     }
240
241     pp_callback_data[ p_sys->i_callback_data++ ] = p_callback_data;
242     p_sys->pp_callback_data = pp_callback_data;
243
244     return VLC_SUCCESS;
245
246 error:
247     free( pp_callback_data );
248     return VLC_ENOMEM;
249 }
250
251 static int archive_init( private_sys_t* p_sys, stream_t* source )
252 {
253     /* CREATE ARCHIVE HANDLE */
254
255     p_sys->p_archive = archive_read_new();
256
257     if( unlikely( !p_sys->p_archive ) )
258     {
259         msg_Dbg( p_sys->p_obj, "unable to create libarchive handle" );
260         return VLC_EGENERIC;
261     }
262
263     /* SETUP SEEKING */
264
265     p_sys->b_seekable_archive = false;
266
267     if( vlc_stream_Control( source, STREAM_CAN_SEEK,
268         &p_sys->b_seekable_source ) )
269     {
270         msg_Warn( p_sys->p_obj, "unable to query whether source stream can seek" );
271         p_sys->b_seekable_source = false;
272     }
273
274     if( p_sys->b_seekable_source )
275     {
276         if( archive_read_set_seek_callback( p_sys->p_archive,
277             libarchive_seek_cb ) )
278         {
279             msg_Err( p_sys->p_obj, "archive_read_set_callback failed, aborting." );
280             return VLC_EGENERIC;
281         }
282     }
283
284     /* ENABLE ALL FORMATS/FILTERS */
285
286     archive_read_support_filter_all( p_sys->p_archive );
287     archive_read_support_format_all( p_sys->p_archive );
288
289     /* REGISTER CALLBACK DATA */
290
291     if( archive_read_set_switch_callback( p_sys->p_archive,
292         libarchive_jump_cb ) )
293     {
294         msg_Err( p_sys->p_obj, "archive_read_set_switch_callback failed, aborting." );
295         return VLC_EGENERIC;
296     }
297
298     for( size_t i = 0; i < p_sys->i_callback_data; ++i )
299     {
300         if( archive_read_append_callback_data( p_sys->p_archive,
301             p_sys->pp_callback_data[i] ) )
302         {
303             return VLC_EGENERIC;
304         }
305     }
306
307     /* OPEN THE ARCHIVE */
308
309     if( archive_read_open2( p_sys->p_archive, p_sys->pp_callback_data[0], NULL,
310         libarchive_read_cb, libarchive_skip_cb, libarchive_exit_cb ) )
311     {
312         msg_Dbg( p_sys->p_obj, "libarchive: %s",
313           archive_error_string( p_sys->p_archive ) );
314
315         return VLC_EGENERIC;
316     }
317
318     return VLC_SUCCESS;
319 }
320
321 static int archive_clean( private_sys_t* p_sys )
322 {
323     libarchive_t* p_arc = p_sys->p_archive;
324
325     if( p_sys->p_entry )
326         archive_entry_free( p_sys->p_entry );
327
328     if( p_arc )
329         archive_read_free( p_arc );
330
331     p_sys->p_entry   = NULL;
332     p_sys->p_archive = NULL;
333
334     return VLC_SUCCESS;
335 }
336
337 static int archive_seek_subentry( private_sys_t* p_sys, char const* psz_subentry )
338 {
339     libarchive_t* p_arc = p_sys->p_archive;
340
341     struct archive_entry* entry;
342     int archive_status;
343
344     while( !( archive_status = archive_read_next_header( p_arc, &entry ) ) )
345     {
346         char const* entry_path = archive_entry_pathname( entry );
347
348         if( strcmp( entry_path, psz_subentry ) == 0 )
349         {
350             p_sys->p_entry = archive_entry_clone( entry );
351
352             if( unlikely( !p_sys->p_entry ) )
353                 return VLC_ENOMEM;
354
355             break;
356         }
357
358         archive_read_data_skip( p_arc );
359     }
360
361     switch( archive_status )
362     {
363         case ARCHIVE_WARN:
364             msg_Warn( p_sys->p_obj,
365               "libarchive: %s", archive_error_string( p_arc ) );
366
367         case ARCHIVE_EOF:
368         case ARCHIVE_FATAL:
369         case ARCHIVE_RETRY:
370             archive_set_error( p_arc, ARCHIVE_FATAL,
371                 "archive does not contain >>> %s <<<", psz_subentry );
372
373             return VLC_EGENERIC;
374     }
375
376     /* check if seeking is supported */
377
378     if( p_sys->b_seekable_source )
379     {
380         if( archive_seek_data( p_sys->p_archive, 0, SEEK_CUR ) >= 0 )
381             p_sys->b_seekable_archive = true;
382     }
383
384     return VLC_SUCCESS;
385 }
386
387 /* ------------------------------------------------------------------------- */
388
389 static private_sys_t* setup( vlc_object_t* obj, stream_t* source )
390 {
391     private_sys_t* p_sys  = calloc( 1, sizeof( *p_sys ) );
392     char* psz_files = var_InheritString( obj, "concat-list" );
393
394     if( unlikely( !p_sys ) )
395         goto error;
396
397     if( archive_push_resource( p_sys, source, NULL ) )
398         goto error;
399
400     if( psz_files )
401     {
402         for( char* state,
403                  * path = strtok_r( psz_files, ",", &state );
404              path; path = strtok_r(     NULL, ",", &state ) )
405         {
406             if( path == psz_files )
407                 continue;
408
409             if( archive_push_resource( p_sys, NULL, path ) )
410                 goto error;
411         }
412
413         free( psz_files );
414     }
415
416     p_sys->source = source;
417     p_sys->p_obj = obj;
418
419     return p_sys;
420
421 error:
422     free( psz_files );
423     free( p_sys );
424     return NULL;
425 }
426
427 static int probe( stream_t* source )
428 {
429     struct
430     {
431         uint16_t i_offset;
432         uint8_t  i_length;
433         char const * p_bytes;
434     } const magicbytes[] = {
435         /* keep heaviest at top */
436         { 257, 5, "ustar" },              //TAR
437         { 0,   7, "Rar!\x1A\x07" },       //RAR
438         { 0,   6, "7z\xBC\xAF\x27\x1C" }, //7z
439         { 0,   4, "xar!" },               //XAR
440         { 0,   4, "PK\x03\x04" },         //ZIP
441         { 0,   4, "PK\x05\x06" },         //ZIP
442         { 0,   4, "PK\x07\x08" },         //ZIP
443         { 2,   3, "-lh" },                //LHA/LHZ
444         { 0,   3, "\x1f\x8b\x08" },       // Gzip
445         { 0,   3, "PAX" },                //PAX
446         { 0,   6, "070707" },             //CPIO
447         { 0,   6, "070701" },             //CPIO
448         { 0,   6, "070702" },             //CPIO
449         { 0,   4, "MSCH" },               //CAB
450     };
451
452     const uint8_t *p_peek;
453
454     int i_peek = vlc_stream_Peek( source, &p_peek,
455       magicbytes[0].i_offset + magicbytes[0].i_length);
456
457     for(unsigned i=0; i < ARRAY_SIZE( magicbytes ); i++)
458     {
459         if (i_peek < magicbytes[i].i_offset + magicbytes[i].i_length)
460             continue;
461
462         if ( !memcmp(p_peek + magicbytes[i].i_offset,
463             magicbytes[i].p_bytes, magicbytes[i].i_length) )
464             return VLC_SUCCESS;
465     }
466
467     return VLC_EGENERIC;
468 }
469
470 /* ------------------------------------------------------------------------- */
471
472 static int Control( stream_extractor_t* p_extractor, int i_query, va_list args )
473 {
474     private_sys_t* p_sys = p_extractor->p_sys;
475
476     if( p_sys->b_dead )
477         return VLC_EGENERIC;
478
479     switch( i_query )
480     {
481         case STREAM_CAN_FASTSEEK:
482             *va_arg( args, bool* ) = false;
483             break;
484
485         case STREAM_CAN_SEEK:
486             *va_arg( args, bool* ) = p_sys->b_seekable_source;
487             break;
488
489         case STREAM_GET_SIZE:
490             if( p_sys->p_entry == NULL )
491                 return VLC_EGENERIC;
492
493             *va_arg( args, uint64_t* ) = archive_entry_size( p_sys->p_entry );
494             break;
495
496         default:
497             return vlc_stream_vaControl( p_extractor->source, i_query, args );
498     }
499
500     return VLC_SUCCESS;
501 }
502
503 static int ReadDir( stream_directory_t* p_directory, input_item_node_t* p_node )
504 {
505     private_sys_t* p_sys = p_directory->p_sys;
506     libarchive_t* p_arc = p_sys->p_archive;
507
508     struct archive_entry* entry;
509     int archive_status;
510
511     while( !( archive_status = archive_read_next_header( p_arc, &entry ) ) )
512     {
513         if( archive_entry_filetype( entry ) == AE_IFDIR )
514             continue;
515
516         char const* path = archive_entry_pathname( entry );
517         char*       mrl  = vlc_stream_extractor_CreateMRL( p_directory, path );
518
519         if( unlikely( !mrl ) )
520             return VLC_ENOMEM;
521
522         input_item_t* p_item = input_item_New( mrl, path );
523
524         free( mrl );
525
526         if( unlikely( !p_item ) )
527             return VLC_ENOMEM;
528
529
530         input_item_CopyOptions( p_node->p_item, p_item );
531         input_item_node_AppendItem( p_node, p_item );
532         input_item_Release( p_item );
533
534         if( archive_read_data_skip( p_arc ) )
535             break;
536     }
537
538     if( archive_status != ARCHIVE_EOF )
539         return VLC_EGENERIC;
540
541     return VLC_SUCCESS;
542 }
543
544 static ssize_t Read( stream_extractor_t *p_extractor, void* p_data, size_t i_size )
545 {
546     char dummy_buffer[ 8192 ];
547
548     private_sys_t* p_sys = p_extractor->p_sys;
549     libarchive_t* p_arc = p_sys->p_archive;
550     ssize_t       i_ret;
551
552     if( p_sys->b_dead || p_sys->p_entry == NULL )
553         return 0;
554
555     i_ret = archive_read_data( p_arc,
556       p_data ? p_data :                        dummy_buffer,
557       p_data ? i_size : __MIN( i_size, sizeof( dummy_buffer ) ) );
558
559     switch( i_ret )
560     {
561         case ARCHIVE_RETRY:
562         case ARCHIVE_FAILED:
563             msg_Dbg( p_extractor, "libarchive: %s", archive_error_string( p_arc ) );
564             return -1;
565
566         case ARCHIVE_WARN:
567             msg_Warn( p_extractor, "libarchive: %s", archive_error_string( p_arc ) );
568             return -1;
569
570         case ARCHIVE_FATAL:
571             p_sys->b_dead = true;
572             msg_Err( p_extractor, "libarchive: %s", archive_error_string( p_arc ) );
573             return 0;
574     }
575
576     p_sys->i_offset += i_ret;
577     return i_ret;
578 }
579
580 static int Seek( stream_extractor_t* p_extractor, uint64_t i_req )
581 {
582     private_sys_t* p_sys = p_extractor->p_sys;
583
584     if( p_sys->b_dead )
585         return VLC_EGENERIC;
586
587     if( !p_sys->p_entry )
588         return VLC_EGENERIC;
589
590     if( !p_sys->b_seekable_source )
591         return VLC_EGENERIC;
592
593     if( !p_sys->b_seekable_archive
594       || archive_seek_data( p_sys->p_archive, i_req, SEEK_SET ) < 0 )
595     {
596         msg_Dbg( p_extractor, "libarchive intrinsic seek failed:"
597                               " '%s' (falling back to dumb seek)",
598             archive_error_string( p_sys->p_archive ) );
599
600         uint64_t i_offset = p_sys->i_offset;
601         uint64_t i_skip   = i_req - i_offset;
602
603         /* RECREATE LIBARCHIE HANDLE IF WE ARE SEEKING BACKWARDS */
604
605         if( i_req < i_offset )
606         {
607             if( archive_clean( p_sys ) )
608                 return VLC_EGENERIC;
609
610             if( archive_init( p_sys, p_extractor->source ) ||
611                 archive_seek_subentry( p_sys, p_extractor->identifier ) )
612             {
613                 msg_Err( p_extractor, "unable to recreate libarchive handle" );
614                 return VLC_EGENERIC;
615             }
616
617             i_skip = i_req;
618             i_offset = 0;
619         }
620
621         /* SKIP _DECOMPRESSED_ DATA */
622
623         while( i_skip )
624         {
625             ssize_t i_read = Read( p_extractor, NULL, i_skip );
626
627             if( i_read < 1 )
628                 return VLC_EGENERIC;
629
630             i_offset += i_read;
631             i_skip   -= i_read;
632         }
633     }
634
635     p_sys->i_offset = i_req;
636     return VLC_SUCCESS;
637 }
638
639
640 static void CommonClose( private_sys_t* p_sys )
641 {
642     p_sys->b_dead = true;
643     archive_clean( p_sys );
644
645     for( size_t i = 0; i < p_sys->i_callback_data; ++i )
646     {
647         free( p_sys->pp_callback_data[i]->psz_url );
648         free( p_sys->pp_callback_data[i] );
649     }
650
651     free( p_sys->pp_callback_data );
652     free( p_sys );
653 }
654
655 static void DirectoryClose( vlc_object_t* p_obj )
656 {
657     stream_directory_t* p_directory = (void*)p_obj;
658     return CommonClose( p_directory->p_sys );
659 }
660
661 static void ExtractorClose( vlc_object_t* p_obj )
662 {
663     stream_extractor_t* p_extractor = (void*)p_obj;
664     return CommonClose( p_extractor->p_sys );
665 }
666
667 static private_sys_t* CommonOpen( vlc_object_t* p_obj, stream_t* source  )
668 {
669     if( probe( source ) )
670         return NULL;
671
672     private_sys_t* p_sys = setup( p_obj, source );
673
674     if( p_sys == NULL )
675         return NULL;
676
677     if( archive_init( p_sys, source ) )
678     {
679         CommonClose( p_sys );
680         return NULL;
681     }
682
683     return p_sys;
684 }
685
686 static int DirectoryOpen( vlc_object_t* p_obj )
687 {
688     stream_directory_t* p_directory = (void*)p_obj;
689     private_sys_t* p_sys = CommonOpen( p_obj, p_directory->source );
690
691     if( p_sys == NULL )
692         return VLC_EGENERIC;
693
694     p_directory->p_sys = p_sys;
695     p_directory->pf_readdir = ReadDir;
696
697     return VLC_SUCCESS;
698 }
699
700 static int ExtractorOpen( vlc_object_t* p_obj )
701 {
702     stream_extractor_t* p_extractor = (void*)p_obj;
703     private_sys_t* p_sys = CommonOpen( p_obj, p_extractor->source );
704
705     if( p_sys == NULL )
706         return VLC_EGENERIC;
707
708     if( archive_seek_subentry( p_sys, p_extractor->identifier ) )
709     {
710         CommonClose( p_sys );
711         return VLC_EGENERIC;
712     }
713
714     p_extractor->p_sys = p_sys;
715     p_extractor->pf_read = Read;
716     p_extractor->pf_control = Control;
717     p_extractor->pf_seek = Seek;
718
719     return VLC_SUCCESS;
720 }