Skip to content

Commit

Permalink
Give ZipOutputStream an INameTransform property, and use it to transf…
Browse files Browse the repository at this point in the history
…orm the names of newly added entries
  • Loading branch information
Numpsy committed Aug 5, 2020
1 parent a11665d commit ab9fbc7
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 2 deletions.
1 change: 1 addition & 0 deletions src/ICSharpCode.SharpZipLib/Zip/FastZip.cs
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,7 @@ public void CreateZip(Stream outputStream, string sourceDirectory, bool recurse,
using (outputStream_ = new ZipOutputStream(outputStream))
{
outputStream_.SetLevel((int)CompressionLevel);
outputStream_.NameTransform = null; // all required transforms handled by us

if (password_ != null)
{
Expand Down
72 changes: 72 additions & 0 deletions src/ICSharpCode.SharpZipLib/Zip/ZipNameTransform.cs
Original file line number Diff line number Diff line change
Expand Up @@ -247,4 +247,76 @@ public static bool IsValidName(string name)

#endregion Class Fields
}

/// <summary>
/// An implementation of INameTransform that transforms entry paths as per the Zip file naming convention.
/// Strips path roots and puts directory separators in the correct format ('/')
/// </summary>
public class PathTransformer : INameTransform
{
/// <summary>
/// Initialize a new instance of <see cref="PathTransformer"></see>
/// </summary>
public PathTransformer()
{
}

/// <summary>
/// Transform a windows directory name according to the Zip file naming conventions.
/// </summary>
/// <param name="name">The directory name to transform.</param>
/// <returns>The transformed name.</returns>
public string TransformDirectory(string name)
{
name = TransformFile(name);

if (name.Length > 0)
{
if (!name.EndsWith("/", StringComparison.Ordinal))
{
name += "/";
}
}
else
{
throw new ZipException("Cannot have an empty directory name");
}

return name;
}

/// <summary>
/// Transform a windows file name according to the Zip file naming conventions.
/// </summary>
/// <param name="name">The file name to transform.</param>
/// <returns>The transformed name.</returns>
public string TransformFile(string name)
{
if (name != null)
{
// Put separators in the expected format.
name = name.Replace(@"\", "/");

// Remove the path root.
name = WindowsPathUtils.DropPathRoot(name);

// Drop any leading and trailing slashes.
name = name.Trim('/');

// Convert consecutive // characters to /
int index = name.IndexOf("//", StringComparison.Ordinal);
while (index >= 0)
{
name = name.Remove(index, 1);
index = name.IndexOf("//", StringComparison.Ordinal);
}
}
else
{
name = string.Empty;
}

return name;
}
}
}
39 changes: 37 additions & 2 deletions src/ICSharpCode.SharpZipLib/Zip/ZipOutputStream.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using ICSharpCode.SharpZipLib.Checksum;
using ICSharpCode.SharpZipLib.Core;
using ICSharpCode.SharpZipLib.Zip.Compression;
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
using System;
Expand Down Expand Up @@ -146,6 +147,12 @@ public UseZip64 UseZip64
set { useZip64_ = value; }
}

/// <summary>
/// Used for transforming the names of entries added by <see cref="PutNextEntry(ZipEntry)"/>.
/// Defaults to <see cref="PathTransformer"/>, set to null to disable transforms and use names as supplied.
/// </summary>
public INameTransform NameTransform { get; set; } = new PathTransformer();

/// <summary>
/// Write an unsigned short in little endian byte order.
/// </summary>
Expand Down Expand Up @@ -182,6 +189,34 @@ private void WriteLeLong(long value)
}
}

// Apply any configured transforms/cleaning to the name of the supplied entry.
private string TransformEntryName(ZipEntry entry)
{
string transformedName = entry.Name;

if (this.NameTransform != null)
{
if (entry.IsDirectory)
{
transformedName = this.NameTransform.TransformDirectory(entry.Name);
}
else
{
transformedName = this.NameTransform.TransformFile(entry.Name);
}
}

return transformedName;
}

// Convert an entry name to a byte array, and apply any transforms
private byte[] TransformEntryNameToArray(ZipEntry entry)
{
string transformedName = TransformEntryName(entry);
byte[] name = ZipStrings.ConvertToArray(entry.Flags, transformedName);
return name;
}

/// <summary>
/// Starts a new Zip entry. It automatically closes the previous
/// entry if present.
Expand Down Expand Up @@ -367,7 +402,7 @@ public void PutNextEntry(ZipEntry entry)
}
}

byte[] name = ZipStrings.ConvertToArray(entry.Flags, entry.Name);
byte[] name = TransformEntryNameToArray(entry);

if (name.Length > 0xFFFF)
{
Expand Down Expand Up @@ -787,7 +822,7 @@ public override void Finish()
WriteLeInt((int)entry.Size);
}

byte[] name = ZipStrings.ConvertToArray(entry.Flags, entry.Name);
byte[] name = TransformEntryNameToArray(entry);

if (name.Length > 0xffff)
{
Expand Down

0 comments on commit ab9fbc7

Please sign in to comment.