Something went wrong. Try again.
various tools I have been using throughout the years
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376const std = @import("std");const builtin = @import("builtin");
const Entry = struct { path: []const u8, accessed: i128,};
pub fn main(init: std.process.Init) !void { const alloc = init.gpa;
const pass_dir = try dir(alloc, init.environ_map); defer alloc.free(pass_dir);
var passwords = std.ArrayListUnmanaged(Entry).empty; defer { for (passwords.items) |p| alloc.free(p.path); passwords.deinit(alloc); }
try collect(alloc, init.io, pass_dir, "", &passwords);
std.mem.sort(Entry, passwords.items, {}, struct { fn lessThan(_: void, a: Entry, b: Entry) bool { return a.accessed > b.accessed; } }.lessThan);
var paths = std.ArrayListUnmanaged([]const u8).empty; defer paths.deinit(alloc); for (passwords.items) |entry| { try paths.append(alloc, entry.path); }
const pick = try fzf(init, alloc, paths.items) orelse return; defer pick.deinit(alloc);
if (std.mem.eql(u8, pick.key, "ctrl-e")) { const target = if (pick.selected.len > 0) pick.selected else pick.query; if (target.len == 0) return; try edit(init, target); return; }
const selected = if (pick.selected.len > 0) pick.selected else pick.query; if (selected.len == 0) return;
const exists = blk: { for (passwords.items) |item| { if (std.mem.eql(u8, item.path, selected)) break :blk true; } break :blk false; }; const basename_start = if (std.mem.lastIndexOfScalar(u8, selected, '/')) |idx| idx + 1 else 0; const has_otp = std.mem.find(u8, selected[basename_start..], "otp") != null;
const secret = if (has_otp and exists) try run(init, alloc, &.{ "pass", "otp", "code", selected }) else if (!exists) try store(init, alloc, selected) else run(init, alloc, &.{ "pass", "show", selected }) catch try store(init, alloc, selected);
try clip(init, secret); alloc.free(secret);}
fn dir(alloc: std.mem.Allocator, environ: *std.process.Environ.Map) ![]const u8 { if (environ.get("PASSWORD_STORE_DIR")) |p| { return try alloc.dupe(u8, p); }
const home = environ.get("HOME") orelse return error.NoHome; return try std.fs.path.join(alloc, &.{ home, ".password-store" });}
fn collect(alloc: std.mem.Allocator, io: std.Io, base_dir: []const u8, sub_path: []const u8, list: *std.ArrayListUnmanaged(Entry)) anyerror!void { const full_path = try std.fs.path.join(alloc, &.{ base_dir, sub_path }); defer alloc.free(full_path);
var d = std.Io.Dir.openDirAbsolute(io, full_path, .{ .iterate = true }) catch return; defer d.close(io);
var it = d.iterate(); while (try it.next(io)) |entry| { if (entry.name.len > 0 and entry.name[0] == '.') continue;
const entry_sub_path = if (sub_path.len == 0) try alloc.dupe(u8, entry.name) else try std.fs.path.join(alloc, &.{ sub_path, entry.name }); errdefer alloc.free(entry_sub_path);
if (entry.kind == .directory) { try collect(alloc, io, base_dir, entry_sub_path, list); alloc.free(entry_sub_path); } else if (entry.kind == .file and std.mem.endsWith(u8, entry.name, ".gpg")) { const name = entry_sub_path[0 .. entry_sub_path.len - 4]; const file_path = try std.fs.path.join(alloc, &.{ base_dir, entry_sub_path }); defer alloc.free(file_path);
const accessed = blk: { const file = std.Io.Dir.openFileAbsolute(io, file_path, .{}) catch |err| { std.log.warn("failed to open {s}: {}", .{ file_path, err }); break :blk @as(i128, 0); }; defer file.close(io);
const file_stat = file.stat(io) catch |err| { std.log.warn("failed to stat {s}: {}", .{ file_path, err }); break :blk @as(i128, 0); };
const atime_ts = file_stat.atime orelse break :blk @as(i128, 0); break :blk @as(i128, @intCast(atime_ts.nanoseconds)); };
try list.append(alloc, Entry{ .path = try alloc.dupe(u8, name), .accessed = accessed, }); alloc.free(entry_sub_path); } else { alloc.free(entry_sub_path); } }}
const Pick = struct { query: []const u8, key: []const u8, selected: []const u8, raw: []u8,
fn deinit(self: Pick, alloc: std.mem.Allocator) void { alloc.free(self.raw); }};
const PickParts = struct { query: []const u8, key: []const u8, selected: []const u8,};
fn parseFzfOutput(output: []const u8) ?PickParts { var lines = [3][]const u8{ "", "", "" }; var it = std.mem.splitScalar(u8, output, '\n'); var i: usize = 0; while (i < lines.len) : (i += 1) { const line = it.next() orelse break; lines[i] = std.mem.trim(u8, line, " \n\r\t"); }
if (lines[0].len == 0 and lines[1].len == 0 and lines[2].len == 0) return null;
return .{ .query = lines[0], .key = lines[1], .selected = lines[2] };}
fn fzf(init: std.process.Init, alloc: std.mem.Allocator, items: []const []const u8) !?Pick { var child = try std.process.spawn(init.io, .{ .argv = &.{ "fzf", "--exact", "--print-query", "--expect=ctrl-e" }, .stdin = .pipe, .stdout = .pipe, .stderr = .inherit, });
if (child.stdin) |stdin| { var buf: [4096]u8 = undefined; var fw = stdin.writer(init.io, &buf); for (items) |item| { try fw.interface.writeAll(item); try fw.interface.writeAll("\n"); } try fw.flush(); stdin.close(init.io); child.stdin = null; }
const output = try read(init.io, alloc, &child); errdefer alloc.free(output);
const term = try child.wait(init.io); if (term != .exited) { alloc.free(output); return null; }
const parts = parseFzfOutput(output) orelse { alloc.free(output); return null; };
return .{ .query = parts.query, .key = parts.key, .selected = parts.selected, .raw = output, };}
fn edit(init: std.process.Init, target: []const u8) !void { var child = try std.process.spawn(init.io, .{ .argv = &.{ "pass", "edit", target }, .stdin = .inherit, .stdout = .inherit, .stderr = .inherit, });
const term = try child.wait(init.io); // `pass edit` exits non-zero with "Password unchanged." when the editor // saved no changes. That is not a failure here. if (term != .exited) return error.FailedExit;}
test "parseFzfOutput query only" { const parts = parseFzfOutput("foo\n") orelse return error.TestUnexpectedResult; try std.testing.expectEqualStrings("foo", parts.query); try std.testing.expectEqualStrings("", parts.key); try std.testing.expectEqualStrings("", parts.selected);}
test "parseFzfOutput query key selection" { const parts = parseFzfOutput("foo\nctrl-e\nbar\n") orelse return error.TestUnexpectedResult; try std.testing.expectEqualStrings("foo", parts.query); try std.testing.expectEqualStrings("ctrl-e", parts.key); try std.testing.expectEqualStrings("bar", parts.selected);}
test "parseFzfOutput enter with selection" { const parts = parseFzfOutput("foo\n\nbar\n") orelse return error.TestUnexpectedResult; try std.testing.expectEqualStrings("foo", parts.query); try std.testing.expectEqualStrings("", parts.key); try std.testing.expectEqualStrings("bar", parts.selected);}
test "parseFzfOutput empty query ctrl-e" { const parts = parseFzfOutput("\nctrl-e\n\n") orelse return error.TestUnexpectedResult; try std.testing.expectEqualStrings("", parts.query); try std.testing.expectEqualStrings("ctrl-e", parts.key); try std.testing.expectEqualStrings("", parts.selected);}
test "parseFzfOutput empty" { try std.testing.expect(parseFzfOutput("\n\n\n") == null); try std.testing.expect(parseFzfOutput("") == null);}
fn run(init: std.process.Init, alloc: std.mem.Allocator, args: []const []const u8) ![]u8 { var child = try std.process.spawn(init.io, .{ .argv = args, .stdout = .pipe, .stderr = .inherit, });
const output = try read(init.io, alloc, &child);
const term = try child.wait(init.io); if (term != .exited or term.exited != 0) { alloc.free(output); return error.FailedExit; }
if (std.mem.findScalar(u8, output, '\n')) |idx| { const result = try alloc.dupe(u8, output[0..idx]); alloc.free(output); return result; }
return output;}
fn read(io: std.Io, alloc: std.mem.Allocator, child: *std.process.Child) ![]u8 { if (child.stdout) |file| { var buf: [4096]u8 = undefined; var fr = file.reader(io, &buf); return try fr.interface.allocRemaining(alloc, .unlimited); } return try alloc.dupe(u8, "");}
fn gen(init: std.process.Init, alloc: std.mem.Allocator) ![]u8 { const charset = std.ascii.letters ++ "0123456789"; const special_chars = "!@#$%^&*()-_=+[]{}|;:,.<>?";
var password = try alloc.alloc(u8, 16); errdefer alloc.free(password);
init.io.random(password); for (password) |*c| { c.* = charset[c.* % charset.len]; }
var idx_bytes: [8]u8 = undefined; init.io.random(&idx_bytes); const pos = std.mem.readInt(u64, idx_bytes[0..8], .little) % password.len; init.io.random(&idx_bytes); const idx = std.mem.readInt(u64, idx_bytes[0..8], .little) % special_chars.len; password[pos] = special_chars[idx];
return password;}
fn store(init: std.process.Init, alloc: std.mem.Allocator, name: []const u8) ![]u8 { const password = try gen(init, alloc); errdefer alloc.free(password);
inner(init, alloc, name, password) catch |err| { std.log.err("failed to store password: {}", .{err}); return err; }; return password;}
fn inner(init: std.process.Init, alloc: std.mem.Allocator, name: []const u8, password: []const u8) !void { const pass_dir = try dir(alloc, init.environ_map); defer alloc.free(pass_dir);
const full_path = try std.fs.path.join(alloc, &.{ pass_dir, name }); defer alloc.free(full_path);
if (std.fs.path.dirname(full_path)) |dir_path| { std.Io.Dir.createDirAbsolute(init.io, dir_path, .default_dir) catch |err| { if (err != error.PathAlreadyExists) return err; }; }
var child = try std.process.spawn(init.io, .{ .argv = &.{ "pass", "insert", "-e", name }, .stdin = .pipe, .stderr = .inherit, });
if (child.stdin) |stdin| { var buf: [4096]u8 = undefined; var fw = stdin.writer(init.io, &buf); try fw.interface.writeAll(password); try fw.interface.writeAll("\n"); try fw.flush(); stdin.close(init.io); child.stdin = null; }
const term = try child.wait(init.io); if (term != .exited or term.exited != 0) return error.FailedExit;}
fn clip(init: std.process.Init, content: []const u8) !void { const args: []const []const u8 = switch (builtin.target.os.tag) { .macos => &.{"pbcopy"}, .linux => args: { if (init.environ_map.get("WAYLAND_DISPLAY")) |_| { break :args &.{"wl-copy"}; } else break :args &.{ "xclip", "-selection", "clipboard" }; }, else => return, };
var child = try std.process.spawn(init.io, .{ .argv = args, .stdin = .pipe, });
if (child.stdin) |stdin| { var buf: [4096]u8 = undefined; var fw = stdin.writer(init.io, &buf); try fw.interface.writeAll(content); try fw.flush(); stdin.close(init.io); child.stdin = null; }
const term = try child.wait(init.io); if (term != .exited) return error.ClipboardFailed;}