/// <summary>
		/// Closes the current entry, updating header and footer information as required
		/// </summary>
		/// <exception cref="System.IO.IOException">
		/// An I/O error occurs.
		/// </exception>
		/// <exception cref="System.InvalidOperationException">
		/// No entry is active.
		/// </exception>
		public void CloseEntry()
		{
			if (curEntry == null) {
				throw new InvalidOperationException("No open entry");
			}
			
			// First finish the deflater, if appropriate
			if (curMethod == CompressionMethod.Deflated) {
				base.Finish();
			}
			
			long csize = curMethod == CompressionMethod.Deflated ? def.TotalOut : size;
			
			if (curEntry.Size < 0) {
				curEntry.Size = size;
			} else if (curEntry.Size != size) {
				throw new ZipException("size was " + size + ", but I expected " + curEntry.Size);
			}
			
			if (curEntry.CompressedSize < 0) {
				curEntry.CompressedSize = csize;
			} else if (curEntry.CompressedSize != csize) {
				throw new ZipException("compressed size was " + csize + ", but I expected " + curEntry.CompressedSize);
			}
			
			if (curEntry.Crc < 0) {
				curEntry.Crc = crc.Value;
			} else if (curEntry.Crc != crc.Value) {
				throw new ZipException("crc was " + crc.Value +	", but I expected " + curEntry.Crc);
			}
			
			offset += csize;

			if (offset > 0xffffffff) {
				throw new ZipException("Maximum Zip file size exceeded");
			}
				
			if (curEntry.IsCrypted == true) {
				curEntry.CompressedSize += ZipConstants.CRYPTO_HEADER_SIZE;
			}
				
			// Patch the header if possible
			if (patchEntryHeader == true) {
				long curPos = baseOutputStream.Position;
				baseOutputStream.Seek(headerPatchPos, SeekOrigin.Begin);
				WriteLeInt((int)curEntry.Crc);
				WriteLeInt((int)curEntry.CompressedSize);
				WriteLeInt((int)curEntry.Size);
				baseOutputStream.Seek(curPos, SeekOrigin.Begin);
				patchEntryHeader = false;
			}

			// Add data descriptor if flagged as required
			if ((curEntry.Flags & 8) != 0) {
				WriteLeInt(ZipConstants.EXTSIG);
				WriteLeInt((int)curEntry.Crc);
				WriteLeInt((int)curEntry.CompressedSize);
				WriteLeInt((int)curEntry.Size);
				offset += ZipConstants.EXTHDR;
			}
			
			entries.Add(curEntry);
			curEntry = null;
		}
