Skip to content

Commit 9b39647

Browse files
committed
✨ Add option to set original file name when encrypting a file #84
1 parent ae7c1e4 commit 9b39647

File tree

5 files changed

+70
-8
lines changed

5 files changed

+70
-8
lines changed

src/Xecrets.Cli/Operation/EncryptToOperation.cs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,21 +56,31 @@ public Task<Status> DryAsync(Parameters parameters)
5656
IStandardIoDataStore fromStore = New<IStandardIoDataStore>(parameters.Arg1);
5757
if (fromStore.IsStdIo && !fromStore.IsNamedStdIo)
5858
{
59-
return Task.FromResult(new Status(XfStatusCode.InvalidOption, "Encryption is not supported from an unnamed standard input stream."));
59+
return Task.FromResult(new Status(XfStatusCode.InvalidOption,
60+
"Encryption is not supported from an unnamed standard input stream."));
6061
}
6162

6263
if (!New<IFileVerify>().CanReadFromFile(fromStore))
6364
{
64-
return Task.FromResult(new Status(XfStatusCode.CannotRead, "Can't read from '{0}'.".Format(fromStore.Name)));
65+
return Task.FromResult(new Status(XfStatusCode.CannotRead,
66+
"Can't read from '{0}'.".Format(fromStore.Name)));
6567
}
6668
if (!fromStore.IsEncryptable)
6769
{
68-
return Task.FromResult(new Status(XfStatusCode.FileUnavailable, "Encryption of '{0}' is not supported, it may be a system file or hidden.".Format(parameters.CurrentOp.Arg1)));
70+
return Task.FromResult(new Status(XfStatusCode.FileUnavailable,
71+
"Encryption of '{0}' is not supported, it may be a system file or hidden.".Format(parameters.CurrentOp.Arg1)));
72+
}
73+
if (fromStore.IsNamedStdIo && parameters.Arg3.Length > 0)
74+
{
75+
return Task.FromResult(new Status(XfStatusCode.InvalidOption,
76+
$"Cannot specify both original name '{parameters.Arg3}' and stdin alias '{fromStore.AliasName}'."));
6977
}
7078

7179
if (parameters.ProgrammaticUse && FileLargerThanLicenseLimit(fromStore))
7280
{
73-
return Task.FromResult(new Status(XfStatusCode.Unlicensed, "'{0}' is too large for encryption. When using options for programmatic use, a valid maintenance subscription is required for files > 1 MB, or use a GPL build.".Format(parameters.CurrentOp.Arg1)));
81+
return Task.FromResult(new Status(XfStatusCode.Unlicensed,
82+
"'{0}' is too large for encryption. When using options for programmatic use, a valid maintenance " +
83+
"subscription is required for files > 1 MB, or use a GPL build.".Format(parameters.CurrentOp.Arg1)));
7484
}
7585

7686
parameters.TotalsTracker.AddWorkItem(fromStore.Length());
@@ -121,7 +131,8 @@ public Task<Status> RealAsync(Parameters parameters)
121131
{
122132
toFreeStore = new AsciiArmorDataStore(toFreeStore);
123133
}
124-
encryption.EncryptTo(toFreeStore, fromStore.AliasName,
134+
string originalName = parameters.Arg3.Length > 0 ? parameters.Arg3 : fromStore.AliasName;
135+
encryption.EncryptTo(toFreeStore, originalName,
125136
parameters.Compress ? AxCryptOptions.EncryptWithCompression : AxCryptOptions.EncryptWithoutCompression);
126137
}
127138

src/Xecrets.Cli/OptionsParser.cs

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@ private ExportableOptionCollection BuildOptionSet(List<ParsedOp> parsed, List<st
5656
"{encrypted} {clear}:Decrypt a file to the given file path.",
5757
(ora, op, from, to) => ora.Add(op, from, to) },
5858
{"e|encrypt-to={}", XfOpCode.EncryptTo,
59-
"{clear} {encrypted}:Encrypt a file to the given file path.",
60-
(ora, op, from, to) => ora.Add(op, from, to) },
59+
"{clear} {encrypted} [{original}]:Encrypt a file to given file path w/optional original name.",
60+
(ora, op, from, to) => ora.AddOneRunning(op, from, to) },
6161
{"f|file=", XfOpCode.OptionsFromFile,
6262
"{name}:Take options from a file (programmatic).",
6363
(ora, op, name) => { ora.Add(op); RecursivelyParseFromFile(name, parsed, extra); } },
@@ -276,13 +276,26 @@ public void Add(XfOpCode opCode, string p1, string p2)
276276
RunningAction = _noop;
277277
}
278278

