commit - c86fbcd84d03494d9288500e642ba3a7c93dfac1
commit + 6a3713883a8f2ab04bb047dfa49439920061c50f
blob - e700701617b01129f0b60469b09a780fd1b971d3
blob + 24691e3c7e5981dd0e9b3a08b7d00937bdce5bfe
--- .gitignore
+++ .gitignore
deps-win-x86_64/
milna-macos-app.zip
milna.app/
+milna.sec
blob - 3e119da204e6c6355e61fb2055c7f06ddcc5fb9e
blob + f5d5903678900c6e3877a59af8e9f3546bfe684d
--- build.sh
+++ build.sh
CORE="src/ui.c src/audio.c src/aproc.c src/net.c src/update.c lib/microui.c"
CFLAGS="-Os -Wall -ffunction-sections -fdata-sections"
+SIGNDIR="${SIGNDIR:-$HOME/.signify}"
+SIGNKEYNAME="${SIGNKEYNAME:-rverma-2026}"
+SIGNKEY="${SIGNKEY:-$SIGNDIR/$SIGNKEYNAME.sec}"
+SIGNPUB="${SIGNPUB:-$SIGNDIR/$SIGNKEYNAME.pub}"
+SIGNIFY=signify
+command -v "$SIGNIFY" >/dev/null 2>&1 || SIGNIFY=signify-openbsd
+
+if [ "$1" = "release" ]; then
+ [ -f "$SIGNPUB" ] || { echo "no pubkey at $SIGNPUB"; exit 1; }
+ [ -f "$SIGNKEY" ] || { echo "no seckey at $SIGNKEY"; exit 1; }
+ echo ">> mac build + sign"
+ "$0"
+ echo ">> windows build + sign"
+ "$0" win
+ cat <<'NOTE'
+
+>> upload to VPS /var/www/mi/ (rename binaries; .sig names already match):
+ milna -> milna-macos (+ milna-macos.sig)
+ milna.exe -> milna-windows.exe (+ milna-windows.exe.sig)
+ milna-macos-app.zip (+ milna-macos-app.zip.sig)
+ then bump version LAST:
+ echo "X.Y" | ssh USER@mi.rsap.sh 'doas tee /var/www/mi/version'
+NOTE
+ exit 0
+fi
+
+PUBDEF=""
+if [ -f "$SIGNPUB" ]; then
+ bytes=$(sed -n 2p "$SIGNPUB" | base64 -d 2>/dev/null | \
+ dd bs=1 skip=10 count=32 2>/dev/null | od -An -tu1 | \
+ tr -s ' \n' ',' | sed 's/^,//;s/,$//')
+ if [ -n "$bytes" ]; then
+ PUBDEF="-DMILNA_PUBKEY=$bytes"
+ echo "pubkey: baked in from $SIGNPUB"
+ fi
+fi
+CFLAGS="$CFLAGS $PUBDEF"
+
+sign_artifact() {
+ [ -f "$SIGNKEY" ] || return 0
+ signame="${2:-$1}"
+ command -v "$SIGNIFY" >/dev/null 2>&1 || {
+ echo " (signify not found — '$1' UNSIGNED; brew install signify-osx)"
+ return 0; }
+ "$SIGNIFY" -S -c '' -s "$SIGNKEY" -m "$1" -x "$signame.sig" 2>/dev/null && \
+ echo " signed $1 -> $signame.sig" || \
+ echo " (signing failed for $1)"
+}
+
if [ "$1" = "win" ]; then
WARCH=${WINARCH:-x86_64}
case "$WARCH" in
arm64) TRIPLE=aarch64-w64-mingw32 ;;
*) echo "WINARCH: x86_64 | arm64"; exit 1 ;;
esac
-
if [ -n "$WINCC" ]; then WCC=$WINCC
elif command -v $TRIPLE-clang >/dev/null 2>&1; then WCC=$TRIPLE-clang
elif command -v $TRIPLE-gcc-posix >/dev/null 2>&1; then WCC=$TRIPLE-gcc-posix
exit 1
fi
PFX=${WINPFX:-./deps-win-$WARCH}
- [ -d "$PFX" ] || PFX=/tmp/w64 # sandbox fallback
+ [ -d "$PFX" ] || PFX=/tmp/w64
[ -d "$PFX" ] || { echo "deps missing: run ./win-deps.sh $WARCH"; exit 1; }
ICON_OBJ=""
-static -mwindows -Wl,--gc-sections
$TRIPLE-strip milna.exe 2>/dev/null || true
echo "built ./milna.exe ($(du -h milna.exe | cut -f1))"
+ sign_artifact milna.exe milna-windows.exe
exit 0
fi
CC=${CC:-cc}
OUT=milna
case "$(uname -s)" in
-Darwin)
+Darwin)
OPUS=$(brew --prefix opus 2>/dev/null || echo /opt/homebrew/opt/opus)
SPX=$(brew --prefix speexdsp 2>/dev/null || echo /opt/homebrew/opt/speexdsp)
SODIUM=$(brew --prefix libsodium 2>/dev/null || echo /opt/homebrew/opt/libsodium)
-Wl,-dead_strip
strip "$OUT" 2>/dev/null || true
echo "built ./$OUT ($(du -h $OUT | cut -f1))"
+ sign_artifact "$OUT" milna-macos
APP=milna.app
rm -rf "$APP"
rm -f milna-macos-app.zip
if command -v zip >/dev/null 2>&1; then
zip -qry milna-macos-app.zip "$APP"
- echo "zipped ./milna-macos-app.zip (upload to /var/www/milna/)"
+ echo "zipped ./milna-macos-app.zip (upload to /var/www/mi/)"
+ sign_artifact milna-macos-app.zip
else
echo "note: 'zip' not found — install it to produce the update zip"
fi
exit 0
;;
-Linux)
+Linux)
$CC $CFLAGS $INC $(pkg-config --cflags opus speexdsp vpx libsodium) \
src/plat_x11.c src/vid.c src/cap_stub.c src/cam_stub.c $CORE -o $OUT \
$(pkg-config --libs opus speexdsp vpx libsodium) \
blob - e9cb2be653e83b08633356e88ac51293627f7736
blob + b46cf5fa3e8cb58fa688e9740ca1303b6e0ac826
--- src/update.c
+++ src/update.c
#include <errno.h>
#include <pthread.h>
#include "update.h"
+#include <sodium.h>
+static int run_argv(char *const argv[]);
+
+#ifndef MILNA_PUBKEY
+#define MILNA_PUBKEY 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, \
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0
+#endif
+static const unsigned char SIGN_PUBKEY[32] = { MILNA_PUBKEY };
+
+static int signing_enabled(void) {
+ for (int i = 0; i < 32; i++) if (SIGN_PUBKEY[i]) return 1;
+ return 0;
+}
+
+static int signify_sig_bytes(const char *path, unsigned char sig[64]) {
+ FILE *f = fopen(path, "rb");
+ if (!f) return -1;
+ char line[512]; int got = 0;
+ while (fgets(line, sizeof(line), f)) {
+ if (!strncmp(line, "untrusted comment:", 18)) continue;
+ char *nl = strpbrk(line, "\r\n"); if (nl) *nl = 0;
+ unsigned char raw[128]; size_t rawlen = 0;
+ if (sodium_base642bin(raw, sizeof(raw), line, strlen(line),
+ NULL, &rawlen, NULL, sodium_base64_VARIANT_ORIGINAL) != 0)
+ break;
+ if (rawlen != 2 + 8 + 64) break;
+ memcpy(sig, raw + 10, 64);
+ got = 1; break;
+ }
+ fclose(f);
+ return got ? 0 : -1;
+}
+
+static int verify_file(const char *file, const char *sigpath) {
+ if (!signing_enabled()) return 0;
+ unsigned char sig[64];
+ if (signify_sig_bytes(sigpath, sig) != 0) {
+ fprintf(stderr, "[milna] update: signature file missing/invalid\n");
+ return -1;
+ }
+ FILE *f = fopen(file, "rb");
+ if (!f) return -1;
+ fseek(f, 0, SEEK_END); long sz = ftell(f); fseek(f, 0, SEEK_SET);
+ if (sz <= 0 || sz > 64 * 1024 * 1024) { fclose(f); return -1; }
+ unsigned char *buf = malloc((size_t)sz);
+ if (!buf) { fclose(f); return -1; }
+ size_t rd = fread(buf, 1, (size_t)sz, f);
+ fclose(f);
+ if (rd != (size_t)sz) { free(buf); return -1; }
+ int ok = crypto_sign_verify_detached(sig, buf, (size_t)sz,
+ SIGN_PUBKEY) == 0;
+ free(buf);
+ if (!ok) fprintf(stderr, "[milna] update: SIGNATURE VERIFY FAILED — "
+ "refusing to install\n");
+ return ok ? 0 : -1;
+}
+
+static int fetch_and_verify(const char *url, const char *localfile) {
+ if (!signing_enabled()) return 0;
+ char sigurl[1300], sigfile[1200];
+ snprintf(sigurl, sizeof(sigurl), "%s.sig", url);
+ snprintf(sigfile, sizeof(sigfile), "%s.sig", localfile);
+ char *sv[] = { (char*)"curl", (char*)"-fsSL", (char*)"--max-time",
+ (char*)"30", sigurl, (char*)"-o", sigfile, NULL };
+ if (run_argv(sv) != 0) {
+ fprintf(stderr, "[milna] update: could not fetch signature %s\n",
+ sigurl);
+ return -1;
+ }
+ int r = verify_file(localfile, sigfile);
+ remove(sigfile);
+ return r;
+}
+
static pthread_mutex_t g_mu = PTHREAD_MUTEX_INITIALIZER;
#ifndef __APPLE__
static void *update_run_blocking_thunk(void *a);
#define VER_URL "https://mi.rsap.sh/version"
#define BIN_BASE "https://mi.rsap.sh/milna-"
-static char g_self[1024]; /* path to the running executable */
-static char g_new[1024]; /* path to the downloaded update */
+static char g_self[1024];
+static char g_new[1024];
static int bundle_path(char *out, size_t cap) {
#ifdef __APPLE__
snprintf(tmp, sizeof(tmp), "%s", g_self);
char *p = strstr(tmp, marker);
if (!p) return 0;
- p[4] = 0; /* cut right after ".app" */
+ p[4] = 0;
snprintf(out, cap, "%s", tmp);
return 1;
#else
(void)out; (void)cap; return 0;
#endif
}
-static volatile int g_ready; /* an update is downloaded & waiting */
+static volatile int g_ready;
static char g_latest[32];
static char g_localver[32];
#elif defined(__APPLE__)
char raw[1024]; uint32_t sz = sizeof(raw);
if (_NSGetExecutablePath(raw, &sz) != 0) { out[0] = 0; return; }
+
if (!realpath(raw, out)) snprintf(out, cap, "%s", raw);
#else
ssize_t n = readlink("/proc/self/exe", out, cap - 1);
#ifdef _WIN32
#include <process.h>
static int run_argv(char *const argv[]) {
+
intptr_t r = _spawnvp(_P_WAIT, argv[0], (const char *const *)argv);
return r == 0 ? 0 : -1;
}
int digits = 0;
for (const char *p = s; *p; p++) {
if (*p >= '0' && *p <= '9') digits++;
- else if (*p != '.') return 0; /* only digits and dots allowed */
+ else if (*p != '.') return 0;
}
return digits > 0 && strlen(s) < 24;
}
static int newer(const char *remote, const char *local) {
- if (!valid_ver(remote)) return 0; /* junk remote: never "newer" */
+ if (!valid_ver(remote)) return 0;
int ra, rb = 0, la, lb = 0;
ra = atoi(remote);
const char *rd = strchr(remote, '.'); if (rd) rb = atoi(rd + 1);
char bp[1024];
int in_bundle = bundle_path(bp, sizeof(bp));
if (in_bundle) {
+
snprintf(url, sizeof(url), "%smacos-app.zip", BIN_BASE);
char zip[1100]; snprintf(zip, sizeof(zip), "%s.zip", g_new);
char *dz[] = { (char*)"curl", (char*)"-fsSL", (char*)"--max-time",
"milna-macos-app.zip on the vps?\n");
remove(zip); return;
}
+
char *xz[] = { (char*)"xattr", (char*)"-d",
(char*)"com.apple.quarantine", zip, NULL };
run_argv(xz);
+
+ if (fetch_and_verify(url, zip) != 0) {
+ remove(zip); return;
+ }
pthread_mutex_lock(&g_mu);
snprintf(g_latest, sizeof(g_latest), "%s", remote);
g_ready = 1;
"uploaded to the vps?\n", url);
remove(g_new); return;
}
+
+ if (fetch_and_verify(url, g_new) != 0) {
+ remove(g_new); return;
+ }
#ifndef _WIN32
chmod(g_new, 0755);
+
char *xargv[] = { (char*)"xattr", (char*)"-d",
(char*)"com.apple.quarantine", g_new, NULL };
run_argv(xargv);
fprintf(stderr, "[milna] update_init: self=%s\n", g_self);
#ifdef __APPLE__
+
{
char bp[1024];
char zip[1100]; snprintf(zip, sizeof(zip), "%s.zip", g_new);
fclose(z);
fprintf(stderr, "[milna] update: staged bundle zip found, "
"launching swap helper for %s\n", bp);
+
char hp[] = "/tmp/milna_update.XXXXXX";
int hfd = mkstemp(hp);
FILE *h = (hfd >= 0) ? fdopen(hfd, "w") : NULL;
if (h) {
fchmod(hfd, 0700);
+
fprintf(h,
"#!/bin/sh\n"
"pid=\"$1\"; app=\"$2\"; zip=\"$3\"\n"
"rm -rf \"$app.old\" \"$tmp\" \"$zip\"\n"
"/usr/bin/open \"$app\"\n");
fclose(h);
- /* spawn detached and exit so the helper can replace us */
+
char pidbuf[16];
snprintf(pidbuf, sizeof(pidbuf), "%d", (int)getpid());
char *hv[] = { (char*)"/bin/sh", hp, pidbuf, bp, zip, NULL };
}
fprintf(stderr, "[milna] update: helper spawned, quitting "
"for bundle swap\n");
- _exit(0); /* let the helper take over */
+ _exit(0);
}
} else if (z) fclose(z);
}
#ifdef _WIN32
char old[1100]; snprintf(old, sizeof(old), "%s.old", g_self);
remove(old);
- MoveFileA(g_self, old); /* unlock the name */
- if (MoveFileA(g_new, g_self) == 0) /* promote */
- MoveFileA(old, g_self); /* failed: roll back */
+ MoveFileA(g_self, old);
+ if (MoveFileA(g_new, g_self) == 0)
+ MoveFileA(old, g_self);
else
- /* relaunch into the new binary, old one exits */
+
;
#else
if (rename(g_new, g_self) != 0) {
"in a writable location (not /Applications)?\n",
strerror(errno), g_self);
} else {
+
fprintf(stderr, "[milna] update: promoted, relaunching\n");
#ifdef __APPLE__
+
{
char bundle[1024];
snprintf(bundle, sizeof(bundle), "%s", g_self);
char *p = strstr(bundle, ".app/Contents/MacOS/");
if (p) {
- p[4] = 0; /* terminate right after ".app" */
+ p[4] = 0;
+
char *cs[] = { (char*)"codesign", (char*)"--force",
(char*)"--sign", (char*)"-", bundle, NULL };
int rc = run_argv(cs);
}
#ifdef __APPLE__
+
void update_dispatch_bg(void);
#endif
}
#ifndef __APPLE__
-/* pthread needs a void*(void*) signature */
+
static void *update_run_blocking_thunk(void *a) {
(void)a; update_run_blocking(); return NULL;
}