Initial verion of notes.sh

This commit is contained in:
Konstantin Nazarov 2021-04-13 20:44:09 +00:00
commit ec46a11cc6
Signed by: knazarov
GPG key ID: 4CFE0A42FA409C22
2 changed files with 234 additions and 0 deletions

7
README.md Normal file
View file

@ -0,0 +1,7 @@
# notes.sh: plain-text notes with IMAP synchronization
This project allows you to keep plaintext notes, and attach files to them (like images, PDFs, etc). The plaintext stays plaintext, easily greppable, and with every note in its own file.
What makes it special is that it uses a Maildir format to store notes. This allows you to easily sync your notes with any mail server, and have your notes accessible on the go through your email client.
NB: This is alpha software, use with caution.

227
notes.sh Executable file
View file

@ -0,0 +1,227 @@
#!/bin/bash
set -e
DATE_FORMAT="%a, %d %b %Y %H:%M:%S %z"
BASEDIR=~/Maildir/personal/Notes
PID=$$
if [ ! -d "$BASEDIR" ]; then
mkdir -p "$BASEDIR"/{tmp,new,cur}
fi
uuid()
{
local N B T
for (( N=0; N < 16; ++N ))
do
B=$(( $RANDOM%255 ))
if (( N == 6 ))
then
printf '4%x' $(( B%15 ))
elif (( N == 8 ))
then
local C='89ab'
printf '%c%x' ${C:$(( $RANDOM%${#C} )):1} $(( B%15 ))
else
printf '%02x' $B
fi
for T in 3 5 7 9
do
if (( T == N ))
then
printf '-'
break
fi
done
done
echo
}
get_header() {
FILE="$1"
HEADER="$2"
echo $(grep -m1 "^${HEADER}: " < "$FILE" | sed -n "s/^${HEADER}: \(.*\)$/\1/p")
}
get_part() {
FILE="$1"
BOUNDARY="$2"
NUM="$3"
FILTER="\
BEGIN { \
rec=0; \
body=0; \
} \
{ \
if (\$0==\"--$BOUNDARY\" || \$0==\"--$BOUNDARY--\") { \
if (body == 0) { \
body=1; \
} \
rec=rec+1; \
} \
else if (body == 1 && rec==$NUM) { \
print \$0 \
} \
}"
awk "$FILTER" "$FILE"
}
unpack_part() {
FILE="$1"
DIR="$2"
MIME_TYPE=$(get_header "$FILE" Content-Type)
DISPOSITION=$(get_header "$FILE" Content-Disposition)
if [[ $DISPOSITION == *"attachment"* ]]; then
ENCODING=$(get_header "$FILE" Content-Transfer-Encoding)
FILENAME=$(echo $DISPOSITION | \
sed -n 's/^.*filename="\{0,1\}\(.*\)"\{0,1\}$/\1/p')
FILTER="\
{ \
if (body==1) {print \$0}\
if (\$0~/^$/) {body=1} \
} \
"
if [[ $ENCODING == *"base64"* ]]; then
awk "$FILTER" "$FILE" | base64 --decode >> "$DIR/$FILENAME"
else
awk "$FILTER" "$FILE" >> "$DIR/$FILENAME"
fi
elif [[ $MIME_TYPE == *"text/plain"* ]]; then
FILTER="\
{ \
if (body==1) {print \$0}\
if (\$0~/^$/) {body=1} \
} \
"
awk "$FILTER" "$FILE" >> "$DIR/note.md"
elif [[ $MIME_TYPE == *"multipart/mixed"* ]]; then
BOUNDARY=$(echo $MIME_TYPE | sed -n 's/^.*boundary="\(.*\)"$/\1/p')
i=1
while true; do
TMP=$(mktemp --tmpdir="$DIR")
get_part "$FILE" "$BOUNDARY" "$i" > "$TMP"
((i++))
if [ ! -s "$TMP" ]; then
rm "$TMP"
break
fi
#cat "$TMP"
(unpack_part "$TMP" "$DIR")
rm "$TMP"
done
elif [[ $MIME_TYPE == *"multipart/related"* ]]; then
echo "multipart/related not yet supported"
exit 1
fi
}
unpack_mime() {
FILE="$1"
DIR="$2"
DATE=$(get_header "$FILE" Date)
SUBJECT=$(get_header "$FILE" Subject)
MIME_TYPE=$(get_header "$FILE" Content-Type)
MESSAGE_ID=$(get_header "$FILE" Message-Id)
echo "Date: $DATE" > "$DIR/note.md"
if [ ! -z "$MESSAGE_ID" ]; then
echo "Message-Id: $MESSAGE_ID" >> "$DIR/note.md"
fi
echo "Subject: $SUBJECT" >> "$DIR/note.md"
echo "" >> "$DIR/note.md"
TMP=$(mktemp --tmpdir="$DIR")
cat "$FILE" > "$TMP"
(unpack_part "$TMP" "$DIR")
rm "$TMP"
}
new_entry() {
ENTRY_FILE=$(mktemp)
vim -c ":set syntax=markdown" \
-c "1 s/^/# /" \
-c "norm $" \
"$ENTRY_FILE"
if [ -s "$ENTRY_FILE" ]; then
MIME_TIMESTAMP=$(LC_ALL="en_US.UTF-8" date "+$DATE_FORMAT")
UNIX_TIMESTAMP=$(date "+%s")
HOSTNAME=$(hostname -s)
SUBJECT=$(head -1 "$ENTRY_FILE")
SUBJECT=${SUBJECT#"# "}
RESULT=$(mktemp)
echo "Date: $MIME_TIMESTAMP" >> "$RESULT"
echo "MIME-Version: 1.0" >> "$RESULT"
echo "Content-Type: text/plain; charset=utf-8" >> "$RESULT"
echo "Content-Disposition: inline" >> "$RESULT"
echo "Message-Id: <$(uuid)@notes.sh>" >> "$RESULT"
echo "Subject: $SUBJECT" >> "$RESULT"
echo "" >> "$RESULT"
sed "1d" < "$ENTRY_FILE" >> "$RESULT"
#cat "$RESULT"
FILENAME="$UNIX_TIMESTAMP.${PID}_1.${HOSTNAME}:2,S"
mv "$RESULT" "$BASEDIR/cur/$FILENAME"
echo "done"
fi
}
edit_entry() {
unpack_mime "$1" ~/scratch/notes
}
list_entries() {
grep -m1 -r "^Subject:" "$BASEDIR" | sed -n 's/^\(.*\):Subject: \(.*\)$/\1:\2/p'
}
usage() {
echo "$0 {--new,--list,--edit,--help}"
}
while (( "$#" )); do
case "$1" in
-n|--new)
new_entry
exit 0
;;
-l|--list)
list_entries
exit 0
;;
-e|--edit)
if [ -z "$2" ]; then
echo "Misssing argument for $1"
exit 1
fi
edit_entry "$2"
exit 0
;;
*)
usage
exit 1
;;
esac
done
usage
exit 1