mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-09-22 23:15:20 +00:00
Mk/add support for extracting password protected LZMA(2) 7z archives (#324)
* Added possibility to decompress a password protected 7z LZMA archive * Fix tests
This commit is contained in:
committed by
Adam Hathcock
parent
550fecd4d3
commit
c2528cf93e
1
.gitignore
vendored
1
.gitignore
vendored
@@ -14,3 +14,4 @@ tests/TestArchives/Scratch
|
||||
.vs
|
||||
tools
|
||||
.vscode
|
||||
.idea/
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.SevenZip;
|
||||
using SharpCompress.Compressors.LZMA.Utilites;
|
||||
using SharpCompress.IO;
|
||||
using SharpCompress.Readers;
|
||||
|
||||
@@ -117,7 +118,7 @@ namespace SharpCompress.Archives.SevenZip
|
||||
stream.Position = 0;
|
||||
var reader = new ArchiveReader();
|
||||
reader.Open(stream);
|
||||
database = reader.ReadDatabase(null);
|
||||
database = reader.ReadDatabase(new PasswordProvider(ReaderOptions.Password));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,7 +145,7 @@ namespace SharpCompress.Archives.SevenZip
|
||||
|
||||
protected override IReader CreateReaderForSolidExtraction()
|
||||
{
|
||||
return new SevenZipReader(this);
|
||||
return new SevenZipReader(ReaderOptions, this);
|
||||
}
|
||||
|
||||
public override bool IsSolid { get { return Entries.Where(x => !x.IsDirectory).GroupBy(x => x.FilePart.Folder).Count() > 1; } }
|
||||
@@ -165,8 +166,8 @@ namespace SharpCompress.Archives.SevenZip
|
||||
private Stream currentStream;
|
||||
private CFileItem currentItem;
|
||||
|
||||
internal SevenZipReader(SevenZipArchive archive)
|
||||
: base(new ReaderOptions(), ArchiveType.SevenZip)
|
||||
internal SevenZipReader(ReaderOptions readerOptions, SevenZipArchive archive)
|
||||
: base(readerOptions, ArchiveType.SevenZip)
|
||||
{
|
||||
this.archive = archive;
|
||||
}
|
||||
@@ -190,7 +191,7 @@ namespace SharpCompress.Archives.SevenZip
|
||||
}
|
||||
else
|
||||
{
|
||||
currentStream = archive.database.GetFolderStream(stream, currentFolder, null);
|
||||
currentStream = archive.database.GetFolderStream(stream, currentFolder, new PasswordProvider(Options.Password));
|
||||
}
|
||||
foreach (var entry in group)
|
||||
{
|
||||
@@ -205,5 +206,21 @@ namespace SharpCompress.Archives.SevenZip
|
||||
return CreateEntryStream(new ReadOnlySubStream(currentStream, currentItem.Size));
|
||||
}
|
||||
}
|
||||
|
||||
private class PasswordProvider : IPasswordProvider
|
||||
{
|
||||
private readonly string _password;
|
||||
|
||||
public PasswordProvider(string password)
|
||||
{
|
||||
_password = password;
|
||||
|
||||
}
|
||||
|
||||
public string CryptoGetTextPassword()
|
||||
{
|
||||
return _password;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,13 @@ namespace SharpCompress.Common.SevenZip
|
||||
internal List<long> PackStreamStartPositions = new List<long>();
|
||||
internal List<int> FolderStartFileIndex = new List<int>();
|
||||
internal List<int> FileIndexToFolderIndexMap = new List<int>();
|
||||
|
||||
internal IPasswordProvider PasswordProvider { get; }
|
||||
|
||||
public ArchiveDatabase(IPasswordProvider passwordProvider)
|
||||
{
|
||||
PasswordProvider = passwordProvider;
|
||||
}
|
||||
|
||||
internal void Clear()
|
||||
{
|
||||
|
||||
@@ -1211,7 +1211,7 @@ namespace SharpCompress.Common.SevenZip
|
||||
|
||||
public ArchiveDatabase ReadDatabase(IPasswordProvider pass)
|
||||
{
|
||||
var db = new ArchiveDatabase();
|
||||
var db = new ArchiveDatabase(pass);
|
||||
db.Clear();
|
||||
|
||||
db.MajorVersion = _header[6];
|
||||
@@ -1279,7 +1279,7 @@ namespace SharpCompress.Common.SevenZip
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
var dataVector = ReadAndDecodePackedStreams(db.StartPositionAfterHeader, pass);
|
||||
var dataVector = ReadAndDecodePackedStreams(db.StartPositionAfterHeader, db.PasswordProvider);
|
||||
|
||||
// compressed header without content is odd but ok
|
||||
if (dataVector.Count == 0)
|
||||
@@ -1301,7 +1301,7 @@ namespace SharpCompress.Common.SevenZip
|
||||
}
|
||||
}
|
||||
|
||||
ReadHeader(db, pass);
|
||||
ReadHeader(db, db.PasswordProvider);
|
||||
}
|
||||
db.Fill();
|
||||
return db;
|
||||
@@ -1441,7 +1441,7 @@ namespace SharpCompress.Common.SevenZip
|
||||
#endregion
|
||||
}
|
||||
|
||||
private Stream GetCachedDecoderStream(ArchiveDatabase _db, int folderIndex, IPasswordProvider pw)
|
||||
private Stream GetCachedDecoderStream(ArchiveDatabase _db, int folderIndex)
|
||||
{
|
||||
Stream s;
|
||||
if (!_cachedStreams.TryGetValue(folderIndex, out s))
|
||||
@@ -1456,13 +1456,13 @@ namespace SharpCompress.Common.SevenZip
|
||||
}
|
||||
|
||||
s = DecoderStreamHelper.CreateDecoderStream(_stream, folderStartPackPos, packSizes.ToArray(), folderInfo,
|
||||
pw);
|
||||
_db.PasswordProvider);
|
||||
_cachedStreams.Add(folderIndex, s);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
public Stream OpenStream(ArchiveDatabase _db, int fileIndex, IPasswordProvider pw)
|
||||
public Stream OpenStream(ArchiveDatabase _db, int fileIndex)
|
||||
{
|
||||
int folderIndex = _db.FileIndexToFolderIndexMap[fileIndex];
|
||||
int numFilesInFolder = _db.NumUnpackStreamsVector[folderIndex];
|
||||
@@ -1479,12 +1479,12 @@ namespace SharpCompress.Common.SevenZip
|
||||
skipSize += _db.Files[firstFileIndex + i].Size;
|
||||
}
|
||||
|
||||
Stream s = GetCachedDecoderStream(_db, folderIndex, pw);
|
||||
Stream s = GetCachedDecoderStream(_db, folderIndex);
|
||||
s.Position = skipSize;
|
||||
return new ReadOnlySubStream(s, _db.Files[fileIndex].Size);
|
||||
}
|
||||
|
||||
public void Extract(ArchiveDatabase _db, int[] indices, IPasswordProvider pw)
|
||||
public void Extract(ArchiveDatabase _db, int[] indices)
|
||||
{
|
||||
int numItems;
|
||||
bool allFilesMode = (indices == null);
|
||||
@@ -1562,7 +1562,7 @@ namespace SharpCompress.Common.SevenZip
|
||||
// TODO: If the decoding fails the last file may be extracted incompletely. Delete it?
|
||||
|
||||
Stream s = DecoderStreamHelper.CreateDecoderStream(_stream, folderStartPackPos, packSizes.ToArray(),
|
||||
folderInfo, pw);
|
||||
folderInfo, _db.PasswordProvider);
|
||||
byte[] buffer = new byte[4 << 10];
|
||||
for (;;)
|
||||
{
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace SharpCompress.Common.SevenZip
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var folderStream = _database.GetFolderStream(_stream, Folder, null);
|
||||
var folderStream = _database.GetFolderStream(_stream, Folder, _database.PasswordProvider);
|
||||
|
||||
int firstFileIndex = _database.FolderStartFileIndex[_database.Folders.IndexOf(Folder)];
|
||||
int skipCount = Index - firstFileIndex;
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace SharpCompress.IO
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override long Length => throw new NotSupportedException();
|
||||
public override long Length => BytesLeftToRead;
|
||||
|
||||
public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); }
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace SharpCompress.Test
|
||||
foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory))
|
||||
{
|
||||
entry.WriteToDirectory(SCRATCH_FILES_PATH,
|
||||
new ExtractionOptions()
|
||||
new ExtractionOptions
|
||||
{
|
||||
ExtractFullPath = true,
|
||||
Overwrite = true
|
||||
@@ -51,24 +51,24 @@ namespace SharpCompress.Test
|
||||
}
|
||||
}
|
||||
|
||||
protected void ArchiveStreamRead(string testArchive)
|
||||
protected void ArchiveStreamRead(string testArchive, ReaderOptions readerOptions = null)
|
||||
{
|
||||
testArchive = Path.Combine(TEST_ARCHIVES_PATH, testArchive);
|
||||
ArchiveStreamRead(testArchive.AsEnumerable());
|
||||
ArchiveStreamRead(readerOptions, testArchive.AsEnumerable());
|
||||
}
|
||||
|
||||
protected void ArchiveStreamRead(params string[] testArchives)
|
||||
protected void ArchiveStreamRead(ReaderOptions readerOptions = null, params string[] testArchives)
|
||||
{
|
||||
ArchiveStreamRead(testArchives.Select(x => Path.Combine(TEST_ARCHIVES_PATH, x)));
|
||||
ArchiveStreamRead(readerOptions, testArchives.Select(x => Path.Combine(TEST_ARCHIVES_PATH, x)));
|
||||
}
|
||||
|
||||
protected void ArchiveStreamRead(IEnumerable<string> testArchives)
|
||||
protected void ArchiveStreamRead(ReaderOptions readerOptions, IEnumerable<string> testArchives)
|
||||
{
|
||||
foreach (var path in testArchives)
|
||||
{
|
||||
ResetScratch();
|
||||
using (Stream stream = File.OpenRead(path))
|
||||
using (var archive = ArchiveFactory.Open(stream))
|
||||
using (var archive = ArchiveFactory.Open(stream, readerOptions))
|
||||
{
|
||||
foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory))
|
||||
{
|
||||
@@ -83,17 +83,17 @@ namespace SharpCompress.Test
|
||||
}
|
||||
}
|
||||
|
||||
protected void ArchiveFileRead(string testArchive)
|
||||
protected void ArchiveFileRead(string testArchive, ReaderOptions readerOptions = null)
|
||||
{
|
||||
testArchive = Path.Combine(TEST_ARCHIVES_PATH, testArchive);
|
||||
ArchiveFileRead(testArchive.AsEnumerable());
|
||||
ArchiveFileRead(testArchive.AsEnumerable(), readerOptions);
|
||||
}
|
||||
protected void ArchiveFileRead(IEnumerable<string> testArchives)
|
||||
protected void ArchiveFileRead(IEnumerable<string> testArchives, ReaderOptions readerOptions = null)
|
||||
{
|
||||
foreach (var path in testArchives)
|
||||
{
|
||||
ResetScratch();
|
||||
using (var archive = ArchiveFactory.Open(path))
|
||||
using (var archive = ArchiveFactory.Open(path, readerOptions))
|
||||
{
|
||||
//archive.EntryExtractionBegin += archive_EntryExtractionBegin;
|
||||
//archive.FilePartExtractionBegin += archive_FilePartExtractionBegin;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Readers;
|
||||
using Xunit;
|
||||
|
||||
namespace SharpCompress.Test.SevenZip
|
||||
@@ -18,6 +19,17 @@ namespace SharpCompress.Test.SevenZip
|
||||
ArchiveFileRead("7Zip.LZMA.7z");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SevenZipArchive_LZMAAES_StreamRead()
|
||||
{
|
||||
ArchiveStreamRead("7Zip.LZMA.Aes.7z", new ReaderOptions() {Password = "testpassword"});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SevenZipArchive_LZMAAES_PathRead()
|
||||
{
|
||||
ArchiveFileRead("7Zip.LZMA.Aes.7z", new ReaderOptions() {Password = "testpassword"});
|
||||
}
|
||||
[Fact]
|
||||
public void SevenZipArchive_PPMd_StreamRead()
|
||||
{
|
||||
@@ -35,6 +47,7 @@ namespace SharpCompress.Test.SevenZip
|
||||
{
|
||||
ArchiveFileRead("7Zip.PPMd.7z");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SevenZipArchive_LZMA2_StreamRead()
|
||||
{
|
||||
@@ -46,6 +59,19 @@ namespace SharpCompress.Test.SevenZip
|
||||
{
|
||||
ArchiveFileRead("7Zip.LZMA2.7z");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SevenZipArchive_LZMA2AES_StreamRead()
|
||||
{
|
||||
ArchiveStreamRead("7Zip.LZMA2.Aes.7z", new ReaderOptions {Password = "testpassword"});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SevenZipArchive_LZMA2AES_PathRead()
|
||||
{
|
||||
ArchiveFileRead("7Zip.LZMA2.Aes.7z", new ReaderOptions {Password = "testpassword"});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SevenZipArchive_BZip2_StreamRead()
|
||||
{
|
||||
@@ -67,9 +93,13 @@ namespace SharpCompress.Test.SevenZip
|
||||
[Fact]
|
||||
public void SevenZipArchive_BZip2_Split()
|
||||
{
|
||||
Assert.Throws<IndexOutOfRangeException>(() => ArchiveStreamRead("Original.7z.001", "Original.7z.002",
|
||||
"Original.7z.003", "Original.7z.004", "Original.7z.005",
|
||||
"Original.7z.006", "Original.7z.007"));
|
||||
Assert.Throws<IndexOutOfRangeException>(() => ArchiveStreamRead(null, "Original.7z.001",
|
||||
"Original.7z.002",
|
||||
"Original.7z.003",
|
||||
"Original.7z.004",
|
||||
"Original.7z.005",
|
||||
"Original.7z.006",
|
||||
"Original.7z.007"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
tests/TestArchives/Archives/7Zip.LZMA.Aes.7z
Normal file
BIN
tests/TestArchives/Archives/7Zip.LZMA.Aes.7z
Normal file
Binary file not shown.
BIN
tests/TestArchives/Archives/7Zip.LZMA2.Aes.7z
Normal file
BIN
tests/TestArchives/Archives/7Zip.LZMA2.Aes.7z
Normal file
Binary file not shown.
Reference in New Issue
Block a user