This commit is contained in:
cmccabe 2019-02-25 06:28:36 -05:00
commit 68a319c7b1
4 changed files with 820 additions and 0 deletions

75
README Normal file
View File

@ -0,0 +1,75 @@
Hold your horses! Don't get too excited! This is a very early,
and rather sloppy, collection of files that might (given a bit of
luck) be able to run on your system. But really, this is released
in attempt to attract additional developer support from the
community.
If you would like to help, contact cmccabe@sdf.org
_ _ _ _ _ _
| (_)_ __ | | ___ _| | __ _| |_ ___ _ __| |
| | | '_ \| |/ / | | | |/ _` | __/ _ \| '__| |
| | | | | | <| |_| | | (_| | || (_) | | |_|
|_|_|_| |_|_|\_\\__,_|_|\__,_|\__\___/|_| (_)
===== WHAT IS IT? =====
Linkulator is a command line link aggregator; like
news.ycombinator.com, or lobste.rs, but strictly for the command
line. But unlike thoes two, Linkulator natively supports both
gopher and http links. Like the others, Linkulator allows the
poster or others to leave comments about the posted link. It was
designed for small public access unix (and gnu/linux!) communities
like Circumlunar Space, Grex, or the many servers in the
Tildeverse (tilde.team, tilde.town and so on).
===== HOW TO INSTALL IT? =====
Installation is still in flux and is very experimental. Below is
one way it has been successfully installed, but your help on
designing a better process would be valuable.
1. Create a linkulator user (this is important because Linkulator
runs setuid).
2. Copy all the files to the linkulator user's directory, or to
any directory under the linkulator user's control.
3. chmod all the files to 700.
4. create a file storage directory somewhere, owned by linkulator
and chmod'ed to 700.
4. edit the 'config' file to point to the file storage directory.
(you can change the other config variables, but don't need to
yet).
5. edit 'linkulator.sh' (line 165'ish) to point to the location
of the config file.
6. edit 'wrapper.c' so that it points to the location of
'linkulator.sh'
7. compile 'wrapper.c' to create a 'linkulator' binary:
gcc -o linkulator wrapper.c
8. chmod the 'linkulator' executable to 755
9. make 'linkulator' setuid: chmod u+s linkulator
10. create a symlink to the 'linkulator' executable in a place
commonly accessible to other user.
11. linkulate.
===== REPORT BUGS OR HELP DEVELOP =====
BUGS - send bug reports to cmccabe@sdf.org. please be as
descriptive as possible about what happened and what you think
might have caused it -- including possible solutions if you have
such an idea.
HELP - if you want to help, start by cloning the repo and adding
your name to the AUTHORS variable in the 'config' file.

45
config Normal file
View File

@ -0,0 +1,45 @@
AUTHORS="cmccabe" # ADD YOUR NAME SEPARATED BY A SPACE IF YOU
# HAVE CONTRIBUTED TO THIS PROGRAM
LEAD_DEV_EMAIL="cmccabe@sdf.org"
LASTCOMMIT=2019 # CHANGE DATE WHEN CODE IS UPDATED
# WHERE SHOULD THE USER GEN'D FILES BE STORED?
FILEPATH=/home/cmccabe/code/linkulator/files/
# NAME OF PROGRAM
PROGNAME=Linkulator
FILE_MANAGEMENT_MODE="auto-hide"
# "auto-hide"
# "auto-clean" - NOT DEVELOPED YET
# "fire-hose" - DEFAULT IF NOT -HIDE OR -CLEAN
# MAX NUMBER OF LINKS TO DISPLAY ON BROWSE, IN AUTO-HIDE MODE
BROWSE_DISPLAY_MAX=20
# DISK SPACE MANAGEMENT FOR AUTO-CLEAN MODE:
MAX_LINKS=100 # AFTER THIS, OLDEST (LEAST TOUCHED) GET DELETED
MAX_AGE=90 # OLDER THAN THIS GET DELETED
# DEFAULT HELPER APPS:
DEFAULT_EDITOR="nano -R"; ## -R FOR SECURITY.
DEFAULT_BROWSER="lynx -restrictions=all";
# USER INPUT RESTRICTIONS:
URL_MAX_LEN=200 ## WHAT IS REASONABLE?
URL_MIN_LEN=4
TITLE_MAX_LEN=100
TITLE_MIN_LEN=1 # WHAT IF SOMEONE POSTS A LINK ABOUT THE C LANGUAGE?
DESC_MAX_LEN=200 # THIS IS A ONE-LINER DESCRIPTION, NOT A COMMENT
KEYWORD_MAX_LEN=28 # antidisestablishmentarianism (DOESN'T WORK YET)
KEYWORDS_MAX_LEN=200 # MAX ALLOWED FOR ALL KEYWORDS COMBINED
COMMENT_MAX_LEN=400
# DISPLAY CONFIGURATION
MAX_TITLE_LEN=50 #=Mare Tranquillitatis People's Circumlunar Zaibatsu
# this is only when printed in a table
HORZ_RULE=72 ## HOW LONG TO MAKE SEPARATING HORIZONTAL RULES?
HORZ_RULE=$(head -c $HORZ_RULE < /dev/zero | tr '\0' '-')
## https://stackoverflow.com/questions/3211891/creating-string-of-repeated-characters-in-shell-script

692
linkulator.sh Executable file
View File

@ -0,0 +1,692 @@
#!/bin/bash -p
# /\
# _ _ the_link aggreGator _ **
# | (_)_ __ | | ___ _| | __ _| |_ ___ _ __ \_}}_/
# | | | '_ \| |/ / | | | |/ _` | __/ _ \| '__| {{
# | | | | | | <| |_| | | (_| | || (_) | | \_}}_/
# |_|_|_| |_|_|\_\\__,_|_|\__,_|\__\___/|_| {{ ,
# >>>^
# $PROGNAME is a command line link aggregator designed for small,
# trusting shell communities. This program allows users to share
# annotated links with others, browse links shared by others, and
# engage in brief discussions about the links.
#
# Functions include:
#
# POST NEW LINK, with annotations including keywords, title and
# short description. Links are not limited to http, but can include
# gopher and ssh as well.
#
# BROWSE LINKS that others have posted, including looking at details
# of individual links or even following links via lynx.
#
# VIEW LINK IN lynx. (bad URLs too, so user beware)
#
# COMMENT ON LINKS and view comments that have accrued on a link.
#
# Search all links (including old, hidden links) by words saved as
# keywords. To do: probably should add ability to search by URLs.
#
# PRINT-TO-GOPHER : create report of recent links in plaintext format
# suitable for serving via gopher.
#
# MANAGE DISK SPACE OR DISPLAY SPACE with optional program modes:
# * auto-clean: delete old links after a given period of inactivity.
# * auto-hide: only display recently touched links in browse mode.
# * fire-hose: display all links in order from most recently touched.
# (NOTE: auto-clean doesn't yet work. See below.)
#
# FEATURES THAT HAVE NOT BEEN DEVELOPED YET:
#
# * SAVE LINK TO PERSONAL FILE
# * PRINT HELP INFO FOR THIS PROGRAM
# * FLAG LINKS AS BAD/BROKEN SO THEY CAN BE FIXED OR REMOVED
# * ANY USER CAN ADD ADDITIONAL KEYWORDS TO A LINK
# THIS IS THE "BREAK ME" VERSION. PLEASE SHOOT THIS PROGRAM WITH
# A FIREHOSE OF BAD INPUT AND DOCUMENT ANY PROBLEMS YOU FIND.
# QUESTIONS? COMMENTS? SMART REMARKS?
# --> cmccabe@sdf.org
#
# LICENSE: THIS SOFTWARE IS LICENSED FOR USE IN DEFENDING THE MASSES
# FROM THE FORCES OF TOTALITARIANISM AND CORPORATOCRACY. IT IS NOT
# DESIGNED TO SUPPORT ARMED CONFLICT, BUT FOR ADDING TO A SUITE OF
# FUNDAMENTAL TOOLS CITIZENS NEED TO AVOID AVOID BEING DUMBED DOWN
# INTO ACQUIESCENCE -- TOOLS FOR COMMUNICATION, EDUCATION, AND
# AWARENESS ABOUT CURRENT EVENTS, AND FOR CONNECTING AND ORGANIZING
# WITH FELLOW HUMANS.
#
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
#
# LIMITATIONS AND ISSUES:
# * CANNOT RELIABLY PREVENT DUPLICATE LINK POSTINGS BECAUSE MANY
# URLS MAY HAVE MEANINGFUL QUERY STRINGS THAT DIFFER BETWEEN
# LINKS TO THE SAME RESOURCE.
# ...SO DUPLICATE CHECKING IS NOT PERFORMED.
#
# * LINK VALIDATION IS HARD, ESPECIALLY WHEN ALLOWING FOR MULTIPLE
# PROTOCOLS (E.G. HTTP, GOPHER AND SSH), SO IT IS POSSIBLE THAT
# USERS MAY SOMETIMES ENTER BAD LINKS.
#
# * THERE IS CURRENTLY NO ABILITY FOR USERS TO EDIT ENTRIES ONCE
# SUBMITTED. A REQUEST TO THE ADMIN WILL BE NECESSARY IN THE CASE
# THAT EDITS ARE REQUIRED. THIS WILL BE RE-BRANDED AS A FEATURE,
# NOT A BUG: "PROMOTES INTERACTION WITH FRIENDLY SYSTEM ADMIN!"
#
# * SETUID WRAPPER DOES NOT (YET) PASS COMMAND LINE ARGUMENTS TO
# THE BASH SCRIPT, SO PRINT-TO-GOPHER MODE MUST BE TRIGGERED
# BY CALLING THE SCRIPT DIRECTLY. WHICH IS OK, BECAUSE SETUID
# IS ONLY NEEDED WHEN ANOTHER USER WANTS TO WRITE TO FILES.
#####################################################################
# TO DO'S:
# IF LINK DOESN'T START WITH GOOHER:// HTTPS:// OR HTTP://, HIDE LYNX OPTION
# DISCARDING ADD-LINK EXITS ENTIRE PROGRAM
# NEED TO PROTECT AGAINST RACE CONDITIONS IN WHICH A FILE OR DIRECTORY
# HAS CHANGED WHILE THE USER IS EDITING A REPLY. THIS IS A DISTINCT
# POSSIBILITY IF THE PROGRAM IS RSYNC FEDERATED WITH OTHER SYSTEMS AND
# IF FILES MIGHT BE DELETED OR CHANGED BY AN ADMIN.
# SETUID IS A RISKY WAY TO RUN THIS SCRIPT. IS THERE A BETTER APPROACH
# WHILE STILL USING BASH, OR DOES IT NEED TO BE PORTED TO A COMPILED
# LANGUAGE THAT CAN SAFELY BE RUN SETUID?
# CLEAN UP - CREATE MODES THAT CAN BE CONFIG'ED ON/OFF (HIDE/HOSE DONE)
# * AUTO-CLEAN: BASED ON CONFIG'ED DATES, REMOVE OLD FILES (DESTRUCTIVE)
# -- AUTO-CLEAN MIGHT BE BETTER AS AUTO-ARCHIVE, SO THAT OLD LINKS
# DON'T GET LOST
# * AUTO-HIDE: DO NOT DISPLAY FILES UNTOUCHED SINCE CONFIG'ED DATE
# * FIRE-HOSE: DISPLAY EVERYTHING, IN ORDER OF MOST RECENT TOUCH FIRST
# -- MAYBE JUST ADD A VERSION OF CLEAN TO AN ADMIN FUNCTION?
# PAGINATE OUTPUT - IN BROWSE MODE, AND POSSIBLY IN INDIV LINK VIEW MODE
# BETTER COLORIZE SOME TEXT FOR STATUS INDICATION:
# * https://stackoverflow.com/questions/5947742/how-to-change-the-output-color-of-echo-in-linux
# HARDEN THE PROGRAM AS MUCH AS REASONABLY POSSIBLE, INCLUDING
# PREVENTION OF USERS DEFACING SUBMISSIONS FROM OTHER USERS.
# * using nano -R rather than $EDITOR (done -- nice tip, solderpunk!)
# * and using lynx -restrictions=all (done -- thanks asdf@tilde.town!)
# * using id rather than $USER (done)
# * setuid wrapper? stackoverflow unanimously says 'no no no', but is
# it ok for a small, trusting pubnix? see solderpunk's raise/lower
# version. can that be done in bash?
#
# ALLOW RSYNC FEDERATION WITH OTHER PUBNIXES
# -- OR WHAT IS THE BEST FEDERATION APPROACH?
# ALLOW USERS TO FLAG LINKS AS BROKEN SO THAT THEY CAN BE REMOVED.
# DECIDE HOW TO VALIDATE DIFFERENT PROTOCOLS
# * HTTP response can be used to validate some but not all HTTP
# links. But see circumulunar.space as an exception.
# * how to validate all links without downloading the linked file?
# * should allow https, http, gopher, ssh. And more?
# * once validated, lynx used to view
# * !! need to if-then block lynx from trying any url that doesn't
# start with http or gopher (e.g. ssh:// )
#
# FINISH KEYWORD VALIDATION CHECKS
# * indiv keywords length restrictions
# * total char count of all keywords combined (done)
# CHECK FOR DEPENDENCIES AND FAIL IF NOT MET
# E.G. lynx and sed
#
# CLEAN UP SLOPPY VARIATION IN USE OF ls VS find
# * ls MIGHT BE FINE IN ALL CASES; BUT DOES ONE OR THE OTHER HAVE
# A HIGHER MEMORY OR CPU DRAW?
# ALLOW USERS TO ADD KEYWORDS TO OTHER USERS' LINKS. THIS WOULD BE
# A LOT EASIER IN A sqlite VERSION. CURRENTLY, ALL WRITE ACTIONS CAN
# BE TRACED TO THE USER WHO MADE THEM. IF USERS WERE ALLOWED TO
# APPEND KEYWORDS TO THE date-username/keywords FILE, THEN YOU
# WILL NEVER KNOW WHO ADDED #PEEPEE #POOPOO TO EVERY SINGLE LINK
# IN THE FILES...
#
#####################################################################
## CONFIGURATION VARIABLES
CONFIG_FILE="/home/cmccabe/code/linkulator/config"
[ ! -f "$CONFIG_FILE" ] && {
printf "Unable to find config file %s\n" "${CONFIG_FILE}"
exit 1
}
. "${CONFIG_FILE}"
#####################################################################
## SUBROUTINES
START_UP_CHECKS () { ## START UP AND SETUP
cd "/" || {
printf "unable to chdir /"
exit 1
}
## THIS PREVENTS A 'find' HICCUP WHEN THE SCRIPT IS CALLED
## FROM WITHIN A DIRECTORY TO WHICH THE SETUID USER DOESN'T
## HAVE ACCESS
if [ ! -d "$FILEPATH" ]; then
printf "%s does not exist.\n" $FILEPATH
printf "Create it? [y/n] anything else aborts:"
read -r RESP
case "$RESP" in
y | Y ) mkdir -p "$FILEPATH"
if [ ! -d "$FILEPATH" ]; then
printf "\nERROR: Could not create $s.\n" $FILEPATH
printf "Check that you have privileges to that location.\n"
printf "Goodbye.\n"
else
printf "\nOk. Created. Goodbye."
fi
;;
* ) printf "Not creating %s. Goodbye." $FILEPATH
;;
esac
exit 0
fi
USER="$(id -run)" # BECAUSE A USER COULD OTHERWISE SET $USER TO
# ANY ARBITRARY VALUE (like rm -rf /yo/mama).
# AND, -run GETS REAL USERNAME, NOT EFFECTIVE.
GOPHER="n" # WILL CHANGE TO "y" LATER IF $1="g"
}
START_UP_CHECKS
PRINT_GOPHER_VERSION () {
printf 'RECENT LINKS POSTED TO LINKULATOR ON THE ZAIBATSU\n'\
' Printed on %s\n'\
' ~ log in for discussion on these links ~'\
"$(date +'%Y/%m/%d')"
BROWSE_LINKS
}
ADD_NEW_LINK () {
## GET AND VALIDATE USER INPUT, AND SAVE TO FILE
TITLE=""
DESCRIPTION=""
KEYWORDS=""
LINK="$1" ## FILL W/COMMAND LINE ARGUMENT IF SUPPLIED
while true; do
while true; do
## (KatolaZ) this needs to be thought through a bit more....
echo -e "\nEnter/Edit your link for submission (or cancel with a blank line): "
read -i "$LINK" -e LINK # -i = default text, -e = allow line editing
# GREATER THAN $URL_MIN_LEN
# LESS THAN $URL_MAX_LEN
# REJECT ON MALFORMED URL (NEED TO DEFINE AND RECOGNIZE THIS)
if [ "${#LINK}" -eq "0" ]; then
printf "\nLink submission canceled.\n"
BROWSE_LINKS
return
elif [ "${#LINK}" -le "$URL_MAX_LEN" ] && [ "${#LINK}" -ge "$URL_MIN_LEN" ]; then
## WHY STILL USE -ge? BECAUSE $URL_MIN_LEN MIGHT NOT BE ZERO.
break;
else
printf "\nError: Link URL must be less than %s and greater than %s characters." "$URL_MAX_LEN" "$URL_MIN_LEN"
LINK='z'
fi
## TODO: ADD VALIDATION OF LINK (HTTP SOMEWHAT POSSIBLE,
## BUT WHAT ABOUT GOPHER OR SSH?)
done
while true; do
# GREATER THAN $TITLE_MIN_LEN
# LESS THAN $TITLE_MAX_LEN
printf "\nEnter/Edit the short title of this link: "
## (KatolaZ) maybe the read below should be rethought?
read -i "$TITLE" -e TITLE
if [ "${#TITLE}" -le "$TITLE_MAX_LEN" ] && [ "${#TITLE}" -ge "$TITLE_MIN_LEN" ]; then
break;
else
printf "\nERROR: Link title must be less than %s and greater than %s characters." "${TITLE_MAX_LEN}" "${TITLE_MIN_LEN}"
fi
done
while true; do
# LESS THAN $DESC_MAX_LEN; ZERO LEN IS OK
printf "\nEnter a short, one-liner description (< %s chars) of the link: " "${DESC_MAX_LEN}"
read -i "$DESCRIPTION" -e DESCRIPTION
if [ "${#DESCRIPTION}" -le "${DESC_MAX_LEN}" ]; then
break;
else
printf "\nERROR: One-liner description must be less than %s characters." "${DESC_MAX_LEN}"
fi
done
while true; do
printf "\nEnter zero or more keywords (alphanumeric and dashes only) separated by spaces: "
## (KatolaZ) -- same as above -- KEYWORDS will be empty here, right?
read -i "$KEYWORDS" -e KEYWORDS
## TODO: KEYWORDS SHOULDN'T START OR END WITH DASHES OR HAVE MORE THAN
## ONE DASH IN A ROW.
## CHECK FOR NON-ALPHANUM + DASH:
KEYWORDS2="$(echo "$KEYWORDS" | tr -cd '[a-zA-Z0-9\-\ ]\n')"
# (KatolaZ) do you really nead an array here? why not a string instead?
KW_ARRAY=($KEYWORDS)
KW_LONGEST=0
for i in "${KW_ARRAY}"; do
if [ "${#i}" -ge "${KW_LONGEST}" ]; then
KW_LONGEST="${#i}"
fi
done
if [ "$KEYWORDS2" != "$KEYWORDS" ] || [ "${#KEYWORDS}" -gt "${KEYWORDS_MAX_LEN}" ]; then
printf "\nERROR: Keywords may only characters A-Z and dashes and no "\
'longer than %s.' "${KEYWORDS_MAX_LEN}"
elif [ "${KW_LONGEST}" -gt "${KEYWORD_MAX_LEN}" ]; then
printf "\nERROR: Individual keywords must be less than %s characters." $"${KEYWORD_MAX_LEN}"
else
break
fi
done
echo "${HORZ_RULE}"
printf "Your link submission:\n"
printf "LINK: %s\n" "$LINK"
printf "TITLE: %s\n" "$TITLE"
printf "DESCRIPTION: %s\n" "$DESCRIPTION"
printf "KEYWORDS: %s\n" "$KEYWORDS"
# "$LINK" "$TITLE" "$DESCRIPTION" "$KEYWORDS"
echo "${HORZ_RULE}"
SAVED='no'
# (KatolaZ) rethink the read below?
read -n1 -p "[s]ave, [e]dit, [d]iscard? (anything else aborts): " ADD_NEW_DECISION
case "${ADD_NEW_DECISION}" in
s ) SAVED="yes"
break
;;
e ) printf '\n'
break
;;
* ) break ;; ## 'd' IS SAME AS ABORT
esac
done
if [ "$SAVED" = "yes" ]; then
# make dir name
POSTID="$(date +%s)-${USER}"
DIRNAME="${FILEPATH}/${POSTID}"
mkdir -p "${DIRNAME}"
# make link file name, 4 lines: link, title, desc, keywords
## (KatolaZ) I am not sure about the logic below...
touch "${DIRNAME}/link" && printf "%s\n" "$LINK" > "${DIRNAME}/link"
# REPLY TO LINKS
touch "${DIRNAME}/title" && printf "%s\n" "$TITLE" > "${DIRNAME}/title"
touch "${DIRNAME}/description" && printf "%s\n" "$DESCRIPTION" > "${DIRNAME}/description"
touch "${DIRNAME}/keywords" && printf "%s\n" "$KEYWORDS" > "${DIRNAME}/keywords"
printf "\nYour link post was added.\n"
fi
while true; do
# (KatolaZ) Maybe rethink the logic below?
read -n1 -p "[b]rowse links, or [q]uit? " TEMP_FILE_DECISION
echo
case "${TEMP_FILE_DECISION}" in
b ) BROWSE_LINKS
;;
q ) break
;;
* ) printf "\nError. Please answer 'b' or 'q'."
;;
esac
done
}
BROWSE_LINKS () {
if [ ! -z ${1+x} ]; then ## KEYWORD SEARCH
KW=$1
FILES=$(find $FILEPATH -name "keywords" | xargs grep -i $KW | awk -F "/keywords:" '{print $1}')
FILES=($FILES)
echo "Searching by keyword \"$KW\"... ${#FILES[@]} total results."
else ## NORMAL BROWSE MODE
FILES=$(ls -dt $FILEPATH*)
FILES=($FILES)
echo "All links. Sorted by most recent touched."
fi
UB=${#FILES[@]}
UB=$((UB-1)) ## B/C IT'S ONE LESS THAN NUM ELEMENTS
if [[ $FILE_MANAGEMENT_MODE = "auto-hide" ]] && [[ $UB -gt $BROWSE_DISPLAY_MAX ]]; then
## TRIM RESULTS DOWN TO THE auto-hide MAX:
FILES=("${FILES[@]::$BROWSE_DISPLAY_MAX}")
fi
if [[ $GOPHER = "y" ]]; then
echo
for i in ${FILES[@]}; do
i=${i//\/keywords/} ## TRIM /keywords OFF FILEPATH
POSTER_NAME=${i##*-} ## USERNAME OF PERSON WHO POSTED LINK
# vv HOLY COW THIS DATE GETTING/CONVERTING IS KLUDGEY. I FEEL ICKY.
BN=$(basename $i)
BN=$(echo $BN | cut -d- -f1)
CREATE_DATE=$(date -d @${BN} +"%Y-%m-%d")
NUM_REPLIES=$(find $i/. -name '[0-9]*' | wc -l)
TITLE_TEXT=$(cat $i/title | cut -c1-$MAX_TITLE_LEN)
echo "TITLE: " $TITLE_TEXT
echo "URL: " $(cat $i/link)
echo "DESCRIPTION: " $(cat $i/description)
echo "POSTED BY: $POSTER_NAME ON $CREATE_DATE"
echo "-----"
# printf "%4.4s" $LINKCOUNT
done
exit
else
## TODO: IMPROVE OUTPUT FORMAT OF ID, USER, DATE, TITLE, NUM-REPLIES
RED='\033[0;31m'
NC='\033[0m' # NO COLOR
echo $HORZ_RULE
printf "%4.4s" "ID#"
printf "%12.12s" " Date "
printf "%18.18s" "Name [#re:] "
printf " Link Title"
echo
LINKCOUNT=0
echo $HORZ_RULE
for i in ${FILES[@]}; do
i=${i//\/keywords/} ## TRIM /keywords OFF FILEPATH
POSTER_NAME=${i##*-} ## USERNAME OF PERSON WHO POSTED LINK
# vv HOLY COW THIS DATE GETTING/CONVERTING IS KLUDGEY. I FEEL ICKY.
BN=$(basename $i)
BN=$(echo $BN | cut -d- -f1)
CREATE_DATE=$(date -d @${BN} +"%Y-%m-%d")
NUM_REPLIES=$(find $i/. -name '[0-9]*' | wc -l)
TITLE_TEXT=$(cat $i/title | cut -c1-$MAX_TITLE_LEN)
printf "%4.4s" $LINKCOUNT
printf " $CREATE_DATE "
printf "${RED}"
printf "%12.12s" $POSTER_NAME
printf "${NC}"
printf " ["
printf "%2.2s" $NUM_REPLIES
printf "] $TITLE_TEXT\n"
LINKCOUNT=$((LINKCOUNT+1))
done
fi
while true; do
echo $HORZ_RULE
if [[ ${#FILES[@]} -gt 0 ]]; then
read -p "[a]dd link, [b]rowse all, [k]eyword search, [q]uit, or link [#ID] number: " REPLY
else
read -p "[a]dd link, [b]rowse all, [k]eyword search, [q]uit: " REPLY
fi
echo
UB=${#FILES[@]}
UB=$((UB-1)) ## B/C IT'S ONE LESS THAN NUM ELEMENTS
if [[ ${REPLY} =~ ^[0-9]+$ && ${REPLY} -ge 0 && ${REPLY} -le $UB ]]; then
DIR_PATH=${FILES[${REPLY}]}
VIEW_LINK_DETAIL $DIR_PATH
break
elif [ "${REPLY}" = "a" ]; then
ADD_NEW_LINK; break ;
elif [ "${REPLY}" = "b" ]; then
BROWSE_LINKS
elif [ "${REPLY}" = "r" ]; then
BROWSE_LINKS; break;
elif [ "${REPLY}" = "k" ]; then
SEARCH; break ;
elif [ "${REPLY}" = "p" ]; then
echo "prev link function not working"
break ;
elif [ "${REPLY}" = "n" ]; then
echo "next link function not working"
break ;
elif [ "${REPLY}" = "q" ]; then
break 2 ;
else
if [[ ${#FILES[@]} -gt 0 ]]; then
echo -e "\nERROR: Valid options are 'a', 'b', 'q' or a link number.";
else
echo -e "\nERROR: Valid options are 'a', 'b' or 'q'.";
fi
fi
done
}
VIEW_LINK_DETAIL () {
LINK=$(cat $1/link)
SHOW_REPLIES=$2
POSTER_NAME=${1##*-} ## USERNAME OF PERSON WHO POSTED LINK
BN=$(basename $i)
BN=$(echo $BN | cut -d- -f1)
CREATE_DATE=$(date -d @${BN} +"%Y-%m-%d")
echo "Title: $(cat $1/title)"
echo "Link: $(cat $1/link)"
echo "Description: $(cat $1/description)"
echo "Keywords: $(cat $1/keywords)"
echo "Posted by: ${POSTER_NAME} on ${CREATE_DATE}"
shopt -s extglob ## MOVE UP TO FILE HEADER
## TODO: LIST REPLIES AND ALLOW VIEWING OF EACH
if [[ $SHOW_REPLIES = "y" ]]; then
unset REPLIES
REPLIES=$(find $1 -type f -name '[0-9]*')
IFS=$'\n' REPLIES=($(sort <<<"${REPLIES[*]}")); unset IFS
# REPLIES=$(ls -rft $1/[0-9]*) ## <<- BUG HERE, WHEN LS RETURNS ZERO RESULTS.
REPLIES=(${REPLIES[@]})
TOT_REPLIES=${#REPLIES[@]}
if [[ "$TOT_REPLIES" -eq "0" ]]; then
echo -e "\nThis link does not yet have replies. Be the first - type 'r'!\n";
fi
for i in ${REPLIES[@]}; do
FN=$(basename $i)
REPLY_USER=$(echo $FN | sed 's/^\([0-9]\)*//')
REPLY_USER=$(echo $REPLY_USER | sed 's/^\(-\)*//')
REPLY_DATE=$(stat -c %y $i | cut -d" " -f1)
echo $HORZ_RULE
echo "$REPLY_DATE $REPLY_USER:"
echo $(cat $i)
done
fi
while true; do
echo $HORZ_RULE
read -n1 -p "view in [l]ynx, [s]how replies, [r]eply, [b]rowse links, or [q]uit? " REPLY
echo; echo $HORZ_RULE
case $REPLY in
## l ) $DEFAULT_BROWSER "$LINK" ;;
l ) if [[ $LINK == http://* ]] || [[ $LINK == https://* ]] || [[ $LINK == gopher://* ]]; then
$DEFAULT_BROWSER "$LINK"
else
echo "SORRY! $PROGNAME can only view URLs prefixed with http:// https:// or gopher://"
fi
;;
s ) VIEW_LINK_DETAIL $1 "y" ; break;;
r ) COMMENT_ON_LINK $1 ; break ;;
b ) BROWSE_LINKS; break ;;
q ) echo -e "\n"; exit ;;
* ) echo -e "\nError. Please answer 'v', 'w', 'r', 'b', or 'q'." ;;
esac
done
}
COMMENT_ON_LINK () {
FILE_DIR=$1
# RANDSTR=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 8 | head -n 1)
# TEMP_FN=LINK8_$RANDSTR.tmp
#
# TEMP_FN=/tmp/$TEMP_FN
# FILE_DIR=$1
# $DEFAULT_EDITOR $TEMP_FN
# ## USER MAY CANCEL EDIT WITHOUT SAVING, THEREBY NOT CREATING THE FILE
# if [[ -f "$TEMP_FN" ]]; then
# cat $TEMP_FN
while true; do
# LESS THAN $COMMENT_MAX_LEN
echo -e "\n\nType your comment about this link (\"Enter\" submits, blank line aborts):"
read -i "$COMMENT" -e COMMENT
if [[ ${#COMMENT} -le $COMMENT_MAX_LEN ]] && [[ ${#COMMENT} -gt 0 ]]; then
read -n1 -p "[s]ave, [e]dit, [d]iscard? (anything else aborts) " SAVE_COMMENT_DECISION
echo
case $SAVE_COMMENT_DECISION in
s ) REPLY_ID=$(date +%s)-$USER
touch $FILE_DIR/$REPLY_ID
echo $COMMENT > $FILE_DIR/$REPLY_ID
unset COMMENT
echo "Reply saved."; echo;
break ;;
e ) : ;; # EDIT
* ) unset COMMENT; echo; break ;; ## 'd' IS SAME AS ABORT
esac
elif [[ ${#COMMENT} -le 0 ]]; then
echo "Reply aborted."
break;
else
echo -e "\nERROR: Comment must be less than $COMMENT_MAX_LEN characters."
fi
done
# while true; do
# read -n1 -p "[s]ave? [e]dit? [d]iscard? " TEMP_FILE_DECISION
# case $TEMP_FILE_DECISION in
# s ) cp $TEMP_FN $FILE_DIR/$REPLY_ID;
# rm $TEMP_FN
# echo -e "\nReply saved. Thank you."
# break ;;
# e ) ${EDITOR: -$DEFAULT_EDITOR} $TEMP_FN ;;
# d ) rm $TEMP_FN; break ;;
# * ) echo -e "\nError. Please answer 's', 'e' or 'd'.";;
# esac
# done
# else
# echo "Reply aborted."
# fi
## - TAKE COMMENT DIRECTORY AS ARGUMENT
## - IF DIRECTORY NO LONGER EXISTS, "ERROR: SORRY, THE TARGET
## COMMENT HAS CHANGED SINCE YOU BEGAN RESPONDING AND IT NO
## LONGER EXISTS"
## - CREATE NEW FILE NAME JUST BEFORE SAVING, TO AVOID COLLISIONS
VIEW_LINK_DETAIL $1 "y"
}
#CHECK_IF_FILE_EXISTS () {
## INTENDED TO VALIDATE THAT A LINK POINTS TO AN EXISTING RESOURCE
## WITHOUT HAVING TO DOWNLOAD THAT RESOURCE.
## WORKS WITH WWW SERVERS THAT RESPOND WITH HTTP/1.1 2.00 OK, BUT
## NOT ALL DO (E.G. CIRCUMLUNAR.SPACE), SO NOT A COMPLETE SOLUTION.
## ALSO, DOES NOT WORK WITH GOPHER LINKS. MAYBE THIS FUNCTION IS NOT
## FEASIBLE...
#
# FILE_EXISTS=false
# if [[ `wget -S --spider $LINK 2>&1 | grep 'HTTP/1.1 200 OK'` ]];
# then FILE_EXISTS=true;
# fi
#}
CLEANUP () {
## REMOVE OLD LINKS, ACCORDING TO CONFIGURATION VARIABLE SETTINGS..
echo "Clean up subroutine not yet written..."
}
SEARCH () {
read -p "Enter a keyword to search (leave blank to abort): " REPLY
## TODO: VALIDATE KEYWORD INPUT (ONE WORD, ALPHANUM + DASHES?)
if [[ ${#REPLY} -ge 1 ]]; then
BROWSE_LINKS $REPLY
else
echo -e "\nSearch aborted."
BROWSE_LINKS
fi
}
HELP () {
## PRINT PROGRAM HELP AND ABOUT INFO
echo $HORZ_RULE
echo
echo "$PROGNAME help and about:"
echo "Author(s): $AUTHORS, Last Code Update: $LASTCOMMIT"
echo
echo "$PROGNAME is a super-lightweight, shell-only link aggregator"
echo "for small, trusting shell communities like public access "
echo "Unix/GNU/Linux systems or tilde-boxes."
echo
echo "You can add or browse gopher or www links, or comment on links."
echo "Quit with [q] most times, or ctrl-c at any time."
echo
echo "Found a bug or have a feature request?"
echo " --> Send email to $LEAD_DEV_EMAIL."
echo
}
#####################################################################
## MAIN PROGRAM FLOW CONTROL:
## RESPOND ACCORDING TO PROGRAM MODE (COMMAND LINE ARGS):
if [[ $1 = "g" ]]; then
GOPHER="y"
PRINT_GOPHER_VERSION; exit
elif [[ $1 = "a" ]]; then
ADD_NEW_LINK $2; exit
elif [[ $1 = "h" ]]; then
HELP; exit
elif [[ $1 = "k" ]]; then
echo -e "\nKeyword subsetting not yet implemented... :(\n"; exit
else
figlet $PROGNAME
BROWSE_LINKS
fi
echo -e "\n\nThank you for using $PROGNAME. Please come again.\n"
exit
# ==*^^^^^^^^>>>>>
# // \\
# This is Linky, the link aggregator gator.

8
wrapper.c Normal file
View File

@ -0,0 +1,8 @@
#include <unistd.h>
#include <errno.h>
main( int argc, char ** argv, char ** envp )
{
envp = 0; /* blocks IFS attack on non-bash shells */
execve( "/home/cmccabe/code/linkulator/linkulator.sh", argv, envp );
}