@@ -1053,14 +1053,16 @@ impl<R: Read + Seek> ZipArchive<R> {
10531053 ) -> ZipResult < ZipFile < ' _ , R > > {
10541054 self . index_for_path ( path)
10551055 . ok_or ( ZipError :: FileNotFound )
1056- . and_then ( |index| self . by_index_with_optional_password ( index, Some ( password) ) )
1056+ . and_then ( |index| {
1057+ self . by_index_with_options ( index, ZipReadOptions :: new ( ) . password ( Some ( password) ) )
1058+ } )
10571059 }
10581060
10591061 /// Search for a file entry by path
10601062 pub fn by_path < T : AsRef < Path > > ( & mut self , path : T ) -> ZipResult < ZipFile < ' _ , R > > {
10611063 self . index_for_path ( path)
10621064 . ok_or ( ZipError :: FileNotFound )
1063- . and_then ( |index| self . by_index_with_optional_password ( index, None ) )
1065+ . and_then ( |index| self . by_index_with_options ( index, ZipReadOptions :: new ( ) ) )
10641066 }
10651067
10661068 /// Get the index of a file entry by path, if it's present.
@@ -1116,7 +1118,7 @@ impl<R: Read + Seek> ZipArchive<R> {
11161118 let Some ( index) = self . shared . files . get_index_of ( name) else {
11171119 return Err ( ZipError :: FileNotFound ) ;
11181120 } ;
1119- self . by_index_with_optional_password ( index, password)
1121+ self . by_index_with_options ( index, ZipReadOptions :: new ( ) . password ( password ) )
11201122 }
11211123
11221124 /// Get a contained file by index, decrypt with given password
@@ -1137,12 +1139,12 @@ impl<R: Read + Seek> ZipArchive<R> {
11371139 file_number : usize ,
11381140 password : & [ u8 ] ,
11391141 ) -> ZipResult < ZipFile < ' _ , R > > {
1140- self . by_index_with_optional_password ( file_number, Some ( password) )
1142+ self . by_index_with_options ( file_number, ZipReadOptions :: new ( ) . password ( Some ( password) ) )
11411143 }
11421144
11431145 /// Get a contained file by index
11441146 pub fn by_index ( & mut self , file_number : usize ) -> ZipResult < ZipFile < ' _ , R > > {
1145- self . by_index_with_optional_password ( file_number, None )
1147+ self . by_index_with_options ( file_number, ZipReadOptions :: new ( ) )
11461148 }
11471149
11481150 /// Get a contained file by index without decompressing it
@@ -1159,25 +1161,36 @@ impl<R: Read + Seek> ZipArchive<R> {
11591161 } )
11601162 }
11611163
1162- fn by_index_with_optional_password (
1164+ /// Get a contained file by index with options.
1165+ pub fn by_index_with_options (
11631166 & mut self ,
11641167 file_number : usize ,
1165- mut password : Option < & [ u8 ] > ,
1168+ mut options : ZipReadOptions < ' _ > ,
11661169 ) -> ZipResult < ZipFile < ' _ , R > > {
11671170 let ( _, data) = self
11681171 . shared
11691172 . files
11701173 . get_index ( file_number)
11711174 . ok_or ( ZipError :: FileNotFound ) ?;
11721175
1173- match ( password, data. encrypted ) {
1174- ( None , true ) => return Err ( ZipError :: UnsupportedArchive ( ZipError :: PASSWORD_REQUIRED ) ) ,
1175- ( Some ( _) , false ) => password = None , //Password supplied, but none needed! Discard.
1176- _ => { }
1176+ if options. ignore_encryption_flag {
1177+ // Always use no password when we're ignoring the encryption flag.
1178+ options. password = None ;
1179+ } else {
1180+ // Require and use the password only if the file is encrypted.
1181+ match ( options. password , data. encrypted ) {
1182+ ( None , true ) => {
1183+ return Err ( ZipError :: UnsupportedArchive ( ZipError :: PASSWORD_REQUIRED ) )
1184+ }
1185+ // Password supplied, but none needed! Discard.
1186+ ( Some ( _) , false ) => options. password = None ,
1187+ _ => { }
1188+ }
11771189 }
11781190 let limit_reader = find_content ( data, & mut self . reader ) ?;
11791191
1180- let crypto_reader = make_crypto_reader ( data, limit_reader, password, data. aes_mode ) ?;
1192+ let crypto_reader =
1193+ make_crypto_reader ( data, limit_reader, options. password , data. aes_mode ) ?;
11811194
11821195 Ok ( ZipFile {
11831196 data : Cow :: Borrowed ( data) ,
@@ -1555,6 +1568,38 @@ pub trait HasZipMetadata {
15551568 fn get_metadata ( & self ) -> & ZipFileData ;
15561569}
15571570
1571+ /// Options for reading a file from an archive.
1572+ #[ derive( Default ) ]
1573+ pub struct ZipReadOptions < ' a > {
1574+ /// The password to use when decrypting the file. This is ignored if not required.
1575+ password : Option < & ' a [ u8 ] > ,
1576+
1577+ /// Ignore the value of the encryption flag and proceed as if the file were plaintext.
1578+ ignore_encryption_flag : bool ,
1579+ }
1580+
1581+ impl < ' a > ZipReadOptions < ' a > {
1582+ /// Create a new set of options with the default values.
1583+ #[ must_use]
1584+ pub fn new ( ) -> Self {
1585+ Self :: default ( )
1586+ }
1587+
1588+ /// Set the password, if any, to use. Return for chaining.
1589+ #[ must_use]
1590+ pub fn password ( mut self , password : Option < & ' a [ u8 ] > ) -> Self {
1591+ self . password = password;
1592+ self
1593+ }
1594+
1595+ /// Set the ignore encryption flag. Return for chaining.
1596+ #[ must_use]
1597+ pub fn ignore_encryption_flag ( mut self , ignore : bool ) -> Self {
1598+ self . ignore_encryption_flag = ignore;
1599+ self
1600+ }
1601+ }
1602+
15581603/// Methods for retrieving information on zip files
15591604impl < ' a , R : Read > ZipFile < ' a , R > {
15601605 pub ( crate ) fn take_raw_reader ( & mut self ) -> io:: Result < io:: Take < & ' a mut R > > {
@@ -2065,6 +2110,7 @@ fn generate_chrono_datetime(datetime: &DateTime) -> Option<chrono::NaiveDateTime
20652110
20662111#[ cfg( test) ]
20672112mod test {
2113+ use crate :: read:: ZipReadOptions ;
20682114 use crate :: result:: ZipResult ;
20692115 use crate :: write:: SimpleFileOptions ;
20702116 use crate :: CompressionMethod :: Stored ;
@@ -2374,4 +2420,23 @@ mod test {
23742420 }
23752421 Ok ( ( ) )
23762422 }
2423+
2424+ #[ test]
2425+ fn test_ignore_encryption_flag ( ) -> ZipResult < ( ) > {
2426+ let mut reader = ZipArchive :: new ( Cursor :: new ( include_bytes ! (
2427+ "../tests/data/ignore_encryption_flag.zip"
2428+ ) ) ) ?;
2429+
2430+ // Get the file entry by ignoring its encryption flag.
2431+ let mut file =
2432+ reader. by_index_with_options ( 0 , ZipReadOptions :: new ( ) . ignore_encryption_flag ( true ) ) ?;
2433+ let mut contents = String :: new ( ) ;
2434+ assert_eq ! ( file. name( ) , "plaintext.txt" ) ;
2435+
2436+ // The file claims it is encrypted, but it is not.
2437+ assert ! ( file. encrypted( ) ) ;
2438+ file. read_to_string ( & mut contents) ?; // ensures valid UTF-8
2439+ assert_eq ! ( contents, "This file is not encrypted.\n " ) ;
2440+ Ok ( ( ) )
2441+ }
23772442}
0 commit comments