summaryrefslogtreecommitdiffstats
path: root/module/interaction
diff options
context:
space:
mode:
authorGravatar RaNaN <Mast3rRaNaN@hotmail.de> 2011-12-23 21:40:22 +0100
committerGravatar RaNaN <Mast3rRaNaN@hotmail.de> 2011-12-23 21:40:22 +0100
commit8da44b430b957b25e74dff63829d4198a52e7a0b (patch)
treef20fc60db6e7d9a93fe3ca60cd68a6e32b536fc6 /module/interaction
parentoron version increase (diff)
parentlittle fixes (diff)
downloadpyload-8da44b430b957b25e74dff63829d4198a52e7a0b.tar.xz
merge hotfixes in
Diffstat (limited to 'module/interaction')
-rw-r--r--module/interaction/CaptchaManager.py158
-rw-r--r--module/interaction/EventManager.py55
-rw-r--r--module/interaction/InteractionManager.py89
-rw-r--r--module/interaction/InteractionTask.py129
-rw-r--r--module/interaction/PullEvents.py68
-rw-r--r--module/interaction/__init__.py2
6 files changed, 501 insertions, 0 deletions
diff --git a/module/interaction/CaptchaManager.py b/module/interaction/CaptchaManager.py
new file mode 100644
index 000000000..02cd10a11
--- /dev/null
+++ b/module/interaction/CaptchaManager.py
@@ -0,0 +1,158 @@
+# -*- coding: utf-8 -*-
+
+"""
+ 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 3 of the License,
+ 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, see <http://www.gnu.org/licenses/>.
+
+ @author: mkaay, RaNaN
+"""
+
+from time import time
+from traceback import print_exc
+from threading import Lock
+
+class CaptchaManager():
+ def __init__(self, core):
+ self.lock = Lock()
+ self.core = core
+ self.tasks = [] #task store, for outgoing tasks only
+
+ self.ids = 0 #only for internal purpose
+
+ def newTask(self, img, format, file, result_type):
+ task = CaptchaTask(self.ids, img, format, file, result_type)
+ self.ids += 1
+ return task
+
+ def removeTask(self, task):
+ self.lock.acquire()
+ if task in self.tasks:
+ self.tasks.remove(task)
+ self.lock.release()
+
+ def getTask(self):
+ self.lock.acquire()
+ for task in self.tasks:
+ if task.status in ("waiting", "shared-user"):
+ self.lock.release()
+ return task
+ self.lock.release()
+ return None
+
+ def getTaskByID(self, tid):
+ self.lock.acquire()
+ for task in self.tasks:
+ if task.id == str(tid): #task ids are strings
+ self.lock.release()
+ return task
+ self.lock.release()
+ return None
+
+ def handleCaptcha(self, task):
+ cli = self.core.isClientConnected()
+
+ if cli: #client connected -> should solve the captcha
+ task.setWaiting(50) #wait 50 sec for response
+
+ for plugin in self.core.hookManager.activePlugins():
+ try:
+ plugin.newCaptchaTask(task)
+ except:
+ if self.core.debug:
+ print_exc()
+
+ if task.handler or cli: #the captcha was handled
+ self.tasks.append(task)
+ return True
+
+ task.error = _("No Client connected for captcha decrypting")
+
+ return False
+
+
+class CaptchaTask():
+ def __init__(self, id, img, format, file, result_type='textual'):
+ self.id = str(id)
+ self.captchaImg = img
+ self.captchaFormat = format
+ self.captchaFile = file
+ self.captchaResultType = result_type
+ self.handler = [] #the hook plugins that will take care of the solution
+ self.result = None
+ self.waitUntil = None
+ self.error = None #error message
+
+ self.status = "init"
+ self.data = {} #handler can store data here
+
+ def getCaptcha(self):
+ return self.captchaImg, self.captchaFormat, self.captchaResultType
+
+ def setResult(self, text):
+ if self.isTextual():
+ self.result = text
+ if self.isPositional():
+ try:
+ parts = text.split(',')
+ self.result = (int(parts[0]), int(parts[1]))
+ except:
+ self.result = None
+
+ def getResult(self):
+ try:
+ res = self.result.encode("utf8", "replace")
+ except:
+ res = self.result
+
+ return res
+
+ def getStatus(self):
+ return self.status
+
+ def setWaiting(self, sec):
+ """ let the captcha wait secs for the solution """
+ self.waitUntil = max(time() + sec, self.waitUntil)
+ self.status = "waiting"
+
+ def isWaiting(self):
+ if self.result or self.error or time() > self.waitUntil:
+ return False
+
+ return True
+
+ def isTextual(self):
+ """ returns if text is written on the captcha """
+ return self.captchaResultType == 'textual'
+
+ def isPositional(self):
+ """ returns if user have to click a specific region on the captcha """
+ return self.captchaResultType == 'positional'
+
+ def setWatingForUser(self, exclusive):
+ if exclusive:
+ self.status = "user"
+ else:
+ self.status = "shared-user"
+
+ def timedOut(self):
+ return time() > self.waitUntil
+
+ def invalid(self):
+ """ indicates the captcha was not correct """
+ [x.captchaInvalid(self) for x in self.handler]
+
+ def correct(self):
+ [x.captchaCorrect(self) for x in self.handler]
+
+ def __str__(self):
+ return "<CaptchaTask '%s'>" % self.id
diff --git a/module/interaction/EventManager.py b/module/interaction/EventManager.py
new file mode 100644
index 000000000..c45c388f3
--- /dev/null
+++ b/module/interaction/EventManager.py
@@ -0,0 +1,55 @@
+# -*- coding: utf-8 -*-
+from time import time
+
+from PullEvents import ReloadAllEvent
+from module.utils import uniqify
+
+class EventManager:
+ def __init__(self, core):
+ self.core = core
+ self.clients = []
+
+ def newClient(self, uuid):
+ self.clients.append(Client(uuid))
+
+ def clean(self):
+ for n, client in enumerate(self.clients):
+ if client.lastActive + 30 < time():
+ del self.clients[n]
+
+ def getEvents(self, uuid):
+ events = []
+ validUuid = False
+ for client in self.clients:
+ if client.uuid == uuid:
+ client.lastActive = time()
+ validUuid = True
+ while client.newEvents():
+ events.append(client.popEvent().toList())
+ break
+ if not validUuid:
+ self.newClient(uuid)
+ events = [ReloadAllEvent("queue").toList(), ReloadAllEvent("collector").toList()]
+ return uniqify(events, repr)
+
+ def addEvent(self, event):
+ for client in self.clients:
+ client.addEvent(event)
+
+
+class Client:
+ def __init__(self, uuid):
+ self.uuid = uuid
+ self.lastActive = time()
+ self.events = []
+
+ def newEvents(self):
+ return len(self.events) > 0
+
+ def popEvent(self):
+ if not len(self.events):
+ return None
+ return self.events.pop(0)
+
+ def addEvent(self, event):
+ self.events.append(event) \ No newline at end of file
diff --git a/module/interaction/InteractionManager.py b/module/interaction/InteractionManager.py
new file mode 100644
index 000000000..8bb500f3b
--- /dev/null
+++ b/module/interaction/InteractionManager.py
@@ -0,0 +1,89 @@
+# -*- coding: utf-8 -*-
+"""
+ 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 3 of the License,
+ 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, see <http://www.gnu.org/licenses/>.
+
+ @author: RaNaN
+"""
+from time import time
+from utils import lock
+from traceback import print_exc
+from threading import Lock
+
+
+
+
+class InteractionManager:
+ """
+ Class that gives ability to interact with the user.
+ Arbitary task with predefined output and input type can be set off.
+ Asyncronous callbacks and default values keeps the ability to fallback if no user is present.
+ """
+ def __init__(self, core):
+ self.lock = Lock()
+ self.core = core
+ self.tasks = [] #task store, for outgoing tasks only
+
+ self.ids = 0 #only for internal purpose
+
+ def work(self):
+ """Mainloop that gets the work done"""
+
+ def newTask(self, img, format, file, result_type):
+ task = CaptchaTask(self.ids, img, format, file, result_type)
+ self.ids += 1
+ return task
+
+ @lock
+ def removeTask(self, task):
+ if task in self.tasks:
+ self.tasks.remove(task)
+
+ @lock
+ def getTask(self):
+ for task in self.tasks:
+ if task.status in ("waiting", "shared-user"):
+ return task
+
+ @lock
+ def getTaskByID(self, tid):
+ for task in self.tasks:
+ if task.id == str(tid): #task ids are strings
+ self.lock.release()
+ return task
+
+ def handleCaptcha(self, task):
+ cli = self.core.isClientConnected()
+
+ if cli: #client connected -> should solve the captcha
+ task.setWaiting(50) #wait 50 sec for response
+
+ for plugin in self.core.hookManager.activePlugins():
+ try:
+ plugin.newCaptchaTask(task)
+ except:
+ if self.core.debug:
+ print_exc()
+
+ if task.handler or cli: #the captcha was handled
+ self.tasks.append(task)
+ return True
+
+ task.error = _("No Client connected for captcha decrypting")
+
+ return False
+
+
+if __name__ == "__main__":
+
+ it = InteractionTask() \ No newline at end of file
diff --git a/module/interaction/InteractionTask.py b/module/interaction/InteractionTask.py
new file mode 100644
index 000000000..97cb16794
--- /dev/null
+++ b/module/interaction/InteractionTask.py
@@ -0,0 +1,129 @@
+# -*- coding: utf-8 -*-
+"""
+ 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 3 of the License,
+ 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, see <http://www.gnu.org/licenses/>.
+
+ @author: RaNaN
+"""
+
+from module.Api import InteractionTask as BaseInteractionTask
+from module.Api import Input, Output
+
+#noinspection PyUnresolvedReferences
+class InteractionTask(BaseInteractionTask):
+ """
+ General Interaction Task extends ITask defined by thrift with additional fields and methods.
+ """
+ #: Plugins can put needed data here
+ storage = None
+ #: Timestamp when task expires
+ waitUntil = 0
+ #: Default data to be used, or True if preset should be used
+ default = None
+ #: The received result as string representation
+ result = None
+ #: List of registered handles
+ handler = None
+ #: Callback functions
+ callbacks = None
+ #: Error Message
+ error = None
+ #: Status string
+ status = None
+
+ def __init__(self, *args, **kwargs):
+ BaseInteractionTask.__init__(self, *args, **kwargs)
+
+ # additional internal attributes
+ self.storage = {}
+ self.default = []
+ self.handler = []
+ self.callbacks = []
+
+
+class CaptchaTask:
+ def __init__(self, id, img, format, file, result_type='textual'):
+ self.id = str(id)
+ self.captchaImg = img
+ self.captchaFormat = format
+ self.captchaFile = file
+ self.captchaResultType = result_type
+ self.handler = [] #the hook plugins that will take care of the solution
+ self.result = None
+ self.waitUntil = None
+ self.error = None #error message
+
+ self.status = "init"
+ self.data = {} #handler can store data here
+
+ def getCaptcha(self):
+ return self.captchaImg, self.captchaFormat, self.captchaResultType
+
+ def setResult(self, text):
+ if self.isTextual():
+ self.result = text
+ if self.isPositional():
+ try:
+ parts = text.split(',')
+ self.result = (int(parts[0]), int(parts[1]))
+ except:
+ self.result = None
+
+ def getResult(self):
+ try:
+ res = self.result.encode("utf8", "replace")
+ except:
+ res = self.result
+
+ return res
+
+ def getStatus(self):
+ return self.status
+
+ def setWaiting(self, sec):
+ """ let the captcha wait secs for the solution """
+ self.waitUntil = max(time() + sec, self.waitUntil)
+ self.status = "waiting"
+
+ def isWaiting(self):
+ if self.result or self.error or time() > self.waitUntil:
+ return False
+
+ return True
+
+ def isTextual(self):
+ """ returns if text is written on the captcha """
+ return self.captchaResultType == 'textual'
+
+ def isPositional(self):
+ """ returns if user have to click a specific region on the captcha """
+ return self.captchaResultType == 'positional'
+
+ def setWatingForUser(self, exclusive):
+ if exclusive:
+ self.status = "user"
+ else:
+ self.status = "shared-user"
+
+ def timedOut(self):
+ return time() > self.waitUntil
+
+ def invalid(self):
+ """ indicates the captcha was not correct """
+ [x.captchaInvalid(self) for x in self.handler]
+
+ def correct(self):
+ [x.captchaCorrect(self) for x in self.handler]
+
+ def __str__(self):
+ return "<CaptchaTask '%s'>" % self.id
diff --git a/module/interaction/PullEvents.py b/module/interaction/PullEvents.py
new file mode 100644
index 000000000..f34b01d48
--- /dev/null
+++ b/module/interaction/PullEvents.py
@@ -0,0 +1,68 @@
+# -*- coding: utf-8 -*-
+
+"""
+ 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 3 of the License,
+ 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, see <http://www.gnu.org/licenses/>.
+
+ @author: mkaay
+"""
+
+class UpdateEvent():
+ def __init__(self, itype, iid, destination):
+ assert itype == "pack" or itype == "file"
+ assert destination == "queue" or destination == "collector"
+ self.type = itype
+ self.id = iid
+ self.destination = destination
+
+ def toList(self):
+ return ["update", self.destination, self.type, self.id]
+
+class RemoveEvent():
+ def __init__(self, itype, iid, destination):
+ assert itype == "pack" or itype == "file"
+ assert destination == "queue" or destination == "collector"
+ self.type = itype
+ self.id = iid
+ self.destination = destination
+
+ def toList(self):
+ return ["remove", self.destination, self.type, self.id]
+
+class InsertEvent():
+ def __init__(self, itype, iid, after, destination):
+ assert itype == "pack" or itype == "file"
+ assert destination == "queue" or destination == "collector"
+ self.type = itype
+ self.id = iid
+ self.after = after
+ self.destination = destination
+
+ def toList(self):
+ return ["insert", self.destination, self.type, self.id, self.after]
+
+class ReloadAllEvent():
+ def __init__(self, destination):
+ assert destination == "queue" or destination == "collector"
+ self.destination = destination
+
+ def toList(self):
+ return ["reload", self.destination]
+
+class AccountUpdateEvent():
+ def toList(self):
+ return ["account"]
+
+class ConfigUpdateEvent():
+ def toList(self):
+ return ["config"]
diff --git a/module/interaction/__init__.py b/module/interaction/__init__.py
new file mode 100644
index 000000000..de6d13128
--- /dev/null
+++ b/module/interaction/__init__.py
@@ -0,0 +1,2 @@
+__author__ = 'christian'
+ \ No newline at end of file