-- Leo's gemini proxy

-- Connecting to git.thebackupbox.net:1965...

-- Connected

-- Sending request

-- Meta line: 20 text/gemini

repo: actpub
action: commit
revision:
path_from:
revision_from: ac4ca8d5fba03eae41808bdfb01c98e22cd95199:
path_to:
revision_to:

git.thebackupbox.net

actpub

git://git.thebackupbox.net/actpub

commit ac4ca8d5fba03eae41808bdfb01c98e22cd95199
Author: epoch <epoch@thebackupbox.net>
Date:   Fri Feb 10 09:31:19 2023 +0000

    added some of the server-side scripts and install lines for thebackupbox

diff --git a/Makefile b/Makefile

index 1b1b4f5f7749d79fcf0e30a2577e43e956d4b3a2..

index ..676cca9da5dabb416fdecb6bf1179a906bb27316 100644

--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,8 @@
 PREFIX:=/usr/local

-install:
+all: csv
+
+install: all
 	install -t $(PREFIX)/bin webfinger
 	install -t $(PREFIX)/bin ap-follow
 	install -t $(PREFIX)/bin ap-getattachmentlist
@@ -11,4 +13,9 @@ install:
 	install -t $(PREFIX)/bin ap-getobjecturi
 	install -t $(PREFIX)/bin ap-getpubkey
 	install -t $(PREFIX)/bin ap-object2acct
+	install -t $(PREFIX)/bin ap-activity2html
+	install -t $(PREFIX)/bin ap-verifysignature
+	install -t $(PREFIX)/bin csv
+	install ap-inbox.cgi /var/www/sites/hacking/inbox2/index.cgi
+	install ap-inbox.cgi /home/epoch/public_html/inbox/index.cgi
 #	install -t $(PREFIX)/bin ap-signed-post
