deluge/plugins/WebUi/webserver_framework.py

391 lines
12 KiB
Python
Raw Normal View History

2007-10-05 21:16:46 +00:00
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
2007-10-07 04:56:01 +00:00
# webserver_framework.py
#
2007-10-05 21:16:46 +00:00
# Copyright (C) Martijn Voncken 2007 <mvoncken@gmail.com>
#
# 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.
2007-10-07 04:56:01 +00:00
"""
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
2007-10-23 22:16:37 +00:00
from webpy022.webapi import cookies, setcookie as w_setcookie
2007-10-07 04:56:01 +00:00
from webpy022.http import seeother, url
from webpy022 import template,changequery as self_url
2007-10-28 01:35:27 +00:00
from webpy022.utils import Storage
from static_handler import static_handler
from deluge.common import fsize,fspeed
2007-10-07 04:56:01 +00:00
import traceback
2007-10-05 21:16:46 +00:00
import random
2007-10-07 04:56:01 +00:00
from operator import attrgetter
2007-10-28 01:35:27 +00:00
import datetime
import pickle
from md5 import md5
from urlparse import urlparse
2007-10-07 04:56:01 +00:00
from deluge import common
2007-11-06 23:34:23 +00:00
from webserver_common import REVNO, VERSION
2007-10-07 04:56:01 +00:00
import webserver_common as ws
2007-10-23 22:16:37 +00:00
from debugerror import deluge_debugerror
2007-10-07 04:56:01 +00:00
2007-10-23 22:16:37 +00:00
#init:
web.webapi.internalerror = deluge_debugerror
2007-10-07 04:56:01 +00:00
#/init
#methods:
2007-10-23 22:16:37 +00:00
def setcookie(key, val):
"""add 30 days expires header for persistent cookies"""
return w_setcookie(key, val , expires=2592000)
2007-10-28 01:35:27 +00:00
#really simple sessions, to bad i had to implement them myself.
2007-10-07 04:56:01 +00:00
def start_session():
session_id = str(random.random())
2007-10-28 01:35:27 +00:00
ws.SESSIONS.append(session_id)
2007-10-29 20:01:37 +00:00
#if len(ws.SESSIONS) > 20: #save max 20 sessions?
# ws.SESSIONS = ws.SESSIONS[-20:]
2007-10-28 01:35:27 +00:00
#not thread safe! , but a verry rare bug.
2007-10-29 20:01:37 +00:00
#f = open(ws.session_file,'wb')
#pickle.dump(ws.SESSIONS, f)
#f.close()
2007-10-07 04:56:01 +00:00
setcookie("session_id", session_id)
2007-10-28 01:35:27 +00:00
def end_session():
2007-10-31 03:46:36 +00:00
session_id = getcookie("session_id")
2007-10-29 20:01:37 +00:00
#if session_id in ws.SESSIONS:
# ws.SESSIONS.remove(session_id)
2007-10-28 01:35:27 +00:00
#not thread safe! , but a verry rare bug.
2007-10-29 20:01:37 +00:00
#f = open(ws.session_file,'wb')
#pickle.dump(ws.SESSIONS, f)
#f.close()
setcookie("session_id","")
2007-10-07 04:56:01 +00:00
def do_redirect():
"""for redirects after a POST"""
vars = web.input(redir = None)
ck = cookies()
url_vars = {}
2007-10-07 04:56:01 +00:00
if vars.redir:
seeother(vars.redir)
return
#todo:cleanup
if ("order" in ck and "sort" in ck):
url_vars.update({'sort':ck['sort'] ,'order':ck['order'] })
if ("filter" in ck) and ck['filter']:
url_vars['filter'] = ck['filter']
if ("category" in ck) and ck['category']:
url_vars['category'] = ck['category']
seeother(url("/index", **url_vars))
2007-10-07 04:56:01 +00:00
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)
2007-11-06 23:34:23 +00:00
def getcookie(key, default = None):
2007-10-23 22:16:37 +00:00
key = str(key).strip()
2007-10-07 04:56:01 +00:00
ck = cookies()
2007-11-06 23:34:23 +00:00
return ck.get(key, default)
2007-10-07 04:56:01 +00:00
#deco's:
def deluge_page_noauth(func):
"""
add http headers
print result of func
"""
2007-11-06 23:34:23 +00:00
def deco(self, name = None):
2007-10-07 04:56:01 +00:00
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.
"""
2007-11-06 23:34:23 +00:00
def deco(self, name = None):
vars = web.input(redir_after_login = None)
2007-10-07 04:56:01 +00:00
ck = cookies()
2007-10-28 01:35:27 +00:00
if ck.has_key("session_id") and ck["session_id"] in ws.SESSIONS:
2007-10-07 04:56:01 +00:00
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"
2007-11-06 23:34:23 +00:00
def deco(self, name = None):
2007-10-07 04:56:01 +00:00
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"
2007-11-06 23:34:23 +00:00
def deco(self, name = None):
2007-10-07 04:56:01 +00:00
try:
print func(self, name)
except Exception, e:
print 'error:' + e.message
print '-'*20
print traceback.format_exc()
return deco
2007-10-28 01:35:27 +00:00
#utils:
def check_pwd(pwd):
m = md5()
m.update(ws.config.get('pwd_salt'))
m.update(pwd)
return (m.digest() == ws.config.get('pwd_md5'))
def get_stats():
stats = Storage({
'download_rate':fspeed(ws.proxy.get_download_rate()),
'upload_rate':fspeed(ws.proxy.get_upload_rate()),
'max_download':ws.proxy.get_config_value('max_download_speed_bps'),
'max_upload':ws.proxy.get_config_value('max_upload_speed_bps'),
2007-11-06 23:34:23 +00:00
'num_connections':ws.proxy.get_num_connections(),
'max_num_connections':ws.proxy.get_config_value('max_connections_global')
2007-10-28 01:35:27 +00:00
})
if stats.max_upload < 0:
stats.max_upload = _("Unlimited")
else:
stats.max_upload = fspeed(stats.max_upload)
if stats.max_download < 0:
stats.max_download = _("Unlimited")
else:
stats.max_download = fspeed(stats.max_download)
return stats
def get_torrent_status(torrent_id):
"""
helper method.
enhance ws.proxy.get_torrent_status with some extra data
"""
status = Storage(ws.proxy.get_torrent_status(torrent_id,ws.TORRENT_KEYS))
#add missing values for deluge 0.6:
for key in ws.TORRENT_KEYS:
if not key in status:
status[key] = 0
status["id"] = torrent_id
url = urlparse(status.tracker)
if hasattr(url,'hostname'):
2007-11-24 20:39:55 +00:00
status.category = url.hostname or 'unknown'
else:
status.category = 'No-tracker'
2007-10-28 01:35:27 +00:00
#for naming the status-images
2007-11-06 23:34:23 +00:00
status.calc_state_str = "downloading"
if status.paused:
status.calc_state_str= "inactive"
elif status.is_seed:
status.calc_state_str = "seeding"
2007-10-28 01:35:27 +00:00
#action for torrent_pause
if status.user_paused:
2007-11-06 23:34:23 +00:00
status.action = "start"
2007-10-28 01:35:27 +00:00
else:
2007-11-06 23:34:23 +00:00
status.action = "stop"
2007-10-28 01:35:27 +00:00
if status.user_paused:
2007-11-06 23:34:23 +00:00
status.message = _("Paused %s%%") % status.progress
elif status.paused:
status.message = _("Queued %s%%") % status.progress
2007-10-28 01:35:27 +00:00
else:
2007-11-06 23:34:23 +00:00
status.message = "%s %i%%" % (ws.STATE_MESSAGES[status.state]
, status.progress)
2007-10-28 01:35:27 +00:00
#add some pre-calculated values
status.update({
2007-11-06 23:34:23 +00:00
"calc_total_downloaded" : (fsize(status.total_done)
+ " (" + fsize(status.total_download) + ")"),
"calc_total_uploaded": (fsize(status.uploaded_memory
+ status.total_payload_upload) + " ("
+ fsize(status.total_upload) + ")"),
2007-10-28 01:35:27 +00:00
})
2007-11-06 23:34:23 +00:00
#no non-unicode string may enter the templates.
for k, v in status.iteritems():
if (not isinstance(v, unicode)) and isinstance(v, str):
try:
status[k] = unicode(v)
except:
raise Exception('Non Unicode for key:%s' % (k, ))
2007-10-28 01:35:27 +00:00
return status
2007-11-06 23:34:23 +00:00
def get_categories(torrent_list):
2007-11-24 20:39:55 +00:00
trackers = [(torrent['category'] or 'unknown') for torrent in torrent_list]
categories = {}
for tracker in trackers:
categories[tracker] = categories.get(tracker,0) + 1
return categories
def filter_torrent_state(torrent_list,filter_name):
filters = {
'downloading': lambda t: (not t.paused and not t.is_seed)
,'queued':lambda t: (t.paused and not t.user_paused)
,'paused':lambda t: (t.user_paused)
,'seeding':lambda t:(t.is_seed and not t.paused )
}
filter_func = filters[filter_name]
return [t for t in torrent_list if filter_func(t)]
2007-10-28 01:35:27 +00:00
#/utils
2007-10-07 04:56:01 +00:00
#template-defs:
def category_tabs(torrent_list):
categories = get_categories(torrent_list)
filter_tabs = [Storage(title='All (%s)' % len(torrent_list),
filter=None, category=None)]
#static filters
for title, filter_name in [
(_('Downloading'),'downloading') ,
(_('Queued'),'queued') ,
(_('Paused'),'paused') ,
(_('Seeding'),'seeding')
]:
title += ' (%s)' % (
len(filter_torrent_state(torrent_list, filter_name)), )
filter_tabs.append(Storage(title=title, filter=filter_name))
categories = [x for x in get_categories(torrent_list).iteritems()]
categories.sort()
#trackers:
category_tabs = []
category_tabs.append(
Storage(title=_('Trackers'),category=None))
for title,count in categories:
category = title
title += ' (%s)' % (count, )
category_tabs.append(Storage(title=title, category=category))
return ws.render.part_categories(filter_tabs, category_tabs)
2007-10-07 04:56:01 +00:00
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..
2007-11-06 23:34:23 +00:00
vars = web.input(sort = None, order = None)
2007-10-07 04:56:01 +00:00
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)
2007-10-28 01:35:27 +00:00
def template_part_stats():
return ws.render.part_stats(get_stats())
2007-10-07 04:56:01 +00:00
def get_config(var):
return ws.config.get(var)
template.Template.globals.update({
'sort_head': template_sort_head,
2007-10-28 01:35:27 +00:00
'part_stats':template_part_stats,
'category_tabs':category_tabs,
2007-10-07 04:56:01 +00:00
'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,
2007-11-06 23:34:23 +00:00
'get': lambda (var): getattr(web.input(**{var:None}), var) # unreadable :-(
2007-10-07 04:56:01 +00:00
})
#/template-defs
2007-10-28 01:35:27 +00:00
def create_webserver(urls, methods):
from webpy022.request import webpyfunc
from webpy022 import webapi
from gtk_cherrypy_wsgiserver import CherryPyWSGIServer
import os
2007-10-07 04:56:01 +00:00
2007-10-28 01:35:27 +00:00
func = webapi.wsgifunc(webpyfunc(urls, methods, False))
server_address=("0.0.0.0", int(ws.config.get('port')))
2007-10-07 04:56:01 +00:00
server = CherryPyWSGIServer(server_address, func, server_name="localhost")
if ws.config.get('use_https'):
server.ssl_certificate = os.path.join(ws.webui_path,'ssl/deluge.pem')
server.ssl_private_key = os.path.join(ws.webui_path,'ssl/deluge.key')
2007-10-28 01:35:27 +00:00
print "http://%s:%d/" % server_address
2007-10-07 04:56:01 +00:00
return server
#------
__all__ = ['deluge_page_noauth', 'deluge_page', 'remote',
'auto_refreshed', 'check_session',
'do_redirect', 'error_page','start_session','getcookie'
2007-10-28 01:35:27 +00:00
,'setcookie','create_webserver','end_session',
'get_torrent_status', 'check_pwd','static_handler','get_categories'
,'template','filter_torrent_state']