diff options
Diffstat (limited to 'module')
| -rw-r--r-- | module/CaptchaManager.py | 134 | ||||
| -rw-r--r-- | module/HookManager.py | 7 | ||||
| -rw-r--r-- | module/config/default.conf | 7 | ||||
| -rw-r--r-- | module/lib/captchatrader.py | 123 | ||||
| -rw-r--r-- | module/network/HTTPRequest.py | 16 | ||||
| -rw-r--r-- | module/network/RequestFactory.py | 8 | ||||
| -rw-r--r-- | module/plugins/Hook.py | 14 | ||||
| -rw-r--r-- | module/plugins/Plugin.py | 75 | ||||
| -rw-r--r-- | module/plugins/hooks/CaptchaTrader.py | 131 | ||||
| -rw-r--r-- | module/web/pyload_app.py | 1 | ||||
| -rw-r--r-- | module/web/templates/jinja/default/settings.html | 3 | ||||
| -rw-r--r-- | module/web/webinterface.py | 2 | 
12 files changed, 272 insertions, 249 deletions
diff --git a/module/CaptchaManager.py b/module/CaptchaManager.py index d9e3748d7..99d29f37d 100644 --- a/module/CaptchaManager.py +++ b/module/CaptchaManager.py @@ -14,74 +14,91 @@      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 +    @author: mkaay, RaNaN  """ -from uuid import uuid4 as uuid +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 = [] -     -    def newTask(self, plugin): -        task = CaptchaTask(plugin, self) -        self.lock.acquire() -        self.tasks.append(task) -        self.lock.release() +        self.tasks = [] #task store, for outgoing tasks only + +        self.ids = 0 #only for internal purpose + +    def newTask(self, img, type, temp): +        task = CaptchaTask(self.ids, img, type, temp) +        self.ids += 1          return task -     +      def removeTask(self, task):          self.lock.acquire() -        self.tasks.remove(task) +        if task in self.tasks: +            self.tasks.remove(task)          self.lock.release() -     +      def getTask(self):          self.lock.acquire()          for task in self.tasks: -            status = task.getStatus() -            if status == "waiting" or status == "shared-user": +            if task.status in ("waiting", "shared-user"):                  self.lock.release()                  return task          self.lock.release()          return None -     +      def getTaskFromID(self, tid):          self.lock.acquire()          for task in self.tasks: -            if task.getID() == tid: +            if task.id == tid:                  self.lock.release()                  return task          self.lock.release()          return None +    def handleCaptcha(self, task): +        if self.core.isClientConnected: #client connected -> should solve the captcha +            self.tasks.append(task) +            task.setWaiting(40) #wait 40 sec for response +            return True + +        for plugin in self.core.hookManager.activePlugins(): +            try: +                plugin.newCaptchaTask(task) +            except: +                if self.core.debug: +                    print_exc() +             +            if task.handler: # a plugin handles the captcha +                return True + +        task.error = _("No Client connected for captcha decrypting") + +        return False + +  class CaptchaTask(): -    def __init__(self, plugin, manager): -        self.lock = Lock() -        self.plugin = plugin -        self.manager = manager -        self.captchaImg = None -        self.captchaType = None -        self.result = None -        self.status = "preparing" -        self.id = uuid().hex -     -    def setCaptcha(self, img, imgType): -        self.lock.acquire() +    def __init__(self, id, img, type, temp): +        self.id = str(id)          self.captchaImg = img -        self.captchaType = imgType -        self.lock.release() -     +        self.captchaType = type +        self.captchaFile = temp +        self.handler = None #the hook plugin 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.captchaType -     +      def setResult(self, result): -        self.lock.acquire()          self.result = result -        self.lock.release() -     +      def getResult(self):          try:              res = self.result.encode("utf8", "replace") @@ -89,33 +106,38 @@ class CaptchaTask():              res = self.result          return res -     -    def getID(self): -        return self.id -     +      def getStatus(self):          return self.status -     -    def setDone(self): -        self.lock.acquire() -        self.status = "done" -        self.lock.release() -     -    def setWaiting(self): -        self.lock.acquire() + +    def setWaiting(self, sec): +        """ let the captcha wait secs for the solution """ +        self.waitUntil = time() + sec          self.status = "waiting" -        self.lock.release() -     + +    def isWaiting(self): +        if self.result or self.error or time() > self.waitUntil: +            return False + +        return True +      def setWatingForUser(self, exclusive): -        self.lock.acquire()          if exclusive:              self.status = "user"          else:              self.status = "shared-user" -        self.lock.release() -     -    def removeTask(self): -        self.manager.removeTask(self) -     + +    def timedOut(self): +        return self.waitUntil >= time() + +    def invalid(self): +        """ indicates the captcha was not correct """ +        if self.handler: +            self.handler.captchaInvalid() + +    def correct(self): +        if self.handler: +            self.handler.captchaCorrect() +      def __str__(self): -        return "<CaptchaTask '%s'>" % (self.getID(),) +        return "<CaptchaTask '%s'>" % self.id diff --git a/module/HookManager.py b/module/HookManager.py index bc79b3c1c..6c6c0170f 100644 --- a/module/HookManager.py +++ b/module/HookManager.py @@ -20,6 +20,7 @@  import traceback  from threading import RLock +from operator import methodcaller  from module.PluginThread import HookThread  from time import time @@ -75,7 +76,7 @@ class HookManager():          def wrapPeriodical(plugin):              plugin.lastCall = time()              try: -                plugin.periodical() +                if plugin.isActivated(): plugin.periodical()              except Exception, e:                  self.core.log.error(_("Error executing hooks: %s") % str(e))                  if self.core.debug: @@ -143,3 +144,7 @@ class HookManager():      def startThread(self, function, pyfile):          t = HookThread(self.core.threadManager, function, pyfile) + +    def activePlugins(self): +        """ returns all active plugins """ +        return filter(methodcaller("isActivated"), self.plugins) diff --git a/module/config/default.conf b/module/config/default.conf index dfa58608b..18edd5a77 100644 --- a/module/config/default.conf +++ b/module/config/default.conf @@ -4,7 +4,7 @@ remote - "Remote":  	int port : "Port" = 7227
  	ip listenaddr : "Adress" = 0.0.0.0
  	str username : "Username" = admin
 -	str password : "Password" = pwhere
 +	password password : "Password" = pwhere
  ssl - "SSL":
  	bool activated : "Activated"= False
  	file cert : "SSL Certificate" = ssl.crt
 @@ -58,7 +58,4 @@ proxy - "Proxy":  	int port : "Port" = 7070
  	http;socks4;socks5 type : "Protocol" = http
  	str username : "Username" = None
 -	str password : "Password" = None
 -captchatrader - "CaptchaTrader":
 -    str username : "Username" = 
 -    str password : "Password" = 
 +	password password : "Password" = None
\ No newline at end of file diff --git a/module/lib/captchatrader.py b/module/lib/captchatrader.py deleted file mode 100644 index 50a2acbea..000000000 --- a/module/lib/captchatrader.py +++ /dev/null @@ -1,123 +0,0 @@ -#!/usr/bin/env python -# -*- 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 -""" - -try: -    from json import loads -except ImportError: -    from simplejson import loads - -from urllib2 import build_opener -from MultipartPostHandler import MultipartPostHandler - -PYLOAD_KEY = "9f65e7f381c3af2b076ea680ae96b0b7" - -opener = build_opener(MultipartPostHandler) - -class CaptchaTraderException(Exception): -    def __init__(self, err): -        self.err = err -     -    def getCode(self): -        return self.err -     -    def __str__(self): -        return "<CaptchaTraderException %s>" % self.err -     -    def __repr__(self): -        return "<CaptchaTraderException %s>" % self.err - -class CaptchaTrader(): -    SUBMIT_URL = "http://captchatrader.com/api/submit" -    RESPOND_URL = "http://captchatrader.com/api/respond" -    GETCREDITS_URL = "http://captchatrader.com/api/get_credits/username:%(user)s/password:%(password)s/" -     -    def __init__(self, user, password, api_key=PYLOAD_KEY): -        self.api_key = api_key -        self.user = user -        self.password = password -     -    def getCredits(self): -        json = opener.open(CaptchaTrader.GETCREDITS_URL % {"user":self.user, "password":self.password}).read() -        response = loads(json) -        if response[0] < 0: -            raise CaptchaTraderException(response[1]) -        else: -            return response[1] -     -    def submit(self, captcha, captchaType="file", match=None): -        if not self.api_key: -            raise CaptchaTraderException("No API Key Specified!") -        if type(captcha) == str and captchaType == "file": -            raise CaptchaTraderException("Invalid Type") -        assert captchaType in ("file", "url-jpg", "url-jpeg", "url-png", "url-bmp") -        json = opener.open(CaptchaTrader.SUBMIT_URL, data={"api_key":self.api_key, -                                                       "username":self.user, -                                                       "password":self.password, -                                                       "value":captcha, -                                                       "type":captchaType}).read() -        response = loads(json) -        if response[0] < 0: -            raise CaptchaTraderException(response[1]) -         -        class Result(): -            def __init__(self, api, ticket, result): -                self.api = api -                self.ticket = ticket -                self.result = result -             -            def getTicketID(self): -                return self.ticket -             -            def getResult(self): -                return self.result -             -            def success(self): -                self.sendResponse(True) -             -            def fail(self): -                self.sendResponse(False) -             -            def sendResponse(self, success): -                self.api.respond(self.ticket, success) -         -        return Result(self, response[0], response[1]) -     -    def respond(self, ticket, success): -        json = opener.open(CaptchaTrader.RESPOND_URL, data={"is_correct":1 if success else 0, -                                                       "username":self.user, -                                                       "password":self.password, -                                                       "ticket":ticket}).read() -        response = loads(json) -        if response[0] < 0: -            raise CaptchaTraderException(response[1]) - -if __name__ == "__main__": -    ct = CaptchaTrader("<user>", "<password>") -    print "credits", ct.getCredits() -     -    print "testing..." -     -    result = ct.submit(open("test_captcha.jpg", "rb")) -    print "result", result.getResult() -    if result.getResult() == "bettand trifting": -        result.success() -        print "captcha recognized" -    else: -        result.fail() -        print "captcha not recognized" diff --git a/module/network/HTTPRequest.py b/module/network/HTTPRequest.py index 13e76d5a2..bcd2c696c 100644 --- a/module/network/HTTPRequest.py +++ b/module/network/HTTPRequest.py @@ -111,7 +111,7 @@ class HTTPRequest():      def clearCookies(self):          self.c.setopt(pycurl.COOKIELIST, "") -    def setRequestContext(self, url, get, post, referer, cookies): +    def setRequestContext(self, url, get, post, referer, cookies, multipart=False):          """ sets everything needed for the request """          url = myquote(str(url)) @@ -124,9 +124,13 @@ class HTTPRequest():          self.c.lastUrl = url          if post: -            post = urlencode(post) -            self.c.setopt(pycurl.POSTFIELDS, post) - +            if not multipart: +                post = urlencode(post) +                self.c.setopt(pycurl.POSTFIELDS, post) +            else: +                post = [(x, str(quote(y)) if type(y) in (str, unicode) else y ) for x,y in post.iteritems()] +                self.c.setopt(pycurl.HTTPPOST, post) +              if referer and self.lastURL:              self.c.setopt(pycurl.REFERER, self.lastURL) @@ -136,10 +140,10 @@ class HTTPRequest():              self.getCookies() -    def load(self, url, get={}, post={}, referer=True, cookies=True, just_header=False): +    def load(self, url, get={}, post={}, referer=True, cookies=True, just_header=False, multipart=False):          """ load and returns a given page """ -        self.setRequestContext(url, get, post, referer, cookies) +        self.setRequestContext(url, get, post, referer, cookies, multipart)          self.header = "" diff --git a/module/network/RequestFactory.py b/module/network/RequestFactory.py index ec9ce4350..8340d06f7 100644 --- a/module/network/RequestFactory.py +++ b/module/network/RequestFactory.py @@ -49,9 +49,9 @@ class RequestFactory():          self.lock.release()          return req -    def getURL(self, url, get={}, post={}): +    def getURL(self, url, get={}, post={}, multipart=False):          h = HTTPRequest(None, self.iface(), self.getProxies()) -        rep = h.load(url, get, post) +        rep = h.load(url, get, post, multipart=multipart)          h.close()          return rep @@ -97,5 +97,5 @@ class RequestFactory():              self.bucket.setRate(self.core.config["download"]["max_speed"] * 1024)  # needs pyreq in global namespace -def getURL(url, get={}, post={}): -    return pyreq.getURL(url, get, post)
\ No newline at end of file +def getURL(*args, **kwargs): +    return pyreq.getURL(*args, **kwargs)
\ No newline at end of file diff --git a/module/plugins/Hook.py b/module/plugins/Hook.py index 1b3c05ba1..4bbf6e33a 100644 --- a/module/plugins/Hook.py +++ b/module/plugins/Hook.py @@ -19,7 +19,6 @@  """ -  class Hook():      __name__ = "Hook"      __version__ = "0.2" @@ -38,6 +37,9 @@ class Hook():          self.interval = 60          self.setup() + +    def __repr__(self): +        return self.__name__      def setup(self):          """ more init stuff if needed""" @@ -84,3 +86,13 @@ class Hook():      def unrarFinished(self, folder, fname):          pass + +    def newCaptchaTask(self, task): +        """ new captcha task for the plugin, it MUST set the handler and timeout or will be ignored """ +        pass + +    def captchaCorrect(self, task): +        pass + +    def captchaWrong(self, task): +        pass
\ No newline at end of file diff --git a/module/plugins/Plugin.py b/module/plugins/Plugin.py index 617e2e20a..3891cba94 100644 --- a/module/plugins/Plugin.py +++ b/module/plugins/Plugin.py @@ -29,7 +29,6 @@ from os import chmod  from os import stat  from os.path import exists  from os.path import join -from os.path import basename  if os.name != "nt":      from os import chown @@ -38,8 +37,6 @@ if os.name != "nt":  from itertools import islice -from thread import start_new_thread -  from module.utils import save_join  def chunks(iterable, size): @@ -110,7 +107,7 @@ class Plugin(object):          self.lastDownload = ""  # location where the last call to download was saved          self.lastCheck = None  #re match of last checked matched          self.js = self.core.js  # js engine -        self.ctresult = None +        self.cTask = None #captcha task          self.html = None #some plugins store html code here @@ -221,17 +218,15 @@ class Plugin(object):      def retry(self):          """ begin again from the beginning """ -        if self.ctresult: -            self.ctresult.fail()          raise Retry      def invalidCaptcha(self): -        if self.ctresult: -            self.ctresult.fail() +        if self.cTask: +            self.cTask.invalid()      def correctCaptcha(self): -        if self.ctresult: -            self.ctresult.success() +        if self.cTask: +            self.cTask.success()      def decryptCaptcha(self, url, get={}, post={}, cookies=False, forceUser=False, imgtype="jpg"):          """ loads the catpcha and decrypt it or ask the user for input """ @@ -260,52 +255,28 @@ class Plugin(object):          else:              captchaManager = self.core.captchaManager -            task = captchaManager.newTask(self) -            task.setCaptcha(content, imgtype) -            task.setWaiting() -             -            ct = None -            if self.core.config["captchatrader"]["username"] and self.core.config["captchatrader"]["password"]: -                task.setWatingForUser(exclusive=True) -                from module.lib.captchatrader import CaptchaTrader -                ct = CaptchaTrader(self.core.config["captchatrader"]["username"], self.core.config["captchatrader"]["password"]) -                if ct.getCredits < 10: -                    self.log.info("Not enough credits for CaptchaTrader") -                    task.setWaiting() -                else: -                    self.log.info("Submitting to CaptchaTrader") -                    def threaded(ct): -                        cf = open(join("tmp","tmpCaptcha_%s_%s.%s" % (self.__name__, id, imgtype)), "rb") -                        try: -                            result = ct.submit(cf) -                        except: -                            self.log.warning("CaptchaTrader error!") -                            if self.core.debug: -                                from traceback import print_exc -                                print_exc() -                            ct = None -                            task.setWaiting() -                        else: -                            self.ctresult = result -                            task.setResult(result.getResult()) -                            self.log.debug("CaptchaTrader response: %s" % result.getResult()) -                            task.setDone() -                    start_new_thread(threaded, (ct, )) +            task = captchaManager.newTask(content, imgtype, temp.name) +            self.cTask = task +            captchaManager.handleCaptcha(task) -            while not task.getStatus() == "done": -                if not self.core.isClientConnected(): -                    task.removeTask() -                    #temp.unlink(temp.name) -                    if has_plugin and not ct: -                        self.fail(_("Pil and tesseract not installed and no Client connected for captcha decrypting")) -                    elif not ct: -                        self.fail(_("No Client connected for captcha decrypting")) +            while task.isWaiting():                  if self.pyfile.abort: -                    task.removeTask() +                    captchaManager.removeTask(task)                      raise Abort                  sleep(1) -            result = task.getResult() -            task.removeTask() + +            captchaManager.removeTask(task) + +            if task.error and has_plugin: #ignore default error message since the user could use OCR +                self.fail(_("Pil and tesseract not installed and no Client connected for captcha decrypting")) +            elif task.error: +                self.fail(task.error) +            elif not task.result: +                self.fail(_("No captcha result obtained in appropiate time by any of the plugins.")) + + +            result = task.result +            self.log.debug("Received captcha result: %s" % result)          if not self.core.debug:            try: diff --git a/module/plugins/hooks/CaptchaTrader.py b/module/plugins/hooks/CaptchaTrader.py new file mode 100644 index 000000000..9bb8dd2f0 --- /dev/null +++ b/module/plugins/hooks/CaptchaTrader.py @@ -0,0 +1,131 @@ +# -*- 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 +""" + +try: +    from json import loads +except ImportError: +    from simplejson import loads + +from thread import start_new_thread +from pycurl import FORM_FILE + +from module.network.RequestFactory import getURL + +from module.plugins.Hook import Hook + +PYLOAD_KEY = "9f65e7f381c3af2b076ea680ae96b0b7" + +class CaptchaTraderException(Exception): +    def __init__(self, err): +        self.err = err + +    def getCode(self): +        return self.err + +    def __str__(self): +        return "<CaptchaTraderException %s>" % self.err + +    def __repr__(self): +        return "<CaptchaTraderException %s>" % self.err + +class CaptchaTrader(Hook): +    __name__ = "CaptchaTrader" +    __version__ = "0.1" +    __description__ = """send captchas to captchatrader.com""" +    __config__ = [("activated", "bool", "Activated", "True"), +                  ("username", "str", "Username", ""), +                  ("passkey", "password", "Password", "")] +    __author_name__ = ("RaNaN") +    __author_mail__ = ("RaNaN@pyload.org") + +    SUBMIT_URL = "http://captchatrader.com/api/submit" +    RESPOND_URL = "http://captchatrader.com/api/respond" +    GETCREDITS_URL = "http://captchatrader.com/api/get_credits/username:%(user)s/password:%(password)s/" + + +    def getCredits(self): +        json = getURL(CaptchaTrader.GETCREDITS_URL % {"user": self.getConfig("username"), +                                                           "password": self.getConfig("passkey")}) +        response = loads(json) +        if response[0] < 0: +            raise CaptchaTraderException(response[1]) +        else: +            self.log.debug("CaptchaTrader: %s credits left" % response[1]) +            return response[1] + +    def submit(self, captcha, captchaType="file", match=None): +        if not PYLOAD_KEY: +            raise CaptchaTraderException("No API Key Specified!") + +        #if type(captcha) == str and captchaType == "file": +        #    raise CaptchaTraderException("Invalid Type") +        assert captchaType in ("file", "url-jpg", "url-jpeg", "url-png", "url-bmp") +        json = getURL(CaptchaTrader.SUBMIT_URL, post={"api_key": PYLOAD_KEY, +                                                           "username": self.getConfig("username"), +                                                           "password": self.getConfig("passkey"), +                                                           "value": (FORM_FILE, captcha), +                                                           "type": captchaType}, multipart=True) +        response = loads(json) +        if response[0] < 0: +            raise CaptchaTraderException(response[1]) + +        ticket = response[0] +        result = response[1] +        self.log.debug("CaptchaTrader result %s : %s" % (ticket,result)) + +        return ticket, result + +    def respond(self, ticket, success): +        json = getURL(CaptchaTrader.RESPOND_URL, post={"is_correct": 1 if success else 0, +                                                            "username": self.getConfig("username"), +                                                            "password": self.getConfig("passkey"), +                                                            "ticket": ticket}) +        response = loads(json) +        if response[0] < 0: +            raise CaptchaTraderException(response[1]) + +    def newCaptchaTask(self, task): +        if not self.getConfig("username") or not self.getConfig("passkey"): +            return False + +        if self.getCredits() > 10: + +            task.handler = self +            task.setWaiting(40) +            start_new_thread(self.processCaptcha, (task,)) + +        else: +            self.log.info(_("Your CaptchaTrader Account has not enough credits")) + +    def captchaCorrect(self, task): +        ticket = task.data["ticket"] +        self.respond(ticket, True) + +    def captchaWrong(self, task): +        ticket = task.data["ticket"] +        self.respond(ticket, True) + +    def processCaptcha(self, task): +        c = task.captchaFile +        try: +            ticket, result = self.submit(c) +        except CaptchaTraderException, e: +            task.error = e.getCode() + +        task.data["ticket"] = ticket +        task.setResult(result) diff --git a/module/web/pyload_app.py b/module/web/pyload_app.py index 70686532c..2778566e1 100644 --- a/module/web/pyload_app.py +++ b/module/web/pyload_app.py @@ -400,6 +400,7 @@ def path(file="", path=""):      return render_to_response('pathchooser.html', {'cwd': cwd, 'files': files, 'parentdir': parentdir, 'type': type, 'oldfile': oldfile, 'absolute': abs}, [])  @route("/logs") +@route("/logs", method="POST")  @route("/logs/:item")  @route("/logs/:item", method="POST")  @login_required('can_see_logs') diff --git a/module/web/templates/jinja/default/settings.html b/module/web/templates/jinja/default/settings.html index 18bc78e30..68c480ec2 100644 --- a/module/web/templates/jinja/default/settings.html +++ b/module/web/templates/jinja/default/settings.html @@ -127,6 +127,9 @@                                                  <input name="browsebutton" type="button"                                                         onclick="ifield = document.getElementById('{{skey}}|{{okey}}'); filechooser = window.open('{% if option.value %}{{ "/filechooser/" + option.value|quotepath }}{% else %}{{ fileroot }}{% endif %}', 'filechooser', 'scrollbars=yes,toolbar=no,menubar=no,statusbar=no,width=650,height=300'); filechooser.ifield = ifield; window.ifield = ifield;"                                                         value="{{_("Browse")}}"/> +                                            {% elif option.type == "password" %} +                                                <input id="{{skey}}|{{okey}}" name="{{configname}}|{{skey}}|{{okey}}" +                                                       type="password" value="{{option.value}}"/>                                              {% else %}                                                  <input id="{{skey}}|{{okey}}" name="{{configname}}|{{skey}}|{{okey}}"                                                         type="text" value="{{option.value}}"/> diff --git a/module/web/webinterface.py b/module/web/webinterface.py index fe59c57b1..15948666e 100644 --- a/module/web/webinterface.py +++ b/module/web/webinterface.py @@ -113,7 +113,7 @@ from beaker.middleware import SessionMiddleware  session_opts = {      'session.type': 'file', -    'session.cookie_expires': -1, +   # 'session.cookie_expires': -1,      'session.data_dir': './tmp',      'session.auto': False  }  | 
