Compare commits

..

4 Commits

9 changed files with 133 additions and 393 deletions

29
.github/workflows/zig-build.yaml vendored Normal file
View File

@ -0,0 +1,29 @@
name: Generic zig build
on:
workflow_dispatch:
push:
branches:
- '*'
- '!zig-develop*'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: elerch/setup-zig@v3
with:
version: 0.12.0
- uses: elerch/zig-action-cache@v1.1.6
- name: Build project
run: zig build --summary all
- name: Run tests
run: zig build test --summary all
- name: Notify
uses: elerch/action-notify-ntfy@v2.github
if: always() && env.GITEA_ACTIONS == 'true'
with:
host: ${{ secrets.NTFY_HOST }}
topic: ${{ secrets.NTFY_TOPIC }}
status: ${{ job.status }}
user: ${{ secrets.NTFY_USER }}
password: ${{ secrets.NTFY_PASSWORD }}

View File

@ -1,206 +0,0 @@
//! Publish Date: 2021_10_17
//! This file is hosted at github.com/marler8997/zig-build-repos and is meant to be copied
//! to projects that use it.
const std = @import("std");
const GitRepoStep = @This();
pub const ShaCheck = enum {
none,
warn,
err,
pub fn reportFail(self: ShaCheck, comptime fmt: []const u8, args: anytype) void {
switch (self) {
.none => unreachable,
.warn => std.log.warn(fmt, args),
.err => {
std.log.err(fmt, args);
std.os.exit(0xff);
},
}
}
};
step: std.build.Step,
builder: *std.build.Builder,
url: []const u8,
name: []const u8,
branch: ?[]const u8 = null,
sha: []const u8,
path: []const u8 = null,
sha_check: ShaCheck = .warn,
fetch_enabled: bool,
var cached_default_fetch_option: ?bool = null;
pub fn defaultFetchOption(b: *std.build.Builder) bool {
if (cached_default_fetch_option) |_| {} else {
cached_default_fetch_option = if (b.option(bool, "fetch", "automatically fetch network resources")) |o| o else false;
}
return cached_default_fetch_option.?;
}
pub fn create(b: *std.build.Builder, opt: struct {
url: []const u8,
branch: ?[]const u8 = null,
sha: []const u8,
path: ?[]const u8 = null,
sha_check: ShaCheck = .warn,
fetch_enabled: ?bool = null,
}) *GitRepoStep {
var result = b.allocator.create(GitRepoStep) catch @panic("memory");
const name = std.fs.path.basename(opt.url);
result.* = GitRepoStep{
.step = std.build.Step.init(.custom, "clone a git repository", b.allocator, make),
.builder = b,
.url = opt.url,
.name = name,
.branch = opt.branch,
.sha = opt.sha,
.path = if (opt.path) |p| (b.allocator.dupe(u8, p) catch @panic("memory")) else (std.fs.path.resolve(b.allocator, &[_][]const u8{
b.build_root,
"libs",
name,
})) catch @panic("memory"),
.sha_check = opt.sha_check,
.fetch_enabled = if (opt.fetch_enabled) |fe| fe else defaultFetchOption(b),
};
return result;
}
// TODO: this should be included in std.build, it helps find bugs in build files
fn hasDependency(step: *const std.build.Step, dep_candidate: *const std.build.Step) bool {
for (step.dependencies.items) |dep| {
// TODO: should probably use step.loop_flag to prevent infinite recursion
// when a circular reference is encountered, or maybe keep track of
// the steps encounterd with a hash set
if (dep == dep_candidate or hasDependency(dep, dep_candidate))
return true;
}
return false;
}
fn make(step: *std.build.Step) !void {
const self = @fieldParentPtr(GitRepoStep, "step", step);
std.fs.accessAbsolute(self.path, std.fs.File.OpenFlags{ .read = true }) catch {
const branch_args = if (self.branch) |b| &[2][]const u8{ " -b ", b } else &[2][]const u8{ "", "" };
if (!self.fetch_enabled) {
std.debug.print("Error: git repository '{s}' does not exist\n", .{self.path});
std.debug.print(" Use -Dfetch to download it automatically, or run the following to clone it:\n", .{});
std.debug.print(" git clone {s}{s}{s} {s} && git -C {3s} checkout {s} -b for_ziget\n", .{ self.url, branch_args[0], branch_args[1], self.path, self.sha });
std.os.exit(1);
}
{
var args = std.ArrayList([]const u8).init(self.builder.allocator);
defer args.deinit();
try args.append("git");
try args.append("clone");
try args.append("--recurse-submodules");
try args.append(self.url);
// TODO: clone it to a temporary location in case of failure
// also, remove that temporary location before running
try args.append(self.path);
if (self.branch) |branch| {
try args.append("-b");
try args.append(branch);
}
try run(self.builder, args.items);
}
try run(self.builder, &[_][]const u8{
"git",
"-C",
self.path,
"checkout",
self.sha,
"-b",
"fordep",
});
};
try self.checkSha();
}
fn checkSha(self: GitRepoStep) !void {
if (self.sha_check == .none)
return;
const result: union(enum) { failed: anyerror, output: []const u8 } = blk: {
const result = std.ChildProcess.exec(.{
.allocator = self.builder.allocator,
.argv = &[_][]const u8{
"git",
"-C",
self.path,
"rev-parse",
"HEAD",
},
.cwd = self.builder.build_root,
.env_map = self.builder.env_map,
}) catch |e| break :blk .{ .failed = e };
try std.io.getStdErr().writer().writeAll(result.stderr);
switch (result.term) {
.Exited => |code| {
if (code == 0) break :blk .{ .output = result.stdout };
break :blk .{ .failed = error.GitProcessNonZeroExit };
},
.Signal => break :blk .{ .failed = error.GitProcessFailedWithSignal },
.Stopped => break :blk .{ .failed = error.GitProcessWasStopped },
.Unknown => break :blk .{ .failed = error.GitProcessFailed },
}
};
switch (result) {
.failed => |err| {
return self.sha_check.reportFail("failed to retreive sha for repository '{s}': {s}", .{ self.name, @errorName(err) });
},
.output => |output| {
if (!std.mem.eql(u8, std.mem.trimRight(u8, output, "\n\r"), self.sha)) {
return self.sha_check.reportFail("repository '{s}' sha does not match\nexpected: {s}\nactual : {s}\n", .{ self.name, self.sha, output });
}
},
}
}
fn run(builder: *std.build.Builder, argv: []const []const u8) !void {
{
var msg = std.ArrayList(u8).init(builder.allocator);
defer msg.deinit();
const writer = msg.writer();
var prefix: []const u8 = "";
for (argv) |arg| {
try writer.print("{s}\"{s}\"", .{ prefix, arg });
prefix = " ";
}
std.log.info("[RUN] {s}", .{msg.items});
}
const child = try std.ChildProcess.init(argv, builder.allocator);
defer child.deinit();
child.stdin_behavior = .Ignore;
child.stdout_behavior = .Inherit;
child.stderr_behavior = .Inherit;
child.cwd = builder.build_root;
child.env_map = builder.env_map;
try child.spawn();
const result = try child.wait();
switch (result) {
.Exited => |code| if (code != 0) {
std.log.err("git clone failed with exit code {}", .{code});
std.os.exit(0xff);
},
else => {
std.log.err("git clone failed with: {}", .{result});
std.os.exit(0xff);
},
}
}
// Get's the repository path and also verifies that the step requesting the path
// is dependent on this step.
pub fn getPath(self: *const GitRepoStep, who_wants_to_know: *const std.build.Step) []const u8 {
if (!hasDependency(who_wants_to_know, &self.step))
@panic("a step called GitRepoStep.getPath but has not added it as a dependency");
return self.path;
}

