shithub: rc-nntpd

ref: 4138bf3cc011ad73282550d2f3afa78d91131199
dir: /handlers/handle-nntp/

View raw version
#!/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
}