list command
This commit is contained in:
@@ -1,5 +1,7 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
|
// TODO TODO
|
||||||
|
|
||||||
// Although this function looks imperative, note that its job is to
|
// Although this function looks imperative, note that its job is to
|
||||||
// declaratively construct a build graph that will be executed by an external
|
// declaratively construct a build graph that will be executed by an external
|
||||||
// runner.
|
// runner.
|
||||||
|
|||||||
168
src/main.zig
168
src/main.zig
@@ -1,32 +1,75 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const info = std.log.info;
|
const info = std.log.info;
|
||||||
|
const debug = std.log.debug;
|
||||||
const print = std.debug.print;
|
const print = std.debug.print;
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
|
const testing = std.testing;
|
||||||
|
const expect = testing.expect;
|
||||||
|
|
||||||
// otpauth://totp/AWS+Dev?secret=47STA47VFCMMLLWOLHWO3KY7MYNC36MLCDTHOLIYKJCTTSSAMKVM7YA3VWT2AJEP&digits=6&icon=Amazon
|
// otpauth://totp/AWS+Dev?secret=47STA47VFCMMLLWOLHWO3KY7MYNC36MLCDTHOLIYKJCTTSSAMKVM7YA3VWT2AJEP&digits=6&icon=Amazon
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
const allocator = gpa.allocator();
|
const allocator = gpa.allocator();
|
||||||
std.log.info("\n\nstart\n", .{});
|
|
||||||
|
|
||||||
const args = try std.process.argsAlloc(allocator);
|
const args = try std.process.argsAlloc(allocator);
|
||||||
|
|
||||||
std.debug.print("arguments: {s}\n", .{args});
|
std.log.debug("arguments: {s}\n", .{args});
|
||||||
const x: Args = try parseArgs(args);
|
const arg: Args = try parseArgs(allocator, args);
|
||||||
print("parsed Args: {?any}\n", x);
|
//print("parsed Args: {?any}\n", arg);
|
||||||
if (x.add != null) {
|
if (arg.add != null) {
|
||||||
info("add:", .{});
|
info("add:", .{});
|
||||||
info("name: {s}", .{x.add.?.name});
|
info("name: {s}", .{arg.add.?.name});
|
||||||
info("secret: {s}", .{x.add.?.secretEncoded});
|
info("secret: {s}", .{arg.add.?.secretEncoded});
|
||||||
|
|
||||||
const config_location = try configLocation(allocator);
|
const config_location = try configLocation(allocator);
|
||||||
info("config location: {s}", .{config_location});
|
info("config location: {s}", .{config_location});
|
||||||
|
|
||||||
|
try readConfig(allocator);
|
||||||
|
} else if (arg.list) {
|
||||||
|
const names = try executeGetList(arg.config_location);
|
||||||
|
|
||||||
|
for (0..names.items.len) |i| {
|
||||||
|
std.debug.print("{s}\n", .{names.items[i]});
|
||||||
|
}
|
||||||
|
|
||||||
|
//for (names, 0..) |name, index| {
|
||||||
|
// std.debug.print("{d} {s}", .{ index, name });
|
||||||
|
//}
|
||||||
|
} else {
|
||||||
|
printHelp();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//std.process.exit(returnValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn readConfig(allocator: Allocator) !void {
|
||||||
|
const config_location = try configLocation(allocator);
|
||||||
|
|
||||||
|
var file = try std.fs.cwd().createFile(config_location, .{ .exclusive = true });
|
||||||
|
defer file.close();
|
||||||
|
|
||||||
|
const bytes_read = try file.readToEndAlloc(allocator, 64 * 1024 * 1024); // read at most 64MB
|
||||||
|
info("config content: {s}", .{bytes_read});
|
||||||
|
|
||||||
|
//const content = try std.fs.openFileAbsolute(config_location, .{ .mode = .read_only });
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fn printHelp() void {
|
||||||
|
const msg =
|
||||||
|
\\Usage: zig-totp [options]
|
||||||
|
\\
|
||||||
|
\\Options:
|
||||||
|
\\ --add otpauthUrl Add a new TOTP thingy with an otpauth URL.
|
||||||
|
;
|
||||||
|
std.debug.print("{s}", .{msg});
|
||||||
}
|
}
|
||||||
|
|
||||||
const Args = struct {
|
const Args = struct {
|
||||||
add: ?OtpAuthUrl,
|
add: ?OtpAuthUrl,
|
||||||
|
list: bool,
|
||||||
|
config_location: []const u8,
|
||||||
};
|
};
|
||||||
|
|
||||||
const OtpAuthUrl = struct {
|
const OtpAuthUrl = struct {
|
||||||
@@ -35,11 +78,13 @@ const OtpAuthUrl = struct {
|
|||||||
url: []const u8,
|
url: []const u8,
|
||||||
};
|
};
|
||||||
|
|
||||||
const ArgumentError = error{InvalidOtpAuthUrl};
|
const ArgumentError = error{ InvalidOtpAuthUrl, UnknownParameter, MissingConfigLocation };
|
||||||
|
|
||||||
fn parseArgs(args: [][:0]u8) !Args {
|
fn parseArgs(allocator: Allocator, args: []const []const u8) !Args {
|
||||||
var result = Args{
|
var result = Args{
|
||||||
.add = null,
|
.add = null,
|
||||||
|
.list = false,
|
||||||
|
.config_location = try configLocation(allocator),
|
||||||
};
|
};
|
||||||
var i: u17 = 1;
|
var i: u17 = 1;
|
||||||
while (i < args.len) : (i += 1) {
|
while (i < args.len) : (i += 1) {
|
||||||
@@ -51,7 +96,7 @@ fn parseArgs(args: [][:0]u8) !Args {
|
|||||||
return error.InvalidOtpAuthUrl;
|
return error.InvalidOtpAuthUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
const otpauthUrlCandidate: ?[:0]const u8 = args[i];
|
const otpauthUrlCandidate: ?[]const u8 = args[i];
|
||||||
|
|
||||||
if (otpauthUrlCandidate == null) {
|
if (otpauthUrlCandidate == null) {
|
||||||
return error.InvalidOtpAuthUrl;
|
return error.InvalidOtpAuthUrl;
|
||||||
@@ -79,31 +124,120 @@ fn parseArgs(args: [][:0]u8) !Args {
|
|||||||
|
|
||||||
//std.debug.print("add: {?s}\n", .{otpauthUrlCandidate});
|
//std.debug.print("add: {?s}\n", .{otpauthUrlCandidate});
|
||||||
//std.log.info("add: {?s}\n", .{otpauthUrlCandidate});
|
//std.log.info("add: {?s}\n", .{otpauthUrlCandidate});
|
||||||
|
} else if (std.mem.eql(u8, "--config", arg)) {
|
||||||
|
i += 1;
|
||||||
|
if (i >= args.len) {
|
||||||
|
return error.MissingConfigLocation;
|
||||||
|
}
|
||||||
|
const config_location = args[i];
|
||||||
|
result.config_location = config_location;
|
||||||
|
} else if (std.mem.eql(u8, "list", arg)) {
|
||||||
|
result.list = true;
|
||||||
} else {
|
} else {
|
||||||
std.debug.print("unknown parameter: {s}\n", .{arg});
|
std.debug.print("unknown parameter: {s}\n", .{arg});
|
||||||
std.process.exit(1);
|
return ArgumentError.UnknownParameter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn configLocation(allocator: Allocator) Allocator.Error![]const u8 {
|
fn parseOtpAuthUrl(url: []const u8) !OtpAuthUrl {
|
||||||
|
//std.debug.print("parsing >{s}<\n", .{url});
|
||||||
|
const index = std.mem.indexOf(u8, url, "otpauth://totp/");
|
||||||
|
if (index != 0) {
|
||||||
|
return error.InvalidOtpAuthUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
var it = std.mem.splitSequence(u8, url[15..], "?secret=");
|
||||||
|
|
||||||
|
const name = it.next();
|
||||||
|
const secret = it.next();
|
||||||
|
const empty = it.next();
|
||||||
|
|
||||||
|
if (name != null and secret != null and empty == null) {
|
||||||
|
return OtpAuthUrl{ .name = name.?, .secretEncoded = secret.?, .url = url };
|
||||||
|
} else {
|
||||||
|
return ArgumentError.InvalidOtpAuthUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn configLocation(allocator: Allocator) ![]const u8 {
|
||||||
const xdg_config_home: ?[]const u8 = std.process.getEnvVarOwned(allocator, "XDG_CONFIG_HOME") catch null;
|
const xdg_config_home: ?[]const u8 = std.process.getEnvVarOwned(allocator, "XDG_CONFIG_HOME") catch null;
|
||||||
|
|
||||||
if (xdg_config_home) |base| {
|
if (xdg_config_home) |base| {
|
||||||
const config_location = try std.mem.concat(allocator, u8, &[_][]const u8{ base, "/zig-totp" });
|
const config_location = try std.mem.concat(allocator, u8, &[_][]const u8{ base, "/zig-totp" });
|
||||||
info("config_location: {s}", .{config_location});
|
debug("config_location: {s}", .{config_location});
|
||||||
return config_location;
|
return config_location;
|
||||||
}
|
}
|
||||||
|
|
||||||
const home = std.process.getEnvVarOwned(allocator, "HOME") catch null;
|
const home = std.process.getEnvVarOwned(allocator, "HOME") catch null;
|
||||||
const base = home orelse unreachable;
|
const base = home orelse unreachable;
|
||||||
const config_location = try std.mem.concat(allocator, u8, &[_][]const u8{ base, "/.zig-totp" });
|
const config_location = try std.mem.concat(allocator, u8, &[_][]const u8{ base, "/.zig-totp" });
|
||||||
info("config_location: {s}", .{config_location});
|
|
||||||
|
debug("config_location: {s}", .{config_location});
|
||||||
return config_location;
|
return config_location;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exit(returnValue: u8, message: []const u8) void {
|
fn executeGetList(config_location: []const u8) !std.ArrayList([]const u8) {
|
||||||
std.log.warn("{s}", .{message});
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
std.process.exit(returnValue);
|
const allocator = gpa.allocator();
|
||||||
|
|
||||||
|
const file = try std.fs.cwd().openFile(config_location, .{});
|
||||||
|
defer file.close();
|
||||||
|
|
||||||
|
var buf_reader = std.io.bufferedReader(file.reader());
|
||||||
|
var in_stream = buf_reader.reader();
|
||||||
|
|
||||||
|
//_ = try in_stream.readUntilDelimiterOrEofAlloc(allocator, '\n', 1024 * 10);
|
||||||
|
//std.debug.print("line: {s}", .{line1});
|
||||||
|
//
|
||||||
|
//TODO print line
|
||||||
|
//TODO read all lines
|
||||||
|
//TODO parse the names
|
||||||
|
//TODO return list of names
|
||||||
|
//
|
||||||
|
|
||||||
|
var names = std.ArrayList([]const u8).init(allocator);
|
||||||
|
|
||||||
|
while (try in_stream.readUntilDelimiterOrEofAlloc(allocator, '\n', 1024 * 1024)) |line| {
|
||||||
|
//std.debug.print("line: {any}", .{line});
|
||||||
|
if (line.len > 0 and std.mem.trim(u8, line, " \r").len > 0) {
|
||||||
|
const url = try parseOtpAuthUrl(line);
|
||||||
|
try names.append(url.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
|
test "bla" {
|
||||||
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
|
const allocator = gpa.allocator();
|
||||||
|
const err = parseArgs(allocator, &[_][]const u8{ "path/of/executable", "--add", "not a totp url" });
|
||||||
|
|
||||||
|
try expect(err == ArgumentError.InvalidOtpAuthUrl);
|
||||||
|
//try expect(err == Allocator.Error.OutOfMemory);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "parse command line parameter: 'list'" {
|
||||||
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
|
const allocator = gpa.allocator();
|
||||||
|
|
||||||
|
const arg = try parseArgs(allocator, &[_][]const u8{ "path/of/executable", "list" });
|
||||||
|
try expect(arg.list);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "read list of entries" {
|
||||||
|
try std.fs.cwd().makeDir("test-tmp");
|
||||||
|
const file = try std.fs.cwd().createFile("test-tmp/zig-totp", .{ .read = true });
|
||||||
|
defer {
|
||||||
|
file.close();
|
||||||
|
std.fs.cwd().deleteTree("test-tmp") catch unreachable;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = try file.write("otpauth://totp/token1?secret=c2VjcmV0Cg==\notpauth://totp/token2?secret=c2VjcmV0Cg==\n");
|
||||||
|
|
||||||
|
const list: std.ArrayList([]const u8) = try executeGetList("test-tmp/zig-totp");
|
||||||
|
try std.testing.expectEqualStrings("token1", list.items[0]);
|
||||||
|
try std.testing.expectEqualStrings("token2", list.items[1]);
|
||||||
|
try std.testing.expectEqual(2, list.items.len);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user