diff options
Diffstat (limited to 'scripts')
-rwxr-xr-x | scripts/parts | 191 |
1 files changed, 191 insertions, 0 deletions
diff --git a/scripts/parts b/scripts/parts new file mode 100755 index 0000000..ae73c2f --- /dev/null +++ b/scripts/parts @@ -0,0 +1,191 @@ +#! /bin/sh +# usage: parts list +# usage: parts add PATH [NAME [DESCRIPTION]] +set -efu + + +list_parts() { + jq -r ' + def replicate(n; x): + if n == 0 then "" else n * x end + ; + def flattenMap(inner): + def addDepth(depth): + if type == "array" then + map(addDepth(depth + 1)) + else + { depth: depth, value: . } + end + ; + def addIndex: + to_entries | map(.value + { index: .key }) + ; + def getPrefix: + match("( *).*").captures[0].string + ; + def stripPrefix: + match(" *(.*)").captures[0].string + ; + def shiftPrefix: + match("( *[^ ]+)( *)( .*)").captures|map(.string)|.[1]+.[0]+.[2] + ; + addDepth(0) | flatten | addIndex | map(inner) | map(shiftPrefix) + ; + def parseContentType: + if . != null then . else "text/plain; charset=UTF-8" end | + split(";\\s*";"") | + (.[0] | split("/")) as $compoundType | + $compoundType[0] as $type | + $compoundType[1] as $subtype | + (.[1:] | map(split("=") | { key: .[0], value: .[1] }) | from_entries) + as $params | + { + $type, $subtype, $params, + } + ; + def formatPart: + .body as $body | + ((.headers // []) | from_entries) as $headers | + ($headers["content-type"] | parseContentType) as $ct | + "\($ct.type)/\($ct.subtype)" as $compoundType | + $ct.params.name as $name | + [ "\($compoundType) \($name | tojson) \($body | length)" ] + + if $ct.type == "multipart" then .body | map(formatPart) else [] end + ; + + formatPart | + flattenMap("\(replicate(.depth - 1; " "))part#\(.index + 1) \(.value)")[] + ' +} + +add_part() {( + filepath=$1 + filename=${2-$(basename "$filepath")} + description=${3-$filename} + + contentType="$(file -Lib "$filepath"); name=$filename" + case $contentType in + text/plain|text/plain\;*) + contentTransferEncoding=8bit + content() { + cat "$filepath" + } + ;; + *) + contentTransferEncoding=base64 + content() { + base64 "$filepath" + } + ;; + esac + contentDisposition="attachment; filename=$filename" + contentDescription=$description + + boundary=$(date -Ins | sha1sum | cut -d\ -f1) + + { + jq '{ mail: . }' + content | jq -Rs '{ content: . }' + } | + jq -sr \ + --arg boundary "$boundary" \ + --arg contentType "$contentType" \ + --arg contentTransferEncoding "$contentTransferEncoding" \ + --arg contentDisposition "$contentDisposition" \ + --arg contentDescription "$contentDescription" \ + ' + add | + + .mail as $mail | + .content as $content | + + def parseContentType: + if . == null then null else + split(";\\s*";"") | + (.[0] | split("/")) as $type | + (.[1:] | map(split("=") | { key: .[0], value: .[1] }) | from_entries) + as $parameters | + { + type: $type[0], + subtype: $type[1], + parameters: $parameters, + } + end; + + ($mail.headers | from_entries) as $headers | + ($headers["content-type"] | if . == null then + { + type: "text", + subtype: "plain", + parameters: { + charset: "UTF-8", + }, + } + else parseContentType + end) + as $ct | + + + def add_part: + { + headers: ({ + "content-type": $contentType, + "content-transfer-encoding": $contentTransferEncoding, + "content-disposition": $contentDisposition, + "content-description": $contentDescription, + } | to_entries), + body: $content, + } + as $part | + { headers: .headers + , body: (.body + [$part]) + }; + + ($mail.headers | from_entries) as $headers | + + def is_multipart_mixed: + $headers["content-type"] // "" | test("^multipart/mixed\\b.*"); + + if is_multipart_mixed then + $mail | add_part + else + { headers: ( + $mail.headers | + map(select(.key != "mime-version")) | + map(select(.key != "content-type")) | + map(select(.key != "content-transfer-encoding")) | + . + ({ + "mime-version": "1.0", + "Content-Type": "multipart/mixed; boundary=\"----=\($boundary)\"", + } | to_entries) + ) + , body: [{ + headers: ({ + "content-type": + ($headers["content-type"] // "text/plain; charset=UTF-8"), + "content-transfer-encoding": + ($headers["content-transfer-encoding"] // "8bit"), + } | to_entries), + body: $mail.body, + }] + } | + add_part + end + ' +)} + + + +command=$1; shift +case $command in + list) + mailaid | list_parts + ;; + add) + mailaid | add_part "$@" | mailaid -d + ;; + *) + echo "$0: error: unknown command: $command" >&2 + exit 1 + ;; +esac |