Complete.Org: Mailing Lists: Archives: gopher: July 2001:
[gopher] Re: PyGS - a Python Gopher Server...
Home

[gopher] Re: PyGS - a Python Gopher Server...

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: gopher@xxxxxxxxxxxx
Subject: [gopher] Re: PyGS - a Python Gopher Server...
From: Jon Nelson <jdnelson@xxxxxxxxxx>
Date: Thu, 19 Jul 2001 00:16:18 -0500
Reply-to: gopher@xxxxxxxxxxxx

> 
> Hi all,
>       I've finally gotten PyGS, the Python Gopher Server that I've been 
> tinkering with for some time to a point where it actually dishes stuff out
> semi-consistently.

Hey.

I also wrote a python gopher server (quite a while ago).
I haven't had time to mess with it, but it's yours to take a look at if you
like.  If you think any of it is worth keeping (I hope so), then let's
talk about collaboration on a merged project?




-- Attached file included as plaintext by Listar --
-- File: Log.py
-- Desc: Log.py

import sys

LOG_FATAL = 0
LOG_ERROR = 1
LOG_WARN = 2
LOG_ALERT = 3
LOG_INFO = 4
LOG_DEBUG = 5

class Log:
  def __init__(self, logfile=sys.stderr, loglevel=LOG_INFO):
    self.logfile = logfile
    self.loglevel = loglevel

  def log (self, msg, level=LOG_INFO):
    "just prints its parameter"
  
    if level == LOG_FATAL:
      self.logfile.write("F:" + msg + "\n")
      sys.exit(1)
    elif level == LOG_ERROR:
      self.logfile.write("E:" + msg + "\n")
    elif level == LOG_WARN:
      if level <= self.loglevel:
        self.logfile.write("W:" + msg + "\n")
    elif level == LOG_ALERT:
      if level <= self.loglevel:
        self.logfile.write("A:" + msg + "\n")
    elif level == LOG_INFO:
      if level <= self.loglevel:
        self.logfile.write("I:" + msg + "\n")
    elif level == LOG_DEBUG:
      if level <= self.loglevel:
        self.logfile.write("D:" + msg + "\n")
    else:
      self.logfile.write("?:" + msg + "\n")
  


-- Attached file included as plaintext by Listar --
-- File: gopherd.py
-- Desc: gopherd.py

#! /usr/bin/python
import SocketServer,os,re,string,signal,socket,sys,threading,os.path,time
from stat import *
from Log import *

log=None
VERSION="$Id$"
sighup_flag=0
sigterm_flag=0
myLock = threading.Lock()
DOCROOT=""

request_counter = 0

def dirsplit(base):
  try:
    files = os.listdir(base)
  except OSError, details:
    log("Unable to list directory (%s)" % base, LOG_ERROR)
    return ([],[])

  dirs = []

  for f in files[:]:
    if f[0] == '.':
      continue
    newfile = base + "/" + f
    try:
      if S_ISDIR(os.stat(newfile)[ST_MODE]):
        dirs.append(f)
        files.remove(f)
    except:
      files.remove(f)
  return(files, dirs)

def alarm_handler(a,b):
  log("client timed out", LOG_ALERT)

def hup_handler(a,b):
  global sighup_flag
  log('caught sighup...', LOG_ALERT)
  sighup_flag = 1

def term_handler(a,b):
  global sigterm_flag
  log('caught sigterm...', LOG_ALERT)
  sigterm_flag = 1

class myHandler(SocketServer.StreamRequestHandler):
  protocol = """
  {File/Directory=0/1}{User Display String}\t{selector 
string}\t{host}\t{port}crlf

   0   Item is a file
   1   Item is a directory
   2   Item is a CSO phone-book server
   3   Error
   4   Item is a BinHexed Macintosh file.
   5   Item is DOS binary archive of some sort.
       Client must read until the TCP connection closes.  Beware.
   6   Item is a UNIX uuencoded file.
   7   Item is an Index-Search server.
   8   Item points to a text-based telnet session.
   9   Item is a binary file!

       Client must read until the TCP connection closes.  Beware.
   +   Item is a redundant server
   T   Item points to a text-based tn3270 session.
   g   Item is a GIF format graphics file.
   h   HTML, HyperText Markup Language
   I   Item is some kind of image file.  Client decides how to display.
  """

  notes = """
  for files:

  Note:  Lines beginning with periods must be prepended with an extra
     period to ensure that the transmission is not terminated early.
     The client should strip extra periods at the beginning of the line.

  """

  def getFile(self, path):
#    print "File: %s" % path
    try:
      f = open(self.docroot + path, 'rb')
      lines = f.readlines()
      f.close()
      return string.join(lines)
    except IOError:
      return "There was an error!"

  def fileType(self, path):
    root, ext = os.path.splitext(path)
    type = '9'
    if ext == '.jpg' or ext == '.jpeg':
      type = 'I'
    elif ext == '.gif':
      type = 'g'
    elif ext == '.html' or ext == '.htm':
      type = 'h'
    elif ext == '.txt' or ext == '.text':
      type = '0'
    return type

  def getDirectory(self, path):
