NNML::Server - a minimal NNTP server


NNML documentation Contained in the NNML distribution.

Index


Code Index:

NAME

Top

NNML::Server - a minimal NNTP server

SYNOPSIS

Top

  perl -MNNML::Server -e server
  perl -MNNML::Server -e unspool

DESCRIPTION

Top

NNML::Server server implements a minimal NNTP server. It is (hope-) fully conformant to rfc977. In addition the commands XOVER and AUTHINFO are implemented.

Supported commands:

  ARTICLE, AUTHINFO, BODY, GROUP, HEAD, HELP, IHAVE, LAST, LIST,
  MODE, NEWGROUPS, NEWNEWS, NEXT, POST, QUIT, SLAVE, STAT

  XOVER, XHDR, LIST NEWSGROUPS ng-spec

The main reason for writing this was to synchronize my mail directories across different hosts. The Mail directories are MH-Style with a .overview file in each folder and an active file in the base directory. These are maintained by the Emacs Gnus backend NNML. To get started, you can generate/update this files using the overview program. Upon POST and IHAVE commands this files will also be updated.

To start from scratch use:

  touch /tmp/active;
  perl -MNNML::Server -e 'server(base => "/tmp", port => 3000)'

To export your mh-Mail use:

  perl overview -base ~/Mail
  perl -MNNML::Server -e 'server(base => "$ENV{HOME}/Mail", port => 3000)'




The command POST and IHAVE honour the Newsgroups header if not overwritten by the X-Nnml-Groups header. Articles will contain an appropriate X-Nnml-Groups header when retrieved by message-id.

When the client submits the SLAVE command, all forther post requests are spooled in $Config-spool> (usually ~/Mail/NNML.spool) for performance reasons. You can process the spooled articles by submitting the XUNSPOOL command or by calling

  perl -MNNML::Server -e unspool

Rejected articles will be saven in $Config-bad> (usually ~/Mail/NNML.bad)

AUTHORIZATION

Top

To enable access restrictions use:

  perl -MNNML::Auth -e "NNML::Auth::add_user($ENV{LOGANME}, 'passwd', \
    'read', 'write', 'admin')"

If base/passwd exists, three levels of authorization are recognized:

admin

Users with permission admin may shut down the server using SHUT. Also these users may create new groups simply by posting to them. Permission admin is also required for the XUNSPOOL command.

write

Users with permission write may use the POST and IHAVE commands.

read

All other commands require the read permission.

FEATURES

Top

Version 1.06 implements the MODE GZIP command. After submiting this commands, all articles, heads and bodies will be piped through gzip -cf | mimencode. The server will recognize post requeste using the same pipe automatically. This will speed up nnmirror if the line is sufficiant slow.

BUGS

Top

The server handles multiple connections in a single thread. So a hung POST or IHAVE would block all connections. Therfore a post request is interrupted if the server could not read any bytes for 30 seconds. The Client is notified by message 441. If the client continues to send the article, it is interpreted by the command loop.

SEE ALSO

Top

The overview(1) and nnmirror(1) manpages.

AUTHOR

Top

Ulrich Pfeifer <pfeifer@ls6.informatik.uni-dortmund.de>


NNML documentation Contained in the NNML distribution.

#                              -*- Mode: Perl -*-
# Server.pm --
# ITIID           : $ITI$ $Header $__Header$
# Author          : Ulrich Pfeifer
# Created On      : Sat Sep 28 13:53:36 1996
# Last Modified By: Ulrich Pfeifer
# Last Modified On: Tue Apr  1 13:23:28 1997
# Language        : CPerl
# Update Count    : 154
# Status          : Unknown, Use with caution!
#
# (C) Copyright 1996, Universität Dortmund, all rights reserved.
#

package NNML::Server;
use vars qw($VERSION @ISA @EXPORT);
use NNML::Connection;
use NNML::Config qw($Config);
use IO::Socket;
use IO::Select;
use NNML::Handle;
use strict;

require Exporter;
@ISA = qw(Exporter);
@EXPORT = qw(server unspool);

$VERSION = do{my @r=(q$Revision: 1.13 $=~/(\d+)/g);sprintf "%d."."%02d"x$#r,@r};

sub server {
  my %opt  = @_;
  my $port = $opt{port} || $Config->port;

  if (exists $opt{base}) {
    $Config->base($opt{base});
  }
  NNML::Auth::_update;          # just for the message
  my $lsn  = new NNML::Handle(Reuse     => 1,
                              Listen    => 5,
                              LocalPort => $port,
                              Proto     => 'tcp');
  die "Could not connect to port $port: $!\n" unless defined $lsn;

  my $SEL  = new IO::Select( $lsn );
  my %CON;
  my $fh;
  my @ready;

  print STDERR "listening on port $port\n";
  
  while(1) {
    @ready = $SEL->can_read;
  REQUEST:
    foreach $fh (@ready) {
      if($fh == $lsn) {
        my $new = $lsn->accept; # Create a new socket
        $CON{$new} = new NNML::Connection $new, $VERSION;
        $SEL->add($new);
      } else {
        my ($cmd, $func, @args);
        my $fno = fileno($fh); 

        $cmd = $fh->getline();
        ($func, @args) = split ' ', $cmd;
        unless (fileno($fh)) {
          # client has closed connection without sending 'quitt'
          printf STDERR "Shuttig down $fh(%d)\n", $fno;
          delete $CON{$fh};
          $SEL->remove($fno);
          next REQUEST;
        }
        $func = lc($func);
        if ($func eq 'shut') {  # shut down the server
          if (NNML::Auth::perm($CON{$fh}, $func)) {
            my $fx;
            print STDERR "Going down\n";
            for $fx (keys %CON) {
              $CON{$fx}->msg(400);
              $CON{$fx}->close;
              delete $CON{$fx};
            }
            $SEL->remove($lsn);
            $lsn->close();
            return;
          } else {
            $CON{$fh}->msg(480);
            next REQUEST;
          }
        } else {
          $func = $CON{$fh}->dispatch($func, @args);
          if ($func eq 'quit') {
            print STDERR "closed\n";
            $SEL->remove($fh);
            $CON{$fh}->close;
            delete $CON{$fh};
          }
        }
      }
    }
  }
}

1;

__END__