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
}