-- 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:
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
--- 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