Add template functionality, sendmail gopher interface - postreich - Unnamed repository; edit this file 'description' to name the repository. (DIR) Log (DIR) Files (DIR) Refs (DIR) README --- (DIR) commit 5a25e472e0a8ce876bcc9ce97a9a3a15d13a098f (DIR) parent c09ca83bc7540c3e7ff1bdd1cb70f18434e9f642 (HTM) Author: Scarlett McAllister <no+reply@roygbyte.com> Date: Tue, 23 Jan 2024 21:21:24 -0400 Add template functionality, sendmail gopher interface Diffstat: A geomyidae/postoffice/api/common | 82 +++++++++++++++++++++++++++++++ R geomyidae/postoffice/create-mailbo… | 0 R geomyidae/postoffice/get-mailbox -… | 0 A geomyidae/postoffice/api/index.cgi | 23 +++++++++++++++++++++++ A geomyidae/postoffice/api/send-mail | 64 +++++++++++++++++++++++++++++++ A geomyidae/postoffice/api/templates… | 16 ++++++++++++++++ A geomyidae/postoffice/api/templates… | 2 ++ A geomyidae/postoffice/api/templates… | 2 ++ A geomyidae/postoffice/api/templates… | 87 +++++++++++++++++++++++++++++++ A geomyidae/postoffice/api/templates… | 32 +++++++++++++++++++++++++++++++ D geomyidae/postoffice/common | 65 ------------------------------- D geomyidae/postoffice/index.cgi | 19 ------------------- A geomyidae/postoffice/index.dcgi | 39 +++++++++++++++++++++++++++++++ D geomyidae/postoffice/send-mail | 47 ------------------------------- A geomyidae/postoffice/sendmail.dcgi | 57 +++++++++++++++++++++++++++++++ 15 files changed, 404 insertions(+), 131 deletions(-) --- (DIR) diff --git a/geomyidae/postoffice/api/common b/geomyidae/postoffice/api/common @@ -0,0 +1,82 @@ +#!/bin/sh + +PUBKEYS="pubkeys" +TEMPLATES="templates" +MAILBOXES="mailboxes" +MAILROOM="mailroom" + +sanitize_restful_path() { + printf "%s" "$1" \ + | sed -E 's/[^a-zA-Z0-9\-\/_]//g' +} + +sanitize_base64() { + printf "%s" "$1" \ + | sed -E 's/[^a-zA-Z0-9\-\/_?+=]//g' +} + +sanitize_handle() { + printf "%s" "$1" \ + | sed -E 's/[^a-zA-Z0-9\-\\_+=]//g' \ + | head -c 16 +} + +sanitize_message() { + printf "%s" "$1" \ + | sed -E 's/[^a-zA-Z0-9\-\\_+=]//g' \ + | head -c 16 +} + +find_handle_in_path() { + printf "%s" "$1" \ + | awk -F/ '{ print $4 }' +} + +find_value_in_args() { + # Invoked like this `find_arg_in_path "type" "type=1?handle=roygbyte"` + # return the value associated with type. "type" can of course + # be any value expected to be found in $2. + printf "%s" "$2" \ + | xargs -d ? -I x echo x \ + | grep -oE "$1=(.*)" \ + | sed "s/$1=//" +} + +decode_and_verify_pubkey() { + if [ -z "$1" ]; then + printf "No input provided\n" + return 1 + fi + decoded_payload=$( printf "$1" "$base64_payload" \ + | base64 -d --ignore-garbage ) + # test result of `base64` invocation. + if [ ! $? ]; then + printf "Invalid input given. Payload was not base 64.\n" + return 1 + fi + printf "%s" "$decoded_payload" \ + | openssl pkey -pubcheck -pubin > /dev/null + # test result of `openssl` invocation. + if [ ! $? ]; then + printf "Key is not valid.\n" + return 1 + fi + printf "%s\n" "$decoded_payload" + return 0 +} + +encrypt_with_key_and_encode() { + # message to encrypt will be read from standard in + result=$( openssl pkeyutl -encrypt -inkey "$1" -pubin \ + | base64 -w 0 ) + printf "%s" "$result" + return $? +} + +verify_template() { + if [ -z "$1" -o ! -d "$TEMPLATES/$1" ]; then + printf "Template doesn't exist.\n" + return 1 + fi + return 0 +} (DIR) diff --git a/geomyidae/postoffice/create-mailbox b/geomyidae/postoffice/api/create-mailbox (DIR) diff --git a/geomyidae/postoffice/get-mailbox b/geomyidae/postoffice/api/get-mailbox (DIR) diff --git a/geomyidae/postoffice/api/index.cgi b/geomyidae/postoffice/api/index.cgi @@ -0,0 +1,23 @@ +#!/bin/sh + +. ./common + +path=$( sanitize_restful_path "$TRAVERSAL" ) +args="$2" + +handle=$( find_handle_in_path "$path" ) + +case "$path" in + /mailbox/create/*) + ./create-mailbox "$handle" "$args" + ;; + /mailbox/get/*) + ./get-mailbox -c "$handle" + ;; + /mail/send/*) + template=$( find_value_in_args "template" "$args" ) + message=$( find_value_in_args "message" "$args" ) + ./send-mail -h "$handle" -t "$template" -m "$message" + ;; +esac + (DIR) diff --git a/geomyidae/postoffice/api/send-mail b/geomyidae/postoffice/api/send-mail @@ -0,0 +1,64 @@ +#!/bin/sh +# +# Example: send-mail -h roygbyte -t 1 -m ":)" +. ./common + +handle="" +message="" +template="" + +while getopts "h:t:m:" f; do + case $f in + h) handle="$OPTARG" ;; + t) + template="$OPTARG" + result=$( verify_template "$template" ) + if [ $? -eq 1 ]; then + printf "$result\n" + exit + fi + ;; + m) message=$( sanitize_message "$OPTARG" ) ;; + esac +done + +# verify message accocrding to type +result=$( printf "%s\n" | "$TEMPLATES/$template/main" ) +if [ ! $? -eq 0 ]; then + printf "$result" + return 1 +fi +printf "%s\n" "$result" + +result=$( ./get-mailbox -k "$handle" ) +if [ ! $? -eq 0 ]; then + printf "$result" + return 1 +fi +pubkey="$result" + +result=$( ./get-mailbox "$handle" ) +if [ ! $? -eq 0 ]; then + printf "$result" + return 1 +fi +mailbox="$result" + +result=$( printf "%s" "$message" \ + | encrypt_with_key_and_encode "$pubkey" ) +if [ ! $? ]; then + printf "Encryption or encoding failed.\n" + return 1 +fi + +timestamp=$( date ) +printf "%s,%s\n" "$timestamp" "$result" >> "$mailbox" + +# what if... there is a `type` of mail option. and the first `type` to +# implement will be the `vday card`. there could also be like.. +# `snail mail` or `hate mail` or... `love letters` or... +# `fan mail` + + + + (DIR) diff --git a/geomyidae/postoffice/api/templates/valentine/borders.txt b/geomyidae/postoffice/api/templates/valentine/borders.txt @@ -0,0 +1,16 @@ + ___ _ _ ___ _ _ ___ _ _ ___ _ _ ___ +/___\ | \/ | /___\ | \/ | /___\ | \/ | /___\ | \/ | /___\ +|___| \__/ |___| \__/ |___| \__/ |___| \__/ |___| + _ _ _ _ +| \/ | +| \/ | + \__/ \__/ + ___ ___ +/___\ /___\ +|___| |___| + _ _ _ _ +| \/ | | \/ | + \__/ \__/ + ___ _ _ ___ _ _ ___ _ _ ___ _ _ ___ +/___\ | \/ | /___\ | \/ | /___\ | \/ | /___\ | \/ | /___\ +|dc_| \__/ |___| \__/ |___| \__/ |___| \__/ |___| (DIR) diff --git a/geomyidae/postoffice/api/templates/valentine/common b/geomyidae/postoffice/api/templates/valentine/common @@ -0,0 +1 @@ +../../common +\ No newline at end of file (DIR) diff --git a/geomyidae/postoffice/api/templates/valentine/info b/geomyidae/postoffice/api/templates/valentine/info @@ -0,0 +1 @@ +valentine Valentine - Enclose a flirty message inside a fancy border +\ No newline at end of file (DIR) diff --git a/geomyidae/postoffice/api/templates/valentine/main b/geomyidae/postoffice/api/templates/valentine/main @@ -0,0 +1,87 @@ +#!/bin/sh +# +# The template's `main` file does all validation +# +# via gopher, this script will be executed from /postoffice direction, not +# the directory where the file is actually located. +# i could cd into the template dir... bad idea? + +. ./api/common # this directory is based on sendmail.dcgi PWD + +WIDTH=45 +MESSAGE_BYTE_LIMIT=128 + +find_value_in_message() { + # Invoked like this `find_arg_in_path "type" "type=1?handle=roygbyte"` + # return the value associated with type. "type" can of course + # be any value expected to be found in $2. + printf "%s" "$2" \ + | xargs -d '&' -I x echo x \ + | grep -oE "$1=(.*)" \ + | sed "s/$1=//" +} + +validate_text() { + echo $1 +} + +validate_border() { + echo $1 +} + +validate_sweetheart() { + echo $1 +} + +path="$1" +query="$2" +read message + +border=$( find_value_in_args "border" "$path" ) +text=$( find_value_in_args "text" "$path" ) +if [ -z "$text" ]; then + # text might be in the query (if it was just entered), so check that next. + text="$query" +fi +sweetheart=$( find_value_in_args "sweetheart" "$path" ) + +choose_sweetheart() { + cat "api/$TEMPLATES/valentine/sweethearts.txt" \ + | awk -v args="$path" \ + 'BEGIN {FS = "\t"} { printf "[1|%s|sendmail.dcgi?%s?sweetheart=%s|localhost|70]\n", $1, args, $1 }' +} + +write_text() { + printf "[7|Write your message|sendmail.dcgi?$path|localhost|70]\n" +} + +echo $sweetheart # debug +echo $text # debug + +if [ -z "$sweetheart" ]; then + printf "Choose the letter's sweetheart:\n" + choose_sweetheart + return 1 +fi + +if [ -z "$text" ]; then + write_text + printf "Limit is $MESSAGE_BYTE_LIMIT bytes.\n" + return 1 +fi + +return 0 + +# valentine's template will expect a single record with two fields. +# the fields should be separated with a tab. +# (ignore remaining tabs? or replace them by another char) + +#echo $message + +# need to verify the message is below a certain length +# need to verify the chosen border exists +# these two things shoLd be extracted from message, which +# probably isn't a great variable name. +# border=1?message=blah blah bla + +# verify message length, and the border... (DIR) diff --git a/geomyidae/postoffice/api/templates/valentine/sweethearts.txt b/geomyidae/postoffice/api/templates/valentine/sweethearts.txt @@ -0,0 +1,32 @@ +LOVE YA +MISS YOU +LET'S GET BUSY +LOL +WAY 2 GO +CRUSH IT +HIGH FIVE +YOUDA BEST +FEAR LESS +GO TIME +SUPER STAR +PROUD OF U +BIG FAN +DON'T QUIT +GO 4 IT +BE YOU +HOW MUCH +GOOD JOB +U GOT THIS +CHIN UP +PUSH THRU +YOU GO, GIRL +LOVE ME TENDER +YOU ARE GAY +FAX ME +MY, SUCH EYES +TELL ME HOW +PLAY TIME +SAUCY BOY +YOU + ME +GIGGLE +#1 FAN (DIR) diff --git a/geomyidae/postoffice/common b/geomyidae/postoffice/common @@ -1,65 +0,0 @@ -#!/bin/sh - -PUBKEYS="pubkeys" -MAILBOXES="mailboxes" -MAILROOM="mailroom" - -sanitize_restful_path() { - printf "%s" "$1" \ - | sed -E 's/[^a-zA-Z0-9\-\/_]//g' -} - -sanitize_base64() { - printf "%s" "$1" \ - | sed -E 's/[^a-zA-Z0-9\-\/_?+=]//g' -} - -sanitize_handle() { - printf "%s" "$1" \ - | sed -E 's/[^a-zA-Z0-9\-\\_+=]//g' \ - | head -c 16 -} - -sanitize_message() { - printf "%s" "$1" \ - | sed -E 's/[^a-zA-Z0-9\-\\_+=]//g' \ - | head -c 16 -} - -find_handle_in_path() { - printf "%s" "$1" \ - | awk -F/ '{ print $4 }' -} - -decode_and_verify_pubkey() { - if [ -z "$1" ]; then - printf "No input provided\n" - return 1 - fi - decoded_payload=$( printf "$1" "$base64_payload" \ - | base64 -d --ignore-garbage ) - # test result of `base64` invocation. - if [ ! $? ]; then - printf "Invalid input given. Payload was not base 64.\n" - return 1 - fi - printf "%s" "$decoded_payload" \ - | openssl pkey -pubcheck -pubin > /dev/null - # test result of `openssl` invocation. - if [ ! $? ]; then - printf "Key is not valid.\n" - return 1 - fi - printf "%s\n" "$decoded_payload" - return 0 -} - -encrypt_with_key_and_encode() { - # message to encrypt will be read from standard in - result=$( openssl pkeyutl -encrypt -inkey "$1" -pubin \ - | base64 -w 0 ) - printf "%s" "$result" - return $? -} - - (DIR) diff --git a/geomyidae/postoffice/index.cgi b/geomyidae/postoffice/index.cgi @@ -1,19 +0,0 @@ -#!/bin/sh - -. ./common - -path=$( sanitize_restful_path "$TRAVERSAL" ) -args="$2" - -case "$path" in - /mailbox/create/*) - handle=$( find_handle_in_path "$path" ) - ./create-mailbox "$handle" "$args" - ;; - /mailbox/get/*) - handle=$( find_handle_in_path "$path" ) - ./get-mailbox -c "$handle" - ;; -esac - - (DIR) diff --git a/geomyidae/postoffice/index.dcgi b/geomyidae/postoffice/index.dcgi @@ -0,0 +1,39 @@ +#!/bin/sh +. ./api/common + +# +# |\ /\ +# | \/ \/ +# |_/\ /\/\ +# | \/ / +# +# +# Mailboxes can be setup with a 2048 bit PEM format RSA key. +# Simply cr +# + +echo "Welcome to Postreich, Bitreich's gopher powered mail service.\n" \ + "Here you can setup a mailbox and receive mail over the gopher protocol.\n" + +echo "[1|Send mail|sendmail.dcgi|localhost|70]" +echo "[1|Get mail|getmail.dcgi|localhost|70]" + +echo ' + ~~| _.,, + ~~~| C`-o-o_ + ~~~| \ )_/ + ~~| .=|_|=, + ~| / \ \ x \ +*************** +' + +echo ' +This month we are featuring a special promotion of valentines letters.' + +echo 'Setup a mailbox with your handle and a base64 encoded 2048 bit +PEM format RSA public key' + +echo 'Message are encryted at REST. We do not guarentee any sort of security +or privacy of messages received or stored. However, if you access this service +over gophers you can expect your communications to be sent over tls, of course. +This is more like a community mailbox.' (DIR) diff --git a/geomyidae/postoffice/send-mail b/geomyidae/postoffice/send-mail @@ -1,47 +0,0 @@ -#!/bin/sh -# Args: -# $1 handle of recipient -# $2 message -# -. ./common - -# I am sanitizing inside the ./get-mailbox program, so do I need -# to do it here for any reason? -message=$( sanitize_message "$2") -handle="$1" -result=$( ./get-mailbox -k "$handle" ) -if [ ! $? -eq 0 ]; then - printf "$result" - return 1 -fi -pubkey="$result" - -result=$( ./get-mailbox "$handle" ) -if [ ! $? -eq 0 ]; then - printf "$result" - return 1 -fi -mailbox="$result" - -result=$( printf "%s" "$message" \ - | encrypt_with_key_and_encode "$pubkey" ) -if [ ! $? ]; then - printf "Encryption or encoding failed.\n" - return 1 -fi - -timestamp=$( date ) -printf "%s,%s\n" "$timestamp" "$result" >> "$mailbox" - -# need to verify the message is below a certain length - -# need to verify the chosen border exists, ideally using some -# smart way... -# what if... there is a `type` of mail option. and the first `type` to -# implement will be the `vday card`. there could also be like.. -# `snail mail` or `hate mail` or... `love letters` or... -# `fan mail` - - - - (DIR) diff --git a/geomyidae/postoffice/sendmail.dcgi b/geomyidae/postoffice/sendmail.dcgi @@ -0,0 +1,57 @@ +#!/bin/sh + +. ./api/common + +handle=$( find_value_in_args "handle" "$2" ) +template=$( find_value_in_args "template" "$2" ) +message=$( find_value_in_args "message" "$2" ) + +choose_mailbox() { + ls -x1 "api/$MAILBOXES" \ + | awk -v t="$template" \ + '{ printf "[1|%s|sendmail.dcgi?handle=%s?template=%s|localhost|70]\n", $1, $1, t }' +} + +choose_template() { + ls -x1 "api/$TEMPLATES" \ + | xargs -d '\n' -I x cat "api/$TEMPLATES/"x"/info" \ + | awk -F '\t' -v h="$handle" \ + '{ printf "[1|%s|sendmail.dcgi?template=%s?handle=%s|localhost|70]\n", $2, $1, h }' +} + +show_footer() { + printf "\n\n[1|Start over|sendmail.dcgi|localhost|70]" +} + +if [ -z "$handle" ]; then + printf "Pick recipient:\n" + choose_mailbox + show_footer + return 0 +fi + +if [ -z "$template" ]; then + printf "Pick template:\n" + choose_template + show_footer + return 0 +fi + +# first positional argument will be the rest query, second positional +# argument will be search query string, if present. +result=$( printf "%s" "$message" | api/$TEMPLATES/$template/main "$2" "$1" ) +if [ ! $? -eq 0 ]; then + printf "%s" "$result" + return 0 +fi + +echo "ready to send" + +# echo $handle #debugging +# echo $template #debugging + +show_footer + + + +