diff options
| -rw-r--r-- | locale/de/LC_MESSAGES/setup.mo | bin | 0 -> 356 bytes | |||
| -rw-r--r-- | module/XMLConfigParser.py | 18 | ||||
| -rw-r--r-- | module/config/core_default.xml | 2 | ||||
| -rw-r--r-- | module/setup.py | 370 | ||||
| -rw-r--r-- | module/web/ServerThread.py | 27 | ||||
| -rwxr-xr-x | pyLoadCore.py | 80 | 
6 files changed, 462 insertions, 35 deletions
| diff --git a/locale/de/LC_MESSAGES/setup.mo b/locale/de/LC_MESSAGES/setup.moBinary files differ new file mode 100644 index 000000000..2c20cfbd4 --- /dev/null +++ b/locale/de/LC_MESSAGES/setup.mo diff --git a/module/XMLConfigParser.py b/module/XMLConfigParser.py index 0aede384c..87b863d28 100644 --- a/module/XMLConfigParser.py +++ b/module/XMLConfigParser.py @@ -20,7 +20,7 @@ from __future__ import with_statement  from os.path import exists  from xml.dom.minidom import parse - +import re  from shutil import copy  class XMLConfigParser(): @@ -42,7 +42,7 @@ class XMLConfigParser():          file = self.file          if self.forceDefault:              file = self.file_default -        if not exists(self.file) and self.forceDefault: +        if not exists(self.file) or self.forceDefault:              self._copyConfig()          with open(file, 'r') as fh:              self.xml = parse(fh) @@ -117,7 +117,10 @@ class XMLConfigParser():      def getConfigDict(self):          return self.config -         + +    def getDataDict(self): +        return self.data +      def set(self, section, data, value):          root = self.root          replace = False @@ -213,7 +216,6 @@ class XMLConfigParser():      def checkInput(self, section, option, value):          oinput = self.getInputValues(section, option) -        print oinput          if oinput:              for i in oinput:                  if i == value: @@ -222,13 +224,13 @@ class XMLConfigParser():          otype = self.getType(section, option)          if not otype:              return True -        if otype == "int" and (type(value) == int or re.match("^\d+$")): +        if otype == "int" and (type(value) == int or re.match("^\d+$", value)):              return True -        elif otype == "bool" and (type(value) == bool or re.match("^(true|false|True|False)$")): +        elif otype == "bool" and (type(value) == bool or re.match("^(true|false|True|False)$", value)):              return True -        elif otype == "time" and re.match("^[0-2]{0,1}\d:[0-5]{0,1}\d$"): +        elif otype == "time" and re.match("^[0-2]{0,1}\d:[0-5]{0,1}\d$", value):              return True -        elif otype == "ip" and re.match("^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"): +        elif otype == "ip" and re.match("^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$", value):              return True          elif otype == "str":              return True diff --git a/module/config/core_default.xml b/module/config/core_default.xml index 4925c06cf..d1c059060 100644 --- a/module/config/core_default.xml +++ b/module/config/core_default.xml @@ -31,7 +31,7 @@          <log_count type="int" name="Count">5</log_count>      </log>      <general name="General"> -        <language type="str" input="en;de;fr" name="Language">de</language> +        <language type="str" input="en;de;fr;nl;pl" name="Language">de</language>          <download_folder type="str" name="Download Folder">Downloads</download_folder>          <max_downloads type="int" name="Max Parallel Downloads">3</max_downloads>          <link_file type="str" name="File For Links">links.txt</link_file> diff --git a/module/setup.py b/module/setup.py new file mode 100644 index 000000000..a5d4db118 --- /dev/null +++ b/module/setup.py @@ -0,0 +1,370 @@ +#!/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: RaNaN +""" +from getpass import getpass +import gettext +from hashlib import sha1 +from os import remove +from os.path import abspath +from os.path import dirname +from os.path import isfile +from os.path import join +import random +import re +from subprocess import PIPE +from subprocess import call +import sys + +class Setup(): +    """ +    pyLoads initial setup configuration assistent +    """ +    def __init__(self, path, config): + +        self.path = path +        self.config = config + + +    def start(self): +         +        lang = self.ask("Choose your Language / Wähle deine Sprache", "en", ["en", "de"]) +        translation = gettext.translation("setup", join(self.path, "locale"), languages=[lang]) +        translation.install(unicode=(True if  sys.getfilesystemencoding().startswith("utf") else False)) + +        print "" +        print _("Welcome to the pyLoad Configuration Assistent.") +        print _("It will check your system and make a basic setup in order to run pyLoad.") +        print "" +        print _("The value in brackets [] always is the default value,") +        print _("in case you don't want to change it or you are unsure what to choose, just hit enter.") +        print _("Don't forget: You can always rerun this assistent with --setup or -s parameter, when you start pyLoadCore.") +        print _("If you have any problems with this assistent hit STRG-C,") +        print _("to abort and don't let him start with pyLoadCore automatically anymore.") +        print "" +        print _("When you are ready for system check, hit enter.") +        raw_input() + +        basic, ssl, captcha, gui, web = self.system_check() +        print "" + +        if not basic: +            print _("You need pycurl and python 2.5 or 2.6 to run pyLoad.") +            print _("Please correct this and re run pyLoad.") +            print _("Setup will now close.") +            raw_input() +            return False + +        raw_input(_("System check finished, hit enter to see your status report.")) +        print "" +        print _("## Status ##") + +        print _("py-crypto available") if self.check_module("Crypto") else _("no py-crypto available") +        print _("You need this if you want to decrypt container files.") +        print "" +        print _("SSL available") if ssl else _("no SSL available") +        print _("This is needed if you want to establish a secure connection to core or webinterface.") +        print _("If you only want to access locally to pyLoad ssl is not usefull.") +        print "" +        print _("Captcha Recognition available") if captcha else _("no Captcha Recognition available") +        print _("Only needed for some hosters and as freeuser.") +        print "" +        print _("Gui available") if gui else _("Gui not available") +        print _("The Graphical User Interface.") +        print "" +        print _("Webinterface available") if web else _("no Webinterface available") +        print _("Gives abillity to control pyLoad with your webbrowser.") +        print "" +        print _("You can abort the setup now and fix some dependicies if you want.") + +        con = self.ask(_("Continue with setup?"), "y", bool=True) + +        if not con: +            return False + +        print "" +        print _("Do you want to configure basic settings?") +        print _("This is recommend for first run.") +        con = self.ask(_("Make basic setup?"), "y", bool=True) + +        if con: +            self.conf_basic() + +        if ssl: +            print "" +            print _("Do you want to configure ssl?") +            ssl = self.ask(_("Configure ssl?"), "n", bool=True) +            if ssl: +                self.conf_ssl() + +        if web: +            print "" +            print _("Do you want to configure webinterface?") +            web = self.ask(_("Configure webinterface?"), "y", bool=True) +            if web: +                self.conf_web() + +        print "" +        print _("Setup finished successfully.") +        return True + +    def system_check(self): +        """ make a systemcheck and return the results""" +        print _("## System Check ##") + +        python = False + +        if sys.version_info > (2, 7): +            print _("Your python version is to new, Please use Python 2.6") +            python = False +        elif sys.version_info < (2, 5): +            print _("Your python version is to old, Please use at least Python 2.5") +            python = False +        else: +            print _("Python Version: OK") +            python = True + +        if not self.check_prog(["python", "-V"]): +            print _("Unable to execute the 'python' command") +            print _("Please add python to system path or create a symlink") +            python = False + +        curl = self.check_module("pycurl") +        self.print_dep("pycurl", curl) + +        crypto = self.check_module("Crypto") +        self.print_dep("pycrypto", crypto) + +        basic = python and curl + +        ssl = self.check_module("OpenSSL") +        self.print_dep("OpenSSL", ssl) + +        print "" + +        pil = self.check_module("Image") +        self.print_dep("py-imaging", pil) + +        tesser = self.check_prog(["tesseract", "-v"]) +        self.print_dep("tesseract", tesser) + +        gocr = self.check_prog(["gocr", "-h"]) +        self.print_dep("gocr", gocr) + +        captcha = pil and tesser + +        print "" + +        gui = self.check_module("PyQt4") +        self.print_dep("PyQt4", gui) + +        print "" + +        web = self.check_module("django") +        sqlite = self.check_module("sqlite3") + +        try: +            import django + +            if django.VERSION < (1, 1): +                print _("Your django version is to old, please upgrade to django 1.1") +                web = False +            elif django.VERSION > (1, 3): +                print _("Your django version is to new, please use django 1.2") +                web = False +        except: +            web = False + +        self.print_dep("django", web) +        self.print_dep("sqlite3", sqlite) +        web = web and sqlite + +        return (basic, ssl, captcha, gui, web) + +    def conf_basic(self): +        print "" +        print _("## Basic Setup ##") + +        print "" +        print _("The following logindata are only valid for CLI and GUI, but NOT for webinterface.") +        self.config["general"]["username"] = self.ask(_("Username"), "User") +        self.config["general"]["password"] = self.ask("", "", password=True) + +        print "" +        self.config["general"]["language"] = self.ask(_("Language"), "en", ["en", "de", "fr", "nl", "pl"]) +        self.config["general"]["download_Folder"] = self.ask(_("Downloadfolder"), "Downloads") +        self.config["general"]["max_downloads"] = self.ask(_("Max parallel downloads"), "3") +        print _("You should disable checksum proofing, if you have low hardware requirements.") +        self.config["general"]["checksum"] = self.ask(_("Proof checksum?"), "y", bool=True) + +        reconnect = self.ask(_("Use Reconnect?"), "n", bool=True) +        self.config["reconnect"]["activated"] = reconnect +        if reconnect: +            self.config["reconnect"]["method"] = self.ask(_("Reconnect script location"), "./reconnect.sh") + + +    def conf_web(self): +        print "" +        print _("## Webinterface Setup ##") + +        db_path = join(self.path, "module", "web", "pyload.db") +        is_db = isfile(db_path) +        db_setup = True + +        if is_db: +            print _("You already have a database for the webinterface.") +            db_setup = self.ask(_("Do you want to delete it and make a new one?"), "n", bool=True) + +        if db_setup: +            if is_db: remove(db_path) +            import sqlite3 + +             +            print "" +            call(["python", join(self.path, "module", "web", "manage.py"), "syncdb", "--noinput"]) +            print _("If you see no errors, your db should be fine and we're adding an user now.") +            username = self.ask(_("Username"), "User") +            call(['python', join(self.path, "module", "web", "manage.py"), 'createsuperuser', '--email=email@trash-mail.com', '--username=%s' % username, '--noinput']) + +            password = self.ask("", "", password=True) +            salt = reduce(lambda x, y: x + y, [str(random.randint(0, 9)) for i in range(0, 5)]) +            hash = sha1(salt + password) +            password = "sha1$%s$%s" % (salt, hash.hexdigest()) + +            conn = sqlite3.connect(db_path) +            c = conn.cursor() +            c.execute('UPDATE "main"."auth_user" SET "password"=? WHERE "username"=?', (password, username)) + +            conn.commit() +            c.close() + +        print "" +        self.config["webinterface"]["activated"] = self.ask(_("Activate webinterface?"), "y", bool=True) +        print "" +        print _("Listen address, if you use 127.0.0.1 or localhost, the webinterface will only accessible locally.") +        self.config["webinterface"]["host"] = self.ask(_("Address"), "0.0.0.0") +        self.config["webinterface"]["port"] = self.ask(_("Port"), "8000") +        #@TODO setup for additional webservers + +    def conf_ssl(self): +        print "" +        print _("## SSL Setup ##") +        print "" +        print _("Execute these commands from pyLoad folder to make ssl certificates:") +        print "" +        print "openssl genrsa - 1024 > ssl.key" +        print "openssl req -new -key ssl.key -out ssl.csr" +        print "openssl req -days 36500 -x509 -key ssl.key -in ssl.csr > ssl.crt " +        print "" +        print _("If you're done and everything went fine, you can activate ssl now.") + +        self.config["ssl"]["activated"] = self.ask(_("Activate SSL?"), "y", bool=True) + +    def set_user(self): + +        translation = gettext.translation("setup", join(self.path, "locale"), languages=[self.config["general"]["language"]]) +        translation.install(unicode=(True if  sys.getfilesystemencoding().startswith("utf") else False)) +   +        self.ask(_("Username"), "User") +        self.ask("", "", password=True) + +    def print_dep(self, name, value): +        """Print Status of dependency""" +        if value: +            print _("%s: OK" % name) +        else: +            print _("%s: missing" % name) + + +    def check_module(self, module): +        try: +            __import__(module) +            return True +        except: +            return False + +    def check_prog(self, command): +        pipe = PIPE +        try: +            call(command, stdout=pipe, stderr=pipe) +            return True +        except: +            return False + +    def ask(self, qst, default, answers=[], bool=False, password=False): +        """produce one line to asking for input""" +        if answers: +            info = "(" + +            for i, answer in enumerate(answers): +                info += (", " if i != 0 else "") + str((answer == default and "[%s]" % answer) or answer) + +            info += ")" +        elif bool: +            if default == "y": +                info = "([y]/n)" +            else: +                info = "(y/[n])" +        else: +            info = "[%s]" % default + +        if password: +            p1 = True +            p2 = False +            while p1 != p2: +                p1 = getpass(_("Password: ")) + +                if len(p1) < 4: +                    print _("Password to short. Use at least 4 symbols.") +                    continue + +                p2 = getpass(_("Password (again): ")) + +                if p1 == p2: +                    return p1 +                else: +                    print _("Passwords did not match.") + +        while True: +            input = raw_input(qst + " %s: " % info) + +            if input.strip() == "": +                input = default + +            if bool: +                if re.match(r"(y|yes|j|ja|true)", input.lower().strip()): +                    return True +                elif re.match(r"(n|no|nein|false)", input.lower().strip()): +                    return False +                else: +                    print _("Invalid Input") +                    continue + + +            if not answers: +                return input + +            else: +                if input in answers: +                    return input +                else: +                    print _("Invalid Input") + + +if __name__ == "__main__": +    test = Setup(join(abspath(dirname(__file__)), ".."), None) +    test.start() diff --git a/module/web/ServerThread.py b/module/web/ServerThread.py index 0fbb65dfa..718dd6136 100644 --- a/module/web/ServerThread.py +++ b/module/web/ServerThread.py @@ -6,7 +6,7 @@ from subprocess import PIPE  from subprocess import Popen  from subprocess import call  from sys import version_info -from sys import stdout +from cStringIO import StringIO  import threading  class WebServer(threading.Thread): @@ -23,10 +23,11 @@ class WebServer(threading.Thread):          host = self.pycore.config['webinterface']['host']          port = self.pycore.config['webinterface']['port']          path = join(self.pycore.path, "module", "web") +        out = StringIO()          if not exists(join(self.pycore.path, "module", "web", "pyload.db")):              print "########## IMPORTANT ###########" -            print "###        Database for Webinterface doesnt exitst, it will not be available." +            print "###        Database for Webinterface does not exitst, it will not be available."              print "###        Please run: python %s syncdb" % join(self.pycore.path, "module", "web", "manage.py")              print "###        You have to add at least one User, to gain access to webinterface: python %s createsuperuser" % join(self.pycore.path, "module", "web", "manage.py")              print "###        Dont forget to restart pyLoad if you are done." @@ -107,10 +108,10 @@ class WebServer(threading.Thread):              new_config.close()              command = ['python', join(self.pycore.path, "module", "web", "manage.py"), "runfcgi", "daemonize=false", "method=threaded", "host=127.0.0.1", "port=9295"] -            self.p = Popen(command, stderr=PIPE, stdin=PIPE, stdout=Output(stdout)) +            self.p = Popen(command, stderr=Output(out), stdin=PIPE, stdout=Output(out))              command2 = ['nginx', '-c', join(path, "servers", "nginx.conf"),] -            self.p2 = Popen(command2, stderr=PIPE, stdin=PIPE, stdout=Output(stdout)) +            self.p2 = Popen(command2, stderr=PIPE, stdin=PIPE, stdout=Output(out))          elif self.server == "lighttpd": @@ -139,21 +140,21 @@ class WebServer(threading.Thread):              new_config.close()              command = ['python', join(self.pycore.path, "module", "web", "manage.py"), "runfcgi", "daemonize=false", "method=threaded", "host=127.0.0.1", "port=9295"] -            self.p = Popen(command, stderr=PIPE, stdin=PIPE, stdout=Output(stdout)) +            self.p = Popen(command, stderr=Output(out), stdin=PIPE, stdout=Output(out))              command2 = ['lighttpd', '-D', '-f', join(path, "servers", "lighttpd.conf")] -            self.p2 = Popen(command2, stderr=PIPE, stdin=PIPE, stdout=Output(stdout)) +            self.p2 = Popen(command2, stderr=PIPE, stdin=PIPE, stdout=Output(out))          elif self.server == "builtin":              self.pycore.logger.info("Starting django builtin Webserver: %s:%s" % (host, port))              command = ['python', join(self.pycore.path, "module", "web", "run_server.py"), "%s:%s" % (host, port)] -            self.p = Popen(command, stderr=Output(stdout), stdin=PIPE, stdout=Output(stdout)) +            self.p = Popen(command, stderr=Output(out), stdin=Output(out), stdout=Output(out))          else:              #run fastcgi on port              command = ['python', join(self.pycore.path, "module", "web", "manage.py"), "runfcgi", "daemonize=false", "method=threaded", "host=127.0.0.1", "port=%s" % port] -            self.p = Popen(command, stderr=PIPE, stdin=PIPE, stdout=Output(stdout)) +            self.p = Popen(command, stderr=Output(out), stdin=PIPE, stdout=Output(out))      def quit(self): @@ -173,11 +174,15 @@ class WebServer(threading.Thread):          self.running = False  class Output: -     def __init__(self, stream): +    def __init__(self, stream):          self.stream = stream -     def write(self, data): # Do nothing + +    def fileno(self): +        return 1 + +    def write(self, data): # Do nothing          return None           #self.stream.write(data)           #self.stream.flush() -     def __getattr__(self, attr): +    def __getattr__(self, attr):          return getattr(self.stream, attr)
\ No newline at end of file diff --git a/pyLoadCore.py b/pyLoadCore.py index 5264ef1e1..7ef315989 100755 --- a/pyLoadCore.py +++ b/pyLoadCore.py @@ -20,6 +20,7 @@      @author: mkaay      @version: v0.3.2  """ +import os  CURRENT_VERSION = '0.3.2' @@ -76,9 +77,33 @@ class Core(object):      def __init__(self):          self.doDebug = False          self.arg_links = [] +        self.path = abspath(dirname(__file__)) +        chdir(self.path) + +        #check if no config exists, assume its first run +        if not exists(join(self.path, "module", "config", "core.xml")): +            print "No configuration file found." +            print "Startig Configuration Assistent" +            print "" +             +            from module.setup import Setup +            self.xmlconfig = XMLConfigParser(self.make_path("module", "config", "core.xml")) +            self.config = self.xmlconfig.getConfig() + +            s = Setup(self.path, self.config) +            try: +                result = s.start() +                if not result: +                    remove(join(self.path, "module", "config", "core.xml")) +            except Exception, e: +                print e +                remove(join(self.path, "module", "config", "core.xml")) + +            exit() +          if len(argv) > 1:              try: -                options, args = getopt(argv[1:], 'vca:hd', ["version", "clear", "add=", "help", "debug"]) +                options, args = getopt(argv[1:], 'vca:hdus', ["version", "clear", "add=", "help", "debug", "user", "setup"])                  for option, argument in options:                      if option in ("-v", "--version"):                          print "pyLoad", CURRENT_VERSION @@ -94,6 +119,21 @@ class Core(object):                          exit()                      elif option in ("-d", "--debug"):                          self.doDebug = True +                    elif option in ("-u", "--user"): +                        from module.setup import Setup +                        self.xmlconfig = XMLConfigParser(self.make_path("module", "config", "core.xml")) +                        self.config = self.xmlconfig.getConfig() +                        s = Setup(self.path, self.config) +                        s.set_user() +                        exit() +                    elif option in ("-s", "--setup"): +                        from module.setup import Setup +                        self.xmlconfig = XMLConfigParser(self.make_path("module", "config", "core.xml")) +                        self.config = self.xmlconfig.getConfig() +                        s = Setup(self.path, self.config) +                        s.start() +                        exit() +              except GetoptError:                  print 'Unknown Argument(s) "%s"' % " ".join(argv[1:])                  self.print_help() @@ -109,7 +149,9 @@ class Core(object):          print "  -v, --version", " " * 4, "Print version to terminal"          print "  -c, --clear", " " * 6, "Delete the saved linklist"          print "  -a, --add=<list>", " " * 1, "Add the specified links" +        print "  -u, --user", " " * 7, "Set new User and password"          print "  -d, --debug", " " * 6, "Enable debug mode" +        print "  -s, --setup", " " * 6, "Run Setup Assistent"          print "  -h, --help", " " * 7, "Display this help screen"          print "" @@ -128,8 +170,6 @@ class Core(object):      def start(self):          """ starts the machine""" -        self.path = abspath(dirname(__file__)) -        chdir(self.path)          try: signal.signal(signal.SIGQUIT, self.quit)          except: pass @@ -152,7 +192,7 @@ class Core(object):          self.do_kill = False          self.do_restart = False          translation = gettext.translation("pyLoad", self.make_path("locale"), languages=[self.config['general']['language']]) -        translation.install(unicode=(True if sys.stdout.encoding.lower().startswith("utf") else False)) +        translation.install(unicode=(True if sys.getfilesystemencoding().lower().startswith("utf") else False))          self.check_install("Crypto", _("pycrypto to decode container files"))          self.check_install("Image", _("Python Image Libary (PIL) for captha reading")) @@ -226,6 +266,8 @@ class Core(object):          self.logger.info(_("Free space: %sMB") % self.freeSpace())          self.thread_list.pause = False +        print self.server_methods.get_config_data() +          while True:              sleep(2)              if self.do_restart: @@ -376,15 +418,18 @@ class Core(object):      def shutdown(self):          self.logger.info(_("shutting down...")) -        if self.config['webinterface']['activated']: -            self.webserver.quit() -            self.webserver.join() -        for thread in self.thread_list.threads: -            thread.shutdown = True -        self.thread_list.stopAllDownloads() -        for thread in self.thread_list.threads: -            thread.join(15) -        self.file_list.save() +        try: +            if self.config['webinterface']['activated']: +                self.webserver.quit() +                self.webserver.join() +            for thread in self.thread_list.threads: +                thread.shutdown = True +            self.thread_list.stopAllDownloads() +            for thread in self.thread_list.threads: +                thread.join(10) +            self.file_list.save() +        except: +            self.logger.info(_("error when shutting down"))      def check_update(self):          try: @@ -477,6 +522,12 @@ class ServerMethods():          del d["remote"]["username"]          del d["remote"]["password"]          return d +     +    def get_config_data(self): +        d = self.core.xmlconfig.getDataDict() +        del d["remote"]["options"]["username"] +        del d["remote"]["options"]["password"] +        return d      def pause_server(self):          self.core.thread_list.pause = True @@ -696,5 +747,4 @@ if __name__ == "__main__":      except KeyboardInterrupt:          pyload_core.shutdown()          pyload_core.logger.info(_("killed pyLoad from Terminal")) -        import os -        os._exit(1) +        _exit(1) | 
