384 lines
10 KiB
Bash
Executable File
384 lines
10 KiB
Bash
Executable File
#!/bin/sh
|
|
# TODO: gif support and thumbnails if Imagemagick is installed
|
|
# TODO: More comments
|
|
# TODO: discard reply if parent doesnt exist, right now shi hangs
|
|
|
|
backend="./http_backend.sh"
|
|
db_backend="./file_db_backend.sh"
|
|
|
|
. ./globals.sh
|
|
. ./util.sh
|
|
. "$backend"
|
|
. "$db_backend"
|
|
|
|
|
|
sanitize_text() {
|
|
# Sanitizes text, the first argument is the text to be
|
|
# sanitized, the rest represent what sanitations should be applied
|
|
# (html, sed, etc.)
|
|
|
|
# sed sanitation will prepend a leading backslash to sed characters
|
|
# html sanitation will escape '&', '<', and '>' and replace newlines
|
|
# with '<br>' tags
|
|
|
|
# NOTE: 'sed' must be placed before 'html" in the options if they
|
|
# are both specified (This should be fixed later)
|
|
|
|
local text="$1" # text to be sanitized
|
|
|
|
for option in "$@" # parse options to find specified sanitations
|
|
do
|
|
case "$option" in "$text")
|
|
;; # if it is the first option, ignore it
|
|
"sed")
|
|
echo "sanitizing text $text for $option" >> $log_file
|
|
text="$(echo "$text" \
|
|
| sed -e 's/&/&/g' \
|
|
-e 's/</\</g' \
|
|
-e 's/>/\>/g' \
|
|
-e 's/\([-$^*()+{\[\.?\/]\)/\\\1/g')"
|
|
;;
|
|
"html")
|
|
echo "sanitizing text $text for $option" >> $log_file
|
|
text="$(echo "$text" \
|
|
| sed -e ':a;N;$!ba;s/\n/<br>/g')"
|
|
;;
|
|
esac
|
|
done
|
|
|
|
echo "$text"
|
|
}
|
|
|
|
get_op_post_html() {
|
|
# Get the html of an op post if it has already been generated
|
|
local temp_file="$(mktemp)"
|
|
local post_db_path="$1"
|
|
local post_html_path="$(get_post_data "$post_db_path" "html_path")"
|
|
local post_id="$(get_post_data "$post_db_path" "post_id")"
|
|
local trim="$2" # how many replies to get
|
|
local post_replies="$(get_post_data "$post_db_path" "replies")"
|
|
local reply_num="$(echo "$post_replies" | wc -l)" # number of replies to the post
|
|
|
|
|
|
cat "$post_html_path" \
|
|
| sed -e ':a' -e 'N' -e '$!ba' \
|
|
-e "s/.*\(<!--BEGIN OP POST $post_id-->.*<!--END OP POST $post_id-->\).*/\1/g" \
|
|
> "$temp_file"
|
|
|
|
if [ $reply_num -gt $trim ]
|
|
then
|
|
# id of the most recent reply to be included on the board page
|
|
local reply_lim="$(tac "$post_html_path" \
|
|
| grep -E "<!--END REPLY [0-9]+-->" \
|
|
| head -n "$trim" \
|
|
| tail -n1 | grep -Eo '[0-9]+')"
|
|
|
|
|
|
sed -i -e ':a' -e 'N' -e '$!ba' \
|
|
-e "s/<!--BEGIN REPLY [0-9]\+-->.*<!--BEGIN REPLY $reply_lim-->/<!--BEGIN REPLY $reply_lim-->/g" \
|
|
"$temp_file"
|
|
|
|
cat "$temp_file"
|
|
else
|
|
cat "$temp_file"
|
|
fi
|
|
|
|
|
|
rm "$temp_file"
|
|
}
|
|
|
|
gen_post_html() {
|
|
# Takes a path to a database entry and generates html
|
|
# based on it's content
|
|
|
|
# TODO: implement thumbnails
|
|
# TODO: separate html frontend to enable creation of other frontends
|
|
# eval
|
|
|
|
local post_db_path="$1" # db path to post
|
|
local template_path="" # filesystem path to the post template to be used
|
|
|
|
echo "[Genereate post html]" >> "$log_file"
|
|
|
|
echo "Generating post html for $post_db_path" >> "$log_file"
|
|
local date="$(date -u)"
|
|
local user="$(get_post_data "$post_db_path" "user")"
|
|
local title="$(get_post_data "$post_db_path" "title")"
|
|
local content="$(get_post_data "$post_db_path" "content")"
|
|
local image_path="$(get_post_data "$post_db_path" "image_path")"
|
|
local image_size=""
|
|
local image_name=""
|
|
local post_id="$(get_post_data "$post_db_path" "post_id")"
|
|
local post_type="$(get_post_data "$post_db_path" "post_type")"
|
|
|
|
|
|
# http path to post directory (generated from html path)
|
|
local post_url_path="$(get_post_data "$post_db_path" "html_path")"
|
|
post_url_path="$(dirname "$post_url_path")"
|
|
|
|
# http path to image
|
|
local image_url_path=""
|
|
|
|
# http path to thumbail
|
|
local thumb_url_path=""
|
|
|
|
# truncate the url path to start at $board_dir
|
|
case "$post_type" in "oppost")
|
|
post_url_path="$(echo "$post_url_path" \
|
|
| rev \
|
|
| cut -f -3 -d '/' \
|
|
| rev)"
|
|
;;
|
|
"reply")
|
|
post_url_path="$(echo "$post_url_path" \
|
|
| rev \
|
|
| cut -f -4 -d '/' \
|
|
| rev)"
|
|
;;
|
|
esac
|
|
|
|
post_url_path="$shi_sub_url/$post_url_path"
|
|
|
|
if [ -z "$image_path" ] # if no image is found
|
|
then
|
|
post_type="$post_type"-noimage # specify the post type as imageless
|
|
echo "Image path is null, -noimage post" >> "$log_file"
|
|
else
|
|
image_size="$(du -h "$image_path" | cut -f 1)"
|
|
image_name="$(basename "$image_path")"
|
|
image_url_path="$post_url_path/$image_name"
|
|
thumb_url_path="$post_url_path/thumb_$image_name.jpg"
|
|
fi
|
|
|
|
template_path="$template_dir/$post_type".template
|
|
|
|
content="$(sanitize_text "$content" sed html)" # sanitize content
|
|
title="$(sanitize_text "$title" sed)" # sanitize title for sed
|
|
post_url_path="$(sanitize_text "$post_url_path" sed)" # sanitize post url for sed
|
|
image_url_path="$(sanitize_text "$image_url_path" sed)" # sanitize image path for sed
|
|
thumb_url_path="$(sanitize_text "$thumb_url_path" sed)" # sanitize image path for sed
|
|
|
|
# read through the appropriate template and replace
|
|
# placeholders with their actual values
|
|
while read -r line
|
|
do
|
|
echo "$line" \
|
|
| sed -e "s/{{{ POSTNUM }}}/$post_id/g" \
|
|
-e "s/{{{ POSTURL }}}/$post_url_path/g" \
|
|
-e "s/{{{ DATE }}}/$date/g" \
|
|
-e "s/{{{ USER }}}/$user/g" \
|
|
-e "s/{{{ POSTTITLE }}}/$title/g" \
|
|
-e "s/{{{ POSTTEXT }}}/$content/g" \
|
|
-e "s/{{{ IMAGEPATH }}}/$image_url_path/g" \
|
|
-e "s/{{{ THUMBPATH }}}/$thumb_url_path/g" \
|
|
-e "s/{{{ IMAGENAME }}}/$image_name/g" \
|
|
-e "s/{{{ IMAGESIZE }}}/$image_size/g"
|
|
done < "$template_path"
|
|
}
|
|
|
|
insert_post() {
|
|
# TODO: cache (?) the position of <!--BEGIN POSTS--> in the html
|
|
# currently a large and exponential slowdown
|
|
|
|
# read from a cache file, if it doesn't exist, make it
|
|
|
|
local post_db_path="$1" # path to the post data
|
|
local target_html="$2" # the html to be modified
|
|
local trim="3" # how many replies to display on the main board page
|
|
|
|
echo "[Insert post into html]" >> "$log_file"
|
|
echo "Inserting post $post_db_path into $target_html" >> "$log_file"
|
|
|
|
local post_id="$(get_post_data "$post_db_path" "post_id")"
|
|
local post_type="$(get_post_data "$post_db_path" "post_type")"
|
|
local post_replies=""
|
|
local parent=""
|
|
local temp_file="$(mktemp)"
|
|
|
|
local replace="" # regex to find the exitsing post's html
|
|
local insert_at="" # html will be inserted after the line containing this string
|
|
|
|
case "$post_type" in "oppost")
|
|
replace="<!--BEGIN OP POST $post_id-->.*<!--END OP POST $post_id-->"
|
|
insert_at="<!--BEGIN POSTS-->"
|
|
;;
|
|
"reply")
|
|
parent="$(get_post_data "$post_db_path" "parent")"
|
|
replace="<!--BEGIN REPLY $post_id-->.*<!--END REPLY $post_id-->\n"
|
|
# find existing reply end tags in the parent post html
|
|
insert_at="$(cat "$target_html" | grep -E "<!--END REPLY [0-9]+-->")"
|
|
echo "inserting at $insert_at" >> "$log_file"
|
|
if [ -n "$insert_at" ] # if reply end tags are found
|
|
then
|
|
# set `insert_at` to the end tag of the lowermost reply
|
|
insert_at="$(echo "$insert_at" | tail -n1)"
|
|
else
|
|
# else set `insert_at` to beginning of replies
|
|
insert_at="<!--BEGIN REPLIES $parent-->"
|
|
fi
|
|
;;
|
|
esac
|
|
echo "inserting at $insert_at" >> "$log_file"
|
|
|
|
# remove any existing instances of the post in the html
|
|
sed -i \
|
|
-e ':a' \
|
|
-e 'N' \
|
|
-e '$!ba' \
|
|
-e "s/$replace//g" "$target_html"
|
|
|
|
|
|
while read -r line
|
|
do
|
|
echo "$line"
|
|
if echo "$line" | grep -qE "$insert_at"
|
|
then
|
|
echo "Inserting html at line $line matching $insert_at" >> "$log_file"
|
|
if [ "$post_type" = "oppost" ]
|
|
then
|
|
post_replies="$(get_post_data "$post_db_path" "replies")"
|
|
if [ -n "$post_replies" ]
|
|
then
|
|
get_op_post_html "$post_db_path" "$trim"
|
|
else
|
|
gen_post_html "$post_db_path"
|
|
fi
|
|
else
|
|
gen_post_html "$post_db_path"
|
|
fi
|
|
fi
|
|
|
|
done < "$target_html" > "$temp_file"
|
|
cat "$temp_file" > "$target_html"
|
|
|
|
|
|
# NOTE: This section only applies when regenerating an entire board
|
|
# If the post is an oppost, we will need to also generate the replies
|
|
# if [ "$post_type" = "oppost" ]
|
|
# then
|
|
# # NOTE: Very redundant, find a way to process both files at once
|
|
# post_replies=$(get_post_data "$post_db_path" "replies" | tac)
|
|
# for reply_db_path in $post_replies
|
|
# do
|
|
# echo "Inserting "$reply_db_path" into post html" >> "$log_file"
|
|
# insert_post "$reply_db_path" "$(get_post_data "$post_db_path" "html_path")"
|
|
# done
|
|
#
|
|
# # NOTE: This will break if the board directory has spaces
|
|
# for reply_db_path in $(echo $post_replies | cut -f "$trim"- -d ' ')
|
|
# do
|
|
# echo "Inserting "$reply_db_path" into board html" >> "$log_file"
|
|
# insert_post "$reply_db_path" "$target_html"
|
|
# done
|
|
# fi
|
|
}
|
|
|
|
start_listener() {
|
|
# This function reads a continuous stream of data
|
|
# (provided by the `listen` function from a user
|
|
# specified backend) and generates posts from the
|
|
# data it receives.
|
|
|
|
local status=0 # 0 is normal, 1 is receiving post data
|
|
|
|
local board=""
|
|
local parent=""
|
|
local user=""
|
|
local title=""
|
|
local content=""
|
|
local image_name=""
|
|
local image_content=""
|
|
local post_id=""
|
|
local post_type=""
|
|
local post_html_path=""
|
|
|
|
local latest=""
|
|
|
|
listen | while read -r line
|
|
do
|
|
|
|
if [ "$line" = "End new post" ]
|
|
then
|
|
echo [New post] >> "$log_file"
|
|
status=0
|
|
latest="$(cat "$latest_file")"
|
|
post_id="$(( latest + 1 ))"
|
|
post_db_path="$(create_post \
|
|
"$board" \
|
|
"$parent" \
|
|
"$user" \
|
|
"$title" \
|
|
"$content" \
|
|
"$image_name" \
|
|
"$image_content" \
|
|
"$post_id" \
|
|
"$post_type")"
|
|
|
|
post_html_path="$(get_post_data "$post_db_path" "html_path")"
|
|
parent_html_path=""
|
|
parent_db_path=""
|
|
|
|
|
|
if [ "$post_type" == "oppost" ]
|
|
then
|
|
cp "$board_dir/$board/index.html.template" "$post_html_path"
|
|
cp "$template_dir/main.css.template" "$(dirname "$post_html_path")/main.css"
|
|
insert_post "$post_db_path" "$post_html_path"
|
|
insert_post "$post_db_path" "$board_dir/$board/index.html"
|
|
else
|
|
parent_html_path="$board_dir/$board/$parent/index.html"
|
|
parent_db_path="$board_dir/$board/$parent"
|
|
insert_post "$post_db_path" "$parent_html_path"
|
|
insert_post "$parent_db_path" "$board_dir/$board/index.html"
|
|
fi
|
|
|
|
|
|
echo "$post_id" > "$latest_file" # update the latest post file
|
|
echo "Finished adding post $post_id at $(date)"
|
|
fi
|
|
|
|
if [ "$status" -eq 1 ]
|
|
then
|
|
key="$(echo "$line" | cut -f 1 -d :)" value="$(echo "$line" | cut -f 2- -d :)"
|
|
|
|
echo "Reading key $key from backend" >> "$log_file"
|
|
case "$key" in "board")
|
|
board="$value"
|
|
;;
|
|
"parent")
|
|
parent="$value"
|
|
if [ -z "$parent" ]
|
|
then
|
|
post_type="oppost"
|
|
else
|
|
post_type="reply"
|
|
fi
|
|
;;
|
|
"user")
|
|
user="$value"
|
|
;;
|
|
"title")
|
|
title="$value"
|
|
;;
|
|
"content")
|
|
content="$value"
|
|
;;
|
|
"image_name")
|
|
image_name="$value"
|
|
;;
|
|
"image_content")
|
|
image_content="$value"
|
|
;;
|
|
esac
|
|
fi
|
|
|
|
if [ "$line" = "New post" ]
|
|
then
|
|
echo "Processing new post" >> "$log_file"
|
|
status=1
|
|
fi
|
|
done
|
|
}
|
|
|
|
trap break EXIT INT TERM
|