Attachment 'MoinIIS.py'

Download

   1 #
   2 # MoinIIS v.3
   3 #
   4 # IIS ISAPI extension to start MoinMoin from under IIS effectively
   5 #
   6 # Set the virtual path to your wiki instanse
   7 #
   8 virtualPath = '/themoin'
   9 
  10 import sys
  11 #
  12 # If MoinMoin is not set to the default package path, 
  13 # uncomment and edit this:
  14 #
  15 #sys.path.append('C:/MoinWiki-1.3.5/Lib/site-packages')
  16 
  17 from StringIO import StringIO
  18 from MoinMoin import config, wikiutil
  19 from MoinMoin.request import RequestBase
  20 from isapi import InternalReloadException
  21 from isapi import isapicon
  22 from isapi.simple import SimpleExtension
  23 import MoinMoin
  24 import cgi, urllib, os, stat, threading
  25 import win32event, win32file, winerror, win32con, threading
  26 
  27 if hasattr(sys, "isapidllhandle"):
  28     import win32traceutil
  29 
  30 try:
  31     reload_counter += 1
  32     reload( wikiconfig )
  33     reload( MoinMoin.multiconfig )
  34     reload( MoinMoin )
  35     print 'Modules reloaded'
  36 except NameError:
  37     reload_counter = 0
  38     import wikiconfig
  39         
  40 class ReloadWatcherThread(threading.Thread):
  41     def __init__(self):
  42         self.filename = __file__
  43         self.change_detected = False
  44         if self.filename[:4] == '\\\\?\\':
  45             self.filename = self.filename[4:];
  46         if self.filename.endswith("c") or self.filename.endswith("o"):
  47             self.filename = self.filename[:-1]
  48         self.root_dir = os.path.dirname(self.filename)
  49         self.handle = win32file.FindFirstChangeNotification(
  50                         self.root_dir,
  51                         False, # watch tree?
  52                         win32con.FILE_NOTIFY_CHANGE_LAST_WRITE)
  53         threading.Thread.__init__(self)
  54 
  55     def run(self):
  56         last_time = os.stat(self.filename)[stat.ST_MTIME]
  57         while 1:
  58             try:
  59                 win32event.WaitForSingleObject(self.handle, 
  60                                                     win32event.INFINITE)
  61                 win32file.FindNextChangeNotification(self.handle)
  62             except win32event.error, details:
  63                 # handle closed - thread should terminate.
  64                 if details[0] != winerror.ERROR_INVALID_HANDLE:
  65                     raise
  66                 break
  67             this_time = os.stat(self.filename)[stat.ST_MTIME]
  68 #            print "Detected file change - flagging for reload."
  69             self.change_detected = True
  70 #===============================================================================
  71 #            if this_time != last_time:
  72 #                last_time = this_time
  73 #===============================================================================
  74     
  75     def stop(self):
  76         win32file.FindCloseChangeNotification(self.handle)
  77 
  78 # The moinmoin start extention
  79 
  80 class Extension(SimpleExtension):
  81     ''' The MoinMoin Wiki IIS ISAPI starter
  82     '''
  83     def __init__(self):
  84         self.reload_watcher = ReloadWatcherThread()
  85         self.reload_watcher.start()
  86 
  87     def HttpExtensionProc(self, ecb):
  88         if self.reload_watcher.change_detected:
  89             raise InternalReloadException
  90         
  91 #        print "serving request, changind dir to ",self.reload_watcher.root_dir
  92         os.chdir(self.reload_watcher.root_dir)
  93         try:
  94             r = RequestISAPI(ecb)
  95             r.run()
  96         except:
  97             einfo = sys.exc_info()
  98             print >>ecb, "Content-type: text/html\r\n\r\n<html><head></head><body>"
  99             print >>ecb, "<h1>Exception</h1>: "
 100             import traceback
 101             print >>ecb, '<p><pre>',traceback.format_exc(),'</pre>'
 102             print traceback.format_exc()
 103             print >>ecb, '</body></html>'
 104 
 105         ecb.close()
 106         return isapicon.HSE_STATUS_SUCCESS
 107     
 108     def TerminateExtension(self, status):
 109         self.reload_watcher.stop()
 110 
 111 # The entry points for the ISAPI extension.
 112 def __ExtensionFactory__():
 113     return Extension()
 114 
 115 class RequestISAPI( RequestBase ):
 116     def __init__(self,ecb=None):
 117         """ Sets the common Request members by parsing an ISAPI extension
 118             environment
 119         """
 120         self.ecb = ecb
 121         sname = virtualPath
 122         self.script_name = sname
 123         self.env = {}
 124         self.response = None
 125         
 126         self.buffer = []
 127         self.http_accept_language = ecb.GetServerVariable('HTTP_ACCEPT_LANGUAGE', 'en')
 128         self.server_name = ecb.GetServerVariable('SERVER_NAME', 'localhost')
 129         self.server_port = ecb.GetServerVariable('SERVER_PORT', '80')
 130         self.http_host = ecb.GetServerVariable('HTTP_HOST','localhost')
 131         # Make sure http referer use only ascii (IE again)
 132         self.http_referer = unicode(ecb.GetServerVariable('HTTP_REFERER', ''), 'ascii',
 133                                     'replace').encode('ascii', 'replace')
 134         self.saved_cookie = ecb.GetServerVariable('HTTP_COOKIE', '')
 135 
 136         path_info = wikiutil.decodeWindowsPath(ecb.PathInfo.replace(sname,'',1)).encode("utf-8")
 137         self.path_info = self.decodePagename(path_info)
 138         self.env['PATH_INFO'] = path_info + '/' + sname;
 139         
 140         self.env['QUERY_STRING'] = self.query_string = ecb.QueryString
 141         server_software = ecb.GetServerVariable('SERVER_SOFTWARE', '')
 142 
 143         self.env['REQUEST_METHOD'] = self.request_method = ecb.Method
 144 
 145         self.remote_addr = ecb.GetServerVariable('REMOTE_ADDR', '')
 146         self.http_user_agent = ecb.GetServerVariable('HTTP_USER_AGENT', '')
 147         self.is_ssl = ecb.GetServerVariable('SSL_PROTOCOL', '') != '' \
 148             or ecb.GetServerVariable('SSL_PROTOCOL_VERSION', '') != '' \
 149             or ecb.GetServerVariable('HTTPS', 'off') == 'on'
 150 
 151         if self.server_port.strip() and self.server_port != '80':
 152             port = ':' + str(self.server_port)
 153         else:
 154             port = ''
 155 
 156         self.url = urllib.quote((self.server_name + port + 
 157             self.path_info).encode('utf8'))+'?' + self.query_string
 158         self.request_uri = '';
 159         
 160         ac = ecb.GetServerVariable('HTTP_ACCEPT_CHARSET', '')
 161         self.accepted_charsets = self.parse_accept_charset(ac)
 162         
 163         self.auth_username = None
 164 
 165         # need config here, so check:
 166         self._load_multi_cfg()
 167         MoinMoin.multiconfig.getConfig(self.url)
 168 
 169         if self.cfg.auth_http_enabled:
 170             auth_type = ecb.GetServerVariable('AUTH_TYPE','')
 171             if auth_type in ['Basic', 'Digest', 'NTLM', ]:
 172                 username = ecb.GetServerVariable('REMOTE_USER','')
 173                 if auth_type == 'NTLM':
 174                     # converting to standard case so that the user can even enter wrong case
 175                     # (added since windows does not distinguish between e.g. "Mike" and "mike")
 176                     username = username.split('\\')[-1] # split off domain e.g. from DOMAIN\user
 177                     # this "normalizes" the login name from {meier, Meier, MEIER} to Meier
 178                     # put a comment sign in front of next line if you don't want that:
 179                     username = username.title()
 180         RequestBase.__init__(self, {})
 181         self.ret_code = None;
 182         
 183     def open_logs(self):
 184         # create log file for catching stderr output
 185         if not self.opened_logs: #IGNORE:E0203
 186             self.opened_logs = 1
 187             sys.stderr = open(os.path.join(self.cfg.data_dir, 'error.log'), 'at')
 188    
 189     def setup_args(self, form=None):
 190         sio = StringIO()
 191         ecb = self.ecb
 192         sio.write(ecb.AvailableData)
 193         rest = ecb.TotalBytes - sio.len
 194         if rest > 0:
 195             sio.write( ecb.ReadClient( rest ) )
 196         sio.seek(0)
 197         if self.ecb.Method.upper() == "POST":
 198             h = {'content-type': self.ecb.ContentType }
 199             form = cgi.FieldStorage(fp=sio,environ=self.env,headers=h)
 200         else:
 201             form = cgi.FieldStorage(fp=sio,environ=self.env)
 202         return self._setup_args_from_cgi_form(form)
 203     
 204     def read(self, n=None):
 205         print "Ignoring read",n
 206         
 207     def write(self, *data):
 208         """ Write to output stream.
 209         """
 210         self.buffer.extend(data)
 211 
 212     def flush(self):
 213         pass;
 214     
 215     def setResponseCode(self, code, message=None):
 216         if not code:
 217             raise 'ERROR: response code is None!'
 218         self.response = str(code)
 219         if message != None:
 220             self.response += ' ' + message;
 221     
 222     def finish(self):
 223         RequestBase.finish(self)
 224         headers = '\r\n'.join(self.all_headers)
 225         response = self.getHeader('Status')
 226         if response:
 227             self.response = response       
 228         if not self.response: self.response = '200 Ok'
 229         self.ecb.SendResponseHeaders( self.response, headers )
 230         res = '\r\n'*2;
 231         for s in self.buffer:
 232             if s:
 233                 res += s
 234         if headers.find('text/html') > 0:
 235             self.ecb.write( 
 236                 '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd><html>' )
 237         try:
 238             self.ecb.write(res.encode('utf8'))
 239         except:
 240             self.ecb.write(res)
 241 #        else:
 242 #            self.ecb.write( res ) 
 243         
 244     def getHeader(self,name):
 245         name = name.lower()+': '
 246         for x in self.all_headers:
 247             if x.lower().startswith(name):
 248                 return x[len(name):]
 249         return None
 250         
 251     # Accessors --------------------------------------------------------
 252     
 253     def getPathinfo(self):
 254         """ Return the remaining part of the URL. """
 255         pathinfo = self.path_info
 256 
 257         # Fix for bug in IIS/4.0
 258         if os.name == 'nt':
 259             scriptname = self.getScriptname()
 260             if pathinfo.startswith(scriptname):
 261                 pathinfo = pathinfo[len(scriptname):]
 262 
 263         return pathinfo
 264 
 265     # Headers ----------------------------------------------------------
 266     
 267     def setHttpHeader(self, header):
 268         self.user_headers.append(header)
 269 
 270     def http_headers(self, more_headers=[]):
 271         # Send only once
 272         if getattr(self, 'sent_headers', None):
 273             return
 274         
 275         self.sent_headers = 1
 276         
 277         have_ct = 0
 278         self.all_headers = []
 279         # send http headers
 280         for header in more_headers + getattr(self, 'user_headers', []):
 281             if header.lower().startswith("content-type:"):
 282                 # don't send content-type multiple times!
 283                 if have_ct: continue
 284                 have_ct = 1
 285             if type(header) is unicode:
 286                 header = header.encode('ascii')
 287             self.all_headers.append(header)
 288 
 289         if not have_ct:
 290             self.all_headers.append("Content-type: text/html;charset=%s" % config.charset)
 291         #from pprint import pformat
 292         #sys.stderr.write(pformat(more_headers))
 293         #sys.stderr.write(pformat(self.user_headers))
 294     
 295     
 296  
 297 # Post install hook for our entire script
 298 def PostInstall(params, options):
 299     print
 300     print "The MoinMoin ISAPI is installed."
 301     print "Point your browser to ",virtualPath,"to check it"
 302 
 303 if __name__=='__main__':
 304     # If run from the command-line, install ourselves.
 305     from isapi.install import *
 306     params = ISAPIParameters(PostInstall = PostInstall)
 307     # Setup the virtual directories - this is a list of directories our
 308     # extension uses - in this case only 1.
 309     # Each extension has a "script map" - this is the mapping of ISAPI
 310     # extensions.
 311     sm = [
 312         ScriptMapParams(Extension="*", Flags=0)
 313     ]
 314     vd = VirtualDirParameters(Name=virtualPath,
 315                               Description = Extension.__doc__,
 316                               ScriptMaps = sm,
 317                               ScriptMapUpdate = "replace"
 318                               )
 319     params.VirtualDirs = [vd]
 320     # Setup our custom option parser.
 321     from optparse import OptionParser
 322     parser = OptionParser('') # black usage, so isapi sets it.
 323     HandleCommandLine(params, opt_parser=parser)
 324         

Attached Files

To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.
  • [get | view] (2006-03-14 00:54:15, 11.8 KB) [[attachment:MoinIIS.py]]
  • [get | view] (2007-04-03 08:04:21, 11.0 KB) [[attachment:MoinIIS_for_1.5.py]]
 All files | Selected Files: delete move to page copy to page

You are not allowed to attach a file to this page.