# -*- coding: utf-8 -*- # @author: vuolter from __future__ import with_statement import __builtin__ import getpass import os import subprocess import sys from pyload.network.JsEngine import JsEngine from pyload.utils import get_console_encoding, load_translation, fs_join, versiontuple class SetupAssistant(object): """ pyLoads initial setup configuration assistant """ def __init__(self, config): self.config = config self.lang = "en" self.stdin_encoding = get_console_encoding(sys.stdin.encoding) # Input shorthand for yes self.yes = "y" # Input shorthand for no self.no = "n" def start(self): print langs = sorted(self.config.getMetaData("general", "language")['type'].split(";")) self.lang = self.ask(u"Choose setup language", "en", langs) load_translation("setup", self.lang) # Input shorthand for yes self.yes = _("y") # Input shorthand for no self.no = _("n") # print # print _("Would you like to configure pyLoad via Webinterface?") # print _("You need a Browser and a connection to this PC for it.") # viaweb = self.ask(_("Start initial webinterface for configuration?"), "y", bool=True) # ... print print print _("## Welcome to the pyLoad Configuration Assistant ##") print 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 assistant with --setup or -s parameter, when you start pyload.py .") print _("If you have any problems with this assistant hit STRG-C,") print _("to abort and don't let him start with pyload.py automatically anymore.") print print raw_input(_("When you are ready for system check, hit enter.")) print print basic, ssl, captcha, web, js = self.system_check() print print if not basic: print _("You need pycurl, sqlite and python 2.5, 2.6 or 2.7 to run pyLoad.") print _("Please correct this and re-run pyLoad.") print print _("Setup will now close.") print print raw_input(_("Press Enter to exit.")) return False raw_input(_("System check finished, hit enter to see your status report.")) print print print _("## Status ##") print avail = [] if self.check_module("Crypto"): avail.append(_("- container decrypting")) if ssl: avail.append(_("- ssl connection")) if captcha: avail.append(_("- automatic captcha decryption")) if web: avail.append(_("- webinterface")) if js: avail.append(_("- extended Click'N'Load")) if avail: print _("AVAILABLE FEATURES:") for feature in avail: print feature print if len(avail) < 5: print _("MISSING FEATURES:") if not self.check_module("Crypto"): print _("- no py-crypto available") print _("You need this if you want to decrypt container files.") print if not ssl: print _("- 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 if not captcha: print _("- no Captcha Recognition available") print _("Only needed for some hosters and as freeuser.") print if not js: print _("- no JavaScript engine found") print _("You will need this for some Click'N'Load links. Install Spidermonkey, ossp-js, pyv8 or rhino") print print print _("You can abort the setup now and fix some dependicies if you want.") else: print _("NO MISSING FEATURES!") print print con = self.ask(_("Continue with setup?"), self.yes, bool=True) if not con: return False print print print _("CURRENT CONFIG PATH: %s") % configdir print print _("NOTE: If you use pyLoad on a server or the home partition lives on an iternal flash it may be a good idea to change it.") confpath = self.ask(_("Do you want to change the config path?"), self.no, bool=True) if confpath: print self.conf_path() print print print _("Do you want to configure login data and basic settings?") print _("This is recommend for first run.") con = self.ask(_("Make basic setup?"), self.yes, bool=True) if con: print print self.conf_basic() if ssl: print print _("Do you want to configure ssl?") ssl = self.ask(_("Configure ssl?"), self.no, bool=True) if ssl: print print self.conf_ssl() if web: print print _("Do you want to configure webinterface?") web = self.ask(_("Configure webinterface?"), self.yes, bool=True) if web: print print self.conf_web() print print print _("Setup finished successfully!") print print raw_input(_("Hit enter to exit and restart pyLoad.")) return True def system_check(self): """ make a systemcheck and return the results """ import platform print _("## System Information ##") print print _("Platform: ") + platform.platform(aliased=True) print _("OS: ") + platform.system() or "Unknown" print _("Python: ") + sys.version.replace("\n", "") print print print _("## System Check ##") print if (2, 5) > sys.version_info > (2, 7): python = False else: python = True self.print_dep("python", python, false="NOT OK") curl = self.check_module("pycurl") self.print_dep("pycurl", curl) sqlite = self.check_module("sqlite3") self.print_dep("sqlite3", sqlite) basic = python and curl and sqlite print crypto = self.check_module("Crypto") self.print_dep("pycrypto", crypto) ssl = self.check_module("OpenSSL") self.print_dep("py-OpenSSL", ssl) print pil = self.check_module("Image") self.print_dep("py-imaging", pil) if os.name == "nt": tesser = self.check_prog([path.join(pypath, "tesseract", "tesseract.exe"), "-v"]) else: tesser = self.check_prog(["tesseract", "-v"]) self.print_dep("tesseract", tesser) captcha = pil and tesser print try: import jinja2 v = jinja2.__version__ if v and versiontuple(v) < (2, 5, 0): jinja = False else: jinja = True except Exception: jinja = False jinja_error = "MISSING" else: jinja_error = "NOT OK" self.print_dep("jinja2", jinja, false=jinja_error) beaker = self.check_module("beaker") self.print_dep("beaker", beaker) bjoern = self.check_module("bjoern") self.print_dep("bjoern", bjoern) web = sqlite and beaker js = bool(JsEngine.find()) self.print_dep(_("JS engine"), js) if not python: print print if sys.version_info > (2, 7): print _("WARNING: Your python version is too NEW!") print _("Please use Python version 2.6/2.7 .") else: print _("WARNING: Your python version is too OLD!") print _("Please use at least Python version 2.5 .") if not jinja and jinja_error == "NOT OK": print print print _("WARNING: Your installed jinja2 version %s is too OLD!") % jinja2.__version__ print _("You can safely continue but if the webinterface is not working,") print _("please upgrade or uninstall it, because pyLoad self-includes jinja2 libary.") return basic, ssl, captcha, web, js def conf_basic(self): print _("## Basic Setup ##") print print _("The following logindata is valid for CLI and webinterface.") from pyload.Database import DatabaseBackend db = DatabaseBackend(None) db.setup() print _("NOTE: Consider a password of 10 or more symbols if you expect to access to your local network from outside (ex. internet).") print username = self.ask(_("Username"), "User") password = self.ask("", "", password=True) db.addUser(username, password) db.shutdown() print print print _("External clients (GUI, CLI or other) need remote access to work over the network.") print _("However, if you only want to use the webinterface you may disable it to save ram.") self.config.set("remote", "activated", self.ask(_("Enable remote access"), self.no, bool=True)) print langs = sorted(self.config.getMetaData("general", "language")['type'].split(";")) self.config.set("general", "language", self.ask(_("Choose system language"), self.lang, langs)) print self.config.set("general", "download_folder", self.ask(_("Download folder"), "Downloads")) print self.config.set("download", "max_downloads", self.ask(_("Max parallel downloads"), "3")) print reconnect = self.ask(_("Use Reconnect?"), self.no, bool=True) self.config.set("reconnect", "activated", reconnect) if reconnect: self.config.set("reconnect", "method", self.ask(_("Reconnect script location"), "./reconnect.sh")) def conf_web(self): print _("## Webinterface Setup ##") print print _("Listen address, if you use 127.0.0.1 or localhost, the webinterface will only accessible locally.") self.config.set("webui", "host", self.ask(_("Address"), "0.0.0.0")) self.config.set("webui", "port", self.ask(_("Port"), "8000")) print print _("pyLoad offers several server backends, now following a short explanation.") print "- auto:", _("Automatically choose the best webserver for your platform.") print "- builtin:", _("First choice if you plan to use pyLoad just for you.") print "- threaded:", _("Support SSL connection and can serve simultaneously more client flawlessly.") print "- fastcgi:", _( "Can be used by apache, lighttpd, etc.; needs to be properly configured before.") if os.name != "nt": print "- lightweight:", _("Very fast alternative to builtin; requires libev and bjoern packages.") print print _("NOTE: In some rare cases the builtin server not works correctly, so if you have troubles with the web interface") print _("run this setup assistant again and change the builtin server to the threaded.") if os.name == "nt": servers = ["auto", "builtin", "threaded", "fastcgi"] else: servers = ["auto", "builtin", "threaded", "fastcgi", "lightweight"] self.config.set("webui", "server", self.ask(_("Choose webserver"), "auto", servers)) def conf_ssl(self): print _("## SSL Setup ##") print print _("Execute these commands from pyLoad config folder to make ssl certificates:") print print "openssl genrsa -out ssl.key 1024" 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.") ssl = self.ask(_("Activate SSL?"), self.yes, bool=True) self.config.set("remote", "ssl", ssl) self.config.set("webui", "ssl", ssl) def set_user(self): load_translation("setup", self.config.get("general", "language")) from pyload.Database import DatabaseBackend db = DatabaseBackend(None) db.setup() noaction = True try: while True: print _("Select action") print _("1 - Create/Edit user") print _("2 - List users") print _("3 - Remove user") print _("4 - Quit") action = raw_input("1/2/3/4: ") if not action in ("1", "2", "3", "4"): continue elif action == "1": print username = self.ask(_("Username"), "User") password = self.ask("", "", password=True) db.addUser(username, password) noaction = False elif action == "2": print print _("Users") print "-----" users = db.listUsers() noaction = False for user in users: print user print "-----" print elif action == "3": print username = self.ask(_("Username"), "") if username: db.removeUser(username) noaction = False elif action == "4": break finally: if not noaction: db.shutdown() def set_configdir(self, configdir, persistent=False): dirname = path.abspath(configdir) try: if not path.exists(dirname): os.makedirs(dirname, 0700) os.chdir(dirname) if persistent: c = path.join(rootdir, "config", "configdir") if not path.exists(c): os.makedirs(c, 0700) with open(c, "wb") as f: f.write(dirname) except IOError: return False else: __builtin__.configdir = dirname return dirname #: return always abspath def conf_path(self): print _("Setting new config path.") print _("NOTE: Current configuration will not be transfered!") while True: confdir = self.ask(_("CONFIG PATH"), configdir) confpath = self.set_configdir(confdir) print if not confpath: print _("Failed to change the current CONFIG PATH!") print else: print _("CONFIG PATH successfully changed to: %s") % configdir break def print_dep(self, name, value, false="MISSING", true="OK"): """ Print Status of dependency """ if value and isinstance(value, basestring): info = ", ".join(value) else: info = "" print "%(dep)-12s %(bool)s (%(info)s)" % {'dep': name + ':', 'bool': _(true if value else false).upper(), 'info': info} def check_module(self, module): try: __import__(module) return True except Exception: return False def check_prog(self, command): pipe = PIPE try: subprocess.call(command, stdout=pipe, stderr=pipe) return True except Exception: 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 == self.yes: info = "([%s]/%s)" % (self.yes, self.no) else: info = "(%s/[%s])" % (self.yes, self.no) else: info = "[%s]" % default if password: p1 = True p2 = False pwlen = 8 while p1 != p2: sys.stdout.write(_("Password: ")) p1 = getpass.getpass("").strip("\n\r") if len(p1) < pwlen: print print _("Password too short! Use at least %s symbols." % pwlen) print continue elif not p1.isalnum(): print print _("Password must be alphanumeric.") print continue sys.stdout.write(_("Password (again): ")) p2 = getpass.getpass("").strip("\n\r") if p1 == p2: print if self.ask(_("Show password?"), self.no, bool=True): print print _("Your Password is: %s") % p1 return p1 else: print print _("Passwords did not match.") while True: try: input = raw_input(qst + " %s: " % info) except KeyboardInterrupt: print "\nSetup interrupted" sys.exit() input = input.decode(self.stdin_encoding) if input.strip() == "": input = default if bool: # yes, true, t are inputs for booleans with value true if input.lower().strip() in [self.yes, _("yes"), _("true"), _("t"), "yes"]: return True # no, false, f are inputs for booleans with value false elif input.lower().strip() in [self.no, _("no"), _("false"), _("f"), "no"]: return False else: print _("Invalid Input") print continue if not answers or input in answers: return input else: print _("Invalid Input")