diff --git a/plugins/WebUi/__init__.py b/plugins/WebUi/__init__.py index d7c28a3a1..220db62be 100644 --- a/plugins/WebUi/__init__.py +++ b/plugins/WebUi/__init__.py @@ -36,12 +36,13 @@ plugin_description = "A Web based User Interface\n" import deluge.common import deluge.pref from deluge.dialogs import show_popup_warning -from dbus_interface import DbusManager +from dbus_interface import get_dbus_manager import gtk import os from subprocess import Popen from md5 import md5 +from threading import Thread import random random.seed() @@ -49,10 +50,6 @@ plugin_version += open(os.path.join(os.path.dirname(__file__),'revno')).read() plugin_description += ( open(os.path.join(os.path.dirname(__file__),'version')).read()) -#not found a way to stop a dbus manager. -#global so it does not get started twice. -dbus_manager = None - def deluge_init(deluge_path): global path path = deluge_path @@ -61,15 +58,27 @@ def enable(core, interface): global path return plugin_WebUi(path, core, interface) -class plugin_WebUi: +class WebServerThread(Thread): + + def run(self): + #must be imported after plugin-load because of dbus: + import webserver_common + reload(webserver_common) #HACK!!! + from deluge_webserver import WebServer #only import in threaded mode + self.web_server = WebServer() + self.web_server.start() + + def stop(self): + print 'WebUi : Stop threaded server' + self.web_server.stop() + +class plugin_WebUi(object): def __init__(self, path, deluge_core, deluge_interface): - global dbus_manager self.path = path self.core = deluge_core self.interface = deluge_interface self.proc = None - - + self.web_server_thread = None self.config_file = deluge.common.CONFIG_DIR + "/webui.conf" self.config = deluge.pref.Preferences(self.config_file, False) @@ -98,19 +107,17 @@ class plugin_WebUi: if self.config.get("cache_templates") == None: self.config.set("cache_templates", True) + if self.config.get("run_in_thread") == None: + self.config.set("run_in_thread", True) - if not dbus_manager: - self.dbusManager = DbusManager(deluge_core, deluge_interface - , self.config, self.config_file) - - self.dbus_manager = dbus_manager + self.dbus_manager = get_dbus_manager(deluge_core, deluge_interface, + self.config, self.config_file) self.start_server() def unload(self): print 'WebUI:unload..' self.kill_server() - #self.dbusManager. def update(self): pass @@ -124,18 +131,30 @@ class plugin_WebUi: def start_server(self): self.kill_server() - print 'start Webui..' - path = os.path.dirname(__file__) - server_bin = path + '/run_webserver' - port = str(self.config.get('port')) - self.proc = Popen((server_bin, port),cwd=path) + + if self.config.get("run_in_thread"): + print 'start Webui(in thread)..' + self.web_server_thread = WebServerThread() + self.web_server_thread.start() + else: + print 'start Webui(in process)..' + path = os.path.dirname(__file__) + server_bin = path + '/run_webserver' + port = str(self.config.get('port')) + self.proc = Popen((server_bin, port),cwd=path) def kill_server(self): + if self.web_server_thread: + self.web_server_thread.stop() if self.proc: - print "webserver: kill %i"%self.proc.pid - os.system("kill %i"%self.proc.pid) + print "webserver: kill %i" % self.proc.pid + os.system("kill %i" % self.proc.pid) self.proc = None + def __del__(self): + self.kill_server() + + class ConfigDialog(gtk.Dialog): """ @@ -160,12 +179,15 @@ class ConfigDialog(gtk.Dialog): self.template = self.add_widget(_('Template'), gtk.combo_box_new_text()) self.button_style = self.add_widget(_('Button Style'), gtk.combo_box_new_text()) - self.cache_templates = self.add_widget(_('Cache Templates'), - gtk.CheckButton()) self.download_dir = self.add_widget(_('Download Directory'), gtk.FileChooserButton(_('Download Directory'))) self.torrent_dir = self.add_widget(_('Torrent Directory'), gtk.FileChooserButton(_('Torrent Directory'))) + self.cache_templates = self.add_widget(_('Cache Templates'), + gtk.CheckButton()) + self.run_in_thread = self.add_widget(_('Run in thread'), + gtk.CheckButton()) + self.download_dir.set_action(gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER) self.torrent_dir.set_action(gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER) @@ -189,10 +211,13 @@ class ConfigDialog(gtk.Dialog): self.template.set_active( self.templates.index(self.config.get("template"))) self.button_style.set_active(self.config.get("button_style")) - self.cache_templates.set_active(self.config.get("cache_templates")) self.torrent_dir.set_filename(self.config.get("torrent_dir")) self.download_dir.set_filename(self.config.get("download_dir")) + + self.run_in_thread.set_active(self.config.get("run_in_thread")) + self.cache_templates.set_active(self.config.get("cache_templates")) + self.vbox.pack_start(self.vb, True, True, 0) self.vb.show_all() @@ -226,17 +251,9 @@ class ConfigDialog(gtk.Dialog): self.config.set("port", int(self.port.get_value())) self.config.set("template", self.template.get_active_text()) self.config.set("button_style", self.button_style.get_active()) - self.config.set("cache_templates", self.cache_templates.get_active()) self.config.set("torrent_dir", self.torrent_dir.get_filename()) self.config.set("download_dir",self.download_dir.get_filename()) + self.config.set("cache_templates", self.cache_templates.get_active()) + self.config.set("run_in_thread", self.run_in_thread.get_active()) self.config.save(self.plugin.config_file) self.plugin.start_server() #restarts server - - - - - - - - - diff --git a/plugins/WebUi/dbus_interface.py b/plugins/WebUi/dbus_interface.py index d44666da6..79ba53666 100644 --- a/plugins/WebUi/dbus_interface.py +++ b/plugins/WebUi/dbus_interface.py @@ -32,20 +32,25 @@ # this exception statement from your version. If you delete this exception # statement from all source files in the program, then also delete it here. - import os import gtk import dbus import deluge.common as common from dbus_pythonize import pythonize import base64 -from md5 import md5 import random random.seed() dbus_interface="org.deluge_torrent.dbusplugin" dbus_service="/org/deluge_torrent/DelugeDbusPlugin" +dbus_manager = None +def get_dbus_manager(*args): + #another way to make a singleton. + global dbus_manager + if not dbus_manager: + dbus_manager = DbusManager(*args) + return dbus_manager class DbusManager(dbus.service.Object): def __init__(self, core, interface,config,config_file): @@ -89,7 +94,7 @@ class DbusManager(dbus.service.Object): "name": state["name"], "total_size": state["total_size"], "num_pieces": state["num_pieces"], - #"state": int(status.state), #? + "state": state['state'], "paused": self.core.is_user_paused(torrent_id), "progress": int(state["progress"] * 100), "next_announce": state["next_announce"], @@ -103,7 +108,6 @@ class DbusManager(dbus.service.Object): "eta": common.estimate_eta(state), "ratio": self.interface.manager.calc_ratio(torrent_id,state), #non 0.6 values follow here: - "message": self.interface.get_message_from_state(state), "tracker_status": state.get("tracker_status","?"), "uploaded_memory": torrent.uploaded_memory, } @@ -175,39 +179,6 @@ class DbusManager(dbus.service.Object): self._add_torrent(filename) return True - @dbus.service.method(dbus_interface=dbus_interface, - in_signature="s",out_signature="v") - def get_webui_config(self,key): - """ - return data from wevbui config. - not in 0.6 - """ - retval = self.config.get(str(key)) - #print 'get webui config:', str(key), retval - if retval == None: - retval = False #dbus does not accept None :( - - return retval - - @dbus.service.method(dbus_interface=dbus_interface, - in_signature="sv",out_signature="") - def set_webui_config(self, key, value): - """ - return data from wevbui config. - not in 0.6 - """ - #print 'set webui config:', str(key), pythonize(value) - self.config.set(str(key), pythonize(value)) - self.config.save(self.config_file) - - @dbus.service.method(dbus_interface=dbus_interface, - in_signature="s",out_signature="b") - def check_pwd(self, pwd): - m = md5() - m.update(self.config.get('pwd_salt')) - m.update(pwd) - return (m.digest() == self.config.get('pwd_md5')) - #internal def _add_torrent(self, filename): #dbus types break pickle, again..... diff --git a/plugins/WebUi/deluge_webserver.py b/plugins/WebUi/deluge_webserver.py index 5ba5104f3..4d2e9db40 100644 --- a/plugins/WebUi/deluge_webserver.py +++ b/plugins/WebUi/deluge_webserver.py @@ -31,149 +31,33 @@ # this exception statement from your version. If you delete this exception # statement from all source files in the program, then also delete it here. -""" -Todo's before stable: --__init__:kill->restart is not waiting for kill to be finished. ---later/features:--- --alternating rows? --set prio --clear finished? --torrent files. -""" + +from webserver_common import proxy, config ,TORRENT_KEYS, STATE_MESSAGES +from webserver_framework import * + + import webpy022 as web - -from webpy022.webapi import cookies, setcookie from webpy022.http import seeother, url +from webpy022.webapi import setcookie from webpy022.utils import Storage -from webpy022.net import urlquote -from webpy022 import template, changequery as self_url -import dbus - -import gettext, os, platform, locale, traceback -import random -import base64 +from md5 import md5 +from deluge.common import fsize from operator import attrgetter -from deluge import common -from deluge.common import INSTALL_PREFIX - - -#init: -APP = 'deluge' -DIR = os.path.join(INSTALL_PREFIX, 'share', 'locale') -if platform.system() != "Windows": - locale.setlocale(locale.LC_MESSAGES, '') - locale.bindtextdomain(APP, DIR) - locale.textdomain(APP) -else: - locale.setlocale(locale.LC_ALL, '') -gettext.bindtextdomain(APP, DIR) -gettext.textdomain(APP) -gettext.install(APP, DIR) - -random.seed() -bus = dbus.SessionBus() -proxy = bus.get_object("org.deluge_torrent.dbusplugin" - , "/org/deluge_torrent/DelugeDbusPlugin") - -web.webapi.internalerror = web.debugerror - -render = template.render('templates/%s/' % proxy.get_webui_config('template') - ,cache=proxy.get_webui_config('cache_templates')) -#/init - -#framework: - -SESSIONS = {} - -def do_redirect(): - """for redirects after a POST""" - vars = web.input(redir = None) - ck = cookies() - - if vars.redir: - seeother(vars.redir) - elif ("order" in ck and "sort" in ck): - seeother(url("/index", sort=ck['sort'], order=ck['order'])) - else: - seeother(url("/index")) - -def deluge_page_noauth(func): - """ - add http headers - print result of func - """ - def deco(self, name=None): - web.header("Content-Type", "text/html; charset=utf-8") - web.header("Cache-Control", "no-cache, must-revalidate") - res = func(self, name) - print res - return deco - -def check_session(func): - """ - a decorator - return func if session is valid, else redirect to login page. - """ - def deco(self, name): - vars = web.input(redir_after_login=None) - - ck = cookies() - if ck.has_key("session_id") and ck["session_id"] in SESSIONS: - return func(self, name) #ok, continue.. - elif vars.redir_after_login: - seeother("/login?redir=" + urlquote(self_url())) - else: - seeother("/login") #do not continue, and redirect to login page - return deco - -def deluge_page(func): - return check_session(deluge_page_noauth(func)) - -def auto_refreshed(func): - "decorator:adds a refresh header" - def deco(self, name): - if proxy.get_webui_config('auto_refresh'): - web.header("Refresh", "%i ; url=%s" % - (proxy.get_webui_config('auto_refresh_secs'),self_url())) - return func(self, name) - return deco - -def error_page(error): - web.header("Content-Type", "text/html; charset=utf-8") - web.header("Cache-Control", "no-cache, must-revalidate") - print render.error(error) - -def remote(func): - "decorator for remote api's" - def deco(self, name): - try: - print func(self, name) - except Exception, e: - print 'error:' + e.message - print '-'*20 - print traceback.format_exc() - return deco - -#/framework - #utils: -torrent_keys = ['distributed_copies', 'download_payload_rate', - 'download_rate', 'eta', 'is_seed', 'message', 'name', 'next_announce', - 'num_files', 'num_peers', 'num_pieces', 'num_seeds', 'paused', - 'piece_length','progress', 'ratio', 'total_done', 'total_download', - 'total_payload_download', 'total_payload_upload', 'total_peers', - 'total_seeds', 'total_size', 'total_upload', 'total_wanted', - 'tracker_status', 'upload_payload_rate', 'upload_rate', - 'uploaded_memory','tracker'] +def check_pwd(pwd): + m = md5() + m.update(config.get('pwd_salt')) + m.update(pwd) + return (m.digest() == config.get('pwd_md5')) def get_torrent_status(torrent_id): """ helper method. enhance proxy.get_torrent_status with some extra data """ - status = proxy.get_torrent_status(torrent_id,torrent_keys) + status = proxy.get_torrent_status(torrent_id,TORRENT_KEYS) status["id"] = torrent_id #for naming the status-images @@ -189,61 +73,26 @@ def get_torrent_status(torrent_id): else: status["action"] = "stop" + + if status["paused"]: + status["message"] = _("Paused %s%%") % status['progress'] + else: + status["message"] = "%s %i%%" % (STATE_MESSAGES[status["state"]] + , status['progress']) + #add some pre-calculated values status.update({ - "calc_total_downloaded" : (common.fsize(status["total_done"]) - + " (" + common.fsize(status["total_download"]) + ")"), - "calc_total_uploaded": (common.fsize(status['uploaded_memory'] + "calc_total_downloaded" : (fsize(status["total_done"]) + + " (" + fsize(status["total_download"]) + ")"), + "calc_total_uploaded": (fsize(status['uploaded_memory'] + status["total_payload_upload"]) + " (" - + common.fsize(status["total_upload"]) + ")"), + + fsize(status["total_upload"]) + ")"), }) return Storage(status) #Storage for easy templating. #/utils -#template-defs: -def template_crop(text, end): - if len(text) > end: - return text[0:end - 3] + '...' - return text - -def template_sort_head(id,name): - #got tired of doing these complex things inside templetor.. - vars = web.input(sort=None, order=None) - active_up = False - active_down = False - order = 'down' - - if vars.sort == id: - if vars.order == 'down': - order = 'up' - active_down = True - else: - active_up = True - - return render.sort_column_head(id, name, order, active_up, active_down) - -template.Template.globals.update({ - 'sort_head': template_sort_head, - 'crop': template_crop, - '_': _ , #gettext/translations - 'str': str, #because % in templetor is broken. - 'sorted': sorted, - 'get_config': proxy.get_webui_config, - 'self_url': self_url, - 'fspeed': common.fspeed, - 'fsize': common.fsize, - 'render': render, #for easy resuse of templates - 'button_style': (proxy.get_webui_config('button_style')), - 'rev': ('rev.' + - open(os.path.join(os.path.dirname(__file__),'revno')).read()), - 'version': ( - open(os.path.join(os.path.dirname(__file__),'version')).read()), - 'get': lambda (var): getattr(web.input(**{var:None}),var) # unreadable :-( -}) -#/template-defs - #routing: urls = ( "/login(.*)", "login", @@ -277,14 +126,12 @@ class login: def POST(self, name): vars = web.input(pwd = None ,redir = None) - if proxy.check_pwd(vars.pwd): + if check_pwd(vars.pwd): #start new session - session_id = str(random.random()) - SESSIONS[session_id] = {"not":"used"} - setcookie("session_id", session_id) + start_session() do_redirect() elif vars.redir: - seeother('/login?error=1&redir=' + urlquote(vars.redir)) + seeother(url('/login',error=1,redir=vars.redir)) else: seeother('/login?error=1') @@ -360,13 +207,12 @@ class remote_torrent_add: """ For use in remote scripts etc. POST pwd and torrent - Example : curl -F torrent=@./test1.torrent -F pwd=deluge http://localhost:8112/remote/torrent/add" """ @remote def POST(self, name): vars = web.input(pwd = None, torrent = {}) - if not proxy.check_pwd(vars.pwd): + if not check_pwd(vars.pwd): return 'error:wrong password' data_b64 = base64.b64encode(vars.torrent.file.read()) @@ -404,8 +250,8 @@ class resume_all: class refresh: @check_session def POST(self, name): - auto_refresh = {'off':False, 'on':True}[name] - proxy.set_webui_config('auto_refresh', auto_refresh) + auto_refresh = {'off':'0', 'on':'1'}[name] + setcookie('auto_refresh',auto_refresh) do_redirect() class refresh_set: @@ -418,8 +264,8 @@ class refresh_set: vars = web.input(refresh = 0) refresh = int(vars.refresh) if refresh > 0: - proxy.set_webui_config('refresh', refresh) - proxy.set_webui_config('auto_refresh', True) + setcookie('auto_refresh','1') + setcookie('auto_refresh_secs', str(refresh)) do_redirect() else: error_page(_('refresh must be > 0')) @@ -429,10 +275,19 @@ class about: def GET(self, name): return render.about() - #/pages + +def WebServer(): + return create_webserver(urls, globals()) + +def run(): + server = WebServer() + try: + server.start() + except KeyboardInterrupt: + server.stop() + if __name__ == "__main__": - web.run(urls, globals()) - + run() diff --git a/plugins/WebUi/webserver_common.py b/plugins/WebUi/webserver_common.py new file mode 100644 index 000000000..ef6f18e34 --- /dev/null +++ b/plugins/WebUi/webserver_common.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright (C) Martijn Voncken 2007 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, write to: +# The Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor +# Boston, MA 02110-1301, USA. +# +# In addition, as a special exception, the copyright holders give +# permission to link the code of portions of this program with the OpenSSL +# library. +# You must obey the GNU General Public License in all respects for all of +# the code used other than OpenSSL. If you modify file(s) with this +# exception, you may extend this exception to your version of the file(s), +# but you are not obligated to do so. If you do not wish to do so, delete +# this exception statement from your version. If you delete this exception +# statement from all source files in the program, then also delete it here. + +import os +import deluge +from deluge.common import INSTALL_PREFIX +import random +import pickle +random.seed() + +config_file = deluge.common.CONFIG_DIR + "/webui.conf" +#config = deluge.pref.Preferences(config_file, False) +config = pickle.load(open(config_file)) + +if config.get('run_in_thread'): + #do not use dbus ipc for threads! + from dbus_interface import get_dbus_manager + proxy = get_dbus_manager() +else: + import dbus + bus = dbus.SessionBus() + proxy = bus.get_object("org.deluge_torrent.dbusplugin" + , "/org/deluge_torrent/DelugeDbusPlugin") + +REVNO = open(os.path.join(os.path.dirname(__file__),'revno')).read() +VERSION = open(os.path.join(os.path.dirname(__file__),'version')).read() + +TORRENT_KEYS = ['distributed_copies', 'download_payload_rate', + 'download_rate', 'eta', 'is_seed', 'name', 'next_announce', + 'num_files', 'num_peers', 'num_pieces', 'num_seeds', 'paused', + 'piece_length','progress', 'ratio', 'total_done', 'total_download', + 'total_payload_download', 'total_payload_upload', 'total_peers', + 'total_seeds', 'total_size', 'total_upload', 'total_wanted', + 'tracker_status', 'upload_payload_rate', 'upload_rate', + 'uploaded_memory','tracker','state'] + +STATE_MESSAGES = (_("Queued"), + _("Checking"), + _("Connecting"), + _("Downloading Metadata"), + _("Downloading"), + _("Finished"), + _("Seeding"), + _("Allocating")) diff --git a/plugins/WebUi/webserver_framework.py b/plugins/WebUi/webserver_framework.py new file mode 100644 index 000000000..c80c7e71d --- /dev/null +++ b/plugins/WebUi/webserver_framework.py @@ -0,0 +1,302 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# webserver_framework.py +# +# Copyright (C) Martijn Voncken 2007 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, write to: +# The Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor +# Boston, MA 02110-1301, USA. +# +# In addition, as a special exception, the copyright holders give +# permission to link the code of portions of this program with the OpenSSL +# library. +# You must obey the GNU General Public License in all respects for all of +# the code used other than OpenSSL. If you modify file(s) with this +# exception, you may extend this exception to your version of the file(s), +# but you are not obligated to do so. If you do not wish to do so, delete +# this exception statement from your version. If you delete this exception +# statement from all source files in the program, then also delete it here. + +""" +Todo's before stable: +-__init__:kill->restart is not waiting for kill to be finished. +--later/features:--- +-alternating rows? +-set prio +-clear finished? +-torrent files. +""" +import webpy022 as web + +from webpy022.webapi import cookies, setcookie +from webpy022.http import seeother, url +from webpy022 import template, changequery as self_url + +import traceback +import random +from operator import attrgetter + +from deluge import common +from webserver_common import proxy, config , REVNO, VERSION + +#init: +web.webapi.internalerror = web.debugerror +render = template.render('templates/%s/' % config.get('template'), + cache=config.get('cache_templates')) + +#/init + +#methods: +SESSIONS = [] #dumb sessions. +def start_session(): + session_id = str(random.random()) + SESSIONS.append(session_id) + setcookie("session_id", session_id) + +def do_redirect(): + """for redirects after a POST""" + vars = web.input(redir = None) + ck = cookies() + + if vars.redir: + seeother(vars.redir) + elif ("order" in ck and "sort" in ck): + seeother(url("/index", sort=ck['sort'], order=ck['order'])) + else: + seeother(url("/index")) + +def error_page(error): + web.header("Content-Type", "text/html; charset=utf-8") + web.header("Cache-Control", "no-cache, must-revalidate") + print render.error(error) + +def getcookie(key, default=None): + ck = cookies() + return str(ck.get(key, default)) + +#deco's: +def deluge_page_noauth(func): + """ + add http headers + print result of func + """ + def deco(self, name=None): + web.header("Content-Type", "text/html; charset=utf-8") + web.header("Cache-Control", "no-cache, must-revalidate") + res = func(self, name) + print res + return deco + +def check_session(func): + """ + a decorator + return func if session is valid, else redirect to login page. + """ + def deco(self, name): + vars = web.input(redir_after_login=None) + + ck = cookies() + if ck.has_key("session_id") and ck["session_id"] in SESSIONS: + return func(self, name) #ok, continue.. + elif vars.redir_after_login: + seeother(url("/login",redir=self_url())) + else: + seeother("/login") #do not continue, and redirect to login page + return deco + +def deluge_page(func): + return check_session(deluge_page_noauth(func)) + +#combi-deco's: +def auto_refreshed(func): + "decorator:adds a refresh header" + def deco(self, name): + if getcookie('auto_refresh') == '1': + web.header("Refresh", "%i ; url=%s" % + (int(getcookie('auto_refresh_secs',10)),self_url())) + return func(self, name) + return deco + +def remote(func): + "decorator for remote api's" + def deco(self, name): + try: + print func(self, name) + except Exception, e: + print 'error:' + e.message + print '-'*20 + print traceback.format_exc() + return deco + +#template-defs: +def template_crop(text, end): + if len(text) > end: + return text[0:end - 3] + '...' + return text + +def template_sort_head(id,name): + #got tired of doing these complex things inside templetor.. + vars = web.input(sort=None, order=None) + active_up = False + active_down = False + order = 'down' + + if vars.sort == id: + if vars.order == 'down': + order = 'up' + active_down = True + else: + active_up = True + + return render.sort_column_head(id, name, order, active_up, active_down) + +template.Template.globals.update({ + 'sort_head': template_sort_head, + 'crop': template_crop, + '_': _ , #gettext/translations + 'str': str, #because % in templetor is broken. + 'sorted': sorted, + 'get_config': config.get, + 'self_url': self_url, + 'fspeed': common.fspeed, + 'fsize': common.fsize, + 'render': render, #for easy resuse of templates + 'button_style': (config.get('button_style')), + 'rev': 'rev.%s' % (REVNO, ), + 'version': VERSION, + 'get': lambda (var): getattr(web.input(**{var:None}),var) # unreadable :-( +}) +#/template-defs + + + +#------------------------------------------------------------------------------ +#Some copy and paste from web.py +#mostly caused by /static +#TODO : FIX THIS. +from SimpleHTTPServer import SimpleHTTPRequestHandler +from BaseHTTPServer import BaseHTTPRequestHandler +from webpy022.request import webpyfunc +from webpy022 import webapi +import os + +class StaticApp(SimpleHTTPRequestHandler): + """WSGI application for serving static files.""" + def __init__(self, environ, start_response): + self.headers = [] + self.environ = environ + self.start_response = start_response + + def send_response(self, status, msg=""): + self.status = str(status) + " " + msg + + def send_header(self, name, value): + self.headers.append((name, value)) + + def end_headers(self): + pass + + def log_message(*a): pass + + def __iter__(self): + environ = self.environ + + self.path = environ.get('PATH_INFO', '') + self.client_address = environ.get('REMOTE_ADDR','-'), \ + environ.get('REMOTE_PORT','-') + self.command = environ.get('REQUEST_METHOD', '-') + + from cStringIO import StringIO + self.wfile = StringIO() # for capturing error + + f = self.send_head() + self.start_response(self.status, self.headers) + + if f: + block_size = 16 * 1024 + while True: + buf = f.read(block_size) + if not buf: + break + yield buf + f.close() + else: + value = self.wfile.getvalue() + yield value + +class WSGIWrapper(BaseHTTPRequestHandler): + """WSGI wrapper for logging the status and serving static files.""" + def __init__(self, app): + self.app = app + self.format = '%s - - [%s] "%s %s %s" - %s' + + def __call__(self, environ, start_response): + def xstart_response(status, response_headers, *args): + write = start_response(status, response_headers, *args) + self.log(status, environ) + return write + + path = environ.get('PATH_INFO', '') + if path.startswith('/static/'): + return StaticApp(environ, xstart_response) + else: + return self.app(environ, xstart_response) + + def log(self, status, environ): + #mvoncken,no logging.. + return + + outfile = environ.get('wsgi.errors', web.debug) + req = environ.get('PATH_INFO', '_') + protocol = environ.get('ACTUAL_SERVER_PROTOCOL', '-') + method = environ.get('REQUEST_METHOD', '-') + host = "%s:%s" % (environ.get('REMOTE_ADDR','-'), + environ.get('REMOTE_PORT','-')) + + #@@ It is really bad to extend from + #@@ BaseHTTPRequestHandler just for this method + time = self.log_date_time_string() + + print >> outfile, self.format % (host, time, protocol, + method, req, status) + +def create_webserver(urls,methods): + from webpy022.wsgiserver import CherryPyWSGIServer + from BaseHTTPServer import BaseHTTPRequestHandler + + + os.chdir(os.path.dirname(__file__)) #HACK for /static.. + + func = webapi.wsgifunc(webpyfunc(urls,methods, False)) + server_address=("0.0.0.0",config.get('port')) + + func = WSGIWrapper(func) + server = CherryPyWSGIServer(server_address, func, server_name="localhost") + + + print "(created) http://%s:%d/" % server_address + + return server + +#------ +__all__ = ['deluge_page_noauth', 'deluge_page', 'remote', + 'auto_refreshed', 'check_session', + 'do_redirect', 'error_page', 'render', 'start_session','getcookie' + ,'create_webserver'] + + +