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