#    print "Dir: %s" % path

    files, dirs = dirsplit(self.docroot + path)
    response = ""
    for i in files:
      response = response + "%c%s\t%s/%s\t%s\t%d\r\n" % (self.fileType(i), i, 
path, i, self.host, self.port)
    for i in dirs:
      response = response + "1%s\t%s/%s/\t%s\t%d\r\n" % (i, path, i, self.host, 
self.port)
    return response

  def start(self, blah):
    return self.getDirectory("")
    pass

  def respond(self, response):
    # make response pretty (ie, lines that start w/periods, etc....)
    response = response + ".\r\n"
    try:
      self.wfile.write(response)
      log("[%d] Wrote %d bytes." % (self.counter, len(response)), LOG_INFO)
    except socket.error, details:
      log("[%d] Socket error %s writing to client ." % (self.counter, 
str(details)), LOG_ALERT)
#    print response

    return

  def handle_error(request, client_address):
    request.close()
    SocketServer.BaseRequestHandler.handle_error(request, client_address)

  def handle(self):
    global request_counter, myLock, DOCROOT

    myLock.acquire()
    request_counter = request_counter + 1
    self.counter = request_counter
    myLock.release()

    self.docroot = DOCROOT

    self.host, self.port = self.connection.getsockname()
    peerhost, peerport = self.client_address
    log("[%d] Handling request from %s:%d" % (self.counter, peerhost, 
peerport), LOG_INFO)

    signal.alarm(15)
    try:
      data = self.rfile.readline()
      signal.alarm(0)
    except socket.error:
      log("[%d] Socket error receiving from client." % self.counter, LOG_ALERT)
      return

    if not data:
      log("[%d] No response from client." % self.counter, LOG_ALERT)
      self.respond('400 ERR no data recieved.\n')
      return

    if string.find(data, '..') != -1:
      log("[%d] Illegal Request." % (self.counter), LOG_ALERT)
      self.respond('400 ERR Illegal request.\n')
      return

#    log("[%d] Attempting to match request: '%s'" % (self.counter, data), 
LOG_INFO)

    START_RE = re.compile(r'^(\B|/)\r\n$')
    FILE_RE = re.compile(r'^(.+)\r\n$')
    DIR_RE = re.compile(r'^(.+)/\r\n$')
    mycommands = [ (START_RE, self.start, {}), \
                   (DIR_RE, self.getDirectory, {}),\
                   (FILE_RE, self.getFile, {})\
                 ]

    for c in mycommands:
      myre, myfunc, myargs = c
      m = myre.match(data)
      if m:
        self.file = m.group(1)
        log('[%d] Matched request for "%s"' % (self.counter, self.file), 
LOG_INFO)
        response = apply(myfunc, m.groups(), myargs)
        if response:
          self.respond(response)
        return

    log("[%d] Unable to match request. (\"%s\")" % (self.counter, data), 
LOG_ALERT)
    self.respond('400 ERR Unable to match request.\n')
    return

class MyServer(SocketServer.TCPServer):
  pass

def main():
  global log
  global sighup_flag
  global sigterm_flag
  global VERSION
  global DOCROOT

#  LOGFILE='mylog'
#  myLog = Log(open(LOGFILE, "a+", 0), LOG_INFO)
  myLog = Log(sys.stderr, LOG_INFO)
  log = myLog.log
  if len(sys.argv) != 2:
    log("Insufficient parameters.", LOG_FATAL)
  host = "" # bind to all addresses
  s = MyServer( (host, 1170 ), myHandler)

  signal.signal(signal.SIGALRM,alarm_handler)
  signal.signal(signal.SIGHUP,hup_handler)
  signal.signal(signal.SIGTERM,term_handler)
  sighup_flag = 1
  DOCROOT=sys.argv[1]
  if DOCROOT[-1:] != "/":
    DOCROOT = DOCROOT + "/"
  log("Setting DOCROOT to %s" % DOCROOT, LOG_ALERT)
  log("Starting pygopherd server version %s." % VERSION, LOG_ALERT)
#  child = not os.fork()
  child = 1
  if child:
    while 1:
      try:
        if sighup_flag:
          sighup_flag=0
          # do something here
        if sigterm_flag:
          raise KeyboardInterrupt
        s.handle_request()
      except KeyboardInterrupt:
        s.socket.close()
        del s
        try:
          log("Shutting down.", LOG_ALERT)
        except:
          pass
        sys.exit(0)
      except socket.error, details:
        if details[0] == 4: # interrupted system call
          continue
        else:
          del s
          log("socket received an error: %s" % str(details), LOG_ERROR)
          sys.exit(1)
      except:
        del s
        log("aiieee!  unknown error!  closing socket first.", LOG_ERROR)
        raise
  sys.exit(0)  # never gets here

if __name__ == "__main__":
  main()


-- Attached file included as plaintext by Listar --

Pound for pound, the amoeba is the most vicious animal on earth.

Jon Nelson
jnelson@xxxxxxx




[Prev in Thread] Current Thread [Next in Thread]