279+
public void Add(XfOpCode opCode, string p1, string p2, string p3)
280+
{
281+
_parsed.Add(new ParsedOp(opCode, p1, p2, p3));
282+
RunningAction = _noop;
283+
}
284+
279285
public void AddOneRunning(XfOpCode opCode, string first)
280286
{
281287
var op = new ParsedOp(opCode, first);
282288
_parsed.Add(op);
283289
RunningAction = (s) => { op.Arguments.Add(s); RunningAction = _noop; };
284290
}
285291

292+
public void AddOneRunning(XfOpCode opCode, string first, string second)
293+
{
294+
ParsedOp op = new(opCode, first, second);
295+
_parsed.Add(op);
296+
RunningAction = (s) => { op.Arguments.Add(s); RunningAction = _noop; };
297+
}
298+
286299
public void AddManyRunning(XfOpCode opCode, string first)
287300
{
288301
var op = new ParsedOp(opCode, first);
@@ -291,6 +304,17 @@ public void AddManyRunning(XfOpCode opCode, string first)
291304
}
292305
}
293306

307+
private sealed class MyActionOption(string prototype, string description, int count,
308+
Action<OptionValueCollection> action) : OptionBase(prototype, description, count)
309+
{
310+
protected override void OnParseComplete(OptionContext c)
311+
{
312+
ArgumentNullException.ThrowIfNull(c);
313+
314+
action(c.OptionValues);
315+
}
316+
}
317+
294318
private class ExportableOptionCollection(Version cliVersion, RunningArguments ora) : OptionSetCollection
295319
{
296320
/// <summary>
@@ -331,6 +355,21 @@ public void Add(string prototype, XfOpCode opCode, string description, Action<Ru
331355
_ = Add(prototype, description, (p1, p2) => action(ora, opCode, p1, p2));
332356
Export.Add(new ExportableOption((int)opCode, prototype, string.Join(':', (description?.Split(':').Skip(1).ToArray() ?? []))));
333357
}
358+
359+
public void Add(string prototype, string description, Action<string, string, string> action)
360+
{
361+
ArgumentNullException.ThrowIfNull(action);
362+
363+
OptionBase p = new MyActionOption(prototype, description, 3,
364+
delegate (OptionValueCollection v) { action(v[0], v[1], v[2]); });
365+
Add(p);
366+
}
367+
368+
public void Add(string prototype, XfOpCode opCode, string description, Action<RunningArguments, XfOpCode, string, string, string> action)
369+
{
370+
Add(prototype, description, (p1, p2, p3) => action(ora, opCode, p1, p2, p3));
371+
Export.Add(new ExportableOption((int)opCode, prototype, string.Join(':', (description?.Split(':').Skip(1).ToArray() ?? []))));
372+
}
334373
}
335374

336375
private void RecursivleyParseArguments(IEnumerable<string> args, List<ParsedOp> parsedOps, List<string> extra)

src/Xecrets.Cli/ParsedOp.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,6 @@ public ParsedOp(XfOpCode opCode, bool flag)
4444
public string Arg1 { get { return Arguments.Count > 0 ? Arguments[0] : string.Empty; } }
4545

4646
public string Arg2 { get { return Arguments.Count > 1 ? Arguments[1] : string.Empty; } }
47+
48+
public string Arg3 { get { return Arguments.Count > 2 ? Arguments[2] : string.Empty; } }
4749
}

src/Xecrets.Cli/Public/XfExportVersion.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,13 @@ namespace Xecrets.Cli.Public;
3737
/// </summary>
3838
public static class XfExportVersion
3939
{
40-
public static Version CliVersion => new(2, 1);
40+
/// <summary>
41+
/// The version of the Xecrets Cli API.
42+
/// </summary>
43+
/// <remarks>
44+
/// 2.0 -> 2024-09-26 Change parameter names to Arg1 and Arg2 in JSON logger
45+
/// 2.1 -> 2024-10-01 Add --sigint posix signal handling
46+
/// 2.2 -> 2024-10-09 Add third optional argument to --encrypt-to for original name
47+
/// </remarks>
48+
public static Version CliVersion => new(2, 2);
4149
}

src/Xecrets.Cli/Run/Parameters.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ internal class Parameters(OptionsParser parser) : IDisposable
6565

6666
public string Arg2 => CurrentOp.Arg2;
6767

68+
public string Arg3 => CurrentOp.Arg3;
69+
6870
public KnownPublicKeys LoadedPublicKeys { get; } = new KnownPublicKeys();
6971

7072
public OptionsParser Parser { get; } = parser;

0 commit comments

Comments
 (0)