From: schemers@leland.Stanford.EDU (Roland Schemers)
Subject:  gopher<->NNTP perl script
Organization: Distributed Computing Group, Stanford University
Date: Sat, 25 Jul 92 00:25:13 GMT

Ok, here is yet ANOTHER gopher gateway. Gopher to NNTP. This is
different from MSU's approach in that it is done dynamically
by changing gopher requests into NNTP requests and vice-versa.

You have to run it from inetd.

Your NNTP server must understand the XHDR command. Its possible 
to write this gateway so that command isn't needed, but your 
better off upgrading your NNTP server :-)

I just wrote this in a couple of hours, and it works fairly well.

Try:

gopher slapshot.stanford.edu

And select "News".

Links should look like:

Name=USENET News
Type=1
Port=4327
Path=
Host=Slapshot.Stanford.EDU

To get all the news. You can get one group like so:

Name=Stanford Events (su.events)
Type=1
Port=4327
Path=ls su.events
Host=Slapshot.Stanford.EDU

Or a list of groups:

Name=Stanford News Groups
Type=1
Port=4327
Path=ls su
Host=Slapshot.Stanford.EDU

Or even one article:

Name=Mercury news: Amiga dead!
Type=0
Port=4327
Path=article su.amiga 55
Host=Slapshot.Stanford.EDU

The nice thing about this approach is now links could be setup to
better describe news groups.

Please let me know what you think!

Roland

#!/usr/local/bin/perl
#
# switch to nobody if we are root
($<,$>) = (-2,-2) unless $>;

require 'getopts.pl';

#
# Simple gopher2nntp server. You can change the following variables.
# nntp_port is hardcoded here since this is a stateless server and
# no sense in reading it from /etc/services every time...
#
# A server that sticks around might be better...
#
# Note: The NNTP daemon must accept the XHDR command.
#
#
# You should run "gopher2nntp -g" from cron to generate the groups 
# file. If you don't generate the groups file then this server will
# grab it from the NNTP server, but thats a waste of time. 
#
#
  $nntp_server = "leland.stanford.edu";
  $nntp_port   = 119;
  $nntp_groups = "/usr/local/etc/newsgroups.gopher"; # could be active file
#
# Commands this server responds to:
# 
# ""                         -> list top level groups
# ls $group                  -> list group's articles and sub-groups
# article $group $number     -> get 1 article
#

$port=&my_port();         # port this script is running on 
chop($host = `hostname`);

&Getopts('rdga:s:');

if ($errs || $#ARGV!=-1) {

print<<EOF;

 Usage: $0 [-s server] [-a active_file] [-g] [-d] [-r]
 -s server      NNTP server to contact
 -a file        Active file to use
 -n             Don't use active file
 -g             generate new active file\n";
 -d             turn on debug (when typing commands from a tty)
 -r             list articles in reverse order
EOF
exit;

}

$nntp_server = $opt_s if (defined($opt_s));
$nntp_port = $opt_p if (defined($opt_p));
$nntp_groups = $opt_a if (defined($opt_a));

&create_groups($opt_g) if defined($opt_g);

  $_ = <STDIN>; s/\r//; s/\n//;

  &do_ls("") if /^$/;
  &do_ls($1) if /^ls\s+(.*)/i;
  &do_article($1,$2) if /^article\s+(\S+)\s+(\d+)/i;

  &reply("3Unknown command!");
  exit;

sub do_article {
  local($group,$number) = @_;

  &open_nntp;

  &send("GROUP $group");
  $_ = &recv;
  &death if !/^211/;

  &send("ARTICLE $number");
  $_ = &recv;
  &death if !/^220/;
 
  while(<SERVER>) {
    print;
    last if /^\.\r\n$/;
  }

  &close_nntp;
  exit;
}

