From 3b126e94b56ac233e547f228bd550bf72a2425d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nils=20m=C3=A5s=C3=A9n?= Date: Sun, 7 Mar 2021 15:39:07 +0100 Subject: [PATCH 1/5] Replace static ZipStrings with instances of StringCodec --- .../Streams/DeflaterOutputStream.cs | 6 +- src/ICSharpCode.SharpZipLib/Zip/FastZip.cs | 17 ++ .../Zip/ZipConstants.cs | 43 ---- src/ICSharpCode.SharpZipLib/Zip/ZipEntry.cs | 10 +- .../Zip/ZipEntryFactory.cs | 4 +- src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs | 57 +++-- .../Zip/ZipHelperStream.cs | 4 +- .../Zip/ZipInputStream.cs | 9 +- .../Zip/ZipOutputStream.cs | 17 +- src/ICSharpCode.SharpZipLib/Zip/ZipStrings.cs | 214 ++++++------------ .../Zip/FastZipHandling.cs | 64 +++--- .../Zip/GeneralHandling.cs | 27 +-- 12 files changed, 195 insertions(+), 277 deletions(-) diff --git a/src/ICSharpCode.SharpZipLib/Zip/Compression/Streams/DeflaterOutputStream.cs b/src/ICSharpCode.SharpZipLib/Zip/Compression/Streams/DeflaterOutputStream.cs index 03cac7358..1364638e7 100644 --- a/src/ICSharpCode.SharpZipLib/Zip/Compression/Streams/DeflaterOutputStream.cs +++ b/src/ICSharpCode.SharpZipLib/Zip/Compression/Streams/DeflaterOutputStream.cs @@ -2,6 +2,7 @@ using System; using System.IO; using System.Security.Cryptography; +using System.Text; namespace ICSharpCode.SharpZipLib.Zip.Compression.Streams { @@ -185,6 +186,9 @@ public string Password } } + /// + public Encoding ZipCryptoEncoding { get; set; } = StringCodec.DefaultZipCryptoEncoding; + /// /// Encrypt a block of data /// @@ -209,7 +213,7 @@ protected void EncryptBlock(byte[] buffer, int offset, int length) protected void InitializePassword(string password) { var pkManaged = new PkzipClassicManaged(); - byte[] key = PkzipClassic.GenerateKeys(ZipStrings.ConvertToArray(password)); + byte[] key = PkzipClassic.GenerateKeys(ZipCryptoEncoding.GetBytes(password)); cryptoTransform_ = pkManaged.CreateEncryptor(key, null); } diff --git a/src/ICSharpCode.SharpZipLib/Zip/FastZip.cs b/src/ICSharpCode.SharpZipLib/Zip/FastZip.cs index 01725f4c3..041b7d8eb 100644 --- a/src/ICSharpCode.SharpZipLib/Zip/FastZip.cs +++ b/src/ICSharpCode.SharpZipLib/Zip/FastZip.cs @@ -345,6 +345,22 @@ public Deflater.CompressionLevel CompressionLevel set { compressionLevel_ = value; } } + /// + /// Reflects the opposite of the internal , setting it to false overrides the encoding used for reading and writing zip entries + /// + public bool UseUnicode + { + get => !_stringCodec.ForceZipLegacyEncoding; + set => _stringCodec.ForceZipLegacyEncoding = !value; + } + + /// Gets or sets the code page used for reading/writing zip file entries when unicode is disabled + public int LegacyCodePage + { + get => _stringCodec.CodePage; + set => _stringCodec.CodePage = value; + } + #endregion Properties #region Delegates @@ -967,6 +983,7 @@ private static bool NameIsValid(string name) private INameTransform extractNameTransform_; private UseZip64 useZip64_ = UseZip64.Dynamic; private CompressionLevel compressionLevel_ = CompressionLevel.DEFAULT_COMPRESSION; + private readonly StringCodec _stringCodec = new StringCodec(); private string password_; diff --git a/src/ICSharpCode.SharpZipLib/Zip/ZipConstants.cs b/src/ICSharpCode.SharpZipLib/Zip/ZipConstants.cs index b0f33a764..2b1c39b0c 100644 --- a/src/ICSharpCode.SharpZipLib/Zip/ZipConstants.cs +++ b/src/ICSharpCode.SharpZipLib/Zip/ZipConstants.cs @@ -470,48 +470,5 @@ public static class ZipConstants public const int ENDSIG = 'P' | ('K' << 8) | (5 << 16) | (6 << 24); #endregion Header Signatures - - /// - /// Default encoding used for string conversion. 0 gives the default system OEM code page. - /// Using the default code page isnt the full solution necessarily - /// there are many variable factors, codepage 850 is often a good choice for - /// European users, however be careful about compatability. - /// - [Obsolete("Use ZipStrings instead")] - public static int DefaultCodePage - { - get => ZipStrings.CodePage; - set => ZipStrings.CodePage = value; - } - - /// Deprecated wrapper for - [Obsolete("Use ZipStrings.ConvertToString instead")] - public static string ConvertToString(byte[] data, int count) - => ZipStrings.ConvertToString(data, count); - - /// Deprecated wrapper for - [Obsolete("Use ZipStrings.ConvertToString instead")] - public static string ConvertToString(byte[] data) - => ZipStrings.ConvertToString(data); - - /// Deprecated wrapper for - [Obsolete("Use ZipStrings.ConvertToStringExt instead")] - public static string ConvertToStringExt(int flags, byte[] data, int count) - => ZipStrings.ConvertToStringExt(flags, data, count); - - /// Deprecated wrapper for - [Obsolete("Use ZipStrings.ConvertToStringExt instead")] - public static string ConvertToStringExt(int flags, byte[] data) - => ZipStrings.ConvertToStringExt(flags, data); - - /// Deprecated wrapper for - [Obsolete("Use ZipStrings.ConvertToArray instead")] - public static byte[] ConvertToArray(string str) - => ZipStrings.ConvertToArray(str); - - /// Deprecated wrapper for - [Obsolete("Use ZipStrings.ConvertToArray instead")] - public static byte[] ConvertToArray(int flags, string str) - => ZipStrings.ConvertToArray(flags, str); } } diff --git a/src/ICSharpCode.SharpZipLib/Zip/ZipEntry.cs b/src/ICSharpCode.SharpZipLib/Zip/ZipEntry.cs index c607cf9f2..4bb2651b4 100644 --- a/src/ICSharpCode.SharpZipLib/Zip/ZipEntry.cs +++ b/src/ICSharpCode.SharpZipLib/Zip/ZipEntry.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Text; namespace ICSharpCode.SharpZipLib.Zip { @@ -150,7 +151,7 @@ private enum Known : byte /// The name passed is null /// public ZipEntry(string name) - : this(name, 0, ZipConstants.VersionMadeBy, CompressionMethod.Deflated) + : this(name, 0, ZipConstants.VersionMadeBy, CompressionMethod.Deflated, true) { } @@ -171,7 +172,7 @@ public ZipEntry(string name) /// internal ZipEntry(string name, int versionRequiredToExtract) : this(name, versionRequiredToExtract, ZipConstants.VersionMadeBy, - CompressionMethod.Deflated) + CompressionMethod.Deflated, true) { } @@ -182,6 +183,7 @@ internal ZipEntry(string name, int versionRequiredToExtract) /// Version and HostSystem Information /// Minimum required zip feature version required to extract this entry /// Compression method for this entry. + /// Whether the entry uses unicode for name and comment /// /// The name passed is null /// @@ -193,7 +195,7 @@ internal ZipEntry(string name, int versionRequiredToExtract) /// It is not generally useful, use the constructor specifying the name only. /// internal ZipEntry(string name, int versionRequiredToExtract, int madeByInfo, - CompressionMethod method) + CompressionMethod method, bool unicode) { if (name == null) { @@ -216,7 +218,7 @@ internal ZipEntry(string name, int versionRequiredToExtract, int madeByInfo, this.versionToExtract = (ushort)versionRequiredToExtract; this.method = method; - IsUnicodeText = ZipStrings.UseUnicode; + IsUnicodeText = unicode; } /// diff --git a/src/ICSharpCode.SharpZipLib/Zip/ZipEntryFactory.cs b/src/ICSharpCode.SharpZipLib/Zip/ZipEntryFactory.cs index 1e40baaff..ccbb26968 100644 --- a/src/ICSharpCode.SharpZipLib/Zip/ZipEntryFactory.cs +++ b/src/ICSharpCode.SharpZipLib/Zip/ZipEntryFactory.cs @@ -68,7 +68,7 @@ public enum TimeSetting public ZipEntryFactory() { nameTransform_ = new ZipNameTransform(); - isUnicodeText_ = ZipStrings.UseUnicode; + isUnicodeText_ = true; } /// @@ -162,7 +162,7 @@ public int SetAttributes } /// - /// Get set a value indicating whether unidoce text should be set on. + /// Get set a value indicating whether unicode text should be set on. /// public bool IsUnicodeText { diff --git a/src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs b/src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs index a99f1f5ba..95402c3b8 100644 --- a/src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs +++ b/src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs @@ -367,7 +367,7 @@ public string Password } else { - key = PkzipClassic.GenerateKeys(ZipStrings.ConvertToArray(value)); + key = PkzipClassic.GenerateKeys(ZipCryptoEncoding.GetBytes(value)); } rawPassword_ = value; @@ -725,6 +725,14 @@ public ZipEntry this[int index] } } + + /// + public Encoding ZipCryptoEncoding + { + get => _stringCodec.ZipCryptoEncoding; + set => _stringCodec.ZipCryptoEncoding = value; + } + #endregion Properties #region Input Handling @@ -1191,6 +1199,8 @@ private long TestLocalHeader(ZipEntry entry, HeaderTest tests) throw new ZipException(string.Format("Version required to extract this entry is invalid ({0})", extractVersion)); } + var localEncoding = _stringCodec.ZipInputEncoding(localFlags); + // Local entry flags dont have reserved bit set on. if ((localFlags & (int)(GeneralBitFlags.ReservedPKware4 | GeneralBitFlags.ReservedPkware14 | GeneralBitFlags.ReservedPkware15)) != 0) { @@ -1283,7 +1293,7 @@ private long TestLocalHeader(ZipEntry entry, HeaderTest tests) } // Name data has already been read convert it and compare. - string localName = ZipStrings.ConvertToStringExt(localFlags, nameData); + string localName = localEncoding.GetString(nameData); // Central directory and local entry name match if (localName != entry.Name) @@ -1581,7 +1591,7 @@ public void CommitUpdate() // Create an empty archive if none existed originally. if (entries_.Length == 0) { - byte[] theComment = (newComment_ != null) ? newComment_.RawComment : ZipStrings.ConvertToArray(comment_); + byte[] theComment = (newComment_ != null) ? newComment_.RawComment : _stringCodec.ZipArchiveCommentEncoding.GetBytes(comment_); using (ZipHelperStream zhs = new ZipHelperStream(baseStream_)) { zhs.WriteEndOfCentralDirectory(0, 0, 0, theComment); @@ -1619,7 +1629,7 @@ public void SetComment(string comment) CheckUpdating(); - newComment_ = new ZipString(comment); + newComment_ = new ZipString(comment, _stringCodec.ZipArchiveCommentEncoding); if (newComment_.RawLength > 0xffff) { @@ -2147,7 +2157,8 @@ private void WriteLocalEntryHeader(ZipUpdate update) WriteLEInt((int)entry.Size); } - byte[] name = ZipStrings.ConvertToArray(entry.Flags, entry.Name); + var entryEncoding = _stringCodec.ZipInputEncoding(entry.Flags); + byte[] name = entryEncoding.GetBytes(entry.Name); if (name.Length > 0xFFFF) { @@ -2254,7 +2265,8 @@ private int WriteCentralDirectoryHeader(ZipEntry entry) WriteLEInt((int)entry.Size); } - byte[] name = ZipStrings.ConvertToArray(entry.Flags, entry.Name); + var entryEncoding = _stringCodec.ZipInputEncoding(entry.Flags); + byte[] name = entryEncoding.GetBytes(entry.Name); if (name.Length > 0xFFFF) { @@ -3081,7 +3093,7 @@ private void RunUpdates() } } - byte[] theComment = (newComment_ != null) ? newComment_.RawComment : ZipStrings.ConvertToArray(comment_); + byte[] theComment = newComment_?.RawComment ?? _stringCodec.ZipArchiveCommentEncoding.GetBytes(comment_); using (ZipHelperStream zhs = new ZipHelperStream(workFile.baseStream_)) { zhs.WriteEndOfCentralDirectory(updateCount_, sizeEntries, centralDirOffset, theComment); @@ -3481,7 +3493,7 @@ private void ReadEntries() byte[] comment = new byte[commentSize]; StreamUtils.ReadFully(baseStream_, comment); - comment_ = ZipStrings.ConvertToString(comment); + comment_ = _stringCodec.ZipArchiveCommentEncoding.GetString(comment); } else { @@ -3598,11 +3610,13 @@ private void ReadEntries() long offset = ReadLEUint(); byte[] buffer = new byte[Math.Max(nameLen, commentLen)]; + var entryEncoding = _stringCodec.ZipInputEncoding(bitFlags); StreamUtils.ReadFully(baseStream_, buffer, 0, nameLen); - string name = ZipStrings.ConvertToStringExt(bitFlags, buffer, nameLen); + string name = entryEncoding.GetString(buffer, 0, nameLen); + var unicode = entryEncoding.IsZipUnicode(); - var entry = new ZipEntry(name, versionToExtract, versionMadeBy, (CompressionMethod)method) + var entry = new ZipEntry(name, versionToExtract, versionMadeBy, (CompressionMethod)method, unicode) { Crc = crc & 0xffffffffL, Size = size & 0xffffffffL, @@ -3635,7 +3649,7 @@ private void ReadEntries() if (commentLen > 0) { StreamUtils.ReadFully(baseStream_, buffer, 0, commentLen); - entry.Comment = ZipStrings.ConvertToStringExt(bitFlags, buffer, commentLen); + entry.Comment = entryEncoding.GetString(buffer, 0, commentLen); } entries_[i] = entry; @@ -3787,6 +3801,7 @@ private static void WriteEncryptionHeader(Stream stream, long crcValue) private ZipEntry[] entries_; private byte[] key; private bool isNewArchive_; + private readonly StringCodec _stringCodec = new StringCodec(); // Default is dynamic which is not backwards compatible and can cause problems // with XP's built in compression which cant read Zip64 archives. @@ -3825,19 +3840,23 @@ private class ZipString /// Initialise a with a string. /// /// The textual string form. - public ZipString(string comment) + /// + public ZipString(string comment, Encoding encoding) { comment_ = comment; isSourceString_ = true; + _encoding = encoding; } /// /// Initialise a using a string in its binary 'raw' form. /// /// - public ZipString(byte[] rawString) + /// + public ZipString(byte[] rawString, Encoding encoding) { rawComment_ = rawString; + _encoding = encoding; } #endregion Constructors @@ -3846,10 +3865,7 @@ public ZipString(byte[] rawString) /// Get a value indicating the original source of data for this instance. /// True if the source was a string; false if the source was binary data. /// - public bool IsSourceString - { - get { return isSourceString_; } - } + public bool IsSourceString => isSourceString_; /// /// Get the length of the comment when represented as raw bytes. @@ -3894,7 +3910,7 @@ private void MakeTextAvailable() { if (comment_ == null) { - comment_ = ZipStrings.ConvertToString(rawComment_); + comment_ = _encoding.GetString(rawComment_); } } @@ -3902,7 +3918,7 @@ private void MakeBytesAvailable() { if (rawComment_ == null) { - rawComment_ = ZipStrings.ConvertToArray(comment_); + rawComment_ = _encoding.GetBytes(comment_); } } @@ -3911,7 +3927,7 @@ private void MakeBytesAvailable() /// /// The to convert to a string. /// The textual equivalent for the input value. - static public implicit operator string(ZipString zipString) + public static implicit operator string(ZipString zipString) { zipString.MakeTextAvailable(); return zipString.comment_; @@ -3922,6 +3938,7 @@ static public implicit operator string(ZipString zipString) private string comment_; private byte[] rawComment_; private readonly bool isSourceString_; + private readonly Encoding _encoding; #endregion Instance Fields } diff --git a/src/ICSharpCode.SharpZipLib/Zip/ZipHelperStream.cs b/src/ICSharpCode.SharpZipLib/Zip/ZipHelperStream.cs index da65630c6..16d65af75 100644 --- a/src/ICSharpCode.SharpZipLib/Zip/ZipHelperStream.cs +++ b/src/ICSharpCode.SharpZipLib/Zip/ZipHelperStream.cs @@ -236,7 +236,9 @@ private void WriteLocalHeader(ZipEntry entry, EntryPatchData patchData) } } - byte[] name = ZipStrings.ConvertToArray(entry.Flags, entry.Name); + // TODO: This should be class instanced if used! + var stringCodec = new StringCodec(); + byte[] name = stringCodec.ZipInputEncoding(entry.Flags).GetBytes(entry.Name); if (name.Length > 0xFFFF) { diff --git a/src/ICSharpCode.SharpZipLib/Zip/ZipInputStream.cs b/src/ICSharpCode.SharpZipLib/Zip/ZipInputStream.cs index cccac6639..4fba4f017 100644 --- a/src/ICSharpCode.SharpZipLib/Zip/ZipInputStream.cs +++ b/src/ICSharpCode.SharpZipLib/Zip/ZipInputStream.cs @@ -76,6 +76,7 @@ public class ZipInputStream : InflaterInputStream private CompressionMethod method; private int flags; private string password; + private readonly StringCodec _stringCodec = new StringCodec(); #endregion Instance Fields @@ -221,9 +222,11 @@ public ZipEntry GetNextEntry() byte[] buffer = new byte[nameLen]; inputBuffer.ReadRawBuffer(buffer); - string name = ZipStrings.ConvertToStringExt(flags, buffer); + var entryEncoding = _stringCodec.ZipInputEncoding(flags); + string name = entryEncoding.GetString(buffer); + var unicode = entryEncoding.IsZipUnicode(); - entry = new ZipEntry(name, versionRequiredToExtract, ZipConstants.VersionMadeBy, method) + entry = new ZipEntry(name, versionRequiredToExtract, ZipConstants.VersionMadeBy, method, unicode) { Flags = flags, }; @@ -524,7 +527,7 @@ private int InitialRead(byte[] destination, int offset, int count) // Generate and set crypto transform... var managed = new PkzipClassicManaged(); - byte[] key = PkzipClassic.GenerateKeys(ZipStrings.ConvertToArray(password)); + byte[] key = PkzipClassic.GenerateKeys(_stringCodec.ZipCryptoEncoding.GetBytes(password)); inputBuffer.CryptoTransform = managed.CreateDecryptor(key, null); diff --git a/src/ICSharpCode.SharpZipLib/Zip/ZipOutputStream.cs b/src/ICSharpCode.SharpZipLib/Zip/ZipOutputStream.cs index e2c0426fd..7463de7a0 100644 --- a/src/ICSharpCode.SharpZipLib/Zip/ZipOutputStream.cs +++ b/src/ICSharpCode.SharpZipLib/Zip/ZipOutputStream.cs @@ -102,8 +102,7 @@ public bool IsFinished /// public void SetComment(string comment) { - // TODO: Its not yet clear how to handle unicode comments here. - byte[] commentBytes = ZipStrings.ConvertToArray(comment); + byte[] commentBytes = _stringCodec.ZipArchiveCommentEncoding.GetBytes(comment); if (commentBytes.Length > 0xffff) { throw new ArgumentOutOfRangeException(nameof(comment)); @@ -399,7 +398,7 @@ public void PutNextEntry(ZipEntry entry) // Apply any required transforms to the entry name, and then convert to byte array format. TransformEntryName(entry); - byte[] name = ZipStrings.ConvertToArray(entry.Flags, entry.Name); + byte[] name = _stringCodec.ZipOutputEncoding.GetBytes(entry.Name); if (name.Length > 0xFFFF) { @@ -827,7 +826,8 @@ public override void Finish() WriteLeInt((int)entry.Size); } - byte[] name = ZipStrings.ConvertToArray(entry.Flags, entry.Name); + var entryEncoding = _stringCodec.ZipInputEncoding(entry.Flags); + byte[] name = entryEncoding.GetBytes(entry.Name); if (name.Length > 0xffff) { @@ -869,10 +869,9 @@ public override void Finish() } byte[] extra = ed.GetEntryData(); - byte[] entryComment = - (entry.Comment != null) ? - ZipStrings.ConvertToArray(entry.Flags, entry.Comment) : - Empty.Array(); + byte[] entryComment = entry.Comment != null + ? entryEncoding.GetBytes(entry.Comment) + : Empty.Array(); if (entryComment.Length > 0xffff) { @@ -1010,6 +1009,8 @@ public override void Flush() // NOTE: Setting the size for entries before they are added is the best solution! private UseZip64 useZip64_ = UseZip64.Dynamic; + private readonly StringCodec _stringCodec = new StringCodec(); + #endregion Instance Fields } } diff --git a/src/ICSharpCode.SharpZipLib/Zip/ZipStrings.cs b/src/ICSharpCode.SharpZipLib/Zip/ZipStrings.cs index 8c2447134..e20eaae9f 100644 --- a/src/ICSharpCode.SharpZipLib/Zip/ZipStrings.cs +++ b/src/ICSharpCode.SharpZipLib/Zip/ZipStrings.cs @@ -1,15 +1,20 @@ using System; using System.Text; -using ICSharpCode.SharpZipLib.Core; namespace ICSharpCode.SharpZipLib.Zip { + internal static class EncodingExtensions + { + public static bool IsZipUnicode(this Encoding e) + => e.Equals(StringCodec.UnicodeZipEncoding); + } + /// - /// This static class contains functions for encoding and decoding zip file strings + /// Utility class for resolving the encoding used for reading and writing strings /// - public static class ZipStrings + public class StringCodec { - static ZipStrings() + static StringCodec() { try { @@ -20,175 +25,100 @@ static ZipStrings() { SystemDefaultCodePage = FallbackCodePage; } - } - - /// Code page backing field - /// - /// The original Zip specification (https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT) states - /// that file names should only be encoded with IBM Code Page 437 or UTF-8. - /// In practice, most zip apps use OEM or system encoding (typically cp437 on Windows). - /// Let's be good citizens and default to UTF-8 http://utf8everywhere.org/ - /// - private static int codePage = AutomaticCodePage; - /// Automatically select codepage while opening archive - /// see https://github.com/icsharpcode/SharpZipLib/pull/280#issuecomment-433608324 - /// - private const int AutomaticCodePage = -1; + SystemDefaultEncoding = Encoding.GetEncoding(SystemDefaultCodePage); + } /// - /// Encoding used for string conversion. Setting this to 65001 (UTF-8) will - /// also set the Language encoding flag to indicate UTF-8 encoded file names. + /// If set, use the encoding set by for zip entries instead of the defaults /// - public static int CodePage - { - get - { - return codePage == AutomaticCodePage? Encoding.UTF8.CodePage:codePage; - } - set - { - if ((value < 0) || (value > 65535) || - (value == 1) || (value == 2) || (value == 3) || (value == 42)) - { - throw new ArgumentOutOfRangeException(nameof(value)); - } + public bool ForceZipLegacyEncoding { get; set; } - codePage = value; - } - } + /// + /// The default encoding used for ZipCrypto passwords in zip files, set to + /// for greatest compability. + /// + public static Encoding DefaultZipCryptoEncoding => SystemDefaultEncoding; - private const int FallbackCodePage = 437; + /// + /// Returns the encoding for an output . + /// Unless overriden by it returns . + /// + public Encoding ZipOutputEncoding => ZipEncoding(!ForceZipLegacyEncoding); /// - /// Attempt to get the operating system default codepage, or failing that, to - /// the fallback code page IBM 437. + /// Returns if is set, otherwise it returns the encoding indicated by /// - public static int SystemDefaultCodePage { get; } + public Encoding ZipEncoding(bool unicode) => unicode ? UnicodeZipEncoding : _legacyEncoding; /// - /// Get whether the default codepage is set to UTF-8. Setting this property to false will - /// set the to + /// Returns the appropriate encoding for an input according to . + /// If overridden by , it always returns the encoding indicated by . /// + /// + /// + public Encoding ZipInputEncoding(GeneralBitFlags flags) => ZipInputEncoding((int)flags); + + /// + public Encoding ZipInputEncoding(int flags) => ZipEncoding(!ForceZipLegacyEncoding && (flags & (int)GeneralBitFlags.UnicodeText) != 0); + + /// Code page encoding, used for non-unicode strings /// - /// /// Get OEM codepage from NetFX, which parses the NLP file with culture info table etc etc. - /// But sometimes it yields the special value of 1 which is nicknamed CodePageNoOEM in sources (might also mean CP_OEMCP, but Encoding puts it so). - /// This was observed on Ukranian and Hindu systems. - /// Given this value, throws an . - /// So replace it with , (IBM 437 which is the default code page in a default Windows installation console. + /// The original Zip specification (https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT) states + /// that file names should only be encoded with IBM Code Page 437 or UTF-8. + /// In practice, most zip apps use OEM or system encoding (typically cp437 on Windows). + /// Let's be good citizens and default to UTF-8 http://utf8everywhere.org/ /// - public static bool UseUnicode - { - get - { - return codePage == Encoding.UTF8.CodePage; - } - set - { - if (value) - { - codePage = Encoding.UTF8.CodePage; - } - else - { - codePage = SystemDefaultCodePage; - } - } - } + private Encoding _legacyEncoding = SystemDefaultEncoding; + + private Encoding _zipArchiveCommentEncoding; /// - /// Convert a portion of a byte array to a string using + /// Returns the UTF-8 code page (65001) used for zip entries with unicode flag set /// - /// - /// Data to convert to string - /// - /// - /// Number of bytes to convert starting from index 0 - /// - /// - /// data[0]..data[count - 1] converted to a string - /// - public static string ConvertToString(byte[] data, int count) - => data == null - ? string.Empty - : Encoding.GetEncoding(CodePage).GetString(data, 0, count); + public static readonly Encoding UnicodeZipEncoding = Encoding.UTF8; /// - /// Convert a byte array to a string using + /// Code page used for non-unicode strings and legacy zip encoding (if is set). + /// Default value is /// - /// - /// Byte array to convert - /// - /// - /// dataconverted to a string - /// - public static string ConvertToString(byte[] data) - => ConvertToString(data, data.Length); - - private static Encoding EncodingFromFlag(int flags) - => ((flags & (int)GeneralBitFlags.UnicodeText) != 0) - ? Encoding.UTF8 - : Encoding.GetEncoding( - // if CodePage wasn't set manually and no utf flag present - // then we must use SystemDefault (old behavior) - // otherwise, CodePage should be preferred over SystemDefault - // see https://github.com/icsharpcode/SharpZipLib/issues/274 - codePage == AutomaticCodePage? - SystemDefaultCodePage: - codePage); + public int CodePage + { + get => _legacyEncoding.CodePage; + set => _legacyEncoding = (value < 4 || value > 65535 || value == 42) + ? throw new ArgumentOutOfRangeException(nameof(value)) + : Encoding.GetEncoding(value); + } + + private const int FallbackCodePage = 437; /// - /// Convert a byte array to a string using + /// Operating system default codepage, or if it could not be retrieved, the fallback code page IBM 437. /// - /// The applicable general purpose bits flags - /// - /// Byte array to convert - /// - /// The number of bytes to convert. - /// - /// dataconverted to a string - /// - public static string ConvertToStringExt(int flags, byte[] data, int count) - => (data == null) - ? string.Empty - : EncodingFromFlag(flags).GetString(data, 0, count); + public static int SystemDefaultCodePage { get; } /// - /// Convert a byte array to a string using + /// The system default encoding, based on /// - /// - /// Byte array to convert - /// - /// The applicable general purpose bits flags - /// - /// dataconverted to a string - /// - public static string ConvertToStringExt(int flags, byte[] data) - => ConvertToStringExt(flags, data, data.Length); + public static Encoding SystemDefaultEncoding { get; } /// - /// Convert a string to a byte array using + /// The encoding used for the zip archive comment. Defaults to the encoding for , since + /// no unicode flag can be set for it in the files. /// - /// - /// String to convert to an array - /// - /// Converted array - public static byte[] ConvertToArray(string str) - => str == null - ? Empty.Array() - : Encoding.GetEncoding(CodePage).GetBytes(str); + public Encoding ZipArchiveCommentEncoding + { + get => _zipArchiveCommentEncoding ?? _legacyEncoding; + set => _zipArchiveCommentEncoding = value; + } /// - /// Convert a string to a byte array using + /// The encoding used for the ZipCrypto passwords. Defaults to . /// - /// The applicable general purpose bits flags - /// - /// String to convert to an array - /// - /// Converted array - public static byte[] ConvertToArray(int flags, string str) - => (string.IsNullOrEmpty(str)) - ? Empty.Array() - : EncodingFromFlag(flags).GetBytes(str); + public Encoding ZipCryptoEncoding + { + get => _zipArchiveCommentEncoding ?? DefaultZipCryptoEncoding; + set => _zipArchiveCommentEncoding = value; + } } } diff --git a/test/ICSharpCode.SharpZipLib.Tests/Zip/FastZipHandling.cs b/test/ICSharpCode.SharpZipLib.Tests/Zip/FastZipHandling.cs index d394f309c..b736fca25 100644 --- a/test/ICSharpCode.SharpZipLib.Tests/Zip/FastZipHandling.cs +++ b/test/ICSharpCode.SharpZipLib.Tests/Zip/FastZipHandling.cs @@ -242,12 +242,17 @@ public void CreateExceptions() #region String testing helper - private void TestFileNames(params string[] names) - => TestFileNames((IEnumerable)names); + private void TestFileNames(int codePage, params string[] names) + => TestFileNames(codePage, names); - private void TestFileNames(IEnumerable names) + private void TestFileNames(int codePage, IEnumerable names) { var zippy = new FastZip(); + if (codePage > 0) + { + zippy.UseUnicode = false; + zippy.LegacyCodePage = codePage; + } using (var tempDir = new Utils.TempDir()) using (var tempZip = new Utils.TempFile()) @@ -272,7 +277,7 @@ private void TestFileNames(IEnumerable names) var entry = z[index]; - if (ZipStrings.UseUnicode) + if (zippy.UseUnicode) { Assert.IsTrue(entry.IsUnicodeText, "Zip entry #{0} not marked as unicode", index); } @@ -298,15 +303,7 @@ private void TestFileNames(IEnumerable names) [Category("Unicode")] public void UnicodeText() { - var preCp = ZipStrings.CodePage; - try - { - TestFileNames(StringTesting.Filenames); - } - finally - { - ZipStrings.CodePage = preCp; - } + TestFileNames(0, StringTesting.Filenames); } [Test] @@ -314,35 +311,26 @@ public void UnicodeText() [Category("Unicode")] public void NonUnicodeText() { - var preCp = ZipStrings.CodePage; - try + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + + foreach ((string language, string filename, string encoding) in StringTesting.GetTestSamples()) { - Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + Console.WriteLine($"{language} filename \"{filename}\" using \"{encoding}\":"); - foreach ((string language, string filename, string encoding) in StringTesting.GetTestSamples()) + // TODO: samples of this test must be reversible + // Some samples can't be restored back with their encoding. + // test wasn't failing only because SystemDefaultCodepage is 65001 on Net.Core and + // old behaviour actually was using Unicode instead of user's passed codepage + var encoder = Encoding.GetEncoding(encoding); + var bytes = encoder.GetBytes(filename); + var restoredString = encoder.GetString(bytes); + if(string.CompareOrdinal(filename, restoredString) != 0) { - Console.WriteLine($"{language} filename \"{filename}\" using \"{encoding}\":"); - - // TODO: samples of this test must be reversible - // Some samples can't be restored back with their encoding. - // test wasn't failing only because SystemDefaultCodepage is 65001 on Net.Core and - // old behaviour actually was using Unicode instead of user's passed codepage - var encoder = Encoding.GetEncoding(encoding); - var bytes = encoder.GetBytes(filename); - var restoredString = encoder.GetString(bytes); - if(string.CompareOrdinal(filename, restoredString) != 0) - { - Console.WriteLine($"Sample for language {language} with value of {filename} is skipped, because it's irreversable"); - continue; - } - - ZipStrings.CodePage = Encoding.GetEncoding(encoding).CodePage; - TestFileNames(filename); + Console.WriteLine($"Sample for language {language} with value of {filename} is skipped, because it's irreversable"); + continue; } - } - finally - { - ZipStrings.CodePage = preCp; + + TestFileNames(Encoding.GetEncoding(encoding).CodePage, filename); } } diff --git a/test/ICSharpCode.SharpZipLib.Tests/Zip/GeneralHandling.cs b/test/ICSharpCode.SharpZipLib.Tests/Zip/GeneralHandling.cs index b74ed1ddc..536c3f562 100644 --- a/test/ICSharpCode.SharpZipLib.Tests/Zip/GeneralHandling.cs +++ b/test/ICSharpCode.SharpZipLib.Tests/Zip/GeneralHandling.cs @@ -864,20 +864,17 @@ private object UnZipZeroLength(byte[] zipped) return result; } - private void CheckNameConversion(string toCheck) - { - byte[] intermediate = ZipStrings.ConvertToArray(toCheck); - string final = ZipStrings.ConvertToString(intermediate); - - Assert.AreEqual(toCheck, final, "Expected identical result"); - } - [Test] [Category("Zip")] - public void NameConversion() + [TestCase("Hello")] + [TestCase("a/b/c/d/e/f/g/h/SomethingLikeAnArchiveName.txt")] + public void LegacyNameConversion(string name) { - CheckNameConversion("Hello"); - CheckNameConversion("a/b/c/d/e/f/g/h/SomethingLikeAnArchiveName.txt"); + var encoding = new StringCodec().ZipEncoding(false); + byte[] intermediate = encoding.GetBytes(name); + string final = encoding.GetString(intermediate); + + Assert.AreEqual(name, final, "Expected identical result"); } [Test] @@ -886,22 +883,22 @@ public void UnicodeNameConversion() { Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); - ZipStrings.CodePage = 850; + var codec = new StringCodec() {CodePage = 850}; string sample = "Hello world"; byte[] rawData = Encoding.ASCII.GetBytes(sample); - string converted = ZipStrings.ConvertToStringExt(0, rawData); + var converted = codec.ZipInputEncoding(0).GetString(rawData); Assert.AreEqual(sample, converted); - converted = ZipStrings.ConvertToStringExt((int)GeneralBitFlags.UnicodeText, rawData); + converted = codec.ZipInputEncoding((int)GeneralBitFlags.UnicodeText).GetString(rawData); Assert.AreEqual(sample, converted); // This time use some greek characters sample = "\u03A5\u03d5\u03a3"; rawData = Encoding.UTF8.GetBytes(sample); - converted = ZipStrings.ConvertToStringExt((int)GeneralBitFlags.UnicodeText, rawData); + converted = codec.ZipInputEncoding((int)GeneralBitFlags.UnicodeText).GetString(rawData); Assert.AreEqual(sample, converted); } From 7b7fad3343815d0c63739ac62dd1dd46163ae09f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nils=20m=C3=A5s=C3=A9n?= Date: Mon, 8 Mar 2021 01:19:41 +0100 Subject: [PATCH 2/5] Fix fastzip/zipentryfactory and tests --- src/ICSharpCode.SharpZipLib/Zip/FastZip.cs | 15 +++++++++++++-- src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs | 18 +++++++++++++++--- .../Zip/ZipOutputStream.cs | 5 +++++ .../Zip/FastZipHandling.cs | 7 ++----- 4 files changed, 35 insertions(+), 10 deletions(-) diff --git a/src/ICSharpCode.SharpZipLib/Zip/FastZip.cs b/src/ICSharpCode.SharpZipLib/Zip/FastZip.cs index 041b7d8eb..13aedb021 100644 --- a/src/ICSharpCode.SharpZipLib/Zip/FastZip.cs +++ b/src/ICSharpCode.SharpZipLib/Zip/FastZip.cs @@ -360,6 +360,13 @@ public int LegacyCodePage get => _stringCodec.CodePage; set => _stringCodec.CodePage = value; } + + /// + public StringCodec StringCodec + { + get => _stringCodec; + set => _stringCodec = value; + } #endregion Properties @@ -472,7 +479,7 @@ private void CreateZip(Stream outputStream, string sourceDirectory, bool recurse NameTransform = new ZipNameTransform(sourceDirectory); sourceDirectory_ = sourceDirectory; - using (outputStream_ = new ZipOutputStream(outputStream)) + using (outputStream_ = new ZipOutputStream(outputStream, _stringCodec)) { outputStream_.SetLevel((int)CompressionLevel); outputStream_.IsStreamOwner = !leaveOpen; @@ -647,6 +654,10 @@ private void ProcessFile(object sender, ScanEventArgs e) using (FileStream stream = File.Open(e.Name, FileMode.Open, FileAccess.Read, FileShare.Read)) { ZipEntry entry = entryFactory_.MakeFileEntry(e.Name); + if (_stringCodec.ForceZipLegacyEncoding) + { + entry.IsUnicodeText = false; + } // Set up AES encryption for the entry if required. ConfigureEntryEncryption(entry); @@ -983,7 +994,7 @@ private static bool NameIsValid(string name) private INameTransform extractNameTransform_; private UseZip64 useZip64_ = UseZip64.Dynamic; private CompressionLevel compressionLevel_ = CompressionLevel.DEFAULT_COMPRESSION; - private readonly StringCodec _stringCodec = new StringCodec(); + private StringCodec _stringCodec = new StringCodec(); private string password_; diff --git a/src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs b/src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs index 95402c3b8..49b7e3351 100644 --- a/src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs +++ b/src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs @@ -397,13 +397,18 @@ private bool HaveKeys /// /// The file doesn't contain a valid zip archive. /// - public ZipFile(string name) + public ZipFile(string name, StringCodec stringCodec = null) { name_ = name ?? throw new ArgumentNullException(nameof(name)); baseStream_ = File.Open(name, FileMode.Open, FileAccess.Read, FileShare.Read); isStreamOwner = true; + if (stringCodec != null) + { + _stringCodec = stringCodec; + } + try { ReadEntries(); @@ -733,6 +738,13 @@ public Encoding ZipCryptoEncoding set => _stringCodec.ZipCryptoEncoding = value; } + /// + public StringCodec StringCodec + { + get => _stringCodec; + set => _stringCodec = value; + } + #endregion Properties #region Input Handling @@ -3793,7 +3805,7 @@ private static void WriteEncryptionHeader(Stream stream, long crcValue) private bool isDisposed_; private string name_; - private string comment_; + private string comment_ = string.Empty; private string rawPassword_; private Stream baseStream_; private bool isStreamOwner; @@ -3801,7 +3813,7 @@ private static void WriteEncryptionHeader(Stream stream, long crcValue) private ZipEntry[] entries_; private byte[] key; private bool isNewArchive_; - private readonly StringCodec _stringCodec = new StringCodec(); + private StringCodec _stringCodec = new StringCodec(); // Default is dynamic which is not backwards compatible and can cause problems // with XP's built in compression which cant read Zip64 archives. diff --git a/src/ICSharpCode.SharpZipLib/Zip/ZipOutputStream.cs b/src/ICSharpCode.SharpZipLib/Zip/ZipOutputStream.cs index 7463de7a0..db05b39a5 100644 --- a/src/ICSharpCode.SharpZipLib/Zip/ZipOutputStream.cs +++ b/src/ICSharpCode.SharpZipLib/Zip/ZipOutputStream.cs @@ -77,6 +77,11 @@ public ZipOutputStream(Stream baseOutputStream, int bufferSize) { } + internal ZipOutputStream(Stream baseOutputStream, StringCodec stringCodec) : this(baseOutputStream) + { + _stringCodec = stringCodec; + } + #endregion Constructors /// diff --git a/test/ICSharpCode.SharpZipLib.Tests/Zip/FastZipHandling.cs b/test/ICSharpCode.SharpZipLib.Tests/Zip/FastZipHandling.cs index b736fca25..33e33d680 100644 --- a/test/ICSharpCode.SharpZipLib.Tests/Zip/FastZipHandling.cs +++ b/test/ICSharpCode.SharpZipLib.Tests/Zip/FastZipHandling.cs @@ -242,9 +242,6 @@ public void CreateExceptions() #region String testing helper - private void TestFileNames(int codePage, params string[] names) - => TestFileNames(codePage, names); - private void TestFileNames(int codePage, IEnumerable names) { var zippy = new FastZip(); @@ -266,7 +263,7 @@ private void TestFileNames(int codePage, IEnumerable names) zippy.CreateZip(tempZip.Filename, tempDir.Fullpath, true, null); - using (ZipFile z = new ZipFile(tempZip.Filename)) + using (ZipFile z = new ZipFile(tempZip.Filename, zippy.StringCodec)) { Assert.AreEqual(nameCount, z.Count); foreach (var name in names) @@ -330,7 +327,7 @@ public void NonUnicodeText() continue; } - TestFileNames(Encoding.GetEncoding(encoding).CodePage, filename); + TestFileNames(Encoding.GetEncoding(encoding).CodePage, new [] { filename }); } } From 6a2a758cb43c19c0cdf2b336e3424c9b5159ed8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nils=20m=C3=A5s=C3=A9n?= Date: Sat, 9 Oct 2021 23:28:16 +0200 Subject: [PATCH 3/5] fix zipcrypto defaults --- src/ICSharpCode.SharpZipLib/Zip/ZipStrings.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ICSharpCode.SharpZipLib/Zip/ZipStrings.cs b/src/ICSharpCode.SharpZipLib/Zip/ZipStrings.cs index e20eaae9f..12596a35c 100644 --- a/src/ICSharpCode.SharpZipLib/Zip/ZipStrings.cs +++ b/src/ICSharpCode.SharpZipLib/Zip/ZipStrings.cs @@ -38,7 +38,7 @@ static StringCodec() /// The default encoding used for ZipCrypto passwords in zip files, set to /// for greatest compability. /// - public static Encoding DefaultZipCryptoEncoding => SystemDefaultEncoding; + public static Encoding DefaultZipCryptoEncoding => Encoding.Default; /// /// Returns the encoding for an output . @@ -72,6 +72,7 @@ static StringCodec() private Encoding _legacyEncoding = SystemDefaultEncoding; private Encoding _zipArchiveCommentEncoding; + private Encoding _zipCryptoEncoding; /// /// Returns the UTF-8 code page (65001) used for zip entries with unicode flag set @@ -117,8 +118,8 @@ public Encoding ZipArchiveCommentEncoding /// public Encoding ZipCryptoEncoding { - get => _zipArchiveCommentEncoding ?? DefaultZipCryptoEncoding; - set => _zipArchiveCommentEncoding = value; + get => _zipCryptoEncoding ?? DefaultZipCryptoEncoding; + set => _zipCryptoEncoding = value; } } } From bbdd0bc6504e075041ca95a16a0a55bf7d94468e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nils=20m=C3=A5s=C3=A9n?= Date: Sun, 10 Oct 2021 00:17:52 +0200 Subject: [PATCH 4/5] add backwards compatibility helper --- src/ICSharpCode.SharpZipLib/Zip/ZipStrings.cs | 72 ++++++++++++++++++- 1 file changed, 70 insertions(+), 2 deletions(-) diff --git a/src/ICSharpCode.SharpZipLib/Zip/ZipStrings.cs b/src/ICSharpCode.SharpZipLib/Zip/ZipStrings.cs index 12596a35c..96868063e 100644 --- a/src/ICSharpCode.SharpZipLib/Zip/ZipStrings.cs +++ b/src/ICSharpCode.SharpZipLib/Zip/ZipStrings.cs @@ -1,5 +1,6 @@ using System; using System.Text; +using ICSharpCode.SharpZipLib.Core; namespace ICSharpCode.SharpZipLib.Zip { @@ -8,6 +9,73 @@ internal static class EncodingExtensions public static bool IsZipUnicode(this Encoding e) => e.Equals(StringCodec.UnicodeZipEncoding); } + + /// + /// Deprecated way of setting zip encoding provided for backwards compability. + /// Use when possible. + /// + [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] + public static class ZipStrings + { + static StringCodec _compatCodec = new StringCodec(); + + /// + [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] + public static int CodePage + { + get => _compatCodec.CodePage; + set => _compatCodec.CodePage = value; + } + + /// + [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] + public static int SystemDefaultCodePage => StringCodec.SystemDefaultCodePage; + + /// + [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] + public static bool UseUnicode + { + get => !_compatCodec.ForceZipLegacyEncoding; + set => _compatCodec.ForceZipLegacyEncoding = !value; + } + + /// + [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] + private static bool HasUnicodeFlag(int flags) + => ((GeneralBitFlags)flags).HasFlag(GeneralBitFlags.UnicodeText); + + /// + [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] + public static string ConvertToString(byte[] data, int count) + => _compatCodec.ZipOutputEncoding.GetString(data, 0, count); + + /// + [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] + public static string ConvertToString(byte[] data) + => _compatCodec.ZipOutputEncoding.GetString(data); + + /// + [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] + public static string ConvertToStringExt(int flags, byte[] data, int count) + => _compatCodec.ZipEncoding(HasUnicodeFlag(flags)).GetString(data, 0, count); + + /// + [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] + public static string ConvertToStringExt(int flags, byte[] data) + => _compatCodec.ZipEncoding(HasUnicodeFlag(flags)).GetString(data); + + /// + [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] + public static byte[] ConvertToArray(string str) + => ConvertToArray(0, str); + + /// + [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] + public static byte[] ConvertToArray(int flags, string str) + => (string.IsNullOrEmpty(str)) + ? Empty.Array() + : _compatCodec.ZipEncoding(HasUnicodeFlag(flags)).GetBytes(str); + } /// /// Utility class for resolving the encoding used for reading and writing strings @@ -18,7 +86,7 @@ static StringCodec() { try { - var platformCodepage = Encoding.GetEncoding(0).CodePage; + var platformCodepage = Encoding.Default.CodePage; SystemDefaultCodePage = (platformCodepage == 1 || platformCodepage == 2 || platformCodepage == 3 || platformCodepage == 42) ? FallbackCodePage : platformCodepage; } catch @@ -38,7 +106,7 @@ static StringCodec() /// The default encoding used for ZipCrypto passwords in zip files, set to /// for greatest compability. /// - public static Encoding DefaultZipCryptoEncoding => Encoding.Default; + public static Encoding DefaultZipCryptoEncoding => SystemDefaultEncoding; /// /// Returns the encoding for an output . From 9c5f23353bbae481608902e01b0e8f1b371edf8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nils=20m=C3=A5s=C3=A9n?= Date: Sun, 10 Oct 2021 00:37:11 +0200 Subject: [PATCH 5/5] auto compability mode switching --- src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs | 2 +- .../Zip/ZipInputStream.cs | 2 +- .../Zip/ZipOutputStream.cs | 2 +- src/ICSharpCode.SharpZipLib/Zip/ZipStrings.cs | 44 ++++++++++++++----- 4 files changed, 35 insertions(+), 15 deletions(-) diff --git a/src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs b/src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs index de8c1afdd..0a844916e 100644 --- a/src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs +++ b/src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs @@ -3802,7 +3802,7 @@ private static void WriteEncryptionHeader(Stream stream, long crcValue) private ZipEntry[] entries_; private byte[] key; private bool isNewArchive_; - private StringCodec _stringCodec = new StringCodec(); + private StringCodec _stringCodec = ZipStrings.GetStringCodec(); // Default is dynamic which is not backwards compatible and can cause problems // with XP's built in compression which cant read Zip64 archives. diff --git a/src/ICSharpCode.SharpZipLib/Zip/ZipInputStream.cs b/src/ICSharpCode.SharpZipLib/Zip/ZipInputStream.cs index 4fba4f017..1b5b0ad53 100644 --- a/src/ICSharpCode.SharpZipLib/Zip/ZipInputStream.cs +++ b/src/ICSharpCode.SharpZipLib/Zip/ZipInputStream.cs @@ -76,7 +76,7 @@ public class ZipInputStream : InflaterInputStream private CompressionMethod method; private int flags; private string password; - private readonly StringCodec _stringCodec = new StringCodec(); + private readonly StringCodec _stringCodec = ZipStrings.GetStringCodec(); #endregion Instance Fields diff --git a/src/ICSharpCode.SharpZipLib/Zip/ZipOutputStream.cs b/src/ICSharpCode.SharpZipLib/Zip/ZipOutputStream.cs index dbb56b64c..0b292fb3f 100644 --- a/src/ICSharpCode.SharpZipLib/Zip/ZipOutputStream.cs +++ b/src/ICSharpCode.SharpZipLib/Zip/ZipOutputStream.cs @@ -884,7 +884,7 @@ public override void Flush() /// private string password; - private readonly StringCodec _stringCodec = new StringCodec(); + private readonly StringCodec _stringCodec = ZipStrings.GetStringCodec(); #endregion Instance Fields diff --git a/src/ICSharpCode.SharpZipLib/Zip/ZipStrings.cs b/src/ICSharpCode.SharpZipLib/Zip/ZipStrings.cs index 96868063e..29fa98014 100644 --- a/src/ICSharpCode.SharpZipLib/Zip/ZipStrings.cs +++ b/src/ICSharpCode.SharpZipLib/Zip/ZipStrings.cs @@ -14,17 +14,33 @@ public static bool IsZipUnicode(this Encoding e) /// Deprecated way of setting zip encoding provided for backwards compability. /// Use when possible. /// - [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] + /// + /// If any ZipStrings properties are being modified, it will enter a backwards compatibility mode, mimicking the + /// old behaviour where a single instance was shared between all Zip* instances. + /// public static class ZipStrings { - static StringCodec _compatCodec = new StringCodec(); + static readonly StringCodec CompatCodec = new StringCodec(); + + private static bool compatibilityMode; + /// + /// Returns a new instance or the shared backwards compatible instance. + /// + /// + public static StringCodec GetStringCodec() + => compatibilityMode ? CompatCodec : new StringCodec(); + /// [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] public static int CodePage { - get => _compatCodec.CodePage; - set => _compatCodec.CodePage = value; + get => CompatCodec.CodePage; + set + { + CompatCodec.CodePage = value; + compatibilityMode = true; + } } /// @@ -35,10 +51,14 @@ public static int CodePage [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] public static bool UseUnicode { - get => !_compatCodec.ForceZipLegacyEncoding; - set => _compatCodec.ForceZipLegacyEncoding = !value; + get => !CompatCodec.ForceZipLegacyEncoding; + set + { + CompatCodec.ForceZipLegacyEncoding = !value; + compatibilityMode = true; + } } - + /// [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] private static bool HasUnicodeFlag(int flags) @@ -47,22 +67,22 @@ private static bool HasUnicodeFlag(int flags) /// [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] public static string ConvertToString(byte[] data, int count) - => _compatCodec.ZipOutputEncoding.GetString(data, 0, count); + => CompatCodec.ZipOutputEncoding.GetString(data, 0, count); /// [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] public static string ConvertToString(byte[] data) - => _compatCodec.ZipOutputEncoding.GetString(data); + => CompatCodec.ZipOutputEncoding.GetString(data); /// [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] public static string ConvertToStringExt(int flags, byte[] data, int count) - => _compatCodec.ZipEncoding(HasUnicodeFlag(flags)).GetString(data, 0, count); + => CompatCodec.ZipEncoding(HasUnicodeFlag(flags)).GetString(data, 0, count); /// [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] public static string ConvertToStringExt(int flags, byte[] data) - => _compatCodec.ZipEncoding(HasUnicodeFlag(flags)).GetString(data); + => CompatCodec.ZipEncoding(HasUnicodeFlag(flags)).GetString(data); /// [Obsolete("Use ZipFile/Zip*Stream StringCodec instead")] @@ -74,7 +94,7 @@ public static byte[] ConvertToArray(string str) public static byte[] ConvertToArray(int flags, string str) => (string.IsNullOrEmpty(str)) ? Empty.Array() - : _compatCodec.ZipEncoding(HasUnicodeFlag(flags)).GetBytes(str); + : CompatCodec.ZipEncoding(HasUnicodeFlag(flags)).GetBytes(str); } ///