1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
|
#!/usr/bin/python
"""
__version__ = "$Revision: 1.27 $"
__date__ = "$Date: 2005/09/18 03:59:22 $"
"""
import wx
from PythonCard import configuration, model
import os, sys
import shutil
from BaseHTTPServer import HTTPServer
import CGIHTTPServer
from CGIHTTPServer import CGIHTTPRequestHandler
import threading
import Queue
import ConfigParser
import socket
# imports needed code is moved back into standard libs
import urllib
import select
CONFIG_FILE = 'webserver.ini'
# a backwards compatible date_time_string function
# with the one in BaseHTTPRequestHandler
# it seems like this should be moved and made a function
# as I've done below
import time
import rfc822
weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
monthname = [None,
'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
def date_time_string(t=None):
global weekdayname, monthname
"""Return the current date and time formatted for a message header."""
if not t:
t = time.time()
# assume user supplied time value will be local, not gmt!
year, month, day, hh, mm, ss, wd, y, z = time.gmtime(t)
s = "%s, %02d %3s %4d %02d:%02d:%02d GMT" % (
weekdayname[wd],
day, monthname[month], year,
hh, mm, ss)
return s
class MyRequestHandler(CGIHTTPRequestHandler):
# the server variable contains the reference back to the view
def log_message(self, format, *args):
msg = "%s - - [%s] %s\n" % (self.address_string(), self.log_date_time_string(), format%args)
if format.startswith('"%s"'):
self.server._notify_window.msgQueue.put(msg)
wx.WakeUpIdle()
# just log to the GUI now, this could go to a file as well
else:
# don't put CGI error and exit status messages in the main log
print msg
cgi_extensions = [".py", ".pyw", ".cgi"]
def is_python(self, path):
"""Test whether argument path is a Python script."""
head, tail = os.path.splitext(path)
return tail.lower() in self.cgi_extensions
class WebServer(HTTPServer):
def __init__(self, notify_window, server_address, RequestHandlerClass, validIPList=None):
self._notify_window = notify_window
# this list will come from a config file
# and there will be a dialog to edit the valid IP addresses
if validIPList:
self.validIPList = validIPList
else:
self.validIPList = ['127.0.0.1']
# add host IP address that this server is running on
# this will fail for address spaces like 10.0.0.x, 192.168.0.x
# etc., so what is the appropriate way to add those?
# I get errors like
# socket.gaierror: (7, 'No address associated with nodename')
try:
self.validIPList.append(socket.gethostbyname(socket.gethostname()))
except Exception, msg:
pass
self.allowAny = 0
HTTPServer.__init__(self, server_address, RequestHandlerClass)
def verify_request(self, request, client_address):
if self.allowAny or client_address[0] in self.validIPList:
return 1
else:
return 0
# just a guess that this is how we should use threads
def server(self):
self.serve_forever()
class WebServerView(model.Background):
def on_initialize(self, event):
self.initSizers()
self.loadConfig()
# if you wanted to limit the on screen log size, then set
# this to something other than 0
self.maxSizeLog = 0
# Set up event handler for any worker thread results
"""
Robin said:
The Queue class is thread-safe, using a mutex and semaphore to protect
access to its contents, so is ideally suited for something like this.
"""
self.msgQueue = Queue.Queue()
os.chdir(self.htdocs)
self.srvraddr = ('', self.port)
# give the server and request handlers
# a reference back to the view for logging
self.webServer = WebServer(self, self.srvraddr, MyRequestHandler, self.validIPList)
self.thread = threading.Thread(target = self.webServer.server)
# I think this allows Python to kill the thread when we quit wxPython
# setDaemon must be called before start
self.thread.setDaemon(1)
self.thread.start()
def loadConfig(self):
self.configPath = os.path.join(configuration.homedir, 'webserver')
if not os.path.exists(self.configPath):
os.mkdir(self.configPath)
configPath = os.path.join(self.configPath, CONFIG_FILE)
defaultPath = os.path.join(self.application.applicationDirectory, CONFIG_FILE)
if not os.path.exists(configPath):
shutil.copy2(defaultPath, configPath)
parser = ConfigParser.ConfigParser()
parser.read(configPath)
self.htdocs = parser.get('ConfigData', 'htdocs')
self.port = int(parser.get('ConfigData', 'port'))
ips = parser.get('ConfigData', 'iplist')
self.validIPList = ips.split(',')
def initSizers(self):
sizer1 = wx.BoxSizer(wx.VERTICAL)
sizer1.Add(self.components.fldLog, 1, wx.EXPAND)
sizer1.Fit(self)
sizer1.SetSizeHints(self)
self.panel.SetSizer(sizer1)
self.panel.SetAutoLayout(1)
self.panel.Layout()
def on_idle(self, event):
if not self.msgQueue.empty():
msg = self.msgQueue.get()
self.doLogResult(msg)
event.RequestMore()
def doLogResult(self, data):
if data is not None:
# code borrowed from the Message Watcher event history display
log = self.components.fldLog
log.SetReadOnly(0)
if self.maxSizeLog and log.GetLength() > self.maxSizeLog:
# delete many lines at once to reduce overhead
text = log.GetText()
endDel = text.index('\n', self.maxSizeLog / 10) + 1
log.SetTargetStart(0)
log.SetTargetEnd(endDel)
log.ReplaceTarget("")
log.GotoPos(log.GetLength())
log.AddText(data)
log.GotoPos(log.GetLength())
log.SetReadOnly(1)
else:
pass
def on_menuOptionsAllowAny_select(self, event):
self.webServer.allowAny = self.menuBar.getChecked('menuOptionsAllowAny')
if __name__ == '__main__':
app = model.Application(WebServerView)
app.MainLoop()
|