#!/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 '
' 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/\([-$^*()+{\[\.?\/]\)/\\\1/g')" ;; "html") echo "sanitizing text $text for $option" >> $log_file text="$(echo "$text" \ | sed -e ':a;N;$!ba;s/\n/
/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/.*\(.*\).*/\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 "" \ | head -n "$trim" \ | tail -n1 | grep -Eo '[0-9]+')" sed -i -e ':a' -e 'N' -e '$!ba' \ -e "s/.*//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 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=".*" insert_at="" ;; "reply") parent="$(get_post_data "$post_db_path" "parent")" replace=".*\n" # find existing reply end tags in the parent post html insert_at="$(cat "$target_html" | grep -E "")" 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="" 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