context: using System;
  using System.IO;
  using System.Reflection;
  using System.Runtime.Serialization.Formatters.Binary;
  using System.Security;
  using System.Text;
  using System.Threading;
  using ICSharpCode.SharpZipLib.Zip;
  using ICSharpCode.SharpZipLib.Checksums;
  using ICSharpCode.SharpZipLib.Core;
  using ICSharpCode.SharpZipLib.Tests.TestSupport;
  
  using NUnit.Framework;
  
  namespace ICSharpCode.SharpZipLib.Tests.Zip
  {
  	#region Local Support Classes
  	class RuntimeInfo
  	{
  		public RuntimeInfo(CompressionMethod method, int compressionLevel, 
              int size, string password, bool getCrc)
  		{
  			this.method = method;
  			this.compressionLevel = compressionLevel;
  			this.password = password;
  			this.size = size;
  			this.random = false;
  
  			original = new byte[Size];
  			if (random) {
  				System.Random rnd = new Random();
  				rnd.NextBytes(original);
  			}
  			else {
  				for (int i = 0; i < size; ++i) {
  					original[i] = (byte)'A';
  				}
  			}
  
  			if (getCrc) {
  				Crc32 crc32 = new Crc32();
  				crc32.Update(original, 0, size);
  				crc = crc32.Value;
  			}
  		}
  
  
  		public RuntimeInfo(string password, bool isDirectory)
  		{
  			this.method = CompressionMethod.Stored;
  			this.compressionLevel = 1;
  			this.password = password;
  			this.size = 0;
  			this.random = false;
  			isDirectory_ = isDirectory;
  			original = new byte[0];
  		}
  
  		public byte[] Original
  		{
  			get { return original; }
  		}
  
  		public CompressionMethod Method
  		{
  			get { return method; }
  		}
  
  		public int CompressionLevel
  		{
  			get { return compressionLevel; }
  		}
  
  		public int Size
  		{
  			get { return size; }
  		}
  
  		public string Password
  		{
  			get { return password; }
  		}
  
  		bool Random
  		{
  			get { return random; }
  		}
  
  		public long Crc
  		{
  			get { return crc; }
  		}
  
  		public bool IsDirectory
  		{
  			get { return isDirectory_; }
  		}
  
  		#region Instance Fields
  	    readonly byte[] original;
  	    readonly CompressionMethod method;
  		int compressionLevel;
  		int size;
  		string password;
  		bool random;
  		bool isDirectory_;
  		long crc = -1;
  		#endregion
  	}
  
  	class MemoryDataSource : IStaticDataSource
  	{
  		#region Constructors
  		/// <summary>
  		/// Initialise a new instance.
  		/// </summary>
  		/// <param name="data">The data to provide.</param>
  		public MemoryDataSource(byte[] data)
  		{
  			data_ = data;
  		}
  		#endregion
  
  		#region IDataSource Members
  
  		/// <summary>
  		/// Get a Stream for this <see cref="IStaticDataSource"/>
  		/// </summary>
  		/// <returns>Returns a <see cref="Stream"/></returns>
  		public Stream GetSource()
  		{
  			return new MemoryStream(data_);
  		}
  		#endregion
  
  		#region Instance Fields
  	    readonly byte[] data_;
  		#endregion
  	}
  
  	class StringMemoryDataSource : MemoryDataSource
  	{
  		public StringMemoryDataSource(string data)
  			: base(Encoding.ASCII.GetBytes(data))
  		{
  		}
  	}
  	#endregion
  
  	#region ZipBase
  	public class ZipBase
  	{
  		static protected string GetTempFilePath()
  		{
  			string result = null;
  			try {
  				result = Path.GetTempPath();
  			}
  			catch (SecurityException) {
  			}
  			return result;
  		}
  
  		protected byte[] MakeInMemoryZip(bool withSeek, params object[] createSpecs)
  		{
  			MemoryStream ms;
  
  			if (withSeek) {
  				ms = new MemoryStream();
  			}
  			else {
  				ms = new MemoryStreamWithoutSeek();
  			}
  
  			using (ZipOutputStream outStream = new ZipOutputStream(ms)) {
  				for (int counter = 0; counter < createSpecs.Length; ++counter) {
  					RuntimeInfo info = createSpecs[counter] as RuntimeInfo;
  					outStream.Password = info.Password;
  
  					if (info.Method != CompressionMethod.Stored) {
  						outStream.SetLevel(info.CompressionLevel); // 0 - store only to 9 - means best compression
  					}
  
  					string entryName;
  
  					if (info.IsDirectory) {
  						entryName = "dir" + counter + "/";
  					}
  					else {
  						entryName = "entry" + counter + ".tst";
  					}
  
  					ZipEntry entry = new ZipEntry(entryName);
  					entry.CompressionMethod = info.Method;
  					if (info.Crc >= 0) {
  						entry.Crc = info.Crc;
  					}
  
  					outStream.PutNextEntry(entry);
  
  					if (info.Size > 0) {
  						outStream.Write(info.Original, 0, info.Original.Length);
  					}
  				}
  			}
  			return ms.ToArray();
  		}
  
  		protected byte[] MakeInMemoryZip(ref byte[] original, CompressionMethod method,
  			int compressionLevel, int size, string password, bool withSeek)
  		{
  			MemoryStream ms;
  
  			if (withSeek) {
  				ms = new MemoryStream();
  			}
  			else {
  				ms = new MemoryStreamWithoutSeek();
  			}
  
  			using (ZipOutputStream outStream = new ZipOutputStream(ms)) {
  				outStream.Password = password;
  
  				if (method != CompressionMethod.Stored) {
  					outStream.SetLevel(compressionLevel); // 0 - store only to 9 - means best compression
  				}
  
  				ZipEntry entry = new ZipEntry("dummyfile.tst");
  				entry.CompressionMethod = method;
  
  				outStream.PutNextEntry(entry);
  
  				if (size > 0) {
  					System.Random rnd = new Random();
  					original = new byte[size];
  					rnd.NextBytes(original);
  
  					// Although this could be written in one chunk doing it in lumps
  					// throws up buffering problems including with encryption the original
  					// source for this change.
  					int index = 0;
  					while (size > 0) {
  						int count = (size > 0x200) ? 0x200 : size;
  						outStream.Write(original, index, count);
  						size -= 0x200;
  						index += count;
  					}
  				}
  			}
  			return ms.ToArray();
  		}
  
  		protected static void MakeTempFile(string name, int size)
  		{
  			using (FileStream fs = File.Create(name)) {
  				byte[] buffer = new byte[4096];
  				while (size > 0) {
  					fs.Write(buffer, 0, Math.Min(size, buffer.Length));
  					size -= buffer.Length;
  				}
  			}
  		}
  
  		protected static byte ScatterValue(byte rhs)
  		{
  			return (byte)((rhs * 253 + 7) & 0xff);
  		}
  
  		static void AddKnownDataToEntry(ZipOutputStream zipStream, int size)
  		{
  			if (size > 0) {
  				byte nextValue = 0;
  				int bufferSize = Math.Min(size, 65536);
  				byte[] data = new byte[bufferSize];
  				int currentIndex = 0;
  				for (int i = 0; i < size; ++i) {
  					data[currentIndex] = nextValue;
  					nextValue = ScatterValue(nextValue);
  
  					currentIndex += 1;
  					if ((currentIndex >= data.Length) || (i + 1 == size)) {
  						zipStream.Write(data, 0, currentIndex);
  						currentIndex = 0;
  					}
  				}
  			}
  		}
  
  		public void WriteToFile(string fileName, byte[] data)
  		{
  			using (FileStream fs = File.Open(fileName, FileMode.Create, FileAccess.ReadWrite, FileShare.Read)) {
  				fs.Write(data, 0, data.Length);
  			}
  		}
  
  		#region MakeZipFile
  		protected void MakeZipFile(Stream storage, bool isOwner, string[] names, int size, string comment)
  		{
  			using (ZipOutputStream zOut = new ZipOutputStream(storage)) {
  				zOut.IsStreamOwner = isOwner;
  				zOut.SetComment(comment);
  				for (int i = 0; i < names.Length; ++i) {
  					zOut.PutNextEntry(new ZipEntry(names[i]));
  					AddKnownDataToEntry(zOut, size);
  				}
  				zOut.Close();
  			}
  		}
  
  		protected void MakeZipFile(string name, string[] names, int size, string comment)
  		{
  			using (FileStream fs = File.Create(name)) {
  				using (ZipOutputStream zOut = new ZipOutputStream(fs)) {
  					zOut.SetComment(comment);
  					for (int i = 0; i < names.Length; ++i) {
  						zOut.PutNextEntry(new ZipEntry(names[i]));
  						AddKnownDataToEntry(zOut, size);
  					}
  					zOut.Close();
  				}
  				fs.Close();
  			}
  		}
  		#endregion
  		
  		#region MakeZipFile Entries
  		protected void MakeZipFile(string name, string entryNamePrefix, int entries, int size, string comment)
  		{
  			using (FileStream fs = File.Create(name))
  			using (ZipOutputStream zOut = new ZipOutputStream(fs)) {
  				zOut.SetComment(comment);
  				for (int i = 0; i < entries; ++i) {
  					zOut.PutNextEntry(new ZipEntry(entryNamePrefix + (i + 1).ToString()));
  					AddKnownDataToEntry(zOut, size);
  				}
  			}
  		}
  
  		protected void MakeZipFile(Stream storage, bool isOwner,
  			string entryNamePrefix, int entries, int size, string comment)
  		{
  			using (ZipOutputStream zOut = new ZipOutputStream(storage)) {
  				zOut.IsStreamOwner = isOwner;
  				zOut.SetComment(comment);
  				for (int i = 0; i < entries; ++i) {
  					zOut.PutNextEntry(new ZipEntry(entryNamePrefix + (i + 1).ToString()));
  					AddKnownDataToEntry(zOut, size);
  				}
  			}
  		}
  
  		#endregion
  
  		protected static void CheckKnownEntry(Stream inStream, int expectedCount)
  		{
  			byte[] buffer = new byte[1024];
  
  			int bytesRead;
  			int total = 0;
  			byte nextValue = 0;
  			while ((bytesRead = inStream.Read(buffer, 0, buffer.Length)) > 0) {
  				total += bytesRead;
  				for (int i = 0; i < bytesRead; ++i) {
  					Assert.AreEqual(nextValue, buffer[i], "Wrong value read from entry");
  					nextValue = ScatterValue(nextValue);
  				}
  			}
  			Assert.AreEqual(expectedCount, total, "Wrong number of bytes read from entry");
  		}
  
  		protected byte ReadByteChecked(Stream stream)
  		{
  			int rawValue = stream.ReadByte();
  			Assert.IsTrue(rawValue >= 0);
  			return (byte)rawValue;
  		}
  
  		protected int ReadInt(Stream stream)
  		{
  			return ReadByteChecked(stream) |
  				(ReadByteChecked(stream) << 8) |
  				(ReadByteChecked(stream) << 16) |
  				(ReadByteChecked(stream) << 24);
  		}
  
  		protected long ReadLong(Stream stream)
  		{
  			long result = ReadInt(stream) & 0xffffffff;
  			return result | (((long)ReadInt(stream)) << 32);
  		}
  
  	}
  
  	#endregion
  
  	class TestHelper
  	{
  		static public void SaveMemoryStream(MemoryStream ms, string fileName)
  		{
  			byte[] data = ms.ToArray();
  			using (FileStream fs = File.Open(fileName, FileMode.Create, FileAccess.ReadWrite, FileShare.Read)) {
  				fs.Write(data, 0, data.Length);
  			}
  		}
  
  		static public int CompareDosDateTimes(DateTime l, DateTime r)
  		{
  			// Compare dates to dos accuracy...
  			// Ticks can be different yet all these values are still the same!
  			int result = l.Year - r.Year;
  			if (result == 0) {
  				result = l.Month - r.Month;
  				if (result == 0) {
  					result = l.Day - r.Day;
  					if (result == 0) {
  						result = l.Hour - r.Hour;
  						if (result == 0) {
  							result = l.Minute - r.Minute;
  							if (result == 0) {
  								result = (l.Second / 2) - (r.Second / 2);
  							}
  						}
  					}
  				}
  			}
  			return result;
  		}
  	}
  
  	[TestFixture]
  	public class ZipEntryHandling : ZipBase
  	{
  		byte[] MakeLocalHeader(string asciiName, short versionToExtract, short flags, short method,
  							  int dostime, int crc, int compressedSize, int size)
  		{
  			using ( TrackedMemoryStream ms = new TrackedMemoryStream())
  			{
  				ms.WriteByte((byte)'P');
  				ms.WriteByte((byte)'K');
  				ms.WriteByte(3);
  				ms.WriteByte(4);
  			
  				ms.WriteLEShort(versionToExtract);
  				ms.WriteLEShort(flags);
  				ms.WriteLEShort(method);
  				ms.WriteLEInt(dostime);
  				ms.WriteLEInt(crc);
  				ms.WriteLEInt(compressedSize);
  				ms.WriteLEInt(size);
  				
  				byte[] rawName = Encoding.ASCII.GetBytes(asciiName);
  				ms.WriteLEShort((short)rawName.Length);
  				ms.WriteLEShort(0);
  				ms.Write(rawName, 0, rawName.Length);
  				return ms.ToArray();
  			}
  		}
  		
  		ZipEntry MakeEntry(string asciiName, short versionToExtract, short flags, short method,
  							  int dostime, int crc, int compressedSize, int size)
  		{
  			byte[] data = MakeLocalHeader(asciiName,  versionToExtract, flags, method,
  										  dostime, crc, compressedSize, size);
  			
  			ZipInputStream zis = new ZipInputStream(new MemoryStream(data));
  			
  			ZipEntry ze = zis.GetNextEntry();
  			return ze;
  		}
  		
  		void PiecewiseCompare(ZipEntry lhs, ZipEntry rhs)
  		{
  			Type entryType = typeof(ZipEntry);
  			BindingFlags binding = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
  
  			FieldInfo[] fields = entryType.GetFields(binding);
  
  			Assert.Greater(fields.Length, 8, "Failed to find fields");
  
  			foreach (FieldInfo info in fields) {
  				object lValue = info.GetValue(lhs);
  				object rValue = info.GetValue(rhs);
  
  				Assert.AreEqual(lValue, rValue);
  			}
  		}
  
  		/// <summary>
  		/// Test that obsolete copy constructor works correctly.
  		/// </summary>
  		[Test]
  		[Category("Zip")]
  		public void Copying()
  		{
  			long testCrc = 3456;
  			long testSize = 99874276;
  			long testCompressedSize = 72347;
  			byte[] testExtraData = new byte[] { 0x00, 0x01, 0x00, 0x02, 0x0EF, 0xFE };
  			string testName = "Namu";
  			int testFlags = 4567;
  			long testDosTime = 23434536;
  			CompressionMethod testMethod = CompressionMethod.Deflated;
  
  			string testComment = "A comment";
  
  			ZipEntry source = new ZipEntry(testName);
  			source.Crc = testCrc;
  			source.Comment = testComment;
  			source.Size = testSize;
  			source.CompressedSize = testCompressedSize;
  			source.ExtraData = testExtraData;
  			source.Flags = testFlags;
  			source.DosTime = testDosTime;
  			source.CompressionMethod = testMethod;
  
  #pragma warning disable 0618
  			ZipEntry clone = new ZipEntry(source);
  #pragma warning restore
  
  			PiecewiseCompare(source, clone);
  		}
  
  		/// <summary>
  		/// Check that cloned entries are correct.
  		/// </summary>
  		[Test]
  		[Category("Zip")]
  		public void Cloning()
  		{
  			long testCrc = 3456;
  			long testSize = 99874276;
  			long testCompressedSize = 72347;
  			byte[] testExtraData = new byte[] { 0x00, 0x01, 0x00, 0x02, 0x0EF, 0xFE };
  			string testName = "Namu";
  			int testFlags = 4567;
  			long testDosTime = 23434536;
  			CompressionMethod testMethod = CompressionMethod.Deflated;
  
  			string testComment = "A comment";
  
  			ZipEntry source = new ZipEntry(testName);
  			source.Crc = testCrc;
  			source.Comment = testComment;
  			source.Size = testSize;
  			source.CompressedSize = testCompressedSize;
  			source.ExtraData = testExtraData;
  			source.Flags = testFlags;
  			source.DosTime = testDosTime;
  			source.CompressionMethod = testMethod;
  
  			ZipEntry clone = (ZipEntry)source.Clone();
  
  			// Check values against originals
  			Assert.AreEqual(testName, clone.Name, "Cloned name mismatch");
  			Assert.AreEqual(testCrc, clone.Crc, "Cloned crc mismatch");
  			Assert.AreEqual(testComment, clone.Comment, "Cloned comment mismatch");
  			Assert.AreEqual(testExtraData, clone.ExtraData, "Cloned Extra data mismatch");
  			Assert.AreEqual(testSize, clone.Size, "Cloned size mismatch");
  			Assert.AreEqual(testCompressedSize, clone.CompressedSize, "Cloned compressed size mismatch");
  			Assert.AreEqual(testFlags, clone.Flags, "Cloned flags mismatch");
  			Assert.AreEqual(testDosTime, clone.DosTime, "Cloned DOSTime mismatch");
  			Assert.AreEqual(testMethod, clone.CompressionMethod, "Cloned Compression method mismatch");
  
  			// Check against source
  			PiecewiseCompare(source, clone);
  		}
  
  		/// <summary>
  		/// Setting entry comments to null should be allowed
  		/// </summary>
  		[Test]
  		[Category("Zip")]
  		public void NullEntryComment()
  		{
  			ZipEntry test = new ZipEntry("null");
  			test.Comment = null;
  		}
  
  		/// <summary>
  		/// Entries with null names arent allowed
  		/// </summary>
  		[Test]
  		[Category("Zip")]
  		[ExpectedException(typeof(ArgumentNullException))]
  		public void NullNameInConstructor()
  		{
  			string name = null;
  			ZipEntry test = new ZipEntry(name);
  		}		
  		
  		[Test]
  		[Category("Zip")]
  		public void DateAndTime()
  		{
  			ZipEntry ze = new ZipEntry("Pok");
  
  			// -1 is not strictly a valid MS-DOS DateTime value.
  			// ZipEntry is lenient about handling invalid values.
  			ze.DosTime = -1;
  
  			Assert.AreEqual(new DateTime(2107, 12, 31, 23, 59, 59), ze.DateTime);
  
  			// 0 is a special value meaning Now.
  			ze.DosTime = 0;
  			TimeSpan diff = DateTime.Now - ze.DateTime;
  
  			// Value == 2 seconds!
  			ze.DosTime = 1;
  			Assert.AreEqual(new DateTime(1980, 1, 1, 0, 0, 2), ze.DateTime);
  
  			// Over the limit are set to max.
  			ze.DateTime = new DateTime(2108, 1, 1);
  			Assert.AreEqual(new DateTime(2107, 12, 31, 23, 59, 58), ze.DateTime);
  
  			// Under the limit are set to min.
  			ze.DateTime = new DateTime(1906, 12, 4);
  			Assert.AreEqual(new DateTime(1980, 1, 1, 0, 0, 0), ze.DateTime);
  		}
  
  		[Test]
  		[Category("Zip")]
  		public void DateTimeSetsDosTime()
  		{
  			ZipEntry ze = new ZipEntry("Pok");
  
  			long original = ze.DosTime;
  
  			ze.DateTime = new DateTime(1987, 9, 12);
  			Assert.AreNotEqual(original, ze.DosTime);
  			Assert.AreEqual(0, TestHelper.CompareDosDateTimes(new DateTime(1987, 9, 12), ze.DateTime));
  		}
  		
  		[Test]
  		public void CanDecompress()
  		{
  			int dosTime = 12;
  			int crc = 0xfeda;
  
  			ZipEntry ze = MakeEntry("a", 10, 0, (short)CompressionMethod.Deflated,
  									dosTime, crc, 1, 1);
  			
  			Assert.IsTrue(ze.CanDecompress);
  
  			ze = MakeEntry("a", 45, 0, (short)CompressionMethod.Stored,
  									dosTime, crc, 1, 1);
  			Assert.IsTrue(ze.CanDecompress);
  
  
gt: 			ze = MakeEntry("a", 99, 0, (short)CompressionMethod.Deflated,