Exemple #2
0
        /// <summary>
        /// Search for and read the central directory of a zip file filling the entries
        /// array.  This is called exactly once by the constructors.
        /// </summary>
        /// <exception cref="System.IO.IOException">
        /// An i/o error occurs.
        /// </exception>
        /// <exception cref="AdHocDesktop.SharpZipLib.Zip.ZipException">
        /// The central directory is malformed or cannot be found
        /// </exception>
        void ReadEntries()
        {
            // Search for the End Of Central Directory.  When a zip comment is
            // present the directory may start earlier.
            //
            // TODO: The search is limited to 64K which is the maximum size of a trailing comment field to aid speed.
            // This should be compatible with both SFX and ZIP files but has only been tested for Zip files
            // Need to confirm this is valid in all cases.
            // Could also speed this up by reading memory in larger blocks?


            if (baseStream.CanSeek == false)
            {
                throw new ZipException("ZipFile stream must be seekable");
            }

            long pos = baseStream.Length - ZipConstants.ENDHDR;

            if (pos <= 0)
            {
                throw new ZipException("File is too small to be a Zip file");
            }

            long giveUpMarker = Math.Max(pos - 0x10000, 0);

            do
            {
                if (pos < giveUpMarker)
                {
                    throw new ZipException("central directory not found, probably not a zip file");
                }
                baseStream.Seek(pos--, SeekOrigin.Begin);
            } while (ReadLeInt() != ZipConstants.ENDSIG);

            int thisDiskNumber            = ReadLeShort();
            int startCentralDirDisk       = ReadLeShort();
            int entriesForThisDisk        = ReadLeShort();
            int entriesForWholeCentralDir = ReadLeShort();
            int centralDirSize            = ReadLeInt();
            int offsetOfCentralDir        = ReadLeInt();
            int commentSize = ReadLeShort();

            byte[] zipComment = new byte[commentSize];
            baseStream.Read(zipComment, 0, zipComment.Length);
            comment = ZipConstants.ConvertToString(zipComment);

/* Its seems possible that this is too strict, more digging required.
 *                      if (thisDiskNumber != 0 || startCentralDirDisk != 0 || entriesForThisDisk != entriesForWholeCentralDir) {
 *                              throw new ZipException("Spanned archives are not currently handled");
 *                      }
 */

            entries = new ZipEntry[entriesForWholeCentralDir];
            baseStream.Seek(offsetOfCentralDir, SeekOrigin.Begin);

            for (int i = 0; i < entriesForWholeCentralDir; i++)
            {
                if (ReadLeInt() != ZipConstants.CENSIG)
                {
                    throw new ZipException("Wrong Central Directory signature");
                }

                int versionMadeBy    = ReadLeShort();
                int versionToExtract = ReadLeShort();
                int bitFlags         = ReadLeShort();
                int method           = ReadLeShort();
                int dostime          = ReadLeInt();
                int crc        = ReadLeInt();
                int csize      = ReadLeInt();
                int size       = ReadLeInt();
                int nameLen    = ReadLeShort();
                int extraLen   = ReadLeShort();
                int commentLen = ReadLeShort();

                int diskStartNo        = ReadLeShort();                  // Not currently used
                int internalAttributes = ReadLeShort();                  // Not currently used

                int externalAttributes = ReadLeInt();
                int offset             = ReadLeInt();

                byte[] buffer = new byte[Math.Max(nameLen, commentLen)];

                baseStream.Read(buffer, 0, nameLen);
                string name = ZipConstants.ConvertToString(buffer, nameLen);

                ZipEntry entry = new ZipEntry(name, versionToExtract, versionMadeBy);
                entry.CompressionMethod = (CompressionMethod)method;
                entry.Crc            = crc & 0xffffffffL;
                entry.Size           = size & 0xffffffffL;
                entry.CompressedSize = csize & 0xffffffffL;
                entry.Flags          = bitFlags;
                entry.DosTime        = (uint)dostime;

                if (extraLen > 0)
                {
                    byte[] extra = new byte[extraLen];
                    baseStream.Read(extra, 0, extraLen);
                    entry.ExtraData = extra;
                }

                if (commentLen > 0)
                {
                    baseStream.Read(buffer, 0, commentLen);
                    entry.Comment = ZipConstants.ConvertToString(buffer, commentLen);
                }

                entry.ZipFileIndex           = i;
                entry.Offset                 = offset;
                entry.ExternalFileAttributes = externalAttributes;

                entries[i] = entry;
            }
        }
		/// <summary>
		/// Starts a new Zip entry. It automatically closes the previous
		/// entry if present.
		/// All entry elements bar name are optional, but must be correct if present.
		/// If the compression method is stored and the output is not patchable
		/// the compression for that entry is automatically changed to deflate level 0
		/// </summary>
		/// <param name="entry">
		/// the entry.
		/// </param>
		/// <exception cref="System.IO.IOException">
		/// if an I/O error occured.
		/// </exception>
		/// <exception cref="System.InvalidOperationException">
		/// if stream was finished
		/// </exception>
		/// <exception cref="ZipException">
		/// Too many entries in the Zip file<br/>
		/// Entry name is too long<br/>
		/// Finish has already been called<br/>
		/// </exception>
		public void PutNextEntry(ZipEntry entry)
		{
			if (entries == null) {
				throw new InvalidOperationException("ZipOutputStream was finished");
			}
			
			if (curEntry != null) {
				CloseEntry();
			}

			if (entries.Count >= 0xffff) {
				throw new ZipException("Too many entries for Zip file");
			}
			
			CompressionMethod method = entry.CompressionMethod;
			int compressionLevel = defaultCompressionLevel;
			
			entry.Flags = 0;
			patchEntryHeader = false;
			bool headerInfoAvailable = true;
			
			if (method == CompressionMethod.Stored) {
				if (entry.CompressedSize >= 0) {
					if (entry.Size < 0) {
						entry.Size = entry.CompressedSize;
					} else if (entry.Size != entry.CompressedSize) {
						throw new ZipException("Method STORED, but compressed size != size");
					}
				} else {
					if (entry.Size >= 0) {
						entry.CompressedSize = entry.Size;
					}
				}
					
				if (entry.Size < 0 || entry.Crc < 0) {
					if (CanPatchEntries == true) {
						headerInfoAvailable = false;
					}
					else {
						method = CompressionMethod.Deflated;
						compressionLevel = 0;
					}
				}
			}
				
			if (method == CompressionMethod.Deflated) {
				if (entry.Size == 0) {
					entry.CompressedSize = entry.Size;
					entry.Crc = 0;
					method = CompressionMethod.Stored;
				} else if (entry.CompressedSize < 0 || entry.Size < 0 || entry.Crc < 0) {
					headerInfoAvailable = false;
				}
			}
			
			if (headerInfoAvailable == false) {
				if (CanPatchEntries == false) {
					entry.Flags |= 8;
				} else {
					patchEntryHeader = true;
				}
			}
			
			if (Password != null) {
				entry.IsCrypted = true;
				if (entry.Crc < 0) {
					entry.Flags |= 8;
				}
			}
			entry.Offset = (int)offset;
			entry.CompressionMethod = (CompressionMethod)method;
			
			curMethod    = method;
			
			// Write the local file header
			WriteLeInt(ZipConstants.LOCSIG);
			
			WriteLeShort(entry.Version);
			WriteLeShort(entry.Flags);
			WriteLeShort((byte)method);
			WriteLeInt((int)entry.DosTime);
			if (headerInfoAvailable == true) {
				WriteLeInt((int)entry.Crc);
				WriteLeInt(entry.IsCrypted ? (int)entry.CompressedSize + ZipConstants.CRYPTO_HEADER_SIZE : (int)entry.CompressedSize);
				WriteLeInt((int)entry.Size);
			} else {
				if (patchEntryHeader == true) {
					headerPatchPos = baseOutputStream.Position;
				}
				WriteLeInt(0);	// Crc
				WriteLeInt(0);	// Compressed size
				WriteLeInt(0);	// Uncompressed size
			}
			
			byte[] name = ZipConstants.ConvertToArray(entry.Name);
			
			if (name.Length > 0xFFFF) {
				throw new ZipException("Entry name too long.");
			}

			byte[] extra = entry.ExtraData;
			if (extra == null) {
				extra = new byte[0];
			}

			if (extra.Length > 0xFFFF) {
				throw new ZipException("Extra data too long.");
			}
			
			WriteLeShort(name.Length);
			WriteLeShort(extra.Length);
			baseOutputStream.Write(name, 0, name.Length);
			baseOutputStream.Write(extra, 0, extra.Length);
			
			offset += ZipConstants.LOCHDR + name.Length + extra.Length;
			
			// Activate the entry.
			curEntry = entry;
			crc.Reset();
			if (method == CompressionMethod.Deflated) {
				def.Reset();
				def.SetLevel(compressionLevel);
			}
			size = 0;
			
			if (entry.IsCrypted == true) {
				if (entry.Crc < 0) {			// so testing Zip will says its ok
					WriteEncryptionHeader(entry.DosTime << 16);
				} else {
					WriteEncryptionHeader(entry.Crc);
				}
			}
		}