#!/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 REVNO, VERSION import webserver_common as ws #init: web.webapi.internalerror = web.debugerror #/init #methods: SESSIONS = [] #dumb sessions. def start_session(): session_id = str(random.random()) SESSIONS.append(session_id) setcookie("session_id", session_id) if getcookie('auto_refresh_secs') == None: setcookie('auto_refresh_secs','10') 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 ws.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 ws.render.sort_column_head(id, name, order, active_up, active_down) def get_config(var): return ws.config.get(var) 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': get_config, 'self_url': self_url, 'fspeed': common.fspeed, 'fsize': common.fsize, 'render': ws.render, #for easy resuse of templates 'rev': 'rev.%s' % (REVNO, ), 'version': VERSION, 'getcookie':getcookie, '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. #static-files serving should be moved to the normal webserver! from SimpleHTTPServer import SimpleHTTPRequestHandler from BaseHTTPServer import BaseHTTPRequestHandler from gtk_cherrypy_wsgiserver import CherryPyWSGIServer from BaseHTTPServer import BaseHTTPRequestHandler from webpy022.request import webpyfunc from webpy022 import webapi import os import posixpath import urllib import urlparse class RelativeHandler(SimpleHTTPRequestHandler): def translate_path(self, path): """Translate a /-separated PATH to the local filename syntax. Components that mean special things to the local file system (e.g. drive or directory names) are ignored. (XXX They should probably be diagnosed.) """ # abandon query parameters path = urlparse.urlparse(path)[2] path = posixpath.normpath(urllib.unquote(path)) words = path.split('/') words = filter(None, words) path = os.path.dirname(__file__) for word in words: drive, word = os.path.splitdrive(word) head, word = os.path.split(word) if word in (os.curdir, os.pardir): continue path = os.path.join(path, word) return path class StaticApp(RelativeHandler): """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): func = webapi.wsgifunc(webpyfunc(urls,methods, False)) server_address=("0.0.0.0",ws.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','start_session','getcookie' ,'create_webserver']