Skip to content

Instantly share code, notes, and snippets.

@snovvcrash
Last active March 15, 2024 21:42
Show Gist options
  • Save snovvcrash/c39a46f67fc987c94c227817b6155ab9 to your computer and use it in GitHub Desktop.
Save snovvcrash/c39a46f67fc987c94c227817b6155ab9 to your computer and use it in GitHub Desktop.
Self-contained 7-Zip wrapper using SevenZipSharp & Costura.Fody
using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using SevenZip;
using CommandLine;
using CommandLine.Text;
namespace Sharp7Zip
{
public enum OutputFormat
{
Auto,
SevenZip,
Zip,
GZip,
BZip2,
Tar,
XZ
}
public class Compress7Zip
{
public string ArchiveFileName { get; set; }
public string Path { get; set; }
public string Filter { get; set; }
public string OutputPath { get; set; }
public OutputFormat Format { get; set; } = OutputFormat.Auto;
public CompressionLevel CompressionLevel { get; set; } = CompressionLevel.Normal;
public CompressionMethod CompressionMethod { get; set; } = CompressionMethod.Default;
public string Password { get; set; }
public Action<SevenZipCompressor> CustomInitialization { get; set; }
public string TempFolder { get; set; }
public bool EncryptFilenames { get; set; }
public bool FlattenDirectoryStructure { get; set; }
public int VolumeSize { get; set; }
public bool SkipEmptyDirectories { get; set; }
public bool PreserveDirectoryRoot { get; set; }
public bool DisableRecursion { get; set; }
public bool Append { get; set; }
private List<string> _directoryOrFilesFromPipeline;
private OutArchiveFormat _inferredOutArchiveFormat;
public void Compress()
{
_inferredOutArchiveFormat = GetInferredOutArchiveFormat();
if (EncryptFilenames)
{
if (_inferredOutArchiveFormat != OutArchiveFormat.SevenZip)
{
throw new ArgumentException("Encrypting filenames is supported for 7z format only.");
}
if (string.IsNullOrEmpty(Password))
{
throw new ArgumentException("Encrypting filenames is supported only when using a password.");
}
}
if (_directoryOrFilesFromPipeline == null)
{
_directoryOrFilesFromPipeline = new List<string>();
}
_directoryOrFilesFromPipeline.Add(Path);
var compressor = new SevenZipCompressor
{
ArchiveFormat = _inferredOutArchiveFormat,
CompressionLevel = CompressionLevel,
CompressionMethod = CompressionMethod,
VolumeSize = VolumeSize,
EncryptHeaders = EncryptFilenames,
DirectoryStructure = !FlattenDirectoryStructure,
IncludeEmptyDirectories = !SkipEmptyDirectories,
PreserveDirectoryRoot = PreserveDirectoryRoot,
CompressionMode = Append ? CompressionMode.Append : CompressionMode.Create
};
if (!string.IsNullOrEmpty(TempFolder))
{
compressor.TempFolderPath = TempFolder;
}
CustomInitialization?.Invoke(compressor);
if (_directoryOrFilesFromPipeline == null)
{
_directoryOrFilesFromPipeline = new List<string> { Path };
}
var outputPathIsNotEmptyOrNull = !string.IsNullOrEmpty(OutputPath);
var outputPath = outputPathIsNotEmptyOrNull
? OutputPath
: Environment.CurrentDirectory;
var archiveDirectory = System.IO.Path.GetDirectoryName(ArchiveFileName);
if (!string.IsNullOrEmpty(archiveDirectory) && !outputPathIsNotEmptyOrNull)
{
if (System.IO.Path.IsPathRooted(archiveDirectory))
outputPath = archiveDirectory;
else
outputPath = System.IO.Path.Combine(outputPath, archiveDirectory);
}
if (File.Exists(outputPath))
{
throw new ArgumentException("The output path is a file, not a directory");
}
if (!Directory.Exists(outputPath))
{
Directory.CreateDirectory(outputPath);
}
var directoryOrFiles = _directoryOrFilesFromPipeline
.Select(System.IO.Path.GetFullPath).ToArray();
var archiveFileName = System.IO.Path.GetFullPath(System.IO.Path.Combine(outputPath, System.IO.Path.GetFileName(ArchiveFileName)));
if (directoryOrFiles.Any(path => new FileInfo(path).Exists))
{
var notFoundFiles = directoryOrFiles.Where(path => !new FileInfo(path).Exists).ToArray();
if (notFoundFiles.Any())
{
throw new FileNotFoundException("File(s) not found: " + string.Join(", ", notFoundFiles));
}
if (!string.IsNullOrEmpty(Password))
{
compressor.CompressFilesEncrypted(archiveFileName, Password, directoryOrFiles);
}
else
{
compressor.CompressFiles(archiveFileName, directoryOrFiles);
}
}
else if (directoryOrFiles.Any(path => new DirectoryInfo(path).Exists))
{
if (directoryOrFiles.Length > 1)
{
throw new ArgumentException("Only one directory allowed as input");
}
var recursion = !DisableRecursion;
var filter = string.IsNullOrWhiteSpace(Filter) ? "*" : Filter;
if (!string.IsNullOrEmpty(Password))
{
compressor.CompressDirectory(directoryOrFiles[0], archiveFileName, Password, filter, recursion);
}
else
{
compressor.CompressDirectory(directoryOrFiles[0], archiveFileName, null, filter, recursion);
}
}
Console.WriteLine("[+] Compression completed successfully!");
}
private OutArchiveFormat GetInferredOutArchiveFormat()
{
switch (Format)
{
case OutputFormat.Auto:
switch (System.IO.Path.GetExtension(ArchiveFileName).ToLowerInvariant())
{
case ".zip":
return OutArchiveFormat.Zip;
case ".gz":
return OutArchiveFormat.GZip;
case ".bz2":
return OutArchiveFormat.BZip2;
case ".tar":
return OutArchiveFormat.Tar;
case ".xz":
return OutArchiveFormat.XZ;
default:
return OutArchiveFormat.SevenZip;
}
case OutputFormat.SevenZip:
return OutArchiveFormat.SevenZip;
case OutputFormat.Zip:
return OutArchiveFormat.Zip;
case OutputFormat.GZip:
return OutArchiveFormat.GZip;
case OutputFormat.BZip2:
return OutArchiveFormat.BZip2;
case OutputFormat.Tar:
return OutArchiveFormat.Tar;
case OutputFormat.XZ:
return OutArchiveFormat.XZ;
default:
throw new ArgumentOutOfRangeException();
}
}
}
public class Options
{
[Option('a', "archive", Required = true, HelpText = "The full file name of the archive")]
public string ArchiveFileName { get; set; }
[Option('p', "path", Required = true, HelpText = "The source folder or file")]
public string Path { get; set; }
[Option('f', "filter", HelpText = "The filter to be applied if Path points to a directory")]
public string Filter { get; set; }
[Option('o', "output", HelpText = "Output path for a compressed archive")]
public string OutputPath { get; set; }
[Option('m', "method", Default = OutputFormat.Auto, HelpText = "The compression method (Auto, SevenZip, Zip, GZip, BZip2, Tar, XZ)")]
public OutputFormat Format { get; set; }
[Option('l', "level", Default = CompressionLevel.Normal, HelpText = "The compression level")]
public CompressionLevel CompressionLevel { get; set; }
[Option('e', "encrypt", HelpText = "Enables encrypting filenames when using the 7z format")]
public bool EncryptFilenames { get; set; }
[Option('s', "skip-empty", HelpText = "Disables preservation of empty directories")]
public bool SkipEmptyDirectories { get; set; }
[Option('r', "preserve-root", HelpText = "Preserves directory root")]
public bool PreserveDirectoryRoot { get; set; }
[Option('d', "disable-recursion", HelpText = "Disables recursive files search")]
public bool DisableRecursion { get; set; }
[Option('u', "append", HelpText = "Append files to existing archive")]
public bool Append { get; set; }
[Option('t', "temp", HelpText = "The temporary folder path; if not specified, %TEMP% will be used")]
public string TempFolder { get; set; }
[Option('v', "volume-size", Default = 0, HelpText = "Specifies the volume sizes in bytes, 0 for no volumes")]
public int VolumeSize { get; set; }
[Option('w', "password", HelpText = "The password to use for encryption")]
public string Password { get; set; }
[Option('h', "help", HelpText = "Display this help screen")]
public bool ShowHelp { get; set; }
}
internal static class Program
{
static void DisplayHelp<T>(ParserResult<T> result, IEnumerable<Error> errors)
{
var helpText = HelpText.AutoBuild(
result,
h =>
{
h.AdditionalNewLineAfterOption = false;
h.Heading = "";
h.AutoVersion = false;
h.MaximumDisplayWidth = 120;
h.Copyright = "";
return HelpText.DefaultParsingErrorsHandler(result, h);
},
e => e);
Console.WriteLine(helpText);
}
private static void Main(string[] args)
{
// workaround for issue #75: https://github.com/Fody/Costura/issues/75
var dllName = typeof(ArchiveFileInfo).Assembly.GetFile("sevenzipsharp.dll").Name;
var cosuraPath = Path.GetDirectoryName(dllName);
Console.WriteLine($"[!] Temporary assemblies path: {cosuraPath}");
SevenZipBase.SetLibraryPath(Path.Combine(cosuraPath, @"64\7za.dll"));
var parser = new Parser(with => with.HelpWriter = null);
var parserResult = parser.ParseArguments<Options>(args);
try
{
parserResult
.WithParsed(options =>
new Compress7Zip
{
ArchiveFileName = options.ArchiveFileName,
Path = options.Path,
Filter = options.Filter,
OutputPath = options.OutputPath,
Format = options.Format,
CompressionLevel = options.CompressionLevel,
EncryptFilenames = options.EncryptFilenames,
SkipEmptyDirectories = options.SkipEmptyDirectories,
PreserveDirectoryRoot = options.PreserveDirectoryRoot,
DisableRecursion = options.DisableRecursion,
Append = options.Append,
TempFolder = options.TempFolder,
VolumeSize = options.VolumeSize,
Password = options.Password
}.Compress())
.WithNotParsed(errs => DisplayHelp(parserResult, errs));
}
catch (Exception e)
{
Console.WriteLine($"[-] {e.Message}");
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment