You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
289 lines
12 KiB
Python
289 lines
12 KiB
Python
#!/usr/bin/python
|
|
# coding=utf8
|
|
|
|
import sys
|
|
import os
|
|
from cgi import parse_qs
|
|
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
|
|
from urllib import unquote_plus, unquote, quote
|
|
from ConfigParser import ConfigParser
|
|
from opendir import opendir
|
|
from xml.sax import saxutils
|
|
from shutil import copyfileobj
|
|
from bsddb import rnopen
|
|
from getopt import getopt
|
|
from random import randint
|
|
from sphinxsearch import SphinxClient, SPH_MATCH_ANY
|
|
from mako.lookup import TemplateLookup
|
|
from mako import exceptions
|
|
from mimetypes import guess_type
|
|
|
|
class Config(object):
|
|
data = None
|
|
|
|
def __getitem__(self, k):
|
|
return Config.data.get(k)
|
|
|
|
def keys(self):
|
|
return Config.data.keys()
|
|
|
|
def __init__(self):
|
|
if Config.data is None:
|
|
config_file = "mokopedia.cfg"
|
|
|
|
opts, args = getopt(sys.argv[1:], "c:", ["config=",])
|
|
for o, a in opts:
|
|
if o in ("-c", "--config"):
|
|
if os.path.exists(a):
|
|
config_file = a
|
|
|
|
config = ConfigParser()
|
|
try:
|
|
if not config.read(config_file):
|
|
raise
|
|
except:
|
|
sys.exit("config file not valid")
|
|
|
|
sphinx_port = 3312
|
|
Config.data = {}
|
|
class Dummy(object):
|
|
pass
|
|
|
|
Config.data["main"] = Dummy()
|
|
Config.data["main"].__dict__["iw"] = []
|
|
|
|
for section in config.sections():
|
|
if "main" not in section and config.getint(section, "enabled"):
|
|
Config.data[section] = Dummy()
|
|
Config.data["main"].__dict__["iw"].append(section)
|
|
|
|
for (name, value) in config.items(section):
|
|
Config.data[section].__dict__[name] = value
|
|
|
|
Config.data[section].__dict__["lookup"] = TemplateLookup(directories=[os.path.join(section, "templates")], module_directory=os.path.join(section, "modules"), input_encoding='utf-8', output_encoding='utf-8')
|
|
|
|
db = config.get(section, "title_index")
|
|
if db:
|
|
print "opening titles bsddb..."
|
|
Config.data[section].__dict__["titles"] = rnopen(db)
|
|
|
|
if config.get(section, "title_search") or config.get(section, "fulltext_search"):
|
|
print "starting sphinx daemon for %s wiki..."%section
|
|
print "-----------------------------"
|
|
L = ['searchd', '--port', '%s'%sphinx_port, '--config', '%s/sphinx.conf'%section, '--console']
|
|
os.spawnvp(os.P_NOWAIT, 'searchd', L)
|
|
print "-----------------------------"
|
|
print "successfully started"
|
|
|
|
Config.data[section].__dict__["sphinx"] = SphinxClient()
|
|
Config.data[section].__dict__["sphinx"].SetServer ( "localhost", sphinx_port )
|
|
Config.data[section].__dict__["sphinx"].SetMatchMode ( SPH_MATCH_ANY )
|
|
sphinx_port+=1
|
|
elif "main" in section:
|
|
for (name, value) in config.items(section):
|
|
Config.data[section].__dict__[name] = value
|
|
|
|
class MyHandler(BaseHTTPRequestHandler):
|
|
|
|
def output_wiki_page(self, lang, article):
|
|
try:
|
|
file = open(os.path.join(self.config["main"].dump_folder, lang, article), "r")
|
|
except IOError:
|
|
self.output_error_page(lang, article)
|
|
else:
|
|
self.write_header()
|
|
template = self.config[lang].lookup.get_template("header.mako")
|
|
self.wfile.write(template.render(article=unicode(saxutils.escape(article), "utf8"), title=unicode(file.readline(), "utf8"), iw=self.config["main"].iw))
|
|
copyfileobj(file, self.wfile )
|
|
template = self.config[lang].lookup.get_template("footer.mako")
|
|
self.wfile.write(template.render())
|
|
|
|
def output_category_page(self, lang, article, page):
|
|
folder = os.path.join(self.config["main"].dump_folder, lang, article)
|
|
try:
|
|
index = open(os.path.join(folder, "index"), "r")
|
|
except IOError:
|
|
self.output_error_page(lang, article)
|
|
else:
|
|
d = opendir(folder)
|
|
|
|
#seek to page if necessary
|
|
if page > 1:
|
|
for i in xrange(int(self.config["main"].category_size)*(page-1)):
|
|
d.read()
|
|
|
|
last_page = False
|
|
|
|
items = []
|
|
|
|
for i in xrange(int(self.config["main"].category_size)):
|
|
filename = d.read()
|
|
if filename:
|
|
if filename not in [".","..","index"]:
|
|
items.append(
|
|
(unicode(saxutils.escape(quote(os.readlink(os.path.join(folder,filename))[3:])), "utf8"),
|
|
unicode(saxutils.escape(filename), "utf8"))
|
|
)
|
|
else:
|
|
last_page = True
|
|
break
|
|
|
|
self.write_header()
|
|
template = self.config[lang].lookup.get_template("category.mako")
|
|
self.wfile.write(template.render(
|
|
article=unicode(article, "utf8"),
|
|
title=unicode(index.readline(), "utf8"),
|
|
items=items,
|
|
last_page=last_page,
|
|
page=page,
|
|
article=unicode(article, "utf8"),
|
|
text=unicode(index.read(), "utf8"),
|
|
iw=self.config["main"].iw,
|
|
))
|
|
|
|
def output_result_page(self, lang, text, page):
|
|
last_page = False
|
|
|
|
page_size = int(self.config["main"].result_size)
|
|
|
|
self.config[lang].sphinx.SetLimits ( page_size*(page-1), page_size)
|
|
res = self.config[lang].sphinx.Query( text )
|
|
|
|
items = []
|
|
|
|
for match in res['matches']:
|
|
article = unicode(saxutils.escape(self.config[lang].titles[int(match['id'])]), "utf8")
|
|
items.append((article, article.replace("_", " ")))
|
|
|
|
if res['total_found']/page_size < page:
|
|
last_page = True
|
|
|
|
if res['total_found'] is 0:
|
|
page = 0
|
|
|
|
self.write_header()
|
|
template = self.config[lang].lookup.get_template("result.mako")
|
|
self.wfile.write(template.render(
|
|
items=items,
|
|
last_page=last_page,
|
|
page=page,
|
|
text=unicode(saxutils.escape(text), "utf8"),
|
|
iw=self.config["main"].iw,
|
|
))
|
|
|
|
def output_error_page(self, lang, article):
|
|
self.send_response(404)
|
|
self.send_header('Content-type', 'application/xml')
|
|
self.end_headers()
|
|
template = self.config[lang].lookup.get_template("error.mako")
|
|
self.wfile.write(template.render(article=unicode(saxutils.escape(article), "utf8"), iw=self.config["main"].iw))
|
|
|
|
def output_language_chooser(self):
|
|
self.write_header()
|
|
self.wfile.write('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /></head> <body>')
|
|
for i in self.config.keys():
|
|
if "main" not in i:
|
|
self.wfile.write('<a href="/%s">%s</a><br />'%(i,i))
|
|
self.wfile.write('</body></html>')
|
|
|
|
def output_language_index(self, lang):
|
|
self.write_header()
|
|
template = self.config[lang].lookup.get_template("index.mako")
|
|
self.wfile.write(template.render(iw=self.config["main"].iw))
|
|
|
|
def output_image_redirect(self, lang, article):
|
|
self.write_header()
|
|
template = self.config[lang].lookup.get_template("image_redirect.mako")
|
|
self.wfile.write(template.render(article=unicode(saxutils.escape(article), "utf8")))
|
|
|
|
def output_random_redirect(self, lang):
|
|
article = self.config[lang].titles[randint(1,len(self.config[lang].titles))]
|
|
self.write_header()
|
|
template = self.config[lang].lookup.get_template("random_redirect.mako")
|
|
self.wfile.write(template.render(article=unicode(saxutils.escape(article), "utf8")))
|
|
|
|
def output_title_redirect(self, lang, article):
|
|
article = article.replace(' ', '_')
|
|
self.write_header()
|
|
template = self.config[lang].lookup.get_template("title_redirect.mako")
|
|
self.wfile.write(template.render(article=unicode(saxutils.escape(article), "utf8")))
|
|
|
|
def output_raw(self, lang, file):
|
|
print os.path.join(lang, file)
|
|
if os.path.exists(os.path.join(lang, file)):
|
|
self.send_response(200)
|
|
self.send_header('Content-type', guess_type(file))
|
|
self.end_headers()
|
|
copyfileobj(open(os.path.join(lang, file)), self.wfile)
|
|
else:
|
|
self.output_error_page(lang, file)
|
|
|
|
def write_header(self):
|
|
#wos.stat = os.fos.stat(file.fileno())
|
|
self.send_response(200)
|
|
self.send_header('Content-type', 'application/xml')
|
|
#self.send_header("Content-Length", str(wos.stat.st_size + os.stat("de/os.static/header").st_size + os.stat("de/os.static/middle").st_size + os.stat("de/os.static/footer").st_size))
|
|
#self.send_header("Last-Modified", self.date_time_string(wos.stat.st_mtime))
|
|
self.end_headers()
|
|
|
|
def do_GET(self):
|
|
self.config = Config()
|
|
|
|
i = self.path.rfind('?')
|
|
text = ''
|
|
search = ''
|
|
page = 1
|
|
if i >= 0:
|
|
self.path, query = self.path[:i], self.path[i+1:]
|
|
if query:
|
|
dict = parse_qs(query)
|
|
if "page" in dict.keys():
|
|
page = int(dict["page"][0])
|
|
if "text" in dict.keys():
|
|
text = dict["text"][0]
|
|
search = dict.get("search", "")
|
|
|
|
addr = [ unquote(i) for i in self.path.split('/') if i ]
|
|
|
|
if len(addr) > 0 and addr[0] in self.config.keys() and "main" not in addr[0]:
|
|
lang = addr[0]
|
|
if len(addr) > 1:
|
|
addr[1] = '/'.join(addr[1:])
|
|
if addr[1].startswith(self.config[lang].special+":"):
|
|
action = addr[1].split(':', 1)[1]
|
|
if action == "Random":
|
|
self.output_random_redirect(lang=lang)
|
|
else:
|
|
self.output_error_page(lang=lang, article=addr[1])
|
|
elif addr[1].startswith(self.config[lang].image+":"):
|
|
self.output_image_redirect(lang=lang, article=addr[1])
|
|
elif addr[1].startswith(self.config[lang].category+":"):
|
|
self.output_category_page(lang=lang, article=addr[1], page=page)
|
|
elif addr[1].startswith("static"):
|
|
self.output_raw(lang=lang, file=addr[1])
|
|
else:
|
|
self.output_wiki_page(lang=lang, article=addr[1])
|
|
else:
|
|
if text:
|
|
if search and (self.config[lang].title_search or self.config[lang].fulltext_search):
|
|
self.output_result_page(lang, text, page)
|
|
else:
|
|
self.output_title_redirect(lang, text)
|
|
else:
|
|
self.output_language_index(lang=lang)
|
|
else:
|
|
self.output_language_chooser()
|
|
|
|
def main():
|
|
config = Config()
|
|
try:
|
|
server = HTTPServer(('', int(config["main"].port)), MyHandler)
|
|
print 'httpserver started'
|
|
server.serve_forever()
|
|
except KeyboardInterrupt:
|
|
print '^C received, shutting down server'
|
|
server.socket.close()
|
|
|
|
if __name__ == '__main__':
|
|
main()
|