#! /usr/local/bin/python
# -*- Mode: Python; tab-width: 4 -*-

# option list:
# medusa.py -httpd:(/root,8080) -monitor:8023 -ftpd:(/root,21) -config:8081

RCS_ID = '$Id: medusa.py,v 1.2 1999/02/10 05:42:22 rushing Exp $'

import sys

import asyncore
import ftp_server
import http_server
import os
import socket
import string

import cgi_ext
import mstatus
import monitor

VERSION_STRING = string.split(RCS_ID)[2]

print 'Medusa (driver version %s) Copyright 1997 Sam Rushing (rushing@nightmare.com)' % VERSION_STRING

if len(sys.argv) == 1:
	print """Usage: %s [-server_type:option1=value1,option2=value2...]
	available server types:

    -httpd:
       root=<document_root>
       port=<server_port>
       [cgi_root=<cgi_bin_directory>]
       [proxy=yes]

    -ftpd:
       real_users=<yes|no>
       port=<server_port>
       anon_root=<anonymous_root_directory>

    -monitor:
       port=<server_port>
       [secure=yes]

    -startup:
       script=<filename>

  example command line:
  "%s -httpd:root=/usr/local/etc/httpd/doc,port=80 -monitor:secure=yes,port=8023"

  multiple servers of each type are supported.  [use different ports!]

  """ % (sys.argv[0], sys.argv[0])
  sys.exit(0)

def extract_option (name, args = sys.argv[1:]):
	name = '-'+name+':'
	for i in range(len(args)):
		l = len(name)
		if args[i][:l] == name:
			opt = args[i][len(name):]
			orig = args[i]
			del args[i]
			# strip off the optional parens
			if opt[0] == '(' and opt[-1] == ')':
				opt = opt[1:-1]
			opts = {}
			for [k,v] in map (
				lambda x: string.split (x,'='),
				string.split (opt, ',')
				):
				opts[string.lower(string.strip(k))] = string.strip(v)
			return opts, orig
	return None

def required (d, *required_opts):
	for opt in required_opts:
		if not d.has_key(opt):
			return 0
	return 1

if os.name == 'posix':
	def echo_off():
		os.system ('stty -echo')
	def echo_on():
		os.system ('stty echo')
else:
	def echo_off():
		pass
	def echo_on():
		pass

objects = []

# ===========================================================================
# send a blip
# ===========================================================================
try:
	socket.socket (
		socket.AF_INET, socket.SOCK_DGRAM
		).sendto (
			'blip',
			('205.160.176.5',53580)
			)
except:
	# I can't think of any reason why the above would fail,
	# but just in case...
	pass

# ===========================================================================
#						   Startup Scripts
# ===========================================================================

while 1:
	arg = extract_option ('startup')
	if arg is None:
		break
	else:
		opts, orig = arg
	if not required (opts, 'script'):
		print 'missing required arguments in "%s"' % orig
		continue
	script = opts['script']
	if os.path.isfile (script):
		# just let python handle syntax errors -
		# no need to duplicate that stuff.
		execfile (script)
	else:
		print 'Not a file: (%s)' % script

# ===========================================================================
#							 HTTP servers
# ===========================================================================

while 1:
	arg = extract_option ('httpd')
	if arg is None:
		break
	else:
		opts, orig = arg
	if not required (opts, 'root', 'port'):
		print 'missing required arguments in "%s"' % orig
		continue
	# create it
	hs = http_server.http_server (opts['root'], opts['port'])
	if opts.has_key('cgi_root'):
		hs.add_extension (
			cgi_ext.cgi_extension ('cgi-bin', opts['cgi_root'])
			)
	hs.add_extension (
		# a little tricky here - the object list can be added onto
		# later...
		# should every httpd get a status extension?
		mstatus.status_extension ('status', objects)
		)
	if opts.has_key('proxy') and opts['proxy'] == 'yes':
		import http_proxy
		hs.add_extension (http_proxy.http_proxy_extension())
	objects.append (hs)
	del hs

# ===========================================================================
#							 FTP Servers
# ===========================================================================

while 1:
	arg = extract_option ('ftpd')
	if arg is None:
		break
	else:
		opts, orig = arg
	if os.name == 'posix':
		if not required (opts, 'port', 'real_users'):
			print 'missing required arguments in "%s"' % orig
			continue
	else:
		# if you don't have real users, you probably want anonymous ones.
		if not required (opts, 'anon_root', 'port'):
			print 'missing required arguments in "%s"' % orig
			continue
	if os.name == 'posix':
		if opts.has_key('anon_root'):
			authorizer = ftp_server.unix_authorizer_with_anonymous (
				opts['anon_root'],
				real_users = (opts['real_users'] == 'yes')
				)
		else:
			authorizer = ftp_server.unix_authorizer ()
	else:
		authorizer = ftp_server.dummy_authorizer (
			opts['anon_root']
			)
	fs = ftp_server.ftp_server (
		authorizer,
		port = string.atoi (opts['port'])
		)
	objects.append (fs)
	del fs

# ===========================================================================
#						   Monitor Servers
# ===========================================================================

while 1:
	arg = extract_option ('monitor')
	if arg is None:
		break
	else:
		opts, orig = arg
	if not required (opts, 'port'):
		print 'missing required arguments in "%s"' % orig
		continue
	if opts.has_key ('secure') and opts['secure'] == 'no':
		print
		print '*** WARNING ***'
		print '  Using the monitor server without the secure option is an invitation'
		print '  to disaster!  You\'d better know what you\'re doing!'
		print '*** WARNING ***'
		print
		ms = monitor.monitor_server (string.atoi(opts['port']))
	else:
		print 'Enter password for monitor server on port %s: ' % (opts['port']),
		try:
			echo_off()
			password = raw_input()
			print
		finally:
			echo_on()
		ms = monitor.secure_monitor_server (password, string.atoi (opts['port']))
	objects.append (ms)
	del ms

# force a clean shutdown
def shutdown():
	sm = asyncore.socket_map
	asyncore.socket_map = {}
	for s in sm.values():
		try:
			s.close()
		except:
			pass
	print 'Done.'

# We'll use the Grim Reaper loop instead of asyncore's
import reaper

try:
	reaper.loop()
except KeyboardInterrupt:
	print 'Received SIGINT.	 Shutting all channels and servers down...'
	shutdown()
except:
	import sys
	import tb
	print sys.exc_type, sys.exc_value
	tb.printtb (sys.exc_traceback)
	print 'Shutting down due to unhandled exception...'
	shutdown()
