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.You are not allowed to attach a file to this page.