136
build.zig
View File

@ -1,12 +1,6 @@
const std = @import("std"); const std = @import("std");
const GitRepoStep = @import("GitRepoStep.zig");
pub fn build(b: *std.build.Builder) void { pub fn build(b: *std.Build) void {
const zfetch_repo = GitRepoStep.create(b, .{
.url = "https://github.com/truemedian/zfetch",
// .branch = "0.1.10", // branch also takes tags. Tag 0.1.10 isn't quite new enough
.sha = "271cab5da4d12c8f08e67aa0cd5268da100e52f1",
});
// Standard target options allows the person running `zig build` to choose // Standard target options allows the person running `zig build` to choose
// what target to build for. Here we do not override the defaults, which // what target to build for. Here we do not override the defaults, which
// means any target is allowed, and the default is native. Other options // means any target is allowed, and the default is native. Other options
@ -15,12 +9,12 @@ pub fn build(b: *std.build.Builder) void {
// Standard release options allow the person running `zig build` to select // Standard release options allow the person running `zig build` to select
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
const mode = b.standardReleaseOptions(); const optimize = b.standardOptimizeOption(.{});
const uploadexe = switch (target.getOs().tag) { const uploadexe = switch (target.result.os.tag) {
.linux => b.addExecutable("clipboard-upload", "src/main-linux.zig"), .linux => b.addExecutable(.{ .name = "clipboard-upload", .root_source_file = b.path("src/main-linux.zig"), .target = target }),
.windows => b.addExecutable("clipboard-upload", "src/main-windows.zig"), .windows => b.addExecutable(.{ .name = "clipboard-upload", .root_source_file = b.path("src/main-windows.zig"), .target = target }),
else => std.os.exit(1), else => std.process.exit(1),
}; };
const path = if (b.option(bool, "curl", "use external curl command") orelse false) const path = if (b.option(bool, "curl", "use external curl command") orelse false)
@ -30,19 +24,22 @@ pub fn build(b: *std.build.Builder) void {
const enc_path = blk: { const enc_path = blk: {
if (b.option(bool, "seperate-encryption", "use external encryption command") orelse false) { if (b.option(bool, "seperate-encryption", "use external encryption command") orelse false) {
const encryptionexe = b.addExecutable("encrypt", "src/encrypt.zig"); const encryptionexe = b.addExecutable(.{
encryptionexe.setTarget(target); .name = "encrypt",
encryptionexe.setBuildMode(mode); .root_source_file = b.path("src/encrypt.zig"),
encryptionexe.install(); .target = target,
.optimize = optimize,
});
b.getInstallStep().dependOn(&b.addInstallArtifact(encryptionexe, .{}).step);
break :blk "config/external_encryption.zig"; break :blk "config/external_encryption.zig";
} else { } else {
break :blk "config/sane_encryption.zig"; break :blk "config/sane_encryption.zig";
} }
}; };
configureExe(uploadexe, b, target, mode, zfetch_repo, path, enc_path); configureExe(uploadexe, b, target, optimize, path, enc_path);
const run_cmd = uploadexe.run(); const run_cmd = b.addRunArtifact(uploadexe);
run_cmd.step.dependOn(b.getInstallStep()); run_cmd.step.dependOn(b.getInstallStep());
if (b.args) |args| { if (b.args) |args| {
run_cmd.addArgs(args); run_cmd.addArgs(args);
@ -51,11 +48,11 @@ pub fn build(b: *std.build.Builder) void {
const run_step = b.step("run", "Run the app (uplaods clipboard contents)"); const run_step = b.step("run", "Run the app (uplaods clipboard contents)");
run_step.dependOn(&run_cmd.step); run_step.dependOn(&run_cmd.step);
const downloadexe = b.addExecutable("clipboard-download", "src/download.zig"); const downloadexe = b.addExecutable(.{ .name = "clipboard-download", .root_source_file = b.path("src/download.zig"), .target = target });
configureExe(downloadexe, b, target, mode, zfetch_repo, path, enc_path); configureExe(downloadexe, b, target, optimize, path, enc_path);
const run_download_cmd = downloadexe.run(); const run_download_cmd = b.addRunArtifact(downloadexe);
run_download_cmd.step.dependOn(b.getInstallStep()); run_download_cmd.step.dependOn(b.getInstallStep());
if (b.args) |args| { if (b.args) |args| {
run_download_cmd.addArgs(args); run_download_cmd.addArgs(args);
@ -65,10 +62,9 @@ pub fn build(b: *std.build.Builder) void {
run_download_step.dependOn(&run_download_cmd.step); run_download_step.dependOn(&run_download_cmd.step);
} }
fn configureExe(exe: *std.build.LibExeObjStep, b: *std.build.Builder, target: std.zig.CrossTarget, mode: std.builtin.Mode, zfetch_repo: anytype, config_path: []const u8, enc_config_path: []const u8) void { fn configureExe(exe: *std.Build.Step.Compile, b: *std.Build, target: std.Build.ResolvedTarget, mode: std.builtin.OptimizeMode, config_path: []const u8, enc_config_path: []const u8) void {
exe.setTarget(target); exe.root_module.optimize = mode;
exe.setBuildMode(mode); if (target.result.os.tag == .linux) {
if (target.getOs().tag == .linux) {
exe.linkLibC(); exe.linkLibC();
// LibX11 1.7.2: https://gitlab.freedesktop.org/xorg/lib/libx11/-/archive/libX11-1.7.2/libx11-libX11-1.7.2.tar.gz // LibX11 1.7.2: https://gitlab.freedesktop.org/xorg/lib/libx11/-/archive/libX11-1.7.2/libx11-libX11-1.7.2.tar.gz
// LibXfixes 5.0.3: https://gitlab.freedesktop.org/xorg/lib/libxfixes/-/archive/libXfixes-5.0.3/libxfixes-libXfixes-5.0.3.tar.gz // LibXfixes 5.0.3: https://gitlab.freedesktop.org/xorg/lib/libxfixes/-/archive/libXfixes-5.0.3/libxfixes-libXfixes-5.0.3.tar.gz
@ -76,93 +72,39 @@ fn configureExe(exe: *std.build.LibExeObjStep, b: *std.build.Builder, target: st
// We can download the above by taking each url and processing in a // We can download the above by taking each url and processing in a
// command e.g.: // command e.g.:
// curl <url> | tar xz --wildcards '*.h' // curl <url> | tar xz --wildcards '*.h'
exe.addIncludeDir("libx11-libX11-1.7.2/include"); exe.addIncludePath(b.path("libx11-libX11-1.7.2/include"));
exe.addIncludeDir("libxfixes-libXfixes-5.0.3/include"); exe.addIncludePath(b.path("libxfixes-libXfixes-5.0.3/include"));
exe.addIncludeDir("xorgproto-xorgproto-2021.5/include"); exe.addIncludePath(b.path("xorgproto-xorgproto-2021.5/include"));
// More than a little messy. We're grabbing libX11 and libXfixes from // More than a little messy. We're grabbing libX11 and libXfixes from
// the host, while using downloaded headers. This assumes debian // the host, while using downloaded headers. This assumes debian
// bullseye host, and also means you can't cross-compile from Windows. // bullseye host, and also means you can't cross-compile from Windows.
// TODO: Make this better // TODO: Make this better
// const dependent_objects = .{
// "/usr/lib/x86_64-linux-gnu/libX11.so.6",
// "/usr/lib/x86_64-linux-gnu/libXfixes.so.3",
// };
const dependent_objects = .{ const dependent_objects = .{
"/usr/lib/x86_64-linux-gnu/libX11.so.6", "/usr/lib/x86_64-linux-gnu/libX11.so.6",
"/usr/lib/x86_64-linux-gnu/libXfixes.so.3", "/nix/store/wd7b3f9sqgs0flgicx213iiyzjb4jxqm-libXfixes-6.0.1/lib/libXfixes.so.3",
}; };
inline for (dependent_objects) |obj| inline for (dependent_objects) |obj|
exe.addObjectFile(obj); exe.addObjectFile(.{ .cwd_relative = obj });
// exe.addObjectFile(b.path(obj));
} }
if (target.getOs().tag == .windows) { if (target.result.os.tag == .windows) {
// woah...we don't actually need libc! // woah...we don't actually need libc!
exe.linkSystemLibrary("user32"); exe.linkSystemLibrary("user32");
exe.linkSystemLibrary("kernel32"); exe.linkSystemLibrary("kernel32");
exe.linkSystemLibrary("shell32"); exe.linkSystemLibrary("shell32");
} }
const copy_deps = std.build.RunStep.create(b, "copy zfetch deps");
copy_deps.addArgs(&[_][]const u8{
"cp",
"zfetch_deps.zig",
"libs/zfetch/deps.zig",
});
copy_deps.step.dependOn(&zfetch_repo.step);
// exe.step.dependOn(&zfetch_repo.step); exe.root_module.addImport("config", b.createModule(.{
exe.step.dependOn(&copy_deps.step); .root_source_file = b.path(config_path),
}));
exe.root_module.addImport("encryptionconfig", b.createModule(.{
.root_source_file = b.path(enc_config_path),
}));
// This import won't work unless we're already cloned. The way around b.getInstallStep().dependOn(&b.addInstallArtifact(exe, .{}).step);
// this is to have a multi-stage build process, but that's a lot of work.
// Instead, I've copied the addPackage and tweaked it for the build prefix
// so we'll have to keep that in sync with upstream
// const zfetch = @import("libs/zfetch/build.zig");
const zpkg = getZfetchPackage(b, "libs/zfetch") catch unreachable;
exe.addPackage(zpkg);
exe.addPackage(.{
.name = "iguanaTLS",
.path = .{ .path = "libs/zfetch/libs/iguanaTLS/src/main.zig" },
});
exe.addPackage(.{
.name = "config",
.path = .{ .path = config_path },
});
exe.addPackage(.{
.name = "encryptionconfig",
.path = .{ .path = enc_config_path },
});
exe.install();
}
fn getDependency(comptime lib_prefix: []const u8, comptime name: []const u8, comptime root: []const u8) !std.build.Pkg {
const path = lib_prefix ++ "/libs/" ++ name ++ "/" ++ root;
// We don't actually care if the dependency has been checked out, as
// GitRepoStep will handle that for us
// Make sure that the dependency has been checked out.
// std.fs.cwd().access(path, .{}) catch |err| switch (err) {
// error.FileNotFound => {
// std.log.err("zfetch: dependency '{s}' not checked out", .{name});
//
// return err;
// },
// else => return err,
// };
return std.build.Pkg{
.name = name,
.path = .{ .path = path },
};
}
pub fn getZfetchPackage(b: *std.build.Builder, comptime lib_prefix: []const u8) !std.build.Pkg {
var dependencies = b.allocator.alloc(std.build.Pkg, 4) catch unreachable;
dependencies[0] = try getDependency(lib_prefix, "iguanaTLS", "src/main.zig");
dependencies[1] = try getDependency(lib_prefix, "network", "network.zig");
dependencies[2] = try getDependency(lib_prefix, "uri", "uri.zig");
dependencies[3] = try getDependency(lib_prefix, "hzzp", "src/main.zig");
return std.build.Pkg{
.name = "zfetch",
.path = .{ .path = lib_prefix ++ "/src/main.zig" },
.dependencies = dependencies,
};
} }

View File

@ -1,6 +1,5 @@
const builtin = @import("builtin"); const builtin = @import("builtin");
const std = @import("std"); const std = @import("std");
const zfetch = @import("zfetch");
const crypt = @import("crypt.zig"); const crypt = @import("crypt.zig");
const config = @import("config"); const config = @import("config");
const encryptionconfig = @import("encryptionconfig"); const encryptionconfig = @import("encryptionconfig");
@ -13,7 +12,7 @@ const encryptionconfig = @import("encryptionconfig");
// iguanaTLS to support tls 1.3 // iguanaTLS to support tls 1.3
// iguanaTLS to support something else, like ECDHE-ECDSA-CHACHA20-POLY1305 // iguanaTLS to support something else, like ECDHE-ECDSA-CHACHA20-POLY1305
// In the meantime, I've allowed use of http, since we're encrypting anyway // In the meantime, I've allowed use of http, since we're encrypting anyway
const clipboard_url = "http://clippy.lerch.org/work2"; const clipboard_url = "https://clippy.lerch.org/work2";
// const clipboard_url = "https://httpbin.org/post"; // const clipboard_url = "https://httpbin.org/post";
const Self = @This(); const Self = @This();
@ -59,9 +58,8 @@ pub fn clipboardChanged(self: *Self, contents: []const u8) !void {
defer aa.free(clip_contents); defer aa.free(clip_contents);
// Ugh - it's the encryption that Crowdstrike doesn't like.. :( // Ugh - it's the encryption that Crowdstrike doesn't like.. :(
var buf: []u8 = try aa.alloc(u8, contents.len); const buf: []u8 = try aa.dupe(u8, contents);
defer aa.free(buf); defer aa.free(buf);
std.mem.copy(u8, buf, contents);
const encrypted = encrypt(aa, self.key.*, buf) catch |e| { const encrypted = encrypt(aa, self.key.*, buf) catch |e| {
std.log.err("Could not encrypt clipboard contents: {}", .{e}); std.log.err("Could not encrypt clipboard contents: {}", .{e});
if (@errorReturnTrace()) |trace| { if (@errorReturnTrace()) |trace| {
@ -127,19 +125,20 @@ fn get(allocator: std.mem.Allocator) ![]const u8 {
// @embedFile("/etc/ssl/certs/ca-certificates.crt"), // @embedFile("/etc/ssl/certs/ca-certificates.crt"),
// ).reader(); // ).reader();
// const trust = try tls.x509.CertificateChain.from_pem(allocator, cert_reader); // const trust = try tls.x509.CertificateChain.from_pem(allocator, cert_reader);
try zfetch.init();
defer zfetch.deinit();
var headers = zfetch.Headers.init(allocator);
defer headers.deinit();
// try headers.appendValue("Accept", "application/json"); // try headers.appendValue("Accept", "application/json");
// try headers.appendValue("Content-Type", "text/plain"); // try headers.appendValue("Content-Type", "text/plain");
var req = try zfetch.Request.init(allocator, clipboard_url, null); var data = std.ArrayList(u8).init(allocator);
defer req.deinit(); defer data.deinit();
var client = std.http.Client{ .allocator = allocator };
defer client.deinit();
try req.do(.GET, headers, null); const res = try client.fetch(.{
.location = .{ .url = clipboard_url },
.response_storage = .{ .dynamic = &data },
});
_ = res; // TODO: Check res.status
// Printf debugging // Printf debugging
// const stdout = std.io.getStdOut().writer(); // const stdout = std.io.getStdOut().writer();
@ -150,18 +149,6 @@ fn get(allocator: std.mem.Allocator) ![]const u8 {
// } // }
// try stdout.print("body:\n", .{}); // try stdout.print("body:\n", .{});
// //
const reader = req.reader();
var data = std.ArrayList(u8).init(allocator);
defer data.deinit();
const data_writer = data.writer();
var buf: [1024]u8 = undefined;
while (true) {
const read = try reader.read(&buf);
if (read == 0) break;
try data_writer.writeAll(buf[0..read]);
}
return data.toOwnedSlice(); return data.toOwnedSlice();
} }
@ -270,42 +257,20 @@ fn put(allocator: std.mem.Allocator, data: []const u8) !void {
// @embedFile("/etc/ssl/certs/ca-certificates.crt"), // @embedFile("/etc/ssl/certs/ca-certificates.crt"),
// ).reader(); // ).reader();
// const trust = try tls.x509.CertificateChain.from_pem(allocator, cert_reader); // const trust = try tls.x509.CertificateChain.from_pem(allocator, cert_reader);
try zfetch.init(); var client = std.http.Client{ .allocator = allocator };
defer zfetch.deinit(); defer client.deinit();
var headers = zfetch.Headers.init(allocator); const res = try client.fetch(.{
defer headers.deinit(); .location = .{ .url = clipboard_url },
.method = .PUT,
.payload = data,
});
_ = res; // TODO: check status
// try headers.appendValue("Accept", "application/json");
// try headers.appendValue("Content-Type", "text/plain");
var req = try zfetch.Request.init(allocator, clipboard_url, null);
defer req.deinit();
try req.do(.PUT, headers, data);
// Printf debugging
// const stdout = std.io.getStdOut().writer();
// try stdout.print("status: {d} {s}\n", .{ req.status.code, req.status.reason });
// try stdout.print("headers:\n", .{});
// for (req.headers.list.items) |header| {
// try stdout.print(" {s}: {s}\n", .{ header.name, header.value });
// }
// try stdout.print("body:\n", .{});
//
// const reader = req.reader();
//
// var buf: [1024]u8 = undefined;
// while (true) {
// const read = try reader.read(&buf);
// if (read == 0) break;
//
// try stdout.writeAll(buf[0..read]);
// }
} }
test "full integration" { test "full integration" {
var allocator = std.testing.allocator; const allocator = std.testing.allocator;
var watcher = init(allocator); var watcher = init(allocator);
defer watcher.deinit(); defer watcher.deinit();
try watcher.clipboardChanged("hello world"); try watcher.clipboardChanged("hello world");

View File

@ -45,7 +45,7 @@ pub fn encryptWithKey(allocator: std.mem.Allocator, key: [key_size]u8, data: []u
var encrypted = try std.ArrayList(u8).initCapacity(allocator, block_size * (total_blocks + 1)); var encrypted = try std.ArrayList(u8).initCapacity(allocator, block_size * (total_blocks + 1));
defer encrypted.deinit(); defer encrypted.deinit();
while (current_block < total_blocks) { while (current_block < total_blocks) {
in = @ptrCast(*[block_size]u8, data[(current_block * block_size)..(((current_block + 1) * block_size) - 1)]); in = @ptrCast(data[(current_block * block_size)..(((current_block + 1) * block_size) - 1)]);
ctx.encrypt(out[0..], in.*[0..]); ctx.encrypt(out[0..], in.*[0..]);
encrypted.appendSliceAssumeCapacity(out[0..]); encrypted.appendSliceAssumeCapacity(out[0..]);
current_block += 1; current_block += 1;
@ -56,7 +56,7 @@ pub fn encryptWithKey(allocator: std.mem.Allocator, key: [key_size]u8, data: []u
// We can't just ptrcast into data as we are likely to run over the end // We can't just ptrcast into data as we are likely to run over the end
// of the data memory. We'll declare a new input buffer // of the data memory. We'll declare a new input buffer
var in_last: [block_size]u8 = undefined; var in_last: [block_size]u8 = undefined;
const padding: u8 = @intCast(u8, block_size - (data.len % block_size)); const padding: u8 = @intCast(block_size - (data.len % block_size));
var inx: u8 = 0; var inx: u8 = 0;
for (data[(total_blocks * block_size)..]) |b| { for (data[(total_blocks * block_size)..]) |b| {
in_last[inx] = b; in_last[inx] = b;
@ -99,14 +99,14 @@ pub fn decryptWithKey(allocator: std.mem.Allocator, key: [key_size]u8, data: []c
var decrypted = try std.ArrayList(u8).initCapacity(allocator, block_size * (total_blocks - 1)); var decrypted = try std.ArrayList(u8).initCapacity(allocator, block_size * (total_blocks - 1));
defer decrypted.deinit(); defer decrypted.deinit();
while (current_block < total_blocks - 1) { while (current_block < total_blocks - 1) {
in = @ptrCast(*const [block_size]u8, data[(current_block * block_size)..(((current_block + 1) * block_size) - 1)]); in = @ptrCast(data[(current_block * block_size)..(((current_block + 1) * block_size) - 1)]);
ctx.decrypt(out[0..], in.*[0..]); ctx.decrypt(out[0..], in.*[0..]);
decrypted.appendSliceAssumeCapacity(out[0..]); decrypted.appendSliceAssumeCapacity(out[0..]);
current_block += 1; current_block += 1;
} }
// deal with final block, PKCS#7 padding // deal with final block, PKCS#7 padding
in = @ptrCast(*const [block_size]u8, data[(current_block * block_size)..(((current_block + 1) * block_size) - 1)]); in = @ptrCast(data[(current_block * block_size)..(((current_block + 1) * block_size) - 1)]);
ctx.decrypt(out[0..], in.*[0..]); ctx.decrypt(out[0..], in.*[0..]);
// Assertion was triggering when data was plain text // Assertion was triggering when data was plain text
if (out[block_size - 1] > block_size) { if (out[block_size - 1] > block_size) {
@ -131,7 +131,7 @@ test "correct size, even block" {
const str = "0123456789ABCDEF"; const str = "0123456789ABCDEF";
var allocator = std.testing.allocator; var allocator = std.testing.allocator;
var buf: [str.len]u8 = undefined; var buf: [str.len]u8 = undefined;
var data = try std.fmt.bufPrint(&buf, str, .{}); const data = try std.fmt.bufPrint(&buf, str, .{});
const encrypted = try encryptWithKey(allocator, test_key, data); const encrypted = try encryptWithKey(allocator, test_key, data);
defer allocator.free(encrypted); defer allocator.free(encrypted);
const size: usize = 32; const size: usize = 32;
@ -142,7 +142,7 @@ test "round trip, even block" {
const str = "0123456789ABCDEF"; const str = "0123456789ABCDEF";
var allocator = std.testing.allocator; var allocator = std.testing.allocator;
var buf: [str.len]u8 = undefined; var buf: [str.len]u8 = undefined;
var data = try std.fmt.bufPrint(&buf, str, .{}); const data = try std.fmt.bufPrint(&buf, str, .{});
const encrypted = try encryptWithKey(allocator, test_key, data); const encrypted = try encryptWithKey(allocator, test_key, data);
defer allocator.free(encrypted); defer allocator.free(encrypted);
const decrypted = try decryptWithKey(allocator, test_key, encrypted); const decrypted = try decryptWithKey(allocator, test_key, encrypted);
@ -155,7 +155,7 @@ test "round trip, uneven block" {
const str = "0123456789ABCDEFG"; const str = "0123456789ABCDEFG";
var allocator = std.testing.allocator; var allocator = std.testing.allocator;
var buf: [str.len]u8 = undefined; var buf: [str.len]u8 = undefined;
var data = try std.fmt.bufPrint(&buf, str, .{}); const data = try std.fmt.bufPrint(&buf, str, .{});
const encrypted = try encryptWithKey(allocator, test_key, data); const encrypted = try encryptWithKey(allocator, test_key, data);
defer allocator.free(encrypted); defer allocator.free(encrypted);
const decrypted = try decryptWithKey(allocator, test_key, encrypted); const decrypted = try decryptWithKey(allocator, test_key, encrypted);
@ -167,8 +167,8 @@ test "round trip, uneven block, using password" {
const str = "0123456789ABCDEFG"; const str = "0123456789ABCDEFG";
var allocator = std.testing.allocator; var allocator = std.testing.allocator;
var buf: [str.len]u8 = undefined; var buf: [str.len]u8 = undefined;
var data = try std.fmt.bufPrint(&buf, str, .{}); const data = try std.fmt.bufPrint(&buf, str, .{});
var key = try keyFromPassword(allocator, "foo", ""); const key = try keyFromPassword(allocator, "foo", "");
defer allocator.free(key); defer allocator.free(key);
const encrypted = try encryptWithKey(allocator, key.*, data); const encrypted = try encryptWithKey(allocator, key.*, data);
defer allocator.free(encrypted); defer allocator.free(encrypted);

View File

@ -42,11 +42,9 @@ pub fn main() !u8 {
var gpa = std.heap.GeneralPurposeAllocator(.{}){}; var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit(); defer _ = gpa.deinit();
const allocator = gpa.allocator(); const allocator = gpa.allocator();
var args = std.process.args(); var args = try std.process.argsWithAllocator(allocator);
defer args.deinit(); defer args.deinit();
while (args.next(allocator)) |arg_or_err| { while (args.next()) |arg| {
const arg = try arg_or_err;
defer allocator.free(arg);
if (std.mem.eql(u8, arg, "-w")) { if (std.mem.eql(u8, arg, "-w")) {
watch = true; watch = true;
break; break;
@ -101,7 +99,16 @@ fn addToClipboard(allocator: std.mem.Allocator, data: []const u8) !void {
} }
} }
fn execLinux(allocator: std.mem.Allocator, data: []const u8) !std.ChildProcess.ExecResult { const Term = struct {
allocator: std.mem.Allocator,
stderr: []const u8,
term: std.process.Child.Term,
pub fn deinit(self: *Term) void {
self.allocator.free(self.stderr);
}
};
fn execLinux(allocator: std.mem.Allocator, data: []const u8) !Term {
// we need to do this (maybe) because there is no real way to close stdin cleanly, which is what will // we need to do this (maybe) because there is no real way to close stdin cleanly, which is what will
// kill xclip. // kill xclip.
// //
@ -114,15 +121,21 @@ fn execLinux(allocator: std.mem.Allocator, data: []const u8) !std.ChildProcess.E
defer allocator.free(xclip_cmd); defer allocator.free(xclip_cmd);
std.log.debug("cmd: {s}", .{xclip_cmd}); std.log.debug("cmd: {s}", .{xclip_cmd});
return std.ChildProcess.exec(.{ var child = std.process.Child.init(
.allocator = allocator, &[_][]const u8{
.argv = &[_][]const u8{
"/usr/bin/env", "/usr/bin/env",
"sh", "sh",
"-c", "-c",
xclip_cmd, xclip_cmd,
}, },
}); allocator,
);
const term = try std.process.Child.spawnAndWait(&child);
return .{
.term = term,
.allocator = allocator,
.stderr = if (child.stderr) |e| try e.readToEndAlloc(allocator, 10 * 1024 * 1024) else try allocator.dupe(u8, ""),
};
} }
fn execWindows(allocator: std.mem.Allocator, data: []const u8) !std.ChildProcess.ExecResult { fn execWindows(allocator: std.mem.Allocator, data: []const u8) !std.ChildProcess.ExecResult {

View File

@ -12,11 +12,9 @@ pub fn main() !u8 {
var gpa = std.heap.GeneralPurposeAllocator(.{}){}; var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit(); defer _ = gpa.deinit();
const allocator = gpa.allocator(); const allocator = gpa.allocator();
var args = std.process.args(); var args = try std.process.argsWithAllocator(allocator);
defer args.deinit(); defer args.deinit();
while (args.next(allocator)) |arg_or_err| { while (args.next()) |arg| {
const arg = try arg_or_err;
defer allocator.free(arg);
if (std.mem.eql(u8, arg, "-w")) { if (std.mem.eql(u8, arg, "-w")) {
watch = true; watch = true;
break; break;
@ -29,15 +27,15 @@ pub fn main() !u8 {
} }
fn clipboardAction(allocator: std.mem.Allocator, watch: bool, clip: *Clipboard) !void { fn clipboardAction(allocator: std.mem.Allocator, watch: bool, clip: *Clipboard) !void {
var display: *c.Display = c.XOpenDisplay(null).?; const display: *c.Display = c.XOpenDisplay(null).?;
defer _ = c.XCloseDisplay(display); defer _ = c.XCloseDisplay(display);
const default_screen = c.XDefaultScreen(display); const default_screen = c.XDefaultScreen(display);
var color = c.XBlackPixel(display, default_screen); const color = c.XBlackPixel(display, default_screen);
var window = c.XCreateSimpleWindow(display, c.XDefaultRootWindow(display), 0, 0, 1, 1, 0, color, color); const window = c.XCreateSimpleWindow(display, c.XDefaultRootWindow(display), 0, 0, 1, 1, 0, color, color);
defer _ = c.XDestroyWindow(display, window); defer _ = c.XDestroyWindow(display, window);
// Watch will not return // Watch will not return
if (watch) try watchClip(allocator, display, window, "CLIPBOARD", clip); if (watch) try watchClip(allocator, display, window, "CLIPBOARD", clip);
var result = (try printSelection(allocator, display, window, "CLIPBOARD", "UTF8_STRING", clip)) or const result = (try printSelection(allocator, display, window, "CLIPBOARD", "UTF8_STRING", clip)) or
(try printSelection(allocator, display, window, "CLIPBOARD", "STRING", clip)); (try printSelection(allocator, display, window, "CLIPBOARD", "STRING", clip));
if (!result) return error.ClipboardActionFailed; if (!result) return error.ClipboardActionFailed;
} }
@ -46,7 +44,7 @@ fn watchClip(allocator: std.mem.Allocator, display: *c.Display, window: c.Window
var event_base: c_int = undefined; var event_base: c_int = undefined;
var error_base: c_int = undefined; var error_base: c_int = undefined;
var event: c.XEvent = undefined; var event: c.XEvent = undefined;
var bufid = c.XInternAtom(display, bufname, c.False); const bufid = c.XInternAtom(display, bufname, c.False);
_ = c.XFixesQueryExtension(display, &event_base, &error_base); _ = c.XFixesQueryExtension(display, &event_base, &error_base);
_ = c.XFixesSelectSelectionInput(display, c.XDefaultRootWindow(display), bufid, c.XFixesSetSelectionOwnerNotifyMask); _ = c.XFixesSelectSelectionInput(display, c.XDefaultRootWindow(display), bufid, c.XFixesSetSelectionOwnerNotifyMask);
@ -74,10 +72,10 @@ fn printSelection(
var ressize: c_ulong = undefined; var ressize: c_ulong = undefined;
var restail: c_ulong = undefined; var restail: c_ulong = undefined;
var resbits: c_int = undefined; var resbits: c_int = undefined;
var bufid = c.XInternAtom(display, bufname, c.False); const bufid = c.XInternAtom(display, bufname, c.False);
var fmtid = c.XInternAtom(display, fmtname, c.False); var fmtid = c.XInternAtom(display, fmtname, c.False);
var propid = c.XInternAtom(display, "XSEL_DATA", c.False); const propid = c.XInternAtom(display, "XSEL_DATA", c.False);
var incrid = c.XInternAtom(display, "INCR", c.False); const incrid = c.XInternAtom(display, "INCR", c.False);
var event: c.XEvent = undefined; var event: c.XEvent = undefined;
_ = c.XSelectInput(display, window, c.PropertyChangeMask); _ = c.XSelectInput(display, window, c.PropertyChangeMask);
@ -91,13 +89,13 @@ fn printSelection(
_ = c.XGetWindowProperty(display, window, propid, 0, c.LONG_MAX / 4, c.True, c.AnyPropertyType, &fmtid, &resbits, &ressize, &restail, &result); _ = c.XGetWindowProperty(display, window, propid, 0, c.LONG_MAX / 4, c.True, c.AnyPropertyType, &fmtid, &resbits, &ressize, &restail, &result);
defer _ = c.XFree(result); defer _ = c.XFree(result);
if (fmtid != incrid) if (fmtid != incrid)
_ = async clip.clipboardChanged(std.mem.span(result)); // TODO: Ensure we don't start queueing these things up _ = try clip.clipboardChanged(std.mem.span(result)); // TODO: Ensure we don't start queueing these things up
if (fmtid == incrid) { if (fmtid == incrid) {
ressize = 1; ressize = 1;
var arena_allocator = std.heap.ArenaAllocator.init(allocator); var arena_allocator = std.heap.ArenaAllocator.init(allocator);
defer arena_allocator.deinit(); defer arena_allocator.deinit();
var local_allocator = arena_allocator.allocator(); const local_allocator = arena_allocator.allocator();
var buffer = std.ArrayList(u8).init(local_allocator); var buffer = std.ArrayList(u8).init(local_allocator);
defer buffer.deinit(); defer buffer.deinit();
while (ressize > 0) { while (ressize > 0) {
@ -110,7 +108,7 @@ fn printSelection(
//defer _ = c.XFree(result); // Creates double free error, but not sure why //defer _ = c.XFree(result); // Creates double free error, but not sure why
try buffer.appendSlice(try std.fmt.allocPrint(local_allocator, "{s}", .{result})); try buffer.appendSlice(try std.fmt.allocPrint(local_allocator, "{s}", .{result}));
} }
_ = async clip.clipboardChanged(buffer.items); _ = try clip.clipboardChanged(buffer.items);
} }
return true; return true;
} else // request failed, e.g. owner can't convert to the target format } else // request failed, e.g. owner can't convert to the target format

View File

@ -1191,7 +1191,7 @@ fn UpdateContents(hwnd: w.HWND) void {
// Get a lock on the clipboard // Get a lock on the clipboard
if (OpenClipboard(hwnd) > 0) { if (OpenClipboard(hwnd) > 0) {
defer _ = CloseClipboard(); defer _ = CloseClipboard();
var hglb = GetClipboardData(uFormat).?; var hglb = GetClipboardData(uFormat) orelse return;
const hnd = @intCast(isize, @ptrToInt(hglb)); const hnd = @intCast(isize, @ptrToInt(hglb));
var lpstr = GlobalLock(hnd).?; var lpstr = GlobalLock(hnd).?;
defer _ = GlobalUnlock(hnd); defer _ = GlobalUnlock(hnd);

View File

@ -1 +0,0 @@
const use_submodules = 1;