ref: 4138bf3cc011ad73282550d2f3afa78d91131199
dir: /handlers/handle-nntp/
#!/bin/rc . log.rc switch($cmd(1)) { case 'CAPABILITIES' # Capabilities command response 101 'Capability list follows (multi-line)' multiline <<! VERSION 2 READER LIST ACTIVE NEWSGROUPS IMPLEMENTATION RC-NNTPD 0.1 2023-07-14 ! exit 101 case 'DATE' # Returns the current date of the server current_date=`{date -f 'YYYYMMDDhhmmss'} if (! ~ $status '') { log 'unable to get date' response 500 'Internal server error' exit 500 } response 111 $current_date exit 111 case 'LIST' # List newsgroups candidates=`{walk -d $news | sort} newsdirs=() newsgroups=() for (newsdir in $candidates) { first_file=`{ls $newsdir | sed 1q} if (~ $first_file '' || test -f $first_file) newsdirs=($newsdirs $newsdir) } for (newsdir in $newsdirs) { newsgroup=`{echo $newsdir | \ sed 's/\/storage\///g' | \ sed 's/\//./g'} newsgroups=($newsgroups $newsgroup) } list_keyword=$cmd(2) if (! ~ $list_keyword '' && \ ! ~ $list_keyword 'ACTIVE' && \ ! ~ $list_keyword 'NEWSGROUPS') { response 501 'Invalid LIST keyword argument' exit 501 } response 215 'list of newsgroups follows' { for (newsgroup in $newsgroups) { if (~ $list_keyword '' || ~ $list_keyword 'ACTIVE') { group_path=`{echo -n $newsgroup | sed 's/\./\//'} group_path=$news/$group_path group_low=`{ls $group_path | sort -n | \ sed 1q | xargs basename} group_high=`{ls $group_path | sort -n | \ tail -n 1 | xargs basename} if (~ $group_ac '0') { group_low=0 group_high=0 } echo $newsgroup^' '^$group_low^' '^$group_high^' n' } if (~ $list_keyword 'NEWSGROUPS') echo $newsgroup $newsgroup } } | multiline exit 215 case 'GROUP' # Sets the current group if (~ $cmd(2) '') { response 501 'No group supplied' exit 501 } group=$cmd(2) group_path=`{echo -n $group | sed 's/\./\//'} group_path=$news/$group_path if (! test -d $group_path) { response 411 'No such newsgroup' exit 411 } group_ac=`{ls $group_path | wc -l | sed 's/[ ]*//'} group_low=`{ls $group_path | sort -n | sed 1q | xargs basename} group_high=`{ls $group_path | sort -n | tail -n 1 | xargs basename} if (~ $group_ac '0') { group_low=0 group_high=0 } echo $group >$current_group echo $group_ac >$current_group_ac echo $group_low >$current_group_low echo $group_high >$current_group_high echo -n '' >$current_article response 211 $group_ac^' '^$group_low^' '^$group_high^' '^$group exit 211 case 'LISTGROUP' # List group contents group=$cmd(2) range=$cmd(3) if (~ $group '') { group=`{cat $current_group >[2]/dev/null} } if (~ $group '') { response 412 'No newsgroup selected' exit 412 } group_path=`{echo -n $group | sed 's/\./\//'} group_path=$news/$group_path if (! test -d $group_path) { response 411 'No such newsgroup' exit 411 } group_ac=`{ls $group_path | wc -l | sed 's/[ ]*//'} group_low=`{ls $group_path | sort -n | sed 1q | xargs basename} group_high=`{ls $group_path | sort -n | tail -n 1 | xargs basename} if (~ $group_ac '0') { group_low=0 group_high=0 } low=`{echo $range | sed 's/([0-9]*)\-.*/\1/g'} high=`{echo $range | sed 's/.*\-([0-9]*)/\1/g'} if (~ $low '') { low=$group_low } if (~ $high '') { high=$group_high } response 211 $group_ac^' '^$group_low^' '^$group_high^' '^$group ls $group_path | \ syscall -o read 0 buf 512 >[2]/dev/null | \ xargs -n 1 basename | \ awk '{ if ($1 <= '^$high^' && $1 >= '^$low^') print $1 }' | \ multiline case 'ARTICLE' # Retrieve article group=`{cat $current_group} if (~ $group '') { response 412 'No newsgroup selected' exit 412 } if (! ~ $cmd(2) '') { article=$cmd(2) } if not { if (! ~ `{cat $current_article >[2]/dev/null} '') { article=`{cat $current_article} } if not { response 501 'No article number supplied' exit 501 } } group_path=`{echo -n $group | sed 's/\./\//'} group_path=$news/$group_path empty=`{echo $article | tr -d '[0-9]'} is_article_id=0 if (~ $empty '') { is_article_id=1 } if (~ $is_article_id 0) { article=`{get-article-by-uuid $group $article} status=`{echo $status | sed 's/.* ([0-9]+)$/\1/'} switch ($status) { case '' case 430 response 430 'No article with that message-id' exit 430 case * response 500 'Internal server error' exit 500 } } if (! test -f $group_path/$article) { response 420 'Current article number is invalid' exit 420 } echo $article >$current_article article_uuid=`{cat $group_path/$article | \ gunzip | \ grep -i 'message-id: ' | \ sed 's/[Mm]essage-[Ii][Dd]:[ ]?(.*)$/\1/'} response 223 $article^' '^$article_uuid cat $group_path/$article | gunzip | multiline exit 223 case 'LAST' # Retrieve previous article if (~ `{cat $current_group >[2]/dev/null} '') { response 412 'No newsgroup selected' exit 412 } if (~ `{cat $current_article >[2]/dev/null} '') { response 420 'Current article number is invalid' exit 420 } group=`{cat $current_group} group_ac=`{cat $current_group_ac} group_low=`{cat $current_group_low} group_high=`{cat $current_group_high} article=`{cat $current_article} group_path=`{echo -n $group | sed 's/\./\//'} group_path=$news/$group_path last_article=`{awk 'BEGIN { for (i = '^$article^' - 1; i >= '^$group_low^'; i--) { if (system(sprintf("test -f '^$group_path^'/%d", i)) == 0) { print i exit } }}'} if (~ $last_article '') { response 422 'No previous article in this group' exit 422 } echo $last_article >$current_article article=$last_article article_uuid=`{cat $group_path/$article | \ gunzip | \ grep -i 'message-id: ' | \ sed 's/[Mm]essage-[Ii][Dd]:[ ]?(.*)$/\1/'} response 223 $article^' '^$article_uuid exit 223 case 'NEXT' # Retrieve next article if (~ `{cat $current_group >[2]/dev/null} '') { response 412 'No newsgroup selected' exit 412 } if (~ `{cat $current_article >[2]/dev/null} '') { response 420 'Current article number is invalid' exit 420 } group=`{cat $current_group} group_ac=`{cat $current_group_ac} group_low=`{cat $current_group_low} group_high=`{cat $current_group_high} article=`{cat $current_article} group_path=`{echo -n $group | sed 's/\./\//'} group_path=$news/$group_path next_article=`{awk 'BEGIN { for (i = '^$article^' + 1; i <= '^$group_high^'; i++) { if (system(sprintf("test -f '^$group_path^'/%d", i)) == 0) { print i exit } }}'} if (~ $next_article '') { response 422 'No next article in this group' exit 422 } echo $next_article >$current_article article=$next_article article_uuid=`{cat $group_path/$article | \ gunzip | \ grep -i 'message-id: ' | \ sed 's/[Mm]essage-[Ii][Dd]:[ ]?(.*)$/\1/'} response 223 $article^' '^$article_uuid exit 223 case 'HEAD' # Retrieve the article headers group=`{cat $current_group >[2]/dev/null} if (~ $group '') { response 412 'No newsgroup selected' exit 412 } if (! ~ $cmd(2) '') { article=$cmd(2) } if not { if (! ~ `{cat $current_article >[2]/dev/null} '') { article=`{cat $current_article} } if not { response 501 'No article number supplied' exit 501 } } group_path=`{echo -n $group | sed 's/\./\//'} group_path=$news/$group_path empty=`{echo $article | tr -d '[0-9]'} is_article_id=0 if (~ $empty '') { is_article_id=1 } if (~ $is_article_id 0) { article=`{get-article-by-uuid $group $article} status=`{echo $status | sed 's/.* ([0-9]+)$/\1/'} switch ($status) { case '' case 430 response 430 'No article with that message-id' exit 430 case * response 500 'Internal server error' exit 500 } } if (! test -f $group_path/$article) { response 420 'Current article number is invalid' exit 420 } article_uuid=`{cat $group_path/$article | \ gunzip | \ grep -i 'message-id: ' | \ sed 's/[Mm]essage-[Ii][Dd]:[ ]?(.*)$/\1/'} response 221 $article^' '^$article_uuid cat $group_path/$article | \ gunzip | \ awk 'BEGIN { s=0 } { if (length($0) == 0) s=1 if (s == 0) print $0 }' | multiline exit 221 case 'BODY' # Retrieve the article body group=`{cat $current_group >[2]/dev/null} if (~ $group '') { response 412 'No newsgroup selected' exit 412 } if (! ~ $cmd(2) '') { article=$cmd(2) } if not { if (! ~ `{cat $current_article >[2]/dev/null} '') { article=`{cat $current_article} } if not { response 501 'No article number supplied' exit 501 } } group_path=`{echo -n $group | sed 's/\./\//'} group_path=$news/$group_path empty=`{echo $article | tr -d '[0-9]'} is_article_id=0 if (~ $empty '') { is_article_id=1 } if (~ $is_article_id 0) { article=`{get-article-by-uuid $group $article} status=`{echo $status | sed 's/.* ([0-9]+)$/\1/'} switch ($status) { case '' case 430 response 430 'No article with that message-id' exit 430 case * response 500 'Internal server error' exit 500 } } if (! test -f $group_path/$article) { response 420 'Current article number is invalid' exit 420 } article_uuid=`{cat $group_path/$article | \ gunzip | \ grep -i 'message-id: ' | \ sed 's/[Mm]essage-[Ii][Dd]:[ ]?(.*)$/\1/'} response 221 $article^' '^$article_uuid cat $group_path/$article | \ gunzip | \ awk 'BEGIN { s=0 } { if (s == 1) print $0 if (length($0) == 0) s=1 }' | multiline exit 221 case 'STAT' # Test if the article by id and message-id exists group=`{cat $current_group >[2]/dev/null} if (~ $group '') { response 412 'No newsgroup selected' exit 412 } if (! ~ $cmd(2) '') { article=$cmd(2) } if not { if (! ~ `{cat $current_article >[2]/dev/null} '') { article=`{cat $current_article} } if not { response 501 'No article number supplied' exit 501 } } group_path=`{echo -n $group | sed 's/\./\//'} group_path=$news/$group_path empty=`{echo $article | tr -d '[0-9]'} is_article_id=0 if (~ $empty '') { is_article_id=1 } if (~ $is_article_id 0) { article=`{get-article-by-uuid $group $article} status=`{echo $status | sed 's/.* ([0-9]+)$/\1/'} switch ($status) { case '' case 430 response 430 'No article with that message-id' exit 430 case * response 500 'Internal server error' exit 500 } } if (! test -f $group_path/$article) { response 420 'Article number is invalid' exit 420 } article_uuid=`{cat $group_path/$article | \ gunzip | \ grep -i 'message-id: ' | \ sed 's/[Mm]essage-[Ii][Dd]:[ ]?(.*)$/\1/'} response 223 $article^' '^$article_uuid exit 223 case 'QUIT' # Quit command rm $lockfile case * # Default handler log 'client sent command' $cmd 'which is not recognized' response 500 'Unknown command' exit 500 }