Attachment 'MoinIIS_for_1.5.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 = '/mywiki'
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 sys.path.append('C:/Moin/Lib/site-packages')
17
18 from StringIO import StringIO
19 from MoinMoin import config, wikiutil
20 from MoinMoin.request import RequestBase
21 from isapi import InternalReloadException
22 from isapi import isapicon
23 from isapi.simple import SimpleExtension
24 import MoinMoin
25 import cgi, urllib, os, stat, threading
26 import win32event, win32file, winerror, win32con, threading
27 from MoinMoin.auth import http
28
29 def debug(log):
30 f = open('/tmp/env.log', 'a+')
31 try:
32 f.write(log + '\n')
33 finally:
34 f.close()
35
36 if hasattr(sys, "isapidllhandle"):
37 import win32traceutil
38
39 try:
40 reload_counter += 1
41 reload( wikiconfig )
42 reload( MoinMoin.multiconfig )
43 reload( MoinMoin )
44 print 'Modules reloaded'
45 except NameError:
46 reload_counter = 0
47 import wikiconfig
48
49 class ReloadWatcherThread(threading.Thread):
50 def __init__(self):
51 self.filename = __file__
52 self.change_detected = False
53 if self.filename[:4] == '\\\\?\\':
54 self.filename = self.filename[4:];
55 if self.filename.endswith("c") or self.filename.endswith("o"):
56 self.filename = self.filename[:-1]
57 self.root_dir = os.path.dirname(self.filename)
58 self.handle = win32file.FindFirstChangeNotification(
59 self.root_dir,
60 False, # watch tree?
61 win32con.FILE_NOTIFY_CHANGE_LAST_WRITE)
62 threading.Thread.__init__(self)
63
64 def run(self):
65 last_time = os.stat(self.filename)[stat.ST_MTIME]
66 while 1:
67 try:
68 win32event.WaitForSingleObject(self.handle,
69 win32event.INFINITE)
70 win32file.FindNextChangeNotification(self.handle)
71 except win32event.error, details:
72 # handle closed - thread should terminate.
73 if details[0] != winerror.ERROR_INVALID_HANDLE:
74 raise
75 break
76 this_time = os.stat(self.filename)[stat.ST_MTIME]
77 # print "Detected file change - flagging for reload."
78 self.change_detected = True
79 #===============================================================================
80 # if this_time != last_time:
81 # last_time = this_time
82 #===============================================================================
83
84 def stop(self):
85 win32file.FindCloseChangeNotification(self.handle)
86
87 # The moinmoin start extention
88
89 class Extension(SimpleExtension):
90 ''' The MoinMoin Wiki IIS ISAPI starter
91 '''
92 def __init__(self):
93 self.reload_watcher = ReloadWatcherThread()
94 self.reload_watcher.start()
95
96 def HttpExtensionProc(self, ecb):
97 if self.reload_watcher.change_detected:
98 raise InternalReloadException
99
100 # print "serving request, changind dir to ",self.reload_watcher.root_dir
101 os.chdir(self.reload_watcher.root_dir)
102 try:
103 r = RequestISAPI(ecb)
104 r.run()
105 except:
106 einfo = sys.exc_info()
107 print >>ecb, "Content-type: text/html\r\n\r\n<html><head></head><body>"
108 print >>ecb, "<h1>Exception</h1>: "
109 import traceback
110 print >>ecb, '<p><pre>',traceback.format_exc(),'</pre>'
111 print traceback.format_exc()
112 print >>ecb, '</body></html>'
113
114 ecb.close()
115 return isapicon.HSE_STATUS_SUCCESS
116
117 def TerminateExtension(self, status):
118 self.reload_watcher.stop()
119
120 # The entry points for the ISAPI extension.
121 def __ExtensionFactory__():
122 return Extension()
123
124 class EnvAdaptor(dict):
125 """Adaptor from ecb to env."""
126
127 def __init__(self, ecb, force_env={}):
128 self.ecb = ecb
129 self.force_env = force_env
130
131 def get(self, key, default=None):
132 try:
133 return self.force_env[key]
134 except KeyError:
135 try:
136 return self[key]
137 except KeyError:
138 val = self.ecb.GetServerVariable(key, default)
139 if val:
140 #debug('add key %s' % key)
141 self[key] = val
142 return val
143
144 def __contains__(self, key):
145 #debug('%s in env?' % key)
146 haskey = self.force_env.has_key(key) or self.has_key(key)
147 if haskey:
148 #debug('haskey')
149 return True
150 else:
151 val = self.ecb.GetServerVariable(key, None)
152 if val:
153 self[key] = val
154 #debug('getkey')
155 return True
156 else:
157 #debug('nokey')
158 return False
159
160 class RequestISAPI( RequestBase ):
161
162 def __init__(self, ecb=None):
163 """ Sets the common Request members by parsing an ISAPI extension
164 environment
165 """
166 self.buffer = []
167 self.ecb = ecb
168 env = self.env = EnvAdaptor(self.ecb)
169 self.response = None
170 sname = virtualPath
171
172 # different from _setup_vars_from_std_env's implement
173 env.force_env['SCRIPT_NAME'] = sname
174 # Make sure http referer use only ascii (IE again)
175 env.force_env['HTTP_REFERER'] = unicode(
176 env.get('HTTP_REFERER', ''), 'ascii', 'replace').encode(
177 'ascii', 'replace')
178 # not the same as _setup_vars_from_std_env believe
179 env.force_env['PATH_INFO'] = wikiutil.decodeWindowsPath(
180 ecb.PathInfo.replace(sname, '', 1)).encode("utf-8")
181 # cgi.FieldStorage need this var, or will treat the environment as CGI.
182 env['QUERY_STRING'] = env.get('QUERY_STRING', '')
183 # _setup_vars_from_std_env doesn't deal with it
184 ac = env.get('HTTP_ACCEPT_CHARSET', '')
185 self.setAcceptedCharsets(ac)
186
187 self._setup_vars_from_std_env(self.env)
188
189 RequestBase.__init__(self)
190
191 self.ret_code = None;
192
193 def open_logs(self):
194 # create log file for catching stderr output
195 if not self.opened_logs: #IGNORE:E0203
196 self.opened_logs = 1
197 sys.stderr = open(os.path.join(self.cfg.data_dir, 'error.log'), 'at')
198
199 def setup_args(self, form=None):
200 sio = StringIO()
201 ecb = self.ecb
202 sio.write(ecb.AvailableData)
203 rest = ecb.TotalBytes - sio.len
204 if rest > 0:
205 sio.write( ecb.ReadClient( rest ) )
206 sio.seek(0)
207 if self.ecb.Method.upper() == "POST":
208 h = {'content-type': self.ecb.ContentType }
209 form = cgi.FieldStorage(fp=sio, environ=self.env, headers=h)
210 else:
211 form = cgi.FieldStorage(fp=sio, environ=self.env)
212 return self._setup_args_from_cgi_form(form)
213
214 def read(self, n=None):
215 print "Ignoring read", n
216
217 def write(self, *data):
218 """ Write to output stream.
219 """
220 self.buffer.extend(data)
221
222 def flush(self):
223 pass
224
225 def setResponseCode(self, code, message=None):
226 if not code:
227 raise 'ERROR: response code is None!'
228 self.response = str(code)
229 if message != None:
230 self.response += ' ' + message;
231
232 def finish(self):
233 RequestBase.finish(self)
234 headers = '\r\n'.join(self.all_headers)
235 response = self.getHeader('Status')
236 if response:
237 self.response = response
238 if not self.response: self.response = '200 Ok'
239 self.ecb.SendResponseHeaders( self.response, headers )
240 if headers.find('text/html') > 0:
241 self.ecb.write(
242 '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd><html>' )
243 self.ecb.write('\r\n' * 2)
244 self.ecb.write(self.encode(self.buffer))
245
246 def getHeader(self,name):
247 name = name.lower()+': '
248 for x in self.all_headers:
249 if x.lower().startswith(name):
250 return x[len(name):]
251 return None
252
253 # Accessors --------------------------------------------------------
254
255 def getPathinfo(self):
256 """ Return the remaining part of the URL. """
257 pathinfo = self.path_info
258
259 # Fix for bug in IIS/4.0
260 if os.name == 'nt':
261 scriptname = self.getScriptname()
262 if pathinfo.startswith(scriptname):
263 pathinfo = pathinfo[len(scriptname):]
264
265 return pathinfo
266
267 # Headers ----------------------------------------------------------
268
269 def setHttpHeader(self, header):
270 self.user_headers.append(header)
271
272 def http_headers(self, more_headers=[]):
273 # Send only once
274 if getattr(self, 'sent_headers', None):
275 return
276
277 self.sent_headers = 1
278
279 have_ct = 0
280 self.all_headers = []
281 # send http headers
282 for header in more_headers + getattr(self, 'user_headers', []):
283 if header.lower().startswith("content-type:"):
284 # don't send content-type multiple times!
285 if have_ct: continue
286 have_ct = 1
287 if type(header) is unicode:
288 header = header.encode('ascii')
289 self.all_headers.append(header)
290
291 if not have_ct:
292 self.all_headers.append("Content-type: text/html;charset=%s" % config.charset)
293 #from pprint import pformat
294 #sys.stderr.write(pformat(more_headers))
295 #sys.stderr.write(pformat(self.user_headers))
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.You are not allowed to attach a file to this page.