shi 2
This commit is contained in:
parent
65e628570c
commit
db0190b0ff
Binary file not shown.
Binary file not shown.
140
README.md
140
README.md
|
@ -1,33 +1,131 @@
|
|||
#shi
|
||||
# shi
|
||||
**SH**ell **I**mageboard
|
||||
|
||||
no php, no js, just POSIX shell, gnu coreutils, nc, html and css
|
||||
the programming equivalent of a shitpost
|
||||
(second edition)
|
||||
|
||||
### features
|
||||
- its all POSIX sh
|
||||
- seriously
|
||||
- posting
|
||||
- replies
|
||||
- images
|
||||
- timestamps
|
||||
- more
|
||||
|
||||
### to use it
|
||||
- `git clone <this repo>`
|
||||
- `cd <this repos folder>`
|
||||
- `touch log.txt`
|
||||
- `cp templates/mainindex.html ./index.html`
|
||||
- point your web server to ./index.html
|
||||
- to run shi
|
||||
- `./shi.sh &`
|
||||
- `tail -f log.txt`
|
||||
- to make a board:
|
||||
- `mkdir -p boards/<boardname>`
|
||||
- `cp templates/index.html.template boards/<boardname>/index.html`
|
||||
- `cp templates/main.css.template boards/<boardname>/main.css`
|
||||
- to stop shi
|
||||
- `killall shi.sh`
|
||||
- to post to shi
|
||||
- copy the script at the top of the board into your terminal
|
||||
- hit enter, follow the prompts
|
||||
### how to set up
|
||||
- clone this repo
|
||||
- cd to this repo
|
||||
- `. ./console.sh` to start the shi console
|
||||
- `start_listener` to start lisenting for posts
|
||||
- you might want to do `tail -f log.txt` in another terminal to see whats going on
|
||||
### how backends work
|
||||
- shi has a builtin framework for swappable backends both for post recieving and storage,
|
||||
currently there is:
|
||||
- tar backend
|
||||
- like the old version of shi
|
||||
- recives tar files through nc and generates posts
|
||||
- bad
|
||||
- dont use it
|
||||
- http backend
|
||||
- listens for an http POST request with form data
|
||||
- allows user-friendly html post form
|
||||
- cool
|
||||
- good
|
||||
- file db backend
|
||||
- stores posts as a series of text files in folders
|
||||
- unchanged from old shi
|
||||
- ok
|
||||
- **how backends work**
|
||||
- **listening**
|
||||
- a backend is a shell script with a `listen` function
|
||||
- this function is expected to coninuosly output post data it recives like so:
|
||||
```
|
||||
New post
|
||||
board:<board name>
|
||||
parent:<parent of post>
|
||||
user:<user>
|
||||
title:<title>
|
||||
content:<content>
|
||||
image_name:<name of image>
|
||||
image content:<base64 encoded image>
|
||||
End new post
|
||||
```
|
||||
- as long as the backend has a listen function that outputs data to stdout like this,
|
||||
shi doesn't care where it comes from.
|
||||
- **db**
|
||||
- a db backend is simply a shell script provinding the following funtions:
|
||||
- `path_to_post <post id>`
|
||||
- `create post <board> <parent> <user> <title> <content> <image_name> <image_content> <post_id> <post_type>`
|
||||
- `get_post_data <post_data_path> <item>
|
||||
- note that since these functions are both in the same file, `post_data_path` can be in any format you desire
|
||||
- again, shi doesn't care how this is done, only that it follows this format
|
||||
|
||||
### anatomy of a post
|
||||
- user
|
||||
- name of user who posted the post
|
||||
- title
|
||||
- title of post
|
||||
- this will be ignored for replies
|
||||
- content
|
||||
- the text content of a post
|
||||
- thumb_path
|
||||
- optional, filesystem path to the thumbnail of the post's image
|
||||
- image_path
|
||||
- optional, filesystem path to the image
|
||||
- html_path
|
||||
- path to where the html of the post is stored
|
||||
- post_id
|
||||
- unique numerical id of the post
|
||||
- post_type
|
||||
- type of post, specifies what template to use
|
||||
- currently there is 'oppost' and 'reply' supported
|
||||
- replies
|
||||
- newline separated list of post_id's for all the replies to the op post
|
||||
- a trailing /. is added to prevent `find` from seeing `post_data_path` itself
|
||||
|
||||
### defining new post templates
|
||||
- edit your template of choice in the 'templates' folder,
|
||||
- {{{ TRIPLE BRACES }}} will be replaced with their respective variables
|
||||
|
||||
### style notes
|
||||
- if it can be done in a single line, you can set a variable to it,
|
||||
otherwise declare the variable as empty and then set it to your operation
|
||||
- follow shellcheck
|
||||
|
||||
# files n folders
|
||||
- boards
|
||||
- where all the html for the boards is stored
|
||||
- console.sh
|
||||
- souce this to allow you to control shi
|
||||
- globals.sh
|
||||
- global variables (really just settings)
|
||||
- inbox
|
||||
- where posts data is cached before being processed
|
||||
- latest
|
||||
- the id of the latest post
|
||||
- very important!!
|
||||
- LICENSE
|
||||
- free software, free society
|
||||
- log.txt
|
||||
- log
|
||||
- problems.txt
|
||||
- current issues with shi
|
||||
- README.md
|
||||
- this file
|
||||
- reset.sh
|
||||
- debug script that nukes all boards and completly resets shi
|
||||
- don't use this unless you've really screwed up
|
||||
- shi.sh
|
||||
- memories
|
||||
- templates
|
||||
- templates
|
||||
- util.sh
|
||||
- utility functions employed by console.sh
|
||||
|
||||
|
||||
i know the file permissions are weird i'll fix them at some point
|
||||
|
||||
|
||||
this whole rewrite started because of a feature request by desvox. Unfortunately, due to the complexity of responding to http requests with sh, this feature was not added.
|
||||
|
||||
Sorry desvox :(
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
#!/bin/sh
|
||||
. ./globals.sh
|
||||
. ./util.sh
|
||||
. ./shi2.sh
|
||||
|
||||
alias log="tail -f $log_file"
|
||||
|
||||
echo "Your shell is now a shi console!"
|
||||
oldps1="$PS1"
|
||||
PS1="shi>"
|
||||
listener_pid=""
|
||||
|
||||
|
||||
start_shi() {
|
||||
start_listener &
|
||||
listener_pid="$!"
|
||||
echo "Started shi"
|
||||
}
|
||||
|
||||
stop_shi() {
|
||||
kill -- -"$listener_pid"
|
||||
# kill -- -$(ps -o pgid= $listener_pid | grep -o [0-9]*)
|
||||
echo "Stopped shi"
|
||||
}
|
||||
|
||||
restart(){
|
||||
stop_shi
|
||||
. ./globals.sh
|
||||
. ./util.sh
|
||||
. ./shi2.sh
|
||||
echo "Reloaded source files"
|
||||
start_shi
|
||||
}
|
||||
|
||||
#tail -f "$log_file" & # uncomment this if you want constant log output to console
|
||||
|
||||
|
||||
cleanup() {
|
||||
PS1="$oldps1"
|
||||
alias log="log"
|
||||
}
|
||||
|
||||
trap cleanup EXIT INT TERM
|
|
@ -0,0 +1,157 @@
|
|||
#!/bin/sh
|
||||
# the file/folder db backend
|
||||
# since the backend provides both the add_post and fetch_post
|
||||
# functions the data path can be in whatever format you like
|
||||
# TODO: Post deletion, both on front and backend
|
||||
|
||||
# NOTE: A post db path should always begin at $board_dir
|
||||
|
||||
. ./globals.sh
|
||||
|
||||
|
||||
path_to_post() {
|
||||
post_id="$1"
|
||||
find "$board_dir" -maxdepth 4 -name "$post_id" -not -path '*/\.*'
|
||||
}
|
||||
|
||||
create_post() {
|
||||
# This function takes strings correspoding to post
|
||||
# data and stores them in the boards folder in the
|
||||
# shi folder/file format
|
||||
|
||||
|
||||
local board="$1"
|
||||
local parent="$2"
|
||||
local user="$3"
|
||||
local title="$4"
|
||||
local content="$(echo "$5" | tr '\a' '\n')" # change the BEL characters back to newlines
|
||||
local image_name="$6"
|
||||
local image_content="$7"
|
||||
local post_id="$8"
|
||||
local post_type="$9"
|
||||
|
||||
echo "Adding data for post with id $post_id" >> "$log_file"
|
||||
|
||||
local post_directory="$board_dir/$board/$parent/$post_id" # where the post data will be stored
|
||||
post_directory="$(realpath "$post_directory")"
|
||||
|
||||
echo "[file_db_backend.sh] board: $board" >> "$log_file"
|
||||
echo "[file_db_backend.sh] parent: $parent" >> "$log_file"
|
||||
echo "[file_db_backend.sh] user: $user" >> "$log_file"
|
||||
echo "[file_db_backend.sh] title: $title" >> "$log_file"
|
||||
echo "[file_db_backend.sh] content: $content" >> "$log_file"
|
||||
echo "[file_db_backend.sh] image_name: $image_name" >> "$log_file"
|
||||
# uncomment this if you want base64 spam vvv
|
||||
# echo "image_content: $image_content" >> "$log_file"
|
||||
# echo "image_content: $image_content" | head -c 50 >> "$log_file"
|
||||
echo "[file_db_backend.sh] post id: $post_id" >> "$log_file"
|
||||
echo "[file_db_backend.sh] post type: $post_type" >> "$log_file"
|
||||
|
||||
if [ -z "$user" ]; then echo "[file_db_backend.sh] ERROR: no user" >> "$log_file"; return 1; fi
|
||||
if [ -z "$content" ]; then echo "[file_db_backend.sh] ERROR: no content"; return 1; fi
|
||||
if ! (mkdir -p "$post_directory")
|
||||
then
|
||||
echo "[file_db_backend.sh] ERROR: could not create post dir" >> "$log_file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local image_path="$post_directory/$image_name"
|
||||
|
||||
echo "$user" > "$post_directory/user"
|
||||
echo "$title" > "$post_directory/title"
|
||||
echo "$content" > "$post_directory/content"
|
||||
echo "$post_type" > "$post_directory/type"
|
||||
if [ -n "$image_name" ] && [ -n "$image_content" ]
|
||||
then
|
||||
echo "$image_content" | base64 -d > "$image_path"
|
||||
echo "$image_name" > "$post_directory/image_name"
|
||||
fi
|
||||
|
||||
if command -v convert > /dev/null && [ -n "$image_name" ]
|
||||
then
|
||||
local thumb_path="$post_directory/thumb_$image_name.jpg"
|
||||
echo "[file_db_backend.sh] 'convert' found, generating thumbnail" >> "$log_file"
|
||||
# convert the image to a jpeg
|
||||
if echo "$image_path" | grep '.gif'
|
||||
then
|
||||
convert "$image_path"'[0]' "$thumb_path"
|
||||
else
|
||||
convert "$image_path" "$thumb_path"
|
||||
echo "[file_db_backend.sh] converted image to jpeg" >> "$log_file"
|
||||
fi
|
||||
# re-compress the jpeg to be under 100kb
|
||||
convert "$thumb_path" -define jpeg:extent=100kb "$thumb_path"
|
||||
echo "[file_db_backend.sh] re compressed image at $thumb_path" >> "$log_file"
|
||||
fi
|
||||
|
||||
echo "[file_db_backend.sh] Finished adding post $post_id on $(date)" >> "$log_file"
|
||||
echo "[file_db_backend.sh] post dir: $post_directory" >> "$log_file"
|
||||
|
||||
echo "$post_directory"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
get_post_data() {
|
||||
# this function is given a post id and a data item to be
|
||||
# retrieved. It returns the value of the requested item
|
||||
|
||||
# TODO: file path caching
|
||||
|
||||
local post_data_path="$1"
|
||||
local item="$2"
|
||||
local image_path=""
|
||||
|
||||
echo "[file_db_backend.sh] Got request for $item from post at $post_data_path" >> "$log_file"
|
||||
|
||||
if ! [ -d "$post_data_path" ]; then return 1; fi
|
||||
|
||||
case "$item" in "parent")
|
||||
basename $(realpath "$post_data_path/..")
|
||||
;;
|
||||
"user")
|
||||
cat "$post_data_path/user"
|
||||
;;
|
||||
"title")
|
||||
cat "$post_data_path/title"
|
||||
;;
|
||||
"content")
|
||||
cat "$post_data_path/content"
|
||||
;;
|
||||
"thumb_path") # where the post's image is stored on the filesystem
|
||||
echo "thumb_$(cat "$post_data_path/$image_name").jpg"
|
||||
;;
|
||||
"image_path") # where the post's image is stored on the filesystem
|
||||
image_path="$post_data_path/$(cat "$post_data_path/image_name")"
|
||||
if [ -f "$image_path" ]
|
||||
then
|
||||
echo "$image_path"
|
||||
else
|
||||
echo ""
|
||||
fi
|
||||
;;
|
||||
"html_path") # where the post's html is on the filesystem
|
||||
echo "$post_data_path/index.html"
|
||||
;;
|
||||
"post_id")
|
||||
basename "$post_data_path"
|
||||
;;
|
||||
"post_type")
|
||||
cat "$post_data_path/type"
|
||||
;;
|
||||
"replies")
|
||||
# Newline separated list of post_id for all replies to the
|
||||
# specified post
|
||||
# A trailing /. is added to prevent find from seeing
|
||||
# post_data_path itself
|
||||
|
||||
find "$post_data_path" \
|
||||
-mindepth 1 \
|
||||
-type d \
|
||||
-iname '[0-9]*' \
|
||||
-printf '%p\n' | sort
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# no paths, only use the post id
|
|
@ -0,0 +1,8 @@
|
|||
port="7070"
|
||||
log_file="./log.txt"
|
||||
board_dir="./boards"
|
||||
template_dir="./templates"
|
||||
latest_file="./latest"
|
||||
inbox="./inbox"
|
||||
image_types="(\.png|\.jpg|\.gif)"
|
||||
shi_sub_url="/shi" # the sub url shi is being run from (eg. http://domain.tld/[shi/])
|
|
@ -0,0 +1,147 @@
|
|||
#!/bin/sh
|
||||
. ./globals.sh
|
||||
|
||||
|
||||
echo "Using http backend." >> "$log_file"
|
||||
|
||||
gen_post_data_from_file() {
|
||||
# TODO: if variables are unset by the end, set them to "error"
|
||||
local file="$1"
|
||||
local board=""
|
||||
local parent=""
|
||||
local user=""
|
||||
local title=""
|
||||
local content=""
|
||||
local image_name=""
|
||||
local image_content=""
|
||||
|
||||
# find the form boundary
|
||||
local boundary="$(grep -am1 \
|
||||
"Content-Type: multipart/form-data; boundary=" \
|
||||
$file | cut -f 2- -d "=")"
|
||||
|
||||
local in_boundary="0"
|
||||
local data_type=""
|
||||
local file_upload_start="" # the line at which binary file data starts (if any)
|
||||
local iterator="0"
|
||||
echo "New post"
|
||||
while read -r line
|
||||
do
|
||||
iterator="$(( iterator + 1 ))"
|
||||
# dont read the binary data contained in files,
|
||||
# as it will break the script
|
||||
if echo "$line" | grep -q 'Content-Disposition: form-data; name="fileupload";'
|
||||
then
|
||||
file_upload_start="$iterator"
|
||||
break
|
||||
fi
|
||||
|
||||
# if a boundary is seen
|
||||
if echo "$line" | grep -q -- "$boundary"
|
||||
then
|
||||
# and we are not already in a boundary
|
||||
if [ "$in_boundary" -eq 0 ]
|
||||
then
|
||||
# then we are now in a boundary
|
||||
in_boundary="1"
|
||||
else
|
||||
# otherwise, we are in a new boundary,
|
||||
# so reset data_type
|
||||
data_type=""
|
||||
fi
|
||||
fi
|
||||
|
||||
# if we are in a boundary, the beginning of form data is seen,
|
||||
# and we are not already reading form data
|
||||
if [ "$in_boundary" -eq "1" ] \
|
||||
&& echo "$line" \
|
||||
| grep -q "Content-Disposition: form-data; name=" \
|
||||
&& [ -z "$data_type" ]
|
||||
then
|
||||
|
||||
data_type=$(echo "$line" | cut -f 2 -d '"')
|
||||
fi
|
||||
|
||||
case "$data_type" in "post_to")
|
||||
# remove LFs from line
|
||||
line="$(echo "$line" | tr -d '\r')"
|
||||
|
||||
# if our post is a reply
|
||||
if echo "$line" | grep -qE "[0-9]$"
|
||||
then
|
||||
# set parent to the post it is replying to
|
||||
parent="$(find $board_dir \
|
||||
-maxdepth 3 \
|
||||
-name "$line" \
|
||||
-not -path '*/\.*')"
|
||||
# make sure the parent post exists
|
||||
if [ -z "$parent" ]; then return 1; fi
|
||||
# and set the board value to the board it is to be posted to
|
||||
board="$(basename `dirname "$parent"`)"
|
||||
# then change the parent value from adirectory path to a plain string
|
||||
parent="$(basename `realpath "$parent"`)"
|
||||
else # otherwise the post is a oppost
|
||||
# set the board value appropriately
|
||||
board="$line"
|
||||
fi
|
||||
;;
|
||||
"name")
|
||||
user="$(echo "$line" | tr -d '\r')"
|
||||
;;
|
||||
"title")
|
||||
title="$(echo "$line" | tr -d '\r')"
|
||||
;;
|
||||
"content")
|
||||
# carrige returns need to be removed from multiline content
|
||||
content="$content$(printf "\n%s\n" "$line" | tr -d '\r')"
|
||||
;;
|
||||
"fileupload")
|
||||
echo "read line of fileupload"
|
||||
;;
|
||||
esac
|
||||
done < "$file"
|
||||
|
||||
# remove the first line from content, which is html form junk
|
||||
content="$(echo "$content" | sed -e '1,2d' | tr '\n' '\a')"
|
||||
image_name="$(sed -n "$file_upload_start"p $file \
|
||||
| grep -oE 'filename=".*' \
|
||||
| cut -f 2 -d '"' \
|
||||
| tr -d '"' )"
|
||||
image_content="$(sed -e "1,$(( file_upload_start + 2 ))d" "$file" | base64 -w 0)"
|
||||
|
||||
echo "board:$board"
|
||||
echo "parent:$parent"
|
||||
echo "user:$user"
|
||||
echo "title:$title"
|
||||
echo "content:$content"
|
||||
echo "image_name:$image_name"
|
||||
echo "image_content:$image_content"
|
||||
echo "End new post"
|
||||
rm "$file"
|
||||
}
|
||||
|
||||
get_request() {
|
||||
submission_id="$(date '+%s%N')" # unique id of the submission
|
||||
printf "HTTP/1.1 200 OK\r
|
||||
Date: %s\r
|
||||
Server: BSD Netcat/lmao\r
|
||||
Connection: close\r
|
||||
Content-Type: text/html\r\n\r\n
|
||||
<script>window.close();</script>\n" "$(date -Ru | sed -e 's/\+0000/GMT/')" \
|
||||
| nc -lp "$port" -w 3 | head -c 1000000000 > "$inbox/$submission_id"
|
||||
|
||||
mv "$inbox/$submission_id" "$inbox/$submission_id.submission"
|
||||
}
|
||||
|
||||
listen() {
|
||||
while true
|
||||
do
|
||||
# TODO: scale to allow for posting in rapid succession
|
||||
get_request
|
||||
for post in $(find "$inbox" -iname "*.submission")
|
||||
do
|
||||
gen_post_data_from_file "$post"
|
||||
done
|
||||
done
|
||||
}
|
||||
trap break EXIT INT TERM
|
|
@ -0,0 +1,108 @@
|
|||
html *
|
||||
{
|
||||
color: #fff !important;
|
||||
font-family: monospace !important;
|
||||
}
|
||||
|
||||
div.postContainer {
|
||||
border-style: solid;
|
||||
padding-left: 5px;
|
||||
margin: 0px;
|
||||
margin-top: 5px;
|
||||
width: 75%;
|
||||
float: left;
|
||||
border-width: 1px;
|
||||
/*display: inline;*/
|
||||
}
|
||||
|
||||
div.opContainer {
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
padding: 0px 5px;
|
||||
max-width: 1000px;
|
||||
margin: 0px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
div.opContainer + div.opContainer {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
div.postHeader {
|
||||
padding: 0px;
|
||||
}
|
||||
div.opContent {
|
||||
/*display: inline;*/
|
||||
}
|
||||
|
||||
div.postContent {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
p.opText {
|
||||
/*font-family: sans;*/
|
||||
padding-bottom: 2px;
|
||||
margin: 0px;
|
||||
margin-top: 2px;
|
||||
padding-bottom: 10px;
|
||||
font-size: 12px;
|
||||
height: 12px;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
p.postText {
|
||||
/*font-family: sans;*/
|
||||
word-wrap: break-word;
|
||||
padding-left: 5px;
|
||||
padding-bottom: 2px;
|
||||
margin: 0px;
|
||||
margin-top: 2px;
|
||||
font-size: 12px;
|
||||
}
|
||||
p.imageName {
|
||||
margin: 0;
|
||||
margin-top: -5px;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
div.imageContainer {
|
||||
height: 100%;
|
||||
width: 20%;
|
||||
padding-right: 5px;
|
||||
float: left;
|
||||
display: block;
|
||||
}
|
||||
img.postImage {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
width: auto;
|
||||
}
|
||||
div.reply {
|
||||
border-style: double;
|
||||
}
|
||||
p.box {
|
||||
border-style: double;
|
||||
}
|
||||
p.postTitle {
|
||||
margin: 0px;
|
||||
font-size: 14px;
|
||||
display: inline-block;
|
||||
padding-left: 9%;
|
||||
}
|
||||
a.postHeaderText {
|
||||
margin: 1px;
|
||||
font-size: 10px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
p.replyHeaderText {
|
||||
margin: 1px;
|
||||
font-size: 10px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
body {
|
||||
padding: 20px;
|
||||
background-color: black;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
readdat () { read -r var; echo "$var" | grep -qE "[a-Z].*" || var=$2; echo "$var" > "$1"; };echo "Post to:"; read -r parent; echo "$parent" | grep -qE "([a-Z]|[1-9]).*" || exit;postdir=$(echo "$parent"-"$RANDOM");mkdir ./"$postdir" && cd ./"$postdir" || exit; echo "Name (blank for anonymous):";readdat "user" "Anonymous";echo "Title (blank for none):";readdat "title" "";echo "Absolute path to image (blank for none):";read -r imagepath; echo "$imagepath" | grep -qE "[a-Z].*" && if test -f
|
||||
"$imagepath"; then cp "$imagepath" .;else echo "could not find $imagepath";fi;echo "Post text (Ctrl-D to finish):"; cat > content; cd ..; tar -cf "$postdir".tar ./"$postdir";
|
|
@ -0,0 +1,11 @@
|
|||
incredibly slow to post images
|
||||
|
||||
not entirely synchronus, a post is lost if it is received at the same time as another
|
||||
|
||||
tar backend has no clean way to exit, leaves an orphan nc process behind
|
||||
|
||||
css in post page
|
||||
test rapid succession posting
|
||||
- I think it works?
|
||||
test replies in post html
|
||||
|
5
reset.sh
5
reset.sh
|
@ -1,6 +1,10 @@
|
|||
#!/bin/sh
|
||||
# DEBUG PURPOSES ONLY
|
||||
# USERS SHOULD USE THE FUNCTION PROVIDED IN util.sh!
|
||||
|
||||
echo '0' > latest
|
||||
echo "" > log.txt
|
||||
rm -rf ./inbox/*
|
||||
for board in boards/*
|
||||
do
|
||||
echo reseting board "$board"
|
||||
|
@ -15,3 +19,4 @@ do
|
|||
|
||||
done
|
||||
done
|
||||
pkill -f nc
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
#shi
|
||||
**SH**ell **I**mageboard
|
||||
|
||||
no php, no js, just POSIX shell, gnu coreutils, nc, html and css
|
||||
|
||||
### features
|
||||
- posting
|
||||
- replies
|
||||
- images
|
||||
- timestamps
|
||||
- more
|
||||
|
||||
### to use it
|
||||
- `git clone <this repo>`
|
||||
- `cd <this repos folder>`
|
||||
- `touch log.txt`
|
||||
- `cp templates/mainindex.html ./index.html`
|
||||
- point your web server to ./index.html
|
||||
- to run shi
|
||||
- `./shi.sh &`
|
||||
- `tail -f log.txt`
|
||||
- to make a board:
|
||||
- `mkdir -p boards/<boardname>`
|
||||
- `cp templates/index.html.template boards/<boardname>/index.html`
|
||||
- `cp templates/main.css.template boards/<boardname>/main.css`
|
||||
- to stop shi
|
||||
- `killall shi.sh`
|
||||
- to post to shi
|
||||
- copy the script at the top of the board into your terminal
|
||||
- hit enter, follow the prompts
|
||||
|
||||
|
||||
i know the file permissions are weird i'll fix them at some point
|
|
@ -27,7 +27,7 @@ IFS=''
|
|||
LOG=log.txt
|
||||
TEMPLATEDIR="templates"
|
||||
|
||||
|
||||
alias echo='echo [shi.sh]:'
|
||||
newPost() {
|
||||
POSTTYPE=$2
|
||||
POSTNUM=$(basename "$1")
|
||||
|
@ -150,45 +150,48 @@ addPostToBoard() {
|
|||
cat "$TEMPFILE" > "$BOARDDIR"/index.html
|
||||
|
||||
}
|
||||
|
||||
addNewPost() {
|
||||
post="$1"
|
||||
if basename "$post" | grep -qE "[0-9]$" && [ "$(basename "$post")" -gt "$2" ]
|
||||
then
|
||||
TEMPFILE=$(mktemp)
|
||||
if dirname "$post" | grep -qE "[0-9]*+/[0-9]*$" # If reply generate a reply
|
||||
then
|
||||
echo "Post type: reply" > $LOG
|
||||
PARENT=$(dirname "$post" | sed 's/^.*\/\([0-9]\+\)/\1/')
|
||||
while read -r line
|
||||
do
|
||||
if echo "$line" | grep -q "<!--END REPLIES $PARENT-->"
|
||||
then
|
||||
newPost "$post" "reply"
|
||||
printf "\t\t\t\t\t\t<!--END REPLIES %s-->\n" "$PARENT"
|
||||
else
|
||||
echo "$line"
|
||||
fi
|
||||
done < "$post/../index.html" > "$TEMPFILE"
|
||||
cat "$TEMPFILE" > "$post/../index.html"
|
||||
addPostToBoard "$(dirname "$post")"
|
||||
else # Else generate an op post
|
||||
echo "Post type: OP post" >> $LOG
|
||||
while read -r line
|
||||
do
|
||||
echo "$line"
|
||||
if echo "$line" | grep -q "<!--BEGIN POSTS-->"
|
||||
then
|
||||
newPost "$post" "oppost"
|
||||
fi
|
||||
done < "$(dirname "$post")/index.html.template" > "$TEMPFILE"
|
||||
cat "$TEMPFILE" > "$post"/index.html
|
||||
rm "$TEMPFILE"
|
||||
addPostToBoard "$post"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
update() {
|
||||
echo "Posting started at $(date -u)" >> $LOG
|
||||
find "$1" | while read -r post
|
||||
do
|
||||
if basename "$post" | grep -qE "[0-9]$" && [ "$(basename "$post")" -gt "$2" ]
|
||||
then
|
||||
TEMPFILE=$(mktemp)
|
||||
if dirname "$post" | grep -qE "[0-9]*+/[0-9]*$" # If reply generate a reply
|
||||
then
|
||||
echo "Post type: reply" > $LOG
|
||||
PARENT=$(dirname "$post" | sed 's/^.*\/\([0-9]\+\)/\1/')
|
||||
while read -r line
|
||||
do
|
||||
if echo "$line" | grep -q "<!--END REPLIES $PARENT-->"
|
||||
then
|
||||
newPost "$post" "reply"
|
||||
printf "\t\t\t\t\t\t<!--END REPLIES %s-->\n" "$PARENT"
|
||||
else
|
||||
echo "$line"
|
||||
fi
|
||||
done < "$post/../index.html" > "$TEMPFILE"
|
||||
cat "$TEMPFILE" > "$post/../index.html"
|
||||
addPostToBoard "$(dirname "$post")"
|
||||
else # Else generate an op post
|
||||
echo "Post type: OP post" >> $LOG
|
||||
while read -r line
|
||||
do
|
||||
echo "$line"
|
||||
if echo "$line" | grep -q "<!--BEGIN POSTS-->"
|
||||
then
|
||||
newPost "$post" "oppost"
|
||||
fi
|
||||
done < "$(dirname "$post")/index.html.template" > "$TEMPFILE"
|
||||
cat "$TEMPFILE" > "$post"/index.html
|
||||
rm "$TEMPFILE"
|
||||
addPostToBoard "$post"
|
||||
fi
|
||||
fi
|
||||
addNewPost "$post"
|
||||
done
|
||||
echo $(( $2 + 1 )) > ./latest
|
||||
echo "Posting finished at $(date -u)" >> $LOG
|
|
@ -0,0 +1,364 @@
|
|||
#!/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")"
|
||||
|
||||
# id of the most recent reply to be included on the board page
|
||||
local reply_lim="$(echo "$post_replies" | sed -n "$trim"p | tail -c 1)"
|
||||
|
||||
echo "REPLIES $post_replies" >> "$log_file"
|
||||
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"
|
||||
|
||||
sed -i -e ':a' -e 'N' -e '$!ba' \
|
||||
-e "s/<!--BEGIN REPLY $(( post_id - trim ))-->.*<!--END OP POST $post_id-->//g" \
|
||||
"$temp_file"
|
||||
|
||||
cat "$temp_file"
|
||||
|
||||
# printf "<!--END REPLIES %s-->\n</div>\n</div>\n<!--END OP POST %s-->" \
|
||||
# "$post_id" "$post_id"
|
||||
|
||||
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 -4 -d '/' \
|
||||
| rev)"
|
||||
;;
|
||||
"reply")
|
||||
post_url_path="$(echo "$post_url_path" \
|
||||
| rev \
|
||||
| cut -f -5 -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")
|
||||
# In the future, we will want to display replies in reverse chronological order
|
||||
parent="$(get_post_data "$post_db_path" "parent")"
|
||||
replace="<!--BEGIN REPLY $post_id-->.*<!--END REPLY $post_id-->\n"
|
||||
insert_at="<!--BEGIN REPLIES $parent-->"
|
||||
;;
|
||||
esac
|
||||
|
||||
# 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"
|
||||
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
|
|
@ -0,0 +1,99 @@
|
|||
#!/bin/sh
|
||||
# Tar listening backend.
|
||||
# This backend looks for tarfiles in the folder specified by $inbox.
|
||||
# It then un-tars them and and gets the username, title and post content
|
||||
# from their respecively named files. It will use the first image matching
|
||||
# one of $image_types types as the image for the post.
|
||||
|
||||
# TODO: Trap the exit signal to kill nc
|
||||
# or maybe use telnet
|
||||
|
||||
# TODO: make this file backend agnostic
|
||||
|
||||
. ./globals.sh
|
||||
|
||||
echo "Using tar backend." >> "$log_file"
|
||||
gen_post_from_dir() {
|
||||
local dir="$1"
|
||||
local post_to="$(basename "$dir" | sed 's/-.*//g')" # where the post wants to go
|
||||
|
||||
local board=""
|
||||
local parent=""
|
||||
local user=""
|
||||
local title=""
|
||||
local content=""
|
||||
local image_name=""
|
||||
local image_content=""
|
||||
|
||||
local image_path=""
|
||||
|
||||
|
||||
echo "New post"
|
||||
if echo "$post_to" | grep -qE "[0-9]$" # if our post is a reply
|
||||
then
|
||||
# set the parent to the post it is replying to
|
||||
parent="$(find $board_dir -maxdepth 3 -name "$post_to" -not -path '*/\.*')"
|
||||
# make sure the parent post exists
|
||||
if [ -z "$parent" ]; then echo "Parent post does not exist" && return 1; fi
|
||||
|
||||
# and set the board value to the board it is to be posted to
|
||||
board="$(basename `dirname "$parent"`)"
|
||||
# then change the parent value from a directory path to a plain string
|
||||
parent="$(basename `realpath "$parent"`)"
|
||||
else # otherwise, the post is an OP post.
|
||||
# set the board value to the board it is to be posted to
|
||||
board="$post_to"
|
||||
fi
|
||||
|
||||
image_path="$(find "$dir" | grep -Em1 "$image_types")"
|
||||
if ! [ -z "$image_path" ]
|
||||
then
|
||||
image_name="$(basename "$image_path")"
|
||||
image_content="$(cat "$image_path" | base64 -w 0)"
|
||||
fi
|
||||
|
||||
user="$(cat "$dir/user")"
|
||||
content="$(tr '\n' '\a' < "$dir/content")" # change newline characters to BEL
|
||||
title="$(cat "$dir/title")"
|
||||
|
||||
echo "board:$board"
|
||||
echo "parent:$parent"
|
||||
echo "user:$user"
|
||||
echo "title:$title"
|
||||
echo "content:$content"
|
||||
echo "image_name:$image_name"
|
||||
echo "image_content:$image_content"
|
||||
echo "End new post"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
listen() {
|
||||
# NOTE: Tar extraction takes a nontrivial amount of time, which
|
||||
# means if two connections were made in close succecion
|
||||
# there is a possibility that the second connection would
|
||||
# not receive the "busy" signal, and send it's data anyways
|
||||
#
|
||||
# additionally, there should be a way to prevent the sending of
|
||||
# malicious data to interfere with normal post processing
|
||||
#
|
||||
# to prevent zip bombs, break the tar -x -C operation up and use
|
||||
# `zcat $file | head -c $file_limit`
|
||||
|
||||
blocker_pid="" # the pid of the loop that sends the busy signal
|
||||
while nc -lp "$port" | tar -x -C "$inbox"
|
||||
do
|
||||
(while true
|
||||
do
|
||||
echo "Busy" | nc -lp 7070
|
||||
done)>/dev/null & blocker_pid="$!"
|
||||
|
||||
for dir in $inbox/*
|
||||
do
|
||||
echo "Got post" >> "$log_file"
|
||||
gen_post_from_dir "$dir"
|
||||
rm -rf "$dir"
|
||||
done
|
||||
kill "$blocker_pid" >/dev/null
|
||||
done
|
||||
}
|
|
@ -15,9 +15,15 @@
|
|||
a description for the board
|
||||
</p>
|
||||
<br>
|
||||
<p style="text-align:left;font-size: 9px; margin-right: 25%; margin-left: 25%">
|
||||
readdat () { read -r var; echo "$var" | grep -qE "[a-Z].*" || var=$2; echo "$var" > "$1"; };echo "Post to:"; read -r parent; echo "$parent" | grep -qE "([a-Z]|[1-9]).*" || exit;postdir=$(echo "$parent"-"$RANDOM");mkdir ./"$postdir" && cd ./"$postdir" || exit; echo "Name (blank for anonymous):";readdat "user" "Anonymous";echo "Title (blank for none):";readdat "title" "";echo "Absolute path to image (blank for none):";read -r imagepath; echo "$imagepath" | grep -qE "[a-Z].*" && if test -f "$imagepath"; then cp "$imagepath" .;else echo "could not find $imagepath";fi;echo "Post text (Ctrl-D to finish):"; cat > content; cd ..; tar -cf "$postdir".tar ./"$postdir";rm -rf "$postdir"; echo "posting..."; nc -q2 [the adress shi is at] [the port shi is running on] < "$postdir".tar
|
||||
</p>
|
||||
<form>
|
||||
Name: <br>
|
||||
<input type="text" name="username"> <br>
|
||||
Post title: <br>
|
||||
<input type="text" name="title"> <br>
|
||||
Content: <br>
|
||||
<input type="text" name="content"> <br>
|
||||
<input type="submit" value="Submit"> <br>
|
||||
</form>
|
||||
<br>
|
||||
<div class="posts">
|
||||
<!--BEGIN POSTS-->
|
||||
|
|
|
@ -106,3 +106,9 @@ body {
|
|||
padding: 20px;
|
||||
background-color: black;
|
||||
}
|
||||
input {
|
||||
background-color: black;
|
||||
}
|
||||
textarea {
|
||||
background-color: black;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<!--BEGIN OP POST {{{ POSTNUM }}}-->
|
||||
<div class="opContainer">
|
||||
<div class="postHeader">
|
||||
<a class="postHeaderText" href="{{{ POSTNUM }}}/index.html">Post #{{{ POSTNUM }}} by {{{ USER }}} {{{ DATE }}}</a>
|
||||
<a class="postHeaderText" href="{{{ POSTURL }}}">Post #{{{ POSTNUM }}} by {{{ USER }}} {{{ DATE }}}</a>
|
||||
<p class="postTitle">{{{ POSTTITLE }}}</p>
|
||||
</div>
|
||||
<div class="opContent">
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
<!--BEGIN OP POST {{{ POSTNUM }}}-->
|
||||
<div class="opContainer">
|
||||
<div class="postHeader">
|
||||
<a class="postHeaderText" href="{{{ POSTNUM }}}/index.html">Post #{{{ POSTNUM }}} by {{{ USER }}} {{{ DATE }}}</a>
|
||||
<a class="postHeaderText" href="{{{ POSTURL }}}">Post #{{{ POSTNUM }}} by {{{ USER }}} {{{ DATE }}}</a>
|
||||
<p class="postTitle" >{{{ POSTTITLE }}}</p>
|
||||
</div>
|
||||
<div class="opContent">
|
||||
<div class="imageContainer">
|
||||
<a href="/shi/{{{ IMAGEPATH }}}" target="_blank">
|
||||
<img class="postImage" src="/shi/{{{ THUMBPATH }}}" alt="{{{ IMAGENAME }}}">
|
||||
<a href="{{{ IMAGEPATH }}}" target="_blank">
|
||||
<img class="postImage" src="{{{ THUMBPATH }}}" alt="{{{ IMAGENAME }}}">
|
||||
</a>
|
||||
<p class="imageName"> {{{ IMAGENAME }}} ({{{ IMAGESIZE }}})</p>
|
||||
</div>
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
</div>
|
||||
<div class="postContent">
|
||||
<div class="imageContainer">
|
||||
<a href="/shi/{{{ THUMBPATH }}}" target="_blank">
|
||||
<img class="postImage" src="/shi/{{{ IMAGEPATH }}}" alt="{{{ IMAGENAME }}}">
|
||||
<a href="{{{ THUMBPATH }}}" target="_blank">
|
||||
<img class="postImage" src="{{{ IMAGEPATH }}}" alt="{{{ IMAGENAME }}}">
|
||||
</a>
|
||||
<p class="imageName"> {{{ IMAGENAME }}} ({{{ IMAGESIZE }}})</p>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
#!/bin/sh
|
||||
# These are the functions that are run by the admin to manage posts
|
||||
|
||||
file_path_to_post() {
|
||||
post_id="$1"
|
||||
find "$board_dir" -maxdepth 4 -name "$post_id" -not -path '*/\.*'
|
||||
}
|
||||
|
||||
delete_post() {
|
||||
post_id="$1"
|
||||
rm -rf "$(path_to_post "$post_id")"
|
||||
}
|
||||
|
||||
regen_board() {
|
||||
. ./shi2.sh
|
||||
board="$1"
|
||||
posts="$(find "$board" \
|
||||
-mindepth 1 \
|
||||
-maxdepth 1 \
|
||||
-type d \
|
||||
-iname '[0-9]*' \
|
||||
-printf '%p\n' | sort)"
|
||||
for post in $posts
|
||||
do
|
||||
echo "Generating post $post"
|
||||
insert_post "$post" "$board/index.html"
|
||||
done
|
||||
|
||||
}
|
Loading…
Reference in New Issue