import wx
import sys
from threading import Thread
from string import upper
from traceback import print_exc
from cStringIO import StringIO
from Utility.helpers import getSocket
from Utility.constants import *#IGNORE:W0611
################################################################
#
# Class: WebDialog
#
# Let the user specify settings for the webservice
#
################################################################
class WebDialog(wx.Dialog):
def __init__(self, parent):
self.utility = parent.utility
title = self.utility.lang.get('webinterfaceservice')
pre = wx.PreDialog()
pre.Create(parent, -1, title)
self.this = pre.this
self.parent = parent
self.window = self.utility.window
self.utility.webserver.webdlg = self
WebRead = self.utility.webconfig.Read
self.warnlowport = [False, WebRead('webport')]
# # Change old config value
# oldval = WebRead('webIP')
# if (oldval == "Automatics") or (oldval == "Automatic"):
# self.utility.webconfig.Write('webIP', "")
# self.utility.webconfig.Flush()
# ip_choice = ""
# elif (oldval == "Loop Back"):
# self.utility.webconfig.Write('webIP', "127.0.0.1")
# self.utility.webconfig.Flush()
#
# newval = WebRead('webIP')
newval = self.utility.webserver.getIP()
if newval == "":
default_ip = self.utility.lang.get('automatic')
elif newval == "127.0.0.1":
default_ip = self.utility.lang.get('loopback')
else:
default_ip = newval
outerbox = wx.BoxSizer(wx.VERTICAL)
outerbox.Add(wx.StaticText(self, -1, self.utility.lang.get('webinterfacetitle')), 0, wx.ALIGN_CENTER|wx.ALL, 10)
ip_choices = [self.utility.lang.get('automatic'), self.utility.lang.get('loopback')]
ipandport_box = wx.BoxSizer(wx.HORIZONTAL)
ipandport_box.Add(wx.StaticText(self, -1, self.utility.lang.get('webip')), 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5)
self.iptext = wx.ComboBox(self, -1, default_ip, wx.Point(-1, -1), wx.Size(120, -1), ip_choices, wx.CB_DROPDOWN|wx.CB_READONLY)
ipandport_box.Add(self.iptext, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5)
ipandport_box.Add(wx.StaticText(self, -1, self.utility.lang.get('webport')), 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5)
self.porttext = self.utility.makeNumCtrl(self, WebRead('webport'), max = 65536)
ipandport_box.Add(self.porttext, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5)
outerbox.Add(ipandport_box, 0, wx.EXPAND|wx.ALL, 5)
uniquekey_box = wx.BoxSizer(wx.HORIZONTAL)
uniquekey_box.Add(wx.StaticText(self, -1, self.utility.lang.get('uniquekey')), 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5)
self.keytext = wx.TextCtrl(self, -1, WebRead('webID'), wx.Point(-1, -1), wx.Size(165, -1))
uniquekey_box.Add(self.keytext, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5)
outerbox.Add(uniquekey_box, 0, wx.EXPAND|wx.LEFT|wx.RIGHT, 5)
self.permissions = [ ['webquery', 'allow_query'],
['webadd', 'allow_add'],
['webdelete', 'allow_delete'],
['webqueue', 'allow_queue'],
['webstop', 'allow_stop'],
['webresume', 'allow_resume'],
['webpause', 'allow_pause'],
['webclearallcompleted', 'allow_clearcompleted'],
['priority', 'allow_setprio'],
['webgetparam', 'allow_getparam'],
['websetparam', 'allow_setparam' ] ]
self.perm_checkbox = wx.CheckListBox(self, -1, size = wx.Size(100, 120), style = wx.LB_SINGLE)
perm_text = [self.utility.lang.get(item[0]) for item in self.permissions]
self.perm_checkbox.Set(perm_text)
for i in range(0, len(self.permissions)):
param = self.permissions[i][1]
checked = WebRead(param, "boolean")
if checked:
self.perm_checkbox.Check(i)
outerbox.Add(wx.StaticText(self, -1, self.utility.lang.get('commandpermission')), 0, wx.ALL, 5)
outerbox.Add(self.perm_checkbox, 0, wx.EXPAND|wx.LEFT|wx.RIGHT, 5)
self.webautostart = wx.CheckBox(self, -1, self.utility.lang.get('webautostart'))
outerbox.Add(self.webautostart, 0, wx.EXPAND|wx.ALL, 5)
#CheckBox Set Value from Config
#####################################
self.webautostart.SetValue(WebRead('webautostart', "boolean"))
self.actionbtn = wx.Button(self, -1, "")
self.utility.webserver.updateLabels()
applybtn = wx.Button(self, wx.NewId(), self.utility.lang.get('apply'))
self.Bind(wx.EVT_BUTTON, self.onApply, applybtn)
okbtn = wx.Button(self, wx.NewId(), self.utility.lang.get('ok'))
self.Bind(wx.EVT_BUTTON, self.onOK, okbtn)
cancelbtn = wx.Button(self, wx.ID_CANCEL, self.utility.lang.get('cancel'))
self.Bind(wx.EVT_BUTTON, self.onClose, cancelbtn)
self.Bind(wx.EVT_CLOSE, self.onClose)
self.Bind(wx.EVT_BUTTON, self.OnAction, self.actionbtn)
button_box = wx.BoxSizer(wx.HORIZONTAL)
button_box.Add(applybtn, 0, wx.ALL, 5)
button_box.Add(okbtn, 0, wx.ALL, 5)
button_box.Add(cancelbtn, 0, wx.ALL, 5)
outerbox.Add(self.actionbtn, 0, wx.ALIGN_CENTER|wx.ALL, 5)
outerbox.Add(button_box, 0, wx.ALIGN_CENTER)
self.SetAutoLayout(True)
self.SetSizer(outerbox)
self.Fit()
def onOK(self, event = None):
self.utility.webserver.webdlg = None
if self.onApply():
self.EndModal(wx.ID_OK)
def onClose(self, event = None):
self.utility.webserver.webdlg = None
if event is not None:
event.Skip()
def onApply(self, event = None):
self.saveParams()
return True
def saveParams(self):
PORT = int(self.porttext.GetValue())
if ((not self.warnlowport[0]
or self.warnlowport[1] != str(PORT))
and PORT < 1024):
dlg = wx.MessageDialog(None, self.utility.lang.get('warningportunder1024') + "\n(" + str(PORT) + ")", self.utility.lang.get('warning'), wx.YES_NO|wx.ICON_INFORMATION)
if dlg.ShowModal() == wx.ID_NO:
dlg.Destroy()
return False
else:
self.warnlowport[0] = True
self.warnlowport[1] = str(PORT)
dlg.Destroy()
# Re-assign web parameters
self.utility.webconfig.Write('webIP', self.iptext.GetStringSelection())
self.utility.webconfig.Write('webport', str(self.porttext.GetValue()))
self.utility.webconfig.Write('webID', self.keytext.GetValue())
for i in range(0, self.perm_checkbox.GetCount()):
param = self.permissions[i][1]
checked = self.perm_checkbox.IsChecked(i)
self.utility.webconfig.Write(param, checked, "boolean")
if self.webautostart.GetValue():
self.utility.webconfig.Write('webautostart', "1")
else:
self.utility.webconfig.Write('webautostart', "0")
#######################################
# Record New Config to webservice.conf
#######################################
self.utility.webconfig.Flush()
return True
def OnAction(self, event = None):
if not self.utility.webserver.active:
if self.saveParams():
self.startWebService()
else:
self.stopWebService()
def startWebService(self):
######################################
# Start Web Interface Service
######################################
self.utility.webserver.start()
def stopWebService(self):
# Stop Web Service
self.utility.webserver.stop()
def updateLabels(self):
active = self.utility.webserver.active
if active:
label = self.utility.lang.get('stopservice')
else:
label = self.utility.lang.get('startservice')
self.actionbtn.SetLabel(label)
self.porttext.Enable(not active)
self.keytext.Enable(not active)
self.iptext.Enable(not active)
################################################################
#
# Class: WebListener
#
# Listens for webservice commands
#
################################################################
class WebListener:
def __init__(self, utility, webdlg = None):
self.s = None
self.utility = utility
self.utility.webserver = self
self.webdlg = webdlg
self.active = False
self.port = None
self.ip = None
self.client = WebClient(self.utility)
def getIP(self):
# Change old config value
oldval = self.utility.webconfig.Read('webIP')
if (oldval == "Automatics") or (oldval == "Automatic"):
self.utility.webconfig.Write('webIP', "")
self.utility.webconfig.Flush()
elif (oldval == "Loop Back"):
self.utility.webconfig.Write('webIP', "127.0.0.1")
self.utility.webconfig.Flush()
# if self.utility.webconfig.Read('webIP') == "Automatic":
# IP = ""
# else: #LoopBack
# IP = "127.0.0.1"
# return IP
return self.utility.webconfig.Read('webIP')
def getPort(self):
try:
PORT = self.utility.webconfig.Read('webport', "int")
except:
PORT = 56667
return PORT
def start(self):
# Already running
if self.active:
return
self.ip = self.getIP() # Symbolic name meaning the local host
self.port = self.getPort() # Arbitrary non-privileged port
self.active = True
self.updateLabels()
webservice = Thread(target = self.startThread)
webservice.setDaemon(False)
webservice.start()
def stop(self):
# Not running, no need to stop
if not self.active:
return
self.client.sendCmd("CLOSE|")
self.port = None
self.ip = None
self.active = False
self.updateLabels()
def updateLabels(self):
self.utility.actions[ACTION_WEBSERVICE].updateButton()
if self.webdlg is not None:
self.webdlg.updateLabels()
def startThread(self):
self.s = getSocket(self.ip, self.port, "server")
if self.s is None:
#Display Dialog Can't open scoket
dlg = wx.MessageDialog(None, self.utility.lang.get('cantopensocket') , self.utility.lang.get('socketerror'), wx.OK|wx.ICON_ERROR)
dlg.ShowModal()
dlg.Destroy()
self.active = False
self.updateLabels()
return
WebServiceCmd(self).go()
################################################################
#
# Class: WebClient
#
# Used to sent brief commands to the webservice
# (i.e.: use to send the shutdown command)
#
################################################################
class WebClient:
def __init__(self, utility):
self.utility = utility
self.webserver = self.utility.webserver
def sendCmd(self, command):
# Web service isn't even active -- don't bother sending a message
if not self.webserver.active:
return False
HOST = self.webserver.ip # The remote host
PORT = self.webserver.port # The same port as used by the server
s = getSocket(HOST, PORT)
if s is None:
dlg = wx.MessageDialog(None, self.utility.lang.get('cantconnectabcwebinterface') , self.utility.lang.get('socketerror'), wx.OK|wx.ICON_ERROR)
dlg.ShowModal()
dlg.Destroy()
return False
# if request is not close connection request
# so it's torrent request copy .torrent
# in backup torrent folder
##############################################
mesg = "ID|" + self.utility.webconfig.Read('webID') + "\n" + command
s.send(mesg)
s.close()
return True
################################################################
#
# Class: WebServiceCmd
#
# Processes the actual webservice commands
#
################################################################
class WebServiceCmd:
def __init__(self, parent):
self.parent = parent
self.utility = self.parent.utility
self.frame = self.utility.frame
def separate(self, info, splitchar = ","):
try:
separated = info.split(splitchar)
except:
self.conn.send("Feedback\nError=Bad Arguments")
self.conn.close()
separated = None
return separated
def getMappings(self, separated, splitchar = "="):
mappings = {}
for item in separated:
try:
pair = item.split(splitchar)
mappings[pair[0]] = pair[1]
except:
self.conn.send("Feedback\nError=Bad Arguments")
self.conn.close()
mappings = None
break
return mappings
def getTorrents(self, info = "", infohash_list = None):
torrents = None
if infohash_list is None:
infohash_list = self.separate(info)
if infohash_list is None:
return torrents
torrents = []
for infohash in infohash_list:
torrent = self.utility.queue.getABCTorrent(info_hash = infohash)
if torrent is None:
self.conn.send("Feedback\nError=No torrents match with this info hash: " + infohash + "\n")
torrents = None
break
else:
torrents.append(torrent)
return torrents
def cmdSetParam(self, info):
conn = self.conn
if not self.utility.webconfig.Read('allow_setparam', "boolean"):
conn.send("Feedback\nError=SETPARAM,Permission denied")
return
separated = self.separate(info, "|")
if separated is None:
return
mappings = self.getMappings(separated)
if mappings is None:
return
for param in mappings:
self.utility.config.Write(param, mappings[param])
self.utility.config.Flush()
conn.send("Feedback\nOK")
def cmdGetParam(self, info):
conn = self.conn
if not self.utility.webconfig.Read('allow_getparam', "boolean"):
conn.send("Feedback\nError=GETPARAM,Permission denied")
return
separated = self.separate(info)
if separated is None:
return
retmsg = ""
Read = self.utility.config.Read
for param in separated:
value = Read(param)
retmsg += value + "|"
retmsg += "\n"
conn.send("Feedback\n" + str(retmsg))
def cmdClose(self):
conn = self.conn
self.utility.webserver.active = False
self.utility.webserver.updateLabels()
conn.close()
self.parent.s.close()
# sys.stderr.write("\nDone shutting down webservice")
def cmdQuery(self, info = ""):
conn = self.conn
if not self.utility.webconfig.Read('allow_query', "boolean"):
conn.send("Feedback\nError=QUERY,Permission denied")
conn.close()
return
if info == "":
self.QueryFields()
else:
fields = self.separate(info, ",")
if fields is None:
return
self.QueryFields(fields)
def QueryFields(self, fieldlist = None):
conn = self.conn
maxid = self.utility.list.columns.maxid
# Default to returning all fields
if fieldlist is None:
fieldlist = range(4, maxid)
oldfields = { "filename" : COL_TITLE,
"progress" : COL_PROGRESS,
"btstatus" : COL_BTSTATUS,
"eta" : COL_ETA,
"dlspeed" : COL_DLSPEED,
"ulspeed" : COL_ULSPEED,
"ratio" : COL_RATIO,
"peers" : COL_PEERS,
"seeds" : COL_SEEDS,
"copies" : COL_COPIES,
"dlsize" : COL_DLSIZE,
"ulsize" : COL_ULSIZE,
"peeravgprogress" : COL_PEERPROGRESS,
"totalspeed" : COL_TOTALSPEED,
"totalsize" : COL_SIZE,
"priority" : COL_PRIO }
fieldids = []
retmsg = ""
for req_field in fieldlist:
try:
# A field number was specified
fieldid = int(req_field)
if fieldid >= 4 and fieldid < maxid:
fieldids.append(fieldid)
else:
conn.send("Feedback\nError=Invalid field ID (must be between 4 and " + str(maxid) + ") = " + req_field)
conn.close()
return
except:
# Old format -- a field name was specified
if req_field in oldfields:
fieldid = oldfields[req_field]
fieldids.append(fieldid)
else:
# Can't identify the field
conn.send("Feedback\nError=Unknown field name = " + req_field)
conn.close()
return
retmsg += self.utility.lang.get("column" + str(fieldid) + "_text") + "|"
retmsg += "Info Hash\n"
for ABCTorrentTemp in self.utility.torrents["all"]:
retmsg += ABCTorrentTemp.getInfo(fieldlist = fieldids)
conn.send(retmsg)
conn.close()
def cmdAdd(self, info):
conn = self.conn
if not self.utility.webconfig.Read('allow_add', "boolean"):
conn.send("Feedback\nError=ADD,Permission denied")
return
# What do we do if we don't have a default download location specified
# and we call this from the webservice?
####################################################
retmsg = self.utility.queue.addtorrents.AddTorrentURL(info, "web")
conn.send("Feedback\n"+retmsg)
def cmdDelete(self, info):
conn = self.conn
if upper(info) == "COMPLETED":
if not self.utility.webconfig.Read('allow_clearcompleted', "boolean"):
conn.send("Feedback\nError=CLEARCOMPLETED,Permission denied")
return
self.utility.actions[ACTION_CLEARCOMPLETED].action()
conn.send("Feedback\nOK")
else:
if not self.utility.webconfig.Read('allow_delete', "boolean") != "1":
conn.send("Feedback\nError=DELETE,Permission denied")
return
torrents = self.getTorrents(info)
if torrents is None:
return
self.utility.actionhandler.procREMOVE(torrents)
conn.send("Feedback\nOK")
def cmdResume(self, info = "ALL"):
conn = self.conn
if not self.utility.webconfig.Read('allow_resume', "boolean"):
conn.send("Feedback\nError=RESUME,Permission denied")
return
if upper(info) == "ALL":
self.utility.actions[ACTION_UNSTOPALL].action()
conn.send("Feedback\nOK")
else:
torrents = self.getTorrents(info)
if torrents is None:
return
self.utility.actionhandler.procRESUME(torrents)
conn.send("Feedback\nOK")
def cmdStop(self, info = "ALL"):
conn = self.conn
if not self.utility.webconfig.Read('allow_stop', "boolean"):
conn.send("Feedback\nError=STOP,Permission denied")
return
if upper(info) == "ALL":
self.utility.actions[ACTION_STOPALL].action()
conn.send("Feedback\nOK")
else:
torrents = self.getTorrents(info)
if torrents is None:
return
self.utility.actionhandler.procSTOP(torrents)
conn.send("Feedback\nOK")
def cmdPause(self, info, release = False):
conn = self.conn
if not self.utility.webconfig.Read('allow_pause', "boolean"):
conn.send("Feedback\nError=PAUSE,Permission denied")
conn.close()
return
if upper(info) == "ALL":
torrents = self.utility.torrents["active"].keys()
else:
torrents = self.getTorrents(info)
if torrents is None:
return
self.utility.actionhandler.procPAUSE(torrents, release = release)
conn.send("Feedback\nOK")
def cmdQueue(self, info = "ALL"):
conn = self.conn
if not self.utility.webconfig.Read('allow_queue', "boolean"):
conn.send("Feedback\nError=QUEUE,Permission denied")
return
if upper(info) == "ALL":
torrents = self.utility.torrents["all"]
else:
torrents = self.getTorrents(info)
if torrents is None:
return
self.utility.actionhandler.procQUEUE(torrents)
conn.send("Feedback\nOK")
def cmdPriority(self, info):
conn = self.conn
if not self.utility.webconfig.Read('allow_setprio', "boolean"):
conn.send("Feedback\nError=PRIORITY,Permission denied")
return
separated = self.separate(info, "|")
if separated is None:
return
mappings = self.getMappings(separated, ",")
if mappings is None:
return
for infohash in mappings:
value = mappings[infohash]
error = False
try:
prio = int(value)
if (prio < 0) or (prio > 4):
error = True
except:
error = True
if error:
conn.send("Feedback\nError=Priority must be a number from 0-4")
conn.close()
return
hashlist = [infohash for infohash in mappings]
torrents = self.getTorrents(infohash_list = hashlist)
if torrents is None:
return
for torrent in torrents:
torrent.changePriority(prio)
conn.send("Feedback\nOK")
def go(self):
while 1:
conn, addr = self.parent.s.accept()
self.conn = conn
try:
try:
data = conn.recv(5048)
except:
try:
conn.close()
except:
pass
continue
try:
idline, cmdline = data.split("\n")
except:
conn.send("Feedback\nError=You need unique ID to command ABC!")
conn.close()
continue
try:
idtag = idline.split("|")
idkey = idtag[0]
idvalue = idtag[1]
except:
conn.send("Feedback\nError=You need unique ID to command ABC!")
conn.close()
continue
if idkey != "ID":
conn.send("Feedback\nError=You need unique ID to command ABC!")
conn.close()
continue
else:
if idvalue != self.utility.webconfig.Read('webID'):
conn.send("Feedback\nError=Incorrect unique ID")
conn.close()
continue
try:
cmd, info = cmdline.split("|")
except:
#Bad Command
conn.send("Feedback\nError=Command should end with |")
conn.close()
continue
sys.stdout.write('Web Request Recieved.\n' + data + '\n')
# Convert the command to upper case
cmd = upper(cmd)
if cmd == "CLOSE" or self.utility.abcquitting:
self.cmdClose()
return
# elif cmd == "KEEPALIVE":
# # Don't actually do anything
# pass
elif cmd == "QUERY":
self.cmdQuery(info)
elif cmd == "ADD":
self.cmdAdd(info)
elif cmd == "DELETE":
self.cmdDelete(info)
elif cmd == "RESUME":
self.cmdResume(info)
elif cmd == "PAUSE":
self.cmdPause(info)
elif cmd == "UNPAUSE":
self.cmdPause(info, release = True)
elif cmd == "STOP":
self.cmdStop(info)
elif cmd == "QUEUE":
self.cmdQueue(info)
elif cmd == "PRIORITY":
self.cmdPriority(info)
elif cmd == "SETPARAM":
self.cmdSetParam(info)
elif cmd == "GETPARAM":
self.cmdGetParam(info)
elif cmd == "GETSTRING":
conn.send("String\n" + self.utility.lang.get(info))
elif cmd == "VERSION":
conn.send("Version\n" + self.utility.lang.get('version'))
else: # Bad Command
conn.send("Feedback\nError=Command not found: " + cmd)
pass
except:
pass
conn.close()
|