Skip to content

Commit 1a47326

Browse files
authored
PR #497: Transform new entry names using an INameTranform in ZipOutputStream
1 parent 3491abb commit 1a47326

4 files changed

Lines changed: 103 additions & 0 deletions

File tree

src/ICSharpCode.SharpZipLib/Zip/FastZip.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,7 @@ private void CreateZip(Stream outputStream, string sourceDirectory, bool recurse
426426
{
427427
outputStream_.SetLevel((int)CompressionLevel);
428428
outputStream_.IsStreamOwner = !leaveOpen;
429+
outputStream_.NameTransform = null; // all required transforms handled by us
429430

430431
if (password_ != null)
431432
{

src/ICSharpCode.SharpZipLib/Zip/ZipEntry.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -782,6 +782,11 @@ public string Name
782782
{
783783
return name;
784784
}
785+
786+
internal set
787+
{
788+
name = value;
789+
}
785790
}
786791

787792
/// <summary>

src/ICSharpCode.SharpZipLib/Zip/ZipNameTransform.cs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,4 +238,76 @@ public static bool IsValidName(string name)
238238

239239
#endregion Class Fields
240240
}
241+
242+
/// <summary>
243+
/// An implementation of INameTransform that transforms entry paths as per the Zip file naming convention.
244+
/// Strips path roots and puts directory separators in the correct format ('/')
245+
/// </summary>
246+
public class PathTransformer : INameTransform
247+
{
248+
/// <summary>
249+
/// Initialize a new instance of <see cref="PathTransformer"></see>
250+
/// </summary>
251+
public PathTransformer()
252+
{
253+
}
254+
255+
/// <summary>
256+
/// Transform a windows directory name according to the Zip file naming conventions.
257+
/// </summary>
258+
/// <param name="name">The directory name to transform.</param>
259+
/// <returns>The transformed name.</returns>
260+
public string TransformDirectory(string name)
261+
{
262+
name = TransformFile(name);
263+
264+
if (name.Length > 0)
265+
{
266+
if (!name.EndsWith("/", StringComparison.Ordinal))
267+
{
268+
name += "/";
269+
}
270+
}
271+
else
272+
{
273+
throw new ZipException("Cannot have an empty directory name");
274+
}
275+
276+
return name;
277+
}
278+
279+
/// <summary>
280+
/// Transform a windows file name according to the Zip file naming conventions.
281+
/// </summary>
282+
/// <param name="name">The file name to transform.</param>
283+
/// <returns>The transformed name.</returns>
284+
public string TransformFile(string name)
285+
{
286+
if (name != null)
287+
{
288+
// Put separators in the expected format.
289+
name = name.Replace(@"\", "/");
290+
291+
// Remove the path root.
292+
name = WindowsPathUtils.DropPathRoot(name);
293+
294+
// Drop any leading and trailing slashes.
295+
name = name.Trim('/');
296+
297+
// Convert consecutive // characters to /
298+
int index = name.IndexOf("//", StringComparison.Ordinal);
299+
while (index >= 0)
300+
{
301+
name = name.Remove(index, 1);
302+
index = name.IndexOf("//", StringComparison.Ordinal);
303+
}
304+
}
305+
else
306+
{
307+
name = string.Empty;
308+
}
309+
310+
return name;
311+
}
312+
}
241313
}

src/ICSharpCode.SharpZipLib/Zip/ZipOutputStream.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using ICSharpCode.SharpZipLib.Checksum;
2+
using ICSharpCode.SharpZipLib.Core;
23
using ICSharpCode.SharpZipLib.Zip.Compression;
34
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
45
using System;
@@ -146,6 +147,12 @@ public UseZip64 UseZip64
146147
set { useZip64_ = value; }
147148
}
148149

150+
/// <summary>
151+
/// Used for transforming the names of entries added by <see cref="PutNextEntry(ZipEntry)"/>.
152+
/// Defaults to <see cref="PathTransformer"/>, set to null to disable transforms and use names as supplied.
153+
/// </summary>
154+
public INameTransform NameTransform { get; set; } = new PathTransformer();
155+
149156
/// <summary>
150157
/// Write an unsigned short in little endian byte order.
151158
/// </summary>
@@ -182,6 +189,22 @@ private void WriteLeLong(long value)
182189
}
183190
}
184191

192+
// Apply any configured transforms/cleaning to the name of the supplied entry.
193+
private void TransformEntryName(ZipEntry entry)
194+
{
195+
if (this.NameTransform != null)
196+
{
197+
if (entry.IsDirectory)
198+
{
199+
entry.Name = this.NameTransform.TransformDirectory(entry.Name);
200+
}
201+
else
202+
{
203+
entry.Name = this.NameTransform.TransformFile(entry.Name);
204+
}
205+
}
206+
}
207+
185208
/// <summary>
186209
/// Starts a new Zip entry. It automatically closes the previous
187210
/// entry if present.
@@ -367,6 +390,8 @@ public void PutNextEntry(ZipEntry entry)
367390
}
368391
}
369392

393+
// Apply any required transforms to the entry name, and then convert to byte array format.
394+
TransformEntryName(entry);
370395
byte[] name = ZipStrings.ConvertToArray(entry.Flags, entry.Name);
371396

372397
if (name.Length > 0xFFFF)

0 commit comments

Comments
 (0)