sub list_group {
  local($group) = @_;

  &send("GROUP $group");
  $_ = &recv;
  &death if !/^211/;

  ($n,$f,$l) = /211\s+(\d+)\s+(\d+)\s+(\d+)/;

  &send("XHDR Subject $f-$l");
  $_ = &recv;
  &death if !/^221/;
 
  while(<SERVER>) {
    chop; chop;
    last if /^\.$/;
    ($article,$subject) = /^(\d+)\s+(.*)/;
    $subject =~ s/\t/ /g; # just in case!
    if (defined($opt_r)) {
       push(@reply,"0$subject\tarticle $group $article\t$host\t$port");
    } else {
       &reply("0$subject\tarticle $group $article\t$host\t$port");
    }
  }

  if (defined($opt_r)) { 
    for ($i=$#reply; $i!= -1; $i--) { &reply($reply[$i]); } 
  }

  &reply(".");
  &close_nntp;
  exit;
}


sub do_ls {
  local($prefix) = @_;

  &open_nntp;
  &get_groups;

  foreach ( sort @groups) {
    if ($_ eq $prefix) { $do_list_group = $_; }
    elsif (/^$prefix\.([^.]*)\.?/) {
         $leaf=$1;
         $save{"$prefix.$leaf"} = "1$leaf\tls $prefix.$leaf\t$host\t$port";
    }
    elsif ($prefix eq '' && /([^.]*)/) {
         $save{"$1"} = "1$1\tls $1\t$host\t$port";
    }
  }

  foreach ( sort keys %save) { &reply($save{$_}); }
  &list_group($do_list_group) if ($do_list_group);

  &reply(".");
  &close_nntp;
  exit;
}

sub open_nntp {
  local($_);
  &open_server($nntp_server,$nntp_port);
  $_ = &recv;
  &death if !/^2/;
}

sub close_nntp {
  &send("QUIT");
  close(SERVER);
}

sub my_port {
   return -1 if (-t STDIN);
   $sockaddr = 'S n a4 x8';
   $mysockaddr = getsockname(STDIN);
   ($myfamily,$myport,$myaddr) = unpack($sockaddr,$mysockaddr);
   return $myport;
}

sub open_server {

 local($server,$port) = @_;
 $sockaddr = 'S n a4 x8';
 (($name, $aliases, $type, $len, $saddr) = gethostbyname($server))||&death;
 $sin = pack($sockaddr, 2, $port, $saddr);
 socket(SERVER, 2, 1, 0) || &death;
 connect(SERVER, $sin)   || &death;
 select(SERVER); $| = 1; select(STDOUT); $| = 1;

}

sub send     { 
     print "send -> |$_[0]|\n" if (defined($opt_d));
     print SERVER "$_[0]\r\n"; 
}

sub recv { 
   local ($_); 
   $_= <SERVER>; 
   chop; chop;
   print "recv -> |$_|\n" if (defined($opt_d));
   return $_; 
}

sub reply { print "$_[0]\r\n";}
sub death { &reply("."); exit; }

sub get_groups {
 if (!defined($opt_n) && open(GROUPS,$nntp_groups)) {
      while(<GROUPS>) {
          chop;
          ($grp) = /^(\S+)/;
          push(@groups,$grp);
      }
      close(GROUPS);
 } else {                  # can't open file, get list from server!
  &load_groups;
 }
}

sub load_groups {

  &open_nntp;
  &send("LIST");
  $_ = &recv;
  &death if !/^215/;

  while(<SERVER>) {
    chop; chop;
    last if /^\.$/;
    s/^(\S+).*/$1/;
    push(@groups,$_);
  }

}

sub create_groups { 

  &load_groups;
 
  open(GROUPS,">$nntp_groups") || die "$nntp_groups: $!";
  foreach (@groups) { print GROUPS "$_\n"; }
  close GROUPS;

  &close_nntp;
  exit;
}

-- 
Roland J. Schemers III              |            Networking Systems
Systems Programmer                  |            168 Pine Hall   (415)-723-6740
Distributed Computing Group         |            Stanford, CA 94305-4122
Stanford University                 |            schemers@Slapshot.Stanford.EDU
Newsgroups: alt.gopher,comp.windows.x.apps