/// <summary> /// Open a new file as an archive /// </summary> /// <param name="filename">Name of the new file to open</param> /// <param name="timestamp">Timestamp the file should have</param> /// <param name="readHeaders">True if file headers should be read, false otherwise</param> /// <returns>Status of the underlying stream</returns> public ZipReturn Open(string filename, long timestamp, bool readHeaders) { // If a stream already exists, close it Close(); // Now, reset the archive information _zipStatus = ZipStatus.None; _zip64 = false; _centerDirStart = 0; _centerDirSize = 0; _zipFileInfo = null; // Then, attempt to open the file and get information from it try { // If the input file doesn't exist, close the stream and return if (!File.Exists(filename)) { Close(); return(ZipReturn.ZipErrorFileNotFound); } // Get the fileinfo object _zipFileInfo = new FileInfo(filename); // If the timestamps don't match, close the stream and return if (_zipFileInfo.LastWriteTime.Ticks != timestamp) { Close(); return(ZipReturn.ZipErrorTimeStamp); } // Now try to open the file for reading _zipstream = Utilities.TryOpenRead(filename); int read = _zipstream.Read(new byte[1], 0, 1); if (read != 1) { Close(); return(ZipReturn.ZipErrorOpeningFile); } _zipstream.Position = 0; } catch (PathTooLongException) { Close(); return(ZipReturn.ZipFileNameToLong); } catch (IOException) { Close(); return(ZipReturn.ZipErrorOpeningFile); } // If we succeeded, set the flag for read _zipOpen = ZipOpenType.OpenRead; // If we're not reading the headers, return if (!readHeaders) { return(ZipReturn.ZipGood); } //Otherwise, we want to get all of the archive information try { // First, try to get the end of the central directory ZipReturn zr = FindEndOfCentralDirSignature(); if (zr != ZipReturn.ZipGood) { Close(); return(zr); } // Now read the end of the central directory long eocd = _zipstream.Position; zr = ReadEndOfCentralDir(); if (zr != ZipReturn.ZipGood) { Close(); return(zr); } // If we have any indicators of Zip64, check for the Zip64 EOCD if (_centerDirStart == 0xffffffff || _centerDirSize == 0xffffffff || _entriesCount == 0xffff) { _zip64 = true; // Check for the Zip64 EOCD locator _zipstream.Position = eocd - 20; zr = ReadZip64EndOfCentralDirectoryLocator(); if (zr != ZipReturn.ZipGood) { Close(); return(zr); } // If it was found, read the Zip64 EOCD _zipstream.Position = (long)_endOfCenterDir64; zr = ReadZip64EndOfCentralDir(); if (zr != ZipReturn.ZipGood) { Close(); return(zr); } } // Now that we have the rest of the information, check for TorrentZip bool torrentZip = false; if (_fileComment.Length == 22) { if (Encoding.ASCII.GetString(_fileComment).Substring(0, 14) == "TORRENTZIPPED-") { // First get to the right part of the stream OptimizedCRC ocrc = new OptimizedCRC(); byte[] buffer = new byte[_centerDirSize]; _zipstream.Position = (long)_centerDirStart; // Then read in the central directory and hash BinaryReader br = new BinaryReader(_zipstream); buffer = br.ReadBytes((int)_centerDirSize); ocrc.Update(buffer, 0, (int)_centerDirSize); string calculatedCrc = ocrc.Value.ToString("X8"); // If the hashes match, then we have a torrentzip file string extractedCrc = Encoding.ASCII.GetString(_fileComment).Substring(14, 8); if (String.Equals(calculatedCrc, extractedCrc, StringComparison.Ordinal)) { torrentZip = true; } } } // With potential torrentzip out of the way, read the central directory _zipstream.Position = (long)_centerDirStart; // Remove any entries already listed in the archive _entries.Clear(); _entries.Capacity = (int)_entriesCount; // Now populate the entries from the central directory for (int i = 0; i < _entriesCount; i++) { ZipFileEntry zfe = new ZipFileEntry(_zipstream); zr = zfe.ReadCentralDirectory(); // If we get any errors, close and return if (zr != ZipReturn.ZipGood) { Close(); return(zr); } // If we have a Zip64 entry, make sure the archive is _zip64 |= zfe.Zip64; // Now add the entry to the archive _entries.Add(zfe); } // Now that the entries are populated, verify against the actual headers for (int i = 0; i < _entriesCount; i++) { zr = _entries[i].ReadHeader(); // If we get any errors, close and return if (zr != ZipReturn.ZipGood) { Close(); return(zr); } // If we have a torrentzipped entry, make sure the archive is torrentZip &= _entries[i].TorrentZip; } // If we have a torrentzipped file, check the file order if (torrentZip) { for (int i = 0; i < _entriesCount - 1; i++) { if (TorrentZipStringCompare(_entries[i].FileName, _entries[i + 1].FileName) < 0) { continue; } torrentZip = false; break; } } // Now check for torrentzipped directories if we still have a torrentZip file if (torrentZip) { for (int i = 0; i < _entriesCount - 1; i++) { // See if we found a directory string filename0 = _entries[i].FileName; if (filename0.Substring(filename0.Length - 1, 1) != "/") { continue; } // See if the next file is in that directory string filename1 = _entries[i + 1].FileName; if (filename1.Length <= filename0.Length) { continue; } if (TorrentZipStringCompare(filename0, filename1.Substring(0, filename0.Length)) == 0) { continue; } // If we found a file in the directory, then we don't need the directory entry torrentZip = false; break; } } // If we still have torrentzip, say the archive is too if (torrentZip) { _zipStatus |= ZipStatus.TorrentZip; } return(ZipReturn.ZipGood); } catch { Close(); return(ZipReturn.ZipErrorReadingFile); } }
/// <summary> /// Close the file that the stream refers to /// </summary> public void Close() { // If the stream is already closed, then just return if (_zipOpen == ZipOpenType.Closed) { return; } // If the stream is opened for read, close it if (_zipOpen == ZipOpenType.OpenRead) { Dispose(); _zipOpen = ZipOpenType.Closed; return; } // Now, the only other choice is open for writing so we check everything is correct _zip64 = false; bool torrentZip = true; // Check the central directory _centerDirStart = (ulong)_zipstream.Position; if (_centerDirStart >= 0xffffffff) { _zip64 = true; } // Now loop through and add all of the central directory entries foreach (ZipFileEntry zfe in _entries) { zfe.WriteCentralDirectory(_zipstream); _zip64 |= zfe.Zip64; torrentZip &= zfe.TorrentZip; } _centerDirSize = (ulong)_zipstream.Position - _centerDirStart; // Then get the central directory hash OptimizedCRC ocrc = new OptimizedCRC(); byte[] buffer = new byte[_centerDirSize]; long currentPosition = _zipstream.Position; _zipstream.Position = (long)_centerDirStart; // Then read in the central directory and hash BinaryReader br = new BinaryReader(_zipstream); buffer = br.ReadBytes((int)_centerDirSize); ocrc.Update(buffer, 0, (int)_centerDirSize); string calculatedCrc = ocrc.Value.ToString("X8"); // Finally get back to the original position _zipstream.Position = currentPosition; // Now set more of the information _fileComment = (torrentZip ? Encoding.ASCII.GetBytes(("TORRENTZIPPED-" + calculatedCrc).ToCharArray()) : new byte[0]); _zipStatus = (torrentZip ? ZipStatus.TorrentZip : ZipStatus.None); // If we have a Zip64 archive, write the correct information if (_zip64) { _endOfCenterDir64 = (ulong)_zipstream.Position; WriteZip64EndOfCentralDir(); WriteZip64EndOfCentralDirectoryLocator(); } // Now write out the end of the central directory WriteEndOfCentralDir(); // Finally, close and dispose of the stream _zipstream.SetLength(_zipstream.Position); _zipstream.Flush(); _zipstream.Close(); _zipstream.Dispose(); // Get the new file information _zipFileInfo = new FileInfo(_zipFileInfo.FullName); // And set the stream to closed _zipOpen = ZipOpenType.Closed; }
/// <summary> /// Get the data from the current file, if not already checked /// </summary> public void Check() { // If the file has been tested or has an error, return if (_fileStatus != ZipReturn.ZipUntested) { return; } try { Stream stream = null; _zipstream.Seek((long)_dataLocation, SeekOrigin.Begin); switch (_compressionMethod) { case CompressionMethod.Deflated: stream = new DeflateStream(_zipstream, CompressionMode.Decompress, true); break; case CompressionMethod.Stored: stream = _zipstream; break; } if (stream == null) { _fileStatus = ZipReturn.ZipErrorGettingDataStream; return; } // Create the hashers uint tempCrc; OptimizedCRC crc = new OptimizedCRC(); MD5 md5 = System.Security.Cryptography.MD5.Create(); SHA1 sha1 = System.Security.Cryptography.SHA1.Create(); // Now get the hash of the stream BinaryReader fs = new BinaryReader(stream); byte[] buffer = new byte[1024]; int read; while ((read = fs.Read(buffer, 0, buffer.Length)) > 0) { crc.Update(buffer, 0, read); md5.TransformBlock(buffer, 0, read, buffer, 0); sha1.TransformBlock(buffer, 0, read, buffer, 0); } crc.Update(buffer, 0, 0); md5.TransformFinalBlock(buffer, 0, 0); sha1.TransformFinalBlock(buffer, 0, 0); tempCrc = crc.UnsignedValue; _md5 = md5.Hash; _sha1 = sha1.Hash; // Dispose of the hashers crc.Dispose(); md5.Dispose(); sha1.Dispose(); if (_compressionMethod == CompressionMethod.Deflated) { stream.Close(); stream.Dispose(); } _fileStatus = (_crc == tempCrc ? ZipReturn.ZipGood : ZipReturn.ZipCRCDecodeError); } catch { _fileStatus = ZipReturn.ZipDecodeError; } }