SevenZipArchive.cs 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using SharpCompress.Common;
  6. using SharpCompress.Common.SevenZip;
  7. using SharpCompress.Compressors.LZMA.Utilites;
  8. using SharpCompress.IO;
  9. using SharpCompress.Readers;
  10. namespace SharpCompress.Archives.SevenZip
  11. {
  12. public class SevenZipArchive : AbstractArchive<SevenZipArchiveEntry, SevenZipVolume>
  13. {
  14. private ArchiveDatabase database;
  15. #if !NO_FILE
  16. /// <summary>
  17. /// Constructor expects a filepath to an existing file.
  18. /// </summary>
  19. /// <param name="filePath"></param>
  20. /// <param name="readerOptions"></param>
  21. public static SevenZipArchive Open(string filePath, ReaderOptions readerOptions = null)
  22. {
  23. filePath.CheckNotNullOrEmpty("filePath");
  24. return Open(new FileInfo(filePath), readerOptions ?? new ReaderOptions());
  25. }
  26. /// <summary>
  27. /// Constructor with a FileInfo object to an existing file.
  28. /// </summary>
  29. /// <param name="fileInfo"></param>
  30. /// <param name="readerOptions"></param>
  31. public static SevenZipArchive Open(FileInfo fileInfo, ReaderOptions readerOptions = null)
  32. {
  33. fileInfo.CheckNotNull("fileInfo");
  34. return new SevenZipArchive(fileInfo, readerOptions ?? new ReaderOptions());
  35. }
  36. #endif
  37. /// <summary>
  38. /// Takes a seekable Stream as a source
  39. /// </summary>
  40. /// <param name="stream"></param>
  41. /// <param name="readerOptions"></param>
  42. public static SevenZipArchive Open(Stream stream, ReaderOptions readerOptions = null)
  43. {
  44. stream.CheckNotNull("stream");
  45. return new SevenZipArchive(stream, readerOptions ?? new ReaderOptions());
  46. }
  47. #if !NO_FILE
  48. internal SevenZipArchive(FileInfo fileInfo, ReaderOptions readerOptions)
  49. : base(ArchiveType.SevenZip, fileInfo, readerOptions)
  50. {
  51. }
  52. protected override IEnumerable<SevenZipVolume> LoadVolumes(FileInfo file)
  53. {
  54. return new SevenZipVolume(file.OpenRead(), ReaderOptions).AsEnumerable();
  55. }
  56. public static bool IsSevenZipFile(string filePath)
  57. {
  58. return IsSevenZipFile(new FileInfo(filePath));
  59. }
  60. public static bool IsSevenZipFile(FileInfo fileInfo)
  61. {
  62. if (!fileInfo.Exists)
  63. {
  64. return false;
  65. }
  66. using (Stream stream = fileInfo.OpenRead())
  67. {
  68. return IsSevenZipFile(stream);
  69. }
  70. }
  71. #endif
  72. internal SevenZipArchive(Stream stream, ReaderOptions readerOptions)
  73. : base(ArchiveType.SevenZip, stream.AsEnumerable(), readerOptions)
  74. {
  75. }
  76. internal SevenZipArchive()
  77. : base(ArchiveType.SevenZip)
  78. {
  79. }
  80. protected override IEnumerable<SevenZipVolume> LoadVolumes(IEnumerable<Stream> streams)
  81. {
  82. foreach (Stream s in streams)
  83. {
  84. if (!s.CanRead || !s.CanSeek)
  85. {
  86. throw new ArgumentException("Stream is not readable and seekable");
  87. }
  88. SevenZipVolume volume = new SevenZipVolume(s, ReaderOptions);
  89. yield return volume;
  90. }
  91. }
  92. protected override IEnumerable<SevenZipArchiveEntry> LoadEntries(IEnumerable<SevenZipVolume> volumes)
  93. {
  94. var stream = volumes.Single().Stream;
  95. LoadFactory(stream);
  96. for (int i = 0; i < database._files.Count; i++)
  97. {
  98. var file = database._files[i];
  99. yield return new SevenZipArchiveEntry(this, new SevenZipFilePart(stream, database, i, file, ReaderOptions.ArchiveEncoding));
  100. }
  101. }
  102. private void LoadFactory(Stream stream)
  103. {
  104. if (database == null)
  105. {
  106. stream.Position = 0;
  107. var reader = new ArchiveReader();
  108. reader.Open(stream);
  109. database = reader.ReadDatabase(new PasswordProvider(ReaderOptions.Password));
  110. }
  111. }
  112. public static bool IsSevenZipFile(Stream stream)
  113. {
  114. try
  115. {
  116. return SignatureMatch(stream);
  117. }
  118. catch
  119. {
  120. return false;
  121. }
  122. }
  123. private static readonly byte[] SIGNATURE = {(byte)'7', (byte)'z', 0xBC, 0xAF, 0x27, 0x1C};
  124. private static bool SignatureMatch(Stream stream)
  125. {
  126. BinaryReader reader = new BinaryReader(stream);
  127. byte[] signatureBytes = reader.ReadBytes(6);
  128. return signatureBytes.BinaryEquals(SIGNATURE);
  129. }
  130. protected override IReader CreateReaderForSolidExtraction()
  131. {
  132. return new SevenZipReader(ReaderOptions, this);
  133. }
  134. public override bool IsSolid { get { return Entries.Where(x => !x.IsDirectory).GroupBy(x => x.FilePart.Folder).Count() > 1; } }
  135. public override long TotalSize
  136. {
  137. get
  138. {
  139. int i = Entries.Count;
  140. return database._packSizes.Aggregate(0L, (total, packSize) => total + packSize);
  141. }
  142. }
  143. private class SevenZipReader : AbstractReader<SevenZipEntry, SevenZipVolume>
  144. {
  145. private readonly SevenZipArchive archive;
  146. private CFolder currentFolder;
  147. private Stream currentStream;
  148. private CFileItem currentItem;
  149. internal SevenZipReader(ReaderOptions readerOptions, SevenZipArchive archive)
  150. : base(readerOptions, ArchiveType.SevenZip)
  151. {
  152. this.archive = archive;
  153. }
  154. public override SevenZipVolume Volume => archive.Volumes.Single();
  155. protected override IEnumerable<SevenZipEntry> GetEntries(Stream stream)
  156. {
  157. List<SevenZipArchiveEntry> entries = archive.Entries.ToList();
  158. stream.Position = 0;
  159. foreach (var dir in entries.Where(x => x.IsDirectory))
  160. {
  161. yield return dir;
  162. }
  163. foreach (var group in entries.Where(x => !x.IsDirectory).GroupBy(x => x.FilePart.Folder))
  164. {
  165. currentFolder = group.Key;
  166. if (group.Key == null)
  167. {
  168. currentStream = Stream.Null;
  169. }
  170. else
  171. {
  172. currentStream = archive.database.GetFolderStream(stream, currentFolder, new PasswordProvider(Options.Password));
  173. }
  174. foreach (var entry in group)
  175. {
  176. currentItem = entry.FilePart.Header;
  177. yield return entry;
  178. }
  179. }
  180. }
  181. protected override EntryStream GetEntryStream()
  182. {
  183. return CreateEntryStream(new ReadOnlySubStream(currentStream, currentItem.Size));
  184. }
  185. }
  186. private class PasswordProvider : IPasswordProvider
  187. {
  188. private readonly string _password;
  189. public PasswordProvider(string password)
  190. {
  191. _password = password;
  192. }
  193. public string CryptoGetTextPassword()
  194. {
  195. return _password;
  196. }
  197. }
  198. }
  199. }