diff options
| author | Ahmed <git@gumx.cc> | 2026-06-14 20:31:27 +0300 |
|---|---|---|
| committer | Ahmed <git@gumx.cc> | 2026-06-14 20:31:27 +0300 |
| commit | c1c845e8ce34b263f0be5f91b20719def3777fed (patch) | |
| tree | 4508d772874423e26504e79179181e741d910628 /pgp.gumx.cc/cgi-bin | |
| parent | 1dd99e3ddd41f63a31715031cbabbd0aa53a1849 (diff) | |
fix: 404 page, pgp cgi script, and other varios vibed fixes
Diffstat (limited to 'pgp.gumx.cc/cgi-bin')
| -rwxr-xr-x | pgp.gumx.cc/cgi-bin/pgp-hkp.sh | 138 |
1 files changed, 138 insertions, 0 deletions
diff --git a/pgp.gumx.cc/cgi-bin/pgp-hkp.sh b/pgp.gumx.cc/cgi-bin/pgp-hkp.sh new file mode 100755 index 0000000..c4e74d7 --- /dev/null +++ b/pgp.gumx.cc/cgi-bin/pgp-hkp.sh @@ -0,0 +1,138 @@ +#!/bin/bash +# HKP keyserver CGI +# Implements: GET /pks/lookup?op=get|index|vindex and POST /pks/add +# Keys live in /var/pgp/keys/<FINGERPRINT_UPPERCASE>.asc + +KEYS_DIR=/var/pgp/keys + +FAVICON="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1IDUiPjxyZWN0IHg9IjEiIHk9IjAiIHdpZHRoPSIxIiBoZWlnaHQ9IjEiLz48cmVjdCB4PSIyIiB5PSIxIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIi8+PHJlY3QgeD0iMCIgeT0iMiIgd2lkdGg9IjEiIGhlaWdodD0iMSIvPjxyZWN0IHg9IjEiIHk9IjIiIHdpZHRoPSIxIiBoZWlnaHQ9IjEiLz48cmVjdCB4PSIyIiB5PSIyIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIi8+PC9zdmc+" + +CSS='@font-face { font-family: "Kawkab Mono"; src: url(/fonts/KawkabMono-Regular.woff2); font-weight: normal; } +@font-face { font-family: "Kawkab Mono"; src: url(/fonts/KawkabMono-Bold.woff2); font-weight: bold; } +* { unicode-bidi: plaintext; box-sizing: border-box; } +html { color: black; background-color: white; } +body { font-family: "Kawkab Mono"; font-size: 16px; line-height: 1.4; margin: 0; padding: 4rem 0; min-height: 100%; overflow-wrap: break-word; } +main, header, footer { max-width: 800px; margin-inline: auto; padding: 0 2rem; } +h1, header, footer { text-align: center; } +main { text-align: justify; } +p, h2, h3, h4 { margin: 1em 0 0 0; } +table { margin: 1em auto; border-collapse: collapse; } +th, td { border: 1px solid; padding: 0.3em 0.8em; } +code { font-size: 85%; } +hr { border: none; border-top: thin solid; margin: 1.25rem 0; } +header { margin-bottom: 1em; } +footer { margin-top: 3em; } +a { color: inherit; } +@media (max-width: 600px) { body { font-size: 0.9em; } h1 { font-size: 1.8em; } } +@media (max-width: 400px) { body { font-size: 0.8em; } h1 { font-size: 1.6em; } } +@media (prefers-color-scheme: dark) { html { filter: invert(1); } }' + +FOOTER='<footer> +<hr> +<a href="https://twt.gumx.cc">twt</a> / +<a href="https://git.gumx.cc">git</a> / +<a href="https://mail.gumx.cc">mail</a> / +<a href="https://irc.gumx.cc">irc</a> / +<a href="https://files.gumx.cc">files</a> / +<a href="https://vpn.gumx.cc">vpn</a> / +<a href="https://pgp.gumx.cc">pgp</a> / +<a href="https://demo.gumx.cc">demo</a> / +<a href="https://wk.fo">wk.fo</a> +</footer>' + +qs_get() { + echo "$QUERY_STRING" \ + | tr '&' '\n' \ + | grep -E "^$1=" \ + | head -1 \ + | cut -d= -f2- \ + | sed 's/+/ /g; s/%20/ /g' +} + +op=$(qs_get op) +search=$(qs_get search | sed 's/^0x//i; s/^0X//i') + +case "$REQUEST_METHOD" in + GET) + case "$op" in + get) + printf "Content-Type: text/plain\r\n\r\n" + FP="${search^^}" + keyfile="$KEYS_DIR/$FP.asc" + if [[ -f "$keyfile" ]]; then + cat "$keyfile" + else + printf "Error: No key found for %s\n" "$search" + fi + ;; + index) + printf "Content-Type: text/plain\r\n\r\n" + total=$(find "$KEYS_DIR" -name '*.asc' 2>/dev/null | wc -l) + echo "info:1:$total" + for f in "$KEYS_DIR"/*.asc; do + [[ -f "$f" ]] || continue + gpg --with-colons --import-options show-only \ + --import < "$f" 2>/dev/null \ + | awk -F: '/^pub/{print "pub:" $5 ":" $6 ":::" $10 "::"}' + done + ;; + vindex) + printf "Content-Type: text/html\r\n\r\n" + printf '<!DOCTYPE html>\n<html lang="en">\n<head>\n' + printf '<meta charset="utf-8">\n' + printf '<link rel="icon" type="image/svg+xml" href="%s">\n' "$FAVICON" + printf '<meta name="viewport" content="width=device-width,initial-scale=1">\n' + printf '<title>pgp.gumx.cc / keys</title>\n' + printf '<style>\n%s\n</style>\n' "$CSS" + printf '</head>\n<body>\n' + printf '<header>\n<h1><a href="https://gumx.cc">gumx</a> / <a href="https://pgp.gumx.cc">pgp</a> / keys</h1>\n</header>\n' + printf '<main>\n<table>\n<tr><th>fingerprint</th><th>uid</th></tr>\n' + found=0 + for f in "$KEYS_DIR"/*.asc; do + [[ -f "$f" ]] || continue + found=1 + FP=$(basename "$f" .asc) + KEY_UID=$(gpg --with-colons --import-options show-only --import < "$f" 2>/dev/null \ + | awk -F: '/^uid/{print $10; exit}') + printf '<tr><td><a href="/pks/lookup?op=get&search=0x%s"><code>%s</code></a></td><td>%s</td></tr>\n' \ + "$FP" "$FP" "$KEY_UID" + done + [[ $found -eq 0 ]] && printf '<tr><td colspan="2">no keys</td></tr>\n' + printf '</table>\n</main>\n' + printf '%s\n' "$FOOTER" + printf '</body>\n</html>\n' + ;; + *) + printf "Content-Type: text/plain\r\n\r\n" + echo "Error: unknown op" + ;; + esac + ;; + + POST) + printf "Content-Type: text/plain\r\n\r\n" + read -r -d '' -n "${CONTENT_LENGTH:-4096}" SUBMITTED_KEY + + TMPDIR_GPG=$(mktemp -d) + FINGERPRINT=$(echo "$SUBMITTED_KEY" \ + | gpg --homedir "$TMPDIR_GPG" \ + --with-colons --import-options show-only \ + --import 2>/dev/null \ + | awk -F: '/^fpr/{print $10; exit}') + rm -rf "$TMPDIR_GPG" + + if [[ -z "$FINGERPRINT" ]]; then + echo "Error: Could not parse submitted key" + exit 0 + fi + + KEYFILE="$KEYS_DIR/${FINGERPRINT^^}.asc" + if [[ -f "$KEYFILE" ]]; then + echo "$SUBMITTED_KEY" > "$KEYFILE" + echo "Key updated successfully" + else + echo "Error: Fingerprint not registered." + echo "Contact ahmed@gumx.cc to arrange a key signing meeting." + fi + ;; +esac |