diff --git a/ap-activity2html b/ap-activity2html
new file mode 100755
index 0000000000000000000000000000000000000000..5470d1e24eb4ccf30c17131e9dd45137057cb23b
--- /dev/null
+++ b/ap-activity2html
@@ -0,0 +1,46 @@
+#!/usr/bin/env bash
+
+activity_file="$1"
+type="$(jq -r '.type' 2>/dev/null < "${activity_file}")"
+if [ "$type" = "Delete" ];then
+  printf "del"
+  exit 0
+fi
+if jq -r '.to[]' < "${activity_file}" | grep -vF 'https://www.w3.org/ns/activitystreams#Public' >/dev/null 2>&1;then
+  printf "non-public post not shown. %s<br/>" "${activity_file}"
+  exit 0
+else
+  printf "public:<br/>"
+fi
+printf '<div class="imgdiv">\n'
+printf "Id: %s<br/>\n" "$(jq -r '.id' 2>/dev/null < "${activity_file}" | html_entities_encode)"
+actor="$(jq -r '.actor' 2>/dev/null < "${activity_file}" | html_entities_encode)"
+printf 'Actor: <a href="%s">%s</a><br/>\n' "${actor}" "${actor}"
+printf 'Type: %s<br/>\n' "$(jq -r '.type' 2>/dev/null < "${activity_file}" | html_entities_encode)"
+objType="$(jq -r '.object | type' < "${activity_file}")"
+if [ "$objType" = "string" ];then
+  object="$(jq -r '.object' < "${activity_file}")"
+  printf "Object: <a href="%s">%s</a><br/>" "${object}" "${object}"
+else
+  inReplyTo="$(jq -r '.object.inReplyTo//""' 2>/dev/null < "${activity_file}" | html_entities_encode)"
+  if [ "$inReplyTo" ];then
+    printf "inReplyTo: <a href="%s">%s</a><br/>\n" "${inReplyTo}" "${inReplyTo}"
+  fi
+  #printf "Content: %s<br/>\n" "$(jq -r '.object.content' 2>/dev/null < "${activity_file}" | html_entities_encode)"
+  printf "Content: %s<br/>\n" "$(jq -r '.object.content' 2>/dev/null < "${activity_file}")"
+  printf "Created: %s<br/>\n" "$(jq -r '.signature.created' 2>/dev/null < "${activity_file}" | html_entities_encode)"
+  jq -r '.object.attachment | .[] | .mediaType,.url' 2>/dev/null < "${activity_file}" | paste - - | html_entities_encode | while read type url;do
+    case "$type" in
+      image/*)
+        printf 'attachment: <img src="%s" /><br/>' "$url"
+        ;;
+      video/*)
+        printf 'attachment: <video loop autoplay><source src="%s" type="%s"></video><br/>' "$url" "$type"
+        ;;
+      *)
+        printf '<a href="%s">attachment</a><br/>' "$url"
+        ;;
+    esac
+  done
+fi
+printf '</div>'
diff --git a/ap-inbox.cgi b/ap-inbox.cgi
new file mode 100755
index 0000000000000000000000000000000000000000..8f241c1060ae5f0eced6d270400ad856462cbf32
--- /dev/null
+++ b/ap-inbox.cgi
@@ -0,0 +1,85 @@
+#!/usr/bin/env bash
+
+logger "ap-inbox at ${SCRIPT_NAME} requested! HTTP_ACCEPT: ${HTTP_ACCEPT}"
+
+#if grep -E '' <<< "${HTTP_ACCEPT}";then
+
+if [ "$PATH_INFO" = "" ];then
+	if [ "$REQUEST_METHOD" = "POST" ];then
+		id="$(date +%s)"
+		export POST_DATA="$(head -c "$CONTENT_LENGTH")"
+		if [ "$POST_DATA" ];then
+			exec ap-verifysignature "$id"
+		fi
+		printf "Status: 401 Unauthorized\r\n"
+		exit 0
+	fi
+	if [ "$REQUEST_METHOD" = "GET" ];then
+		first="$(ls | sort -rn | grep '^[0-9]' | head -n1)"
+		at="$(query_param "at")"
+		if [ "${at}" ] ;then
+
+per_page=8
+total="$(ls | sort -rn | grep -c '^[0-9]')"
+at="$(query_param at | tr -cd '0-9')"
+if [ ! "$at" ];then
+  at="$(ls | sort -rn | head -n1)"
+else
+  if [ ! -e "$at" ];then
+    printf "Status: 404 Not Found\r\n"
+    printf "Content-Type: text/plain\r\n\r\n"
+    printf "the note you are looking for can not be found."
+  fi
+fi
+next_at="$(ls | grep '^[0-9]' | sort -rn | grep -A "${per_page}" -F "${at}" | tail -n+$[${per_page}+1])"
+
+printf "Content-Type: text/html\r\n\r\n"
+
+ls | grep '^[0-9]' | sort -rn | grep -A "$[${per_page}-1]" -F "${at}" | while read next_file;do
+  if [ "$next_file" ];then
+    ap-activity2html "$next_file"
+  else
+    printf "<div>not really infinite. you got to the end.</div>"
+  fi
+done
+
+if [ "${next_at}" ];then
+  printf '<div hx-get="?at=%s" hx-trigger="revealed" hx-swap="replace">loading loading loading... keep them notes a loading... RAW HIDE!</div>\n' "${next_at}"
+else
+  printf "this is the end. total of %s things\n" "$total"
+fi
+exit 0
+		fi
+		printf "Content-Type: text/html\r\n\r\n"
+cat <<EOF
+<html>
+  <head>
+  <title>htmx fediverse shared inbox infinite scroll</title>
+  <script src="/htmx.org.1.7.0.js"></script>
+  <style>
+.imgdiv {
+        border-style: solid;
+        border-width: 3px;
+        border-color: pink;
+        max-width: 700px;
+        margin: auto;
+}
+  </style>
+  </head>
+  <body>
+    <div hx-get="?at=${first}" hx-trigger="revealed" hx-swap="afterend">INBOX</div>
+  </body>
+</html>
+EOF
+		exit 0
+	fi
+	### error about unexpect request method
+	printf "Status: 200 OK\r\n"
+	printf "Content-Type: text/plain\r\n\r\n"
+	exit 0
+fi
+
+printf "Status: 404 Not Found\r\n"
+printf "Content-Type: text/plain\r\n\r\n"
+printf "dunno what you're looking for."
+
diff --git a/ap-verifysignature b/ap-verifysignature
new file mode 100644
index 0000000000000000000000000000000000000000..f87c8d89e08b35357ba67945596bd22d9fdc446c
--- /dev/null
+++ b/ap-verifysignature
@@ -0,0 +1,69 @@
+#!/usr/bin/env bash
+
+id="$1"
+CALCULATED_DIGEST="$(printf "%s" "$POST_DATA" | openssl sha256 | cut '-d ' -f2)"
+EXPECTED_DIGEST="$(echo "$HTTP_DIGEST" | cut -d= -f2- | base64 -d | xxd -p | tr -d '\n')"
+
+if [ "${EXPECTED_DIGEST}" != "${CALCULATED_DIGEST}" ];then
+	logger "DIGESTS DO NOT MATCH. GO THE FUCK HOME"
+	printf "Status: 401 Not Authorized\r\n"
+	printf "Content-Type: text/plain\r\n\r\n"
+	printf "nope"
+	exit 0
+else
+	logger "DIGESTS MATCH. FUCK YEAH"
+fi
+
+export HTTP_REQUEST_TARGET="$(printf "%s\n" "${REQUEST_METHOD}" | tr 'A-Z' 'a-z') ${REQUEST}"
+export HTTPSIG_KEYID="$(csv "$HTTP_SIGNATURE" keyId | jq -r)"
+
+logger "attempting to verify message to inbox from $HTTPSIG_KEYID ..."
+
+export HTTPSIG_HEADERS="$(csv "$HTTP_SIGNATURE" headers | jq -r)"
+export HTTPSIG_SIG="$(csv "$HTTP_SIGNATURE" signature | jq -r)"
+logger $HTTPSIG_HEADERS
+
+### this should probably get cached ofc, but cbf atm
+sha256="$(printf "%s\n" "${HTTPSIG_KEYID}" | sha256sum | cut '-d ' -f1)"
+pubkey_cache_file="./cache/${sha256}"
+if [ ! -e "$pubkey_cache_file" ];then
+  curl -sH 'Accept: application/activity+json' "${HTTPSIG_KEYID}" \
+    | jq '. | to_entries | .[].value | select(if type == "object" then . else null end) | select(if .id == "'"${HTTPSIG_KEYID}"'" then . else null end) | .publicKeyPem' \
+    | jq -r \
+    > "$pubkey_cache_file"
+fi
+
+### don't actually need to use a variable, since we're leaving it in a cache file anyway
+### and openssl actually prefers it to be in a file
+#pubkey="$(cat "$pubkey_cache_file")"
+
+
+logger "GRABBED PUBKEY: $(cat "${pubkey_cache_file}")"
+
+
+### generate data... probably not the safest thing.
+data="$(
+for name in $HTTPSIG_HEADERS;do
+	env_name="HTTP_$(printf "%s\n" "$name" | tr 'a-z' 'A-Z' | tr '-' '_' | tr -d '()')"
+#	echo ${name}
+#	echo ${env_name}
+	env | grep "^${env_name}=" | sed 's/^[^=]*=/'"${name}"': /g'
+done)"
+
+if openssl dgst \
+		-sha256 \
+		-verify "${pubkey_cache_file}" \
+		-signature <(printf "%s\n" "$HTTPSIG_SIG" | base64 -d) \
+	       	<(printf "%s" "$data") 2>&1 >/dev/null;then
+	logger "HTTP Signature Verified!"
+	printf "%s" "$POST_DATA" > "$id"
+	printf "Status: 200 Ok\r\n"
+	printf "Content-Type: text/plain\r\n\r\n"
+	printf "looks good to me, thanks.\r\n"
+else
+	logger "Go home with your unsigned HTTP POST!"
+	printf "Status: 401 Not Authorized\r\n"
+	printf "Content-Type: text/plain\r\n\r\n"
+	printf "your HTTP signature didn't verify. :/\n"
+	printf "signed data: %s\n" "$data"
+fi
diff --git a/csv.c b/csv.c
new file mode 100644
index 0000000000000000000000000000000000000000..c0903e56ff831b1ef8ea362949999a41c3a117da
--- /dev/null
+++ b/csv.c
@@ -0,0 +1,53 @@
+#include <stdio.h>
+#include <string.h>
+// this is supposed to be an attempt at implementing the type of stuff in:
+// https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.6
+// but I can't be fucked to shave this yak all the way right now.
+// pipe this into jq -r to get the string unquoted and unescaped I guess
+
+int main(int argc,char *argv[]) {
+  char *name;
+  char *nend;
+  char *value;
+  char *vend;
+  if(argc < 2) {
+    printf("usage: csv derp name\n");
+    return 1;
+  }
+  name=argv[1];
+  for(;;) {
+    while(*name && (*name == ' ' || *name == '\t')) name++;//skip over whitespace /before/ the name
+    value=strchr(name,'=');//value will ALWAYS start after first =s after the name.
+    if(!value) return __LINE__;//there wasn't a = after what should be a name.
+    nend=value-1;
+    while(*nend == ' ' || *nend == '\t') nend--; //work backwards from the =
+    nend++;
+    *nend=0;//null out the byte after the name
+    *value=0;
+    value++;
+    while(*value && (*value == ' ' || *value == '\t')) value++;//skip over whitespace
+    if(*value != '"') return __LINE__;//malformed because missing a double-quote after the =
+    value++;
+    vend=strchr(value,'"');
+    if(!vend) return __LINE__;//malformed because missing a closing double-quote
+    while(vend && *vend && *(vend-1)=='\\') vend=strchr(vend+1,'"');
+    if(!vend) return __LINE__;//malformed because missing an unescape double-quote to close value.
+    *vend=0;
+    //fprintf(stderr,"'%s' = '%s'\n",name,value);
+    if(argc == 2) {
+      printf("%s\n",name);
+    }
+    if(argc == 3) {
+      if(!strcmp(name,argv[2])) {
+        printf("\"%s\"\n",value);
+      }
+    }
+    vend++;
+    while(*vend && (*vend == ' ' || *vend == '\t')) vend++;
+    if(*vend == '\0') break;//we're at the end of the string, exit.
+    if(*vend != ',') return __LINE__;//need a comma after the closing string if not at end
+    name=vend+1;
+    if(!*name) return __LINE__;//there was a comma at the end of the input
+  }
+  return 0;
+}

-----END OF PAGE-----

-- Response ended

-- Page fetched on Sun Jun 2 18:47:27 2024