#! /usr/bin/env python
# -*- coding: utf-8 -*-
#-----------------------------------------------------------------------------
# Name: g3rpcserver.py
# Purpose:
#
# Author: Jeremy Arendt
#
# Created: 2004/20/02
# RCS-ID: $Id: g3rpcserver.py,v 1.8 2005/11/21 00:26:03 inigo Exp $
# Copyright: (c) 2002
# Licence: See G3.LICENCE
#-----------------------------------------------------------------------------
import SocketServer
from xmlrpcserver import RequestHandler
from traceback import print_exc
from threading import Thread
from BitTorrent import version
from base64 import decodestring
from Cookie import SimpleCookie
from time import gmtime,strftime,sleep
from btconfig import BTConfig
import socket
import urlparse
import base64
import wx
import re
from urllib import unquote,quote
from os import path
class HtmlRenderer:
def __init__(self, btsessions=None, myip=None, password=""):
self.myip = myip
self.password = password
self.btsessions = btsessions
self.html = []
def FmtFileSize(self, bytes):
if bytes > 2**30:
return '%.2f GB' % (bytes/1073741824.0)
elif bytes > 2**20:
return '%.1f MB' % (bytes/1048576.0)
elif bytes > 2**10:
return '%.1f KB' % (bytes/1024.0)
else:
return '%d B' % bytes
def Session2HTML(self, btsession):
d = btsession.GetStatusData()
s = btsession.GetStaticData()
id = btsession.GetId()
ipriority = btsession.GetQRank()
filename = btsession.GetFilename()
if btsession.GetFilesize() > 0:
filesize = self.FmtFileSize( btsession.GetFilesize() )
else:
filesize = ""
if btsession.IsRunning() and not btsession.IsPaused():
action = "<a href='javascript:Stop(%d);'>Stop</a>" % id
else:
action = "<a href='javascript:Start(%d);'>Start</a>" % id
if d == None or s == None:
return ("<tr><td align='center'>%s <td>%s <td align='right'>%s"\
"<td>In Queue" % (action, filename, filesize)) + "<td><br>" * 5
icompleted = d['fractionDone']
iurate = d['urate']
idrate = d['drate']
iutotal = d['utotal']
idtotal = d['dtotal']
priority = str(ipriority)
if btsession.IsComplete() and not idtotal:
idtotal = btsession.GetFilesize()
elif idtotal == None:
idtotal = 0
if iutotal == None:
iutotal = 0
dtotal = self.FmtFileSize(idtotal)
utotal = self.FmtFileSize(iutotal)
try:
share_ratio = "%0.2f" % ((float(iutotal)+1) / (float(idtotal)+1))
except ZeroDivisionError:
share_ratio = "0"
if btsession.IsChecking():
activity = 'Checking'
elif btsession.IsComplete() and btsession.IsPaused():
activity = 'Complete'
elif btsession.IsComplete():
activity = 'Seeding'
elif btsession.IsStopped():
activity = 'Stopped'
elif btsession.IsPaused():
activity = 'Paused'
else:
activity = 'Downloading'
if d['timeleft'] == None or d['timeleft'] == -1:
if btsession.IsComplete():
timeleft = 'Done'
else:
timeleft = 'Unknown'
else:
if d['timeleft'] < 86400:
t = gmtime(int(d['timeleft']))
timeleft = strftime("%H:%M:%S", t)
else:
timeleft = "%.1f Days" % (float(d['timeleft']) / 86400)
if idrate > 0:
drate = '%.0f KB/s' % (float(idrate) / (1 << 10))
else:
drate = '0 KB/s'
if iurate > 0:
urate = '%.0f KB/s' % (float(iurate) / (1 << 10))
else:
urate = '0 KB/s'
if d['avg_progress']:
avg_progress = "%d" % int(d['avg_progress'] * 100)
else:
avg_progress = "0"
if d['dist_copies']:
dist_copies = "%0.2f" % d['dist_copies']
else:
dist_copies = "0"
if d['seeds']:
seeds = "%d" % d['seeds']
else:
seeds = "0"
if d['peers']:
peers = "%d" % d['peers']
else:
peers = "0"
if peers != 0 and seeds != 0:
leechers = int(peers) - int(seeds)
else:
leechers = 0
completed = "%d%c" % (int(icompleted*100), '%')
return "<tr><td align='center'>%s <td>%s <td align='right'>%s <td>%s" \
"<td>%s <td align='right'>%s <td align='right'>%s <td>%s <td align='right'>%s" \
"<td align='right'>%s <td align='right'>%s <td align='right'>%s" \
% (action, filename, filesize, activity, completed, drate, urate, timeleft, share_ratio, seeds, leechers, peers )
def Header(self):
head = """
<img src='webui.bmp'>
<h1>Rufus %s Web Interface</h1>
""" % version
return head
def JavaScript(self):
js = """
<script type="text/javascript" language="javascript">
function Reload() {
window.document.reload();
}
function Stop(id) {
window.document.stopform.stop.value = id;
window.document.stopform.submit();
}
function Start(id) {
window.document.startform.start.value = id;
window.document.startform.submit();
}
</script>"""
return js
def StyleSheet(self):
ss = """
<style type='text/css'>
a {
color:#027185;
font-family:Verdana,Arial,Helvetica;
font-size: 9pt;
font-weight:bold;
text-decoration: none;
}
h1 { color:#c00000;font-family:Verdana,Arial,Helvetica; font-size: 10pt; font-weight:bold; }
p { color:#000000; font-family:Verdana,Arial,Helvetica; font-size: 8pt; }
table { font-family:Verdana,Arial,Helvetica; black; font-size: 10pt;}
th { font-family:Verdana,Arial,Helvetica; border: single black; font-size: 10pt;
font-weight:bold; text-align: left;}
td { font-family:Verdana,Arial,Helvetica; border: single black; font-size: 8pt; }
</style>"""
return ss
def RenderContent(self):
html = self.html
btsessions = self.btsessions
A = self.html.append
A( self.StyleSheet() )
A("<html>")
A( self.Header() )
input_form = """
<form name="add" action="/" method=post>
<p>
<input type="hidden" name="pass" value="%s">
<table>
<tr>
<td align="right">Add Torrent from URL: </td>
<td> <input type="text" name="addurl" maxLength=256 size=60 value="http://"></td>
</tr>
<tr>
<td align="right">Referer: </td>
<td><input type="text" name="referer" maxLength=256 size=60 value=""></td>
</tr>
<tr>
<td></td>
<td><input type="submit" value="Add"></td>
</tr>
</table>
</p>
</form>""" % (self.password)
A( input_form )
refresh_form = """
<form name="refresh" action="/" method=post>
<p>
<input type="hidden" name="pass" value="%s">
<input type="submit" value="Refresh">
</p>
</form>""" % (self.password)
A( refresh_form )
A( "<table border='1' cellspacing='0' cellpadding='2'" )
A( "<tr><th>Action <th>Filename <th>Filesize <th>Activity <th>Done <th>KB/s Dn <th>KB/s Up <th>ETA <th>Share Ratio <th>Seeds <th>Leechers <th>Peers </tr>" )
for s in btsessions:
A( self.Session2HTML(s) )
A( "</tr>" )
A( "</table>" )
A( refresh_form )
hStop_form = """
<form name="stopform" action="/" method=post>
<input type="hidden" name="pass" value="%s">
<input type="hidden" name="stop" value="-1">
</form>""" % (self.password)
A( hStop_form )
hStart_form = """
<form name="startform" action="/" method=post>
<input type="hidden" name="pass" value="%s">
<input type="hidden" name="start" value="-1">
</form>""" % (self.password)
A( hStart_form )
A( self.JavaScript() )
A("</html>")
def RenderLogin(self):
html = self.html
btsessions = self.btsessions
A = self.html.append
A( self.StyleSheet() )
A("<html>")
A( self.Header() )
body = """
<form action="/" method=post>
<p>
<label for="pass">Enter Password: </LABEL>
<input type="password" name="pass">
<input type="submit" value="Login">
</p>
</form>"""
A( body )
A("</html>")
def GetHtml(self):
if len(self.html) > 0:
return "\n".join(self.html)
else:
return ""
class G3RequestHandler(RequestHandler):
def ServeContent(self, password, store_cookie=False):
ip = self.client_address[0]
write = self.wfile.write
self.send_response(200)
self.send_header("Content-Type", "text/html")
if store_cookie:
self.send_header("Set-Cookie", "p='%s'"%password)
self.end_headers()
btsessions = self.server.btsessions
btsessions_lock = self.server.btsessions_lock
btsessions_lock.acquire(True)
print 'Locking lock in rpcserver'
try:
renderer = HtmlRenderer(btsessions, self.myip, password)
renderer.RenderContent()
write( renderer.GetHtml() )
print 'Releasing lock in rpcserver'
except:
print_exc()
btsessions_lock.release()
return
def ServeLogin(self, password):
write = self.wfile.write
self.send_response(200)
self.send_header("Content-Type", "text/html")
self.end_headers()
btsessions = self.server.btsessions
btsessions_lock = self.server.btsessions_lock
try:
renderer = HtmlRenderer(myip=self.myip, password=password)
renderer.RenderLogin()
write( renderer.GetHtml() )
except:
pass
return
def Post2Params(self, path):
parts = path.split('&')
params = {}
for p in parts:
split = p.split('=')
if len(split) == 2:
params[ split[0] ] = split[1]
return params
def Servebmp(self, filename):
bmp = open(path.join(self.server.btconfig.Get('path'), "images/webui.bmp")).read() ## tweak By Jukka Lehtomaki :P
write = self.wfile.write
self.send_response(200)
self.send_header("Content-Type", "image/bmp")
self.send_header("Content-Length", str(len(bmp)))
self.end_headers()
write(bmp)
def do_POST(self):
## looks in the header to see what type of post (yes a bit lame... but it works for now)
## this needs much improving
if self.headers["content-type"].count('xml') > 0:
self.xml_do_POST()
else:
if self.server.btconfig['use_web_interface'] != True:
return
self.myip = socket.gethostbyaddr(socket.gethostname())[2][0]
password = self.server.btconfig['web_interface_pass']
password = str(password)
password.encode('utf-8')
print self.headers
btsessions = self.server.btsessions
btsessions_lock = self.server.btsessions_lock
try:
# get arguments
data = self.rfile.read(int(self.headers["content-length"]))
params = self.Post2Params(data)
passwd = params.get('pass')
# password must not equal default password
if password != "password" and passwd == password:
if params.get('start') != None:
btsessions_lock.acquire(True)
try:
id = int(params.get('start'))
for s in btsessions:
if s.GetId() == id:
s.Resume()
break
except:
pass
btsessions_lock.release()
sleep(3)
if params.get('stop') != None:
btsessions_lock.acquire(True)
try:
id = int(params.get('stop'))
for s in btsessions:
if s.GetId() == id:
s.Stop()
break
except:
pass
btsessions_lock.release()
if params.get('addurl') != None:
self.AddTorrent_URL( unquote( params.get('addurl') ), unquote( params.get('referer') ) )
sleep(3)
self.ServeContent(passwd)
return
except KeyError:
pass
self.ServeLogin(password)
def do_GET(self):
if self.server.btconfig['use_web_interface'] != True:
return
self.myip = socket.gethostbyaddr(socket.gethostname())[2][0]
password = self.server.btconfig['web_interface_pass']
try:
if '.bmp' in self.path:
m = re.compile('(?<=[/]).+').search(self.path)
if m:
filename = m.group().split('?')[0]
else:
return
self.Servebmp(filename)
return
except KeyError:
pass
self.ServeLogin(password)
def call(self, method, params):
print method, params
valid_methods = ["FriendRenounce", "FriendAnnounce", "GetVersion", "AddTorrent"]
try:
server_method = getattr(self, method)
if method in valid_methods:
return server_method(params[0])
except:
raise AttributeError, "Invalid Request: %s" % method
return 0
def log_request(self, code='', size=''):
pass # don't want to see these in stdout
def FriendRenounce(self, p):
ip = self.client_address[0]
try:
#0:peerid, 1:port, 2:infohash
self.server.UpdateFunc(ip, decodestring(p[0]), 9, [ p[1], decodestring(p[2]) ])
return 1
except:
return 0
def FriendAnnounce(self, p):
ip = self.client_address[0]
try:
#0:peerid, 1:port, 2:infohash
self.server.UpdateFunc(ip, decodestring(p[0]), 0, [ p[1], decodestring(p[2]) ])
return 1
except:
return 0
def GetVersion(self, p):
return version
# Accept requests from localhost ONLY
def AddTorrent(self, responsefile):
ip = self.client_address[0]
if ip != '127.0.0.1':
return -1
try:
self.server.AddTorrentFunc(responsefile)
return 1
except:
return 0
def AddTorrent_URL(self, url, referer):
try:
self.server.AddTorrentFunc(url, referer, 1)
return 1
except:
return 0
class G3RPCServer(SocketServer.TCPServer):
def __init__(self, btconfig, errorfunc, friendupdatefunc, addtorrentfunc,
btsessions, btsessions_lock, images):
self.btconfig = btconfig
self.bound = False
self.ErrorFunc = errorfunc
self.UpdateFunc = friendupdatefunc
self.AddTorrentFunc = addtorrentfunc
self.btsessions = btsessions
self.btsessions_lock = btsessions_lock
self.images = images
self._aborted = False
try:
SocketServer.TCPServer.__init__(self, ("", btconfig.Get('web_interface_port')), G3RequestHandler)
self.bound = True
except:
self.ErrorFunc("ERROR", "Could not bind socket for xml rpc - Address in use", -2)
def Start(self):
t = Thread(target = self.ListenForever, name = "G3RPCServer", args = [])
t.setDaemon(True)
t.start()
def Stop(self):
self._aborted = True
import httplib
try:
req = httplib.HTTP('%s:%d' % ("127.0.0.1", self.btconfig.Get('web_interface_port')))
req.connect()
del req
except:
pass
def ListenForever(self):
try:
if self.bound:
print "G3XMLRPCServer Started"
while not self._aborted:
self.handle_request()
print "G3XMLRPCServer Stopped"
except:
print_exc()
def dummy_errorfunc(name, msg, type):
print name, ' ', msg
def dummy_update(ip, peerid, msgtype, data):
print ip, ' ', peerid, ' ', str(msgtype), ' ', data
def dummy_addtorrent(responsefile):
print responsefile
if __name__ == "__main__":
server = G3RPCServer(dummy_errorfunc, dummy_update, dummy_addtorrent)
server.ListenForever()
|