From df6598f06c7c442371f95b1036ba8997abc490de Mon Sep 17 00:00:00 2001
From: mkaay <mkaay@mkaay.de>
Date: Tue, 1 Feb 2011 00:40:36 +0100
Subject: resolved #178

---
 module/lib/pyunrar.py         | 421 ++++++++++++++++++++++++++++++++++++++++++
 module/plugins/hooks/UnRar.py |  19 +-
 module/pyunrar.py             | 421 ------------------------------------------
 3 files changed, 434 insertions(+), 427 deletions(-)
 create mode 100644 module/lib/pyunrar.py
 delete mode 100644 module/pyunrar.py

diff --git a/module/lib/pyunrar.py b/module/lib/pyunrar.py
new file mode 100644
index 000000000..d873ef9bc
--- /dev/null
+++ b/module/lib/pyunrar.py
@@ -0,0 +1,421 @@
+#!/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 <mkaay@mkaay.de>
+"""
+
+from subprocess import Popen, PIPE
+import re
+from time import sleep
+from tempfile import mkdtemp
+from shutil import rmtree, move
+from shutil import Error as FileError
+import os
+from os.path import join, abspath, basename, dirname, exists
+from os import remove, makedirs
+
+EXITMAP = {
+    255: ("USER BREAK 	User stopped the process"),
+    9: ("CREATE ERROR", "Create file error"),
+    8: ("MEMORY ERROR", "Not enough memory for operation"),
+    7: ("USER ERROR", "Command line option error"),
+    6: ("OPEN ERROR", "Open file error"),
+    5: ("WRITE ERROR", "Write to disk error"),
+    4: ("LOCKED ARCHIVE", "Attempt to modify an archive previously locked by the 'k' command"),
+    3: ("CRC ERROR", "A CRC error occurred when unpacking"),
+    2: ("FATAL ERROR", "A fatal error occurred"),
+    1: ("WARNING", "Non fatal error(s) occurred"),
+    0: ("SUCCESS", "Successful operation (User exit)"),
+}
+
+class UnknownError(Exception):
+    pass
+
+class NoFilesError(Exception):
+    pass
+
+class WrongPasswordError(Exception):
+    pass
+
+class LowRamError(Exception):
+    pass
+
+class CommandError(Exception):
+    def __init__(self, ret=None, stdout=None, stderr=None):
+        self.ret = ret
+        self.stdout = stdout
+        self.stderr = stderr
+    
+    def __str__(self):
+        return "%s %s %s" % (EXITMAP[self.ret], self.stdout, self.stderr)
+    
+    def __repr__(self):
+        try:
+            return "<CommandError %s (%s)>" % (EXITMAP[self.ret][0], EXITMAP[self.ret][1])
+        except:
+            return "<CommandError>"
+    
+    def getExitCode(self):
+        return self.ret
+    
+    def getMappedExitCode(self):
+        return EXITMAP[self.ret]
+
+class Unrar():
+    def __init__(self, archive, tmpdir=None, ramSize=0, cpu=0):
+        """
+            archive should be be first or only part
+        """
+        self.archive = archive
+        self.pattern = None
+        m = re.match("^(.*).part(\d+).rar$", archive)
+        if m:
+            self.pattern = "%s.part*.rar" % m.group(1)
+        else: #old style
+            self.pattern = "%s.r*" % archive.replace(".rar", "")
+        if os.name == "nt":
+            self.cmd = join(pypath, "UnRAR.exe")
+        else:
+            self.cmd = "unrar"
+        self.encrypted = None
+        self.headerEncrypted = None
+        self.smallestFiles = None
+        self.biggestFiles = {"size" : 0}
+        self.password = None
+        if not tmpdir:
+            self.tmpdir = mkdtemp()
+        else:
+            self.tmpdir = tmpdir +"_" + basename(archive).replace(".rar", "").replace(".","")
+
+        self.ram = ramSize
+        self.cpu = cpu
+
+
+    def renice(self, p):
+        """ renice process """
+        if os.name != "nt" and self.cpu:
+            try:
+                Popen(["renice", str(self.cpu), str(p.pid)], stdout=PIPE, stderr=PIPE,bufsize=-1)
+            except:
+                print "Renice failed"
+
+    def listContent(self, password=None):
+        """
+            returns a list with all infos to the files in the archive
+            dict keys: name, version, method, crc, attributes, time, date, ratio, size_packed, size
+            @return list(dict, dict, ...)
+        """
+        f = self.archive
+        if self.pattern:
+            f = self.pattern
+        args = [self.cmd, "v"]
+        if password:
+            args.append("-p%s" % password)
+        else:
+            args.append("-p-")
+        args.append(f)
+        p = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE, bufsize=-1)
+        ret = p.wait() #@TODO blocks for big archives with many files
+        if ret == 3:
+            self.headerEncrypted = True
+            raise WrongPasswordError()
+        elif ret in (0,1) and password:
+            self.headerEncrypted = False
+        o = p.stdout.read()
+        inList = False
+        infos = {}
+        nameLine = False
+        name = ""
+        for line in o.splitlines():
+            if line == "-"*79:
+                inList = not inList
+                continue
+            if inList:
+                nameLine = not nameLine
+                if nameLine:
+                    name = line
+                    if name[0] == "*": #check for pw indicator
+                        name = name[1:]
+                        self.encrypted = True
+                    name = name.strip()
+                    continue
+                s = line.split(" ")
+                s = [e for e in s if e]
+                s.reverse()
+                d = {}
+                for k, v in zip(["version", "method", "crc", "attributes", "time", "date", "ratio", "size_packed", "size"], s[0:9]):
+                    d[k] = v
+                #if d["crc"] == "00000000" and len(d["method"]) == 2:
+                if re.search("d", d["attributes"].lower()): #directory
+                    continue
+                d["name"] = name
+                d["size_packed"] = int(d["size_packed"])
+                d["size"] = int(d["size"])
+                if infos.has_key(name):
+                    infos[name]["size_packed"] = infos[name]["size_packed"] + d["size_packed"]
+                    infos[name]["crc"].append(d["crc"])
+                else:
+                    infos[name] = d
+                    infos[name]["crc"] = [d["crc"]]
+        infos = infos.values()
+        return infos
+    
+    def listSimple(self, password=None):
+        """
+            return a list with full path to all files
+            @return list
+        """
+        l = self.listContent(password=password)
+        return [e["name"] for e in l]
+    
+    def getSmallestFile(self, password=None):
+        """
+            return the file info for the smallest file
+            @return dict
+        """
+        files = self.listContent(password=password)
+        smallest = (-1, -1)
+        biggest = (-1, -1)
+        for i, f in enumerate(files):
+            if f["size"] < smallest[1] or smallest[1] == -1:
+                smallest = (i, f["size"])
+            if f["size"] > biggest[1] or biggest[1] == -1:
+                biggest = (i, f["size"])
+        if smallest[0] == -1 or biggest[0] == -1:
+            raise UnknownError()
+        self.smallestFiles = files[smallest[0]]
+        self.biggestFiles = files[biggest[0]]
+        return files[smallest[0]]
+    
+    def needPassword(self):
+        """
+            do we need a password?
+            @return bool
+        """
+        if not self.smallestFiles:
+            try:
+                self.getSmallestFile()
+            except WrongPasswordError:
+                return True
+        return self.headerEncrypted or self.encrypted
+    
+    def checkPassword(self, password, statusFunction=None):
+        """
+            check if password is okay
+            @return bool
+        """
+        if not self.needPassword():
+            return True
+        f = self.archive
+        if self.pattern:
+            f = self.pattern
+        args = [self.cmd, "t", "-p%s" % password, f]
+        try:
+            args.append(self.getSmallestFile(password)["name"])
+        except WrongPasswordError:
+            return False
+        p = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE, bufsize=-1)
+        self.renice(p)
+        (ret, out) = self.processOutput(p, statusFunction)
+        if ret == 3:
+            raise False
+        elif ret in (0,1):
+            return True
+        else:
+            raise UnknownError()
+    
+    def extract(self, password=None, fullPath=True, files=[], exclude=[], destination=None, overwrite=False, statusFunction=None):
+        """
+            extract the archive
+            @return bool: extract okay?
+            raises WrongPasswordError or CommandError
+        """
+        f = self.archive
+        if self.pattern:
+            f = self.pattern
+        args = [self.cmd]
+        if fullPath:
+            args.append("x")
+        else:
+            args.append("e")
+        if not password:
+            password = "-"
+        if overwrite:
+            args.append("-o+")
+        else:
+            args.append("-o-")
+        args.append("-p%s" % password)
+        args.append(f)
+        if files:
+            args.extend([e for e in files])
+        if exclude:
+            args.extend(["-x%s" % e for e in exclude])
+        if destination:
+            args.append(destination)
+        p = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE, bufsize=-1)
+        self.renice(p)
+        (ret, out) = self.processOutput(p, statusFunction)
+        if ret == 3:
+            raise WrongPasswordError()
+        elif ret in (0,1):
+            return True
+        else:
+            raise CommandError(ret=ret, stdout=out, stderr=p.stderr.read())
+    
+    def crackPassword(self, passwords=[], fullPath=True, destination=None, overwrite=False, statusFunction=None, exclude=[]):
+        """
+            check password list until the right one is found and extract the archive
+            @return bool: password found?
+        """
+        correctPassword = None
+        if self.needPassword():
+            for password in passwords:
+                sf = []
+                try:
+                    sf.append(self.getSmallestFile(password)["name"])
+                except WrongPasswordError:
+                    continue
+
+                if self.ram:
+                    size = self.biggestFiles["size"] / 1024 ** 2
+                    if self.ram < 127 and size > 500:
+                        raise LowRamError
+                    elif self.ram < 256 and size > 1000:
+                        raise LowRamError
+                    elif self.ram < 512 and size > 5000:
+                        raise LowRamError
+
+                tdir = self.tmpdir
+                if not exists(tdir):
+                    makedirs(tdir)
+                try:
+                    self.extract(password=password, fullPath=fullPath, destination=tdir, overwrite=overwrite, statusFunction=statusFunction, files=sf)
+                except WrongPasswordError:
+                    continue
+                else:
+                    if not destination:
+                        destination = "."
+                    if overwrite:
+                        try:
+                            remove(abspath( join(destination, sf[0])))
+                        except OSError, e:
+                            if not e.errno == 2:
+                                raise e
+                    f = sf[0]
+                    d = destination
+                    if fullPath:
+                        try:
+                            makedirs(dirname(join(abspath(destination), sf[0])))
+                        except OSError, e:
+                            if not e.errno == 17:
+                                raise e
+                        d = join(destination, dirname(f))
+                    else:
+                        f = basename(f)
+                    try:
+                        move(join(tdir, f), abspath(d))
+                    except FileError:
+                        pass
+                    exclude.append(sf[0])
+                    correctPassword = password
+                    break
+                finally:
+                    rmtree(tdir)
+        try:
+
+            if self.ram:
+                size = self.biggestFiles["size"] / 1024 ** 2
+                if self.ram < 127 and size > 150:
+                    raise LowRamError
+                elif self.ram < 256 and size > 500:
+                    raise LowRamError
+                elif self.ram < 512 and size > 2000:
+                    raise LowRamError
+
+            self.extract(password=correctPassword, fullPath=fullPath, destination=destination, overwrite=overwrite, statusFunction=statusFunction, exclude=exclude)
+            self.password = correctPassword
+            return True
+        except WrongPasswordError:
+            return False
+    
+    def processOutput(self, p, statusFunction=None):
+        """
+            internal method
+            parse the progress output of the rar/unrar command
+            @return int: exitcode
+                    string: command output
+        """
+        ret = None
+        out = ""
+        tmp = None
+        count = 0
+        perc = 0
+        tperc = "0"
+        last = None
+        digits = "1 2 3 4 5 6 7 8 9 0".split(" ")
+        if not statusFunction:
+            statusFunction = lambda p: None
+        statusFunction(0)
+        while ret is None or tmp:
+            tmp = p.stdout.read(1)
+            if tmp:
+                out += tmp
+                if tmp == chr(8):
+                    if last == tmp:
+                        count += 1
+                        tperc = "0"
+                    else:
+                        count = 0
+                        if perc < int(tperc):
+                            perc = int(tperc)
+                            statusFunction(perc)
+                elif count >= 3:
+                    if tmp in ("\r","\n","\r\n"):
+                        count = 0
+                    elif tmp in digits:
+                        tperc += tmp
+                last = tmp
+            else:
+                sleep(0.01)
+            ret = p.poll()
+        statusFunction(100)
+        return ret, out
+    
+    def getPassword(self):
+        """
+            return the correct password
+            works only in conjunction with 'crackPassword'
+            @return string: password
+        """
+        return self.password
+
+if __name__ == "__main__":
+    from pprint import pprint
+    u = Unrar("archive.part1.rar", multi=True)
+    u = Unrar("parchive.part1.rar", multi=True)
+    pprint(u.listContent())
+    u = Unrar("pharchive.part1.rar", multi=True)
+    pprint(u.listContent(password="test"))
+    u = Unrar("bigarchive.rar")
+    pprint(u.listContent())
+    print u.getSmallestFile()
+    try:
+        def s(p):
+            print p
+        print u.crackPassword(passwords=["test1", "ggfd", "423r", "test"], destination=".", statusFunction=s, overwrite=True)
+    except CommandError, e:
+        print e
diff --git a/module/plugins/hooks/UnRar.py b/module/plugins/hooks/UnRar.py
index fa762a832..b1902f4ad 100644
--- a/module/plugins/hooks/UnRar.py
+++ b/module/plugins/hooks/UnRar.py
@@ -21,10 +21,10 @@ from __future__ import with_statement
 import sys
 
 from module.plugins.Hook import Hook
-from module.pyunrar import Unrar, WrongPasswordError, CommandError, UnknownError, LowRamError
+from module.lib.pyunrar import Unrar, WrongPasswordError, CommandError, UnknownError, LowRamError
 from traceback import print_exc
 
-from os.path import exists, join
+from os.path import exists, join, isabs
 from os import remove
 import re
 
@@ -38,7 +38,8 @@ class UnRar(Hook):
                    ("passwordfile", "str", "unrar password file", "unrar_passwords.txt"),
                    ("deletearchive", "bool", "delete archives when done", False),
                    ("ramwarning", "bool", "warn about low ram", True),
-                   ("renice", "int", "Cpu Priority", 10)]
+                   ("renice", "int", "Cpu Priority", 10),
+                   ("unrar_destination", "str", "Unpack files to", "")]
     __threaded__ = ["packageFinished"]
     __author_name__ = ("mkaay")
     __author_mail__ = ("mkaay@mkaay.de")
@@ -122,17 +123,23 @@ class UnRar(Hook):
             pyfile.progress.setRange(0, 100)
             def s(p):
                 pyfile.progress.setValue(p)
-                
-                
+            
             download_folder = self.core.config['general']['download_folder']
+            
             if self.core.config['general']['folder_per_package']:
                 folder = join(download_folder, pack.folder.decode(sys.getfilesystemencoding()))
             else:
                 folder = download_folder
+                
+            destination = None
+            if self.getConfig("unrar_destination") and not self.getConfig("unrar_destination").lower() == "none":
+                destination = self.getConfig("unrar_destination")
+                if not isabs(destination):
+                    destination = join(folder, destination)
             
             u = Unrar(join(folder, fname), tmpdir=join(folder, "tmp"), ramSize=(self.ram if self.getConfig("ramwarning") else 0), cpu=self.getConfig("renice"))
             try:
-                success = u.crackPassword(passwords=self.passwords, statusFunction=s, overwrite=True, destination=folder, fullPath=self.getConfig("fullpath"))
+                success = u.crackPassword(passwords=self.passwords, statusFunction=s, overwrite=True, destination=folder, fullPath=self.getConfig("fullpath"), destination=destination)
             except WrongPasswordError:
                 self.core.log.info(_("Unrar of %s failed (wrong password)") % fname)
                 continue
diff --git a/module/pyunrar.py b/module/pyunrar.py
deleted file mode 100644
index d873ef9bc..000000000
--- a/module/pyunrar.py
+++ /dev/null
@@ -1,421 +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 <mkaay@mkaay.de>
-"""
-
-from subprocess import Popen, PIPE
-import re
-from time import sleep
-from tempfile import mkdtemp
-from shutil import rmtree, move
-from shutil import Error as FileError
-import os
-from os.path import join, abspath, basename, dirname, exists
-from os import remove, makedirs
-
-EXITMAP = {
-    255: ("USER BREAK 	User stopped the process"),
-    9: ("CREATE ERROR", "Create file error"),
-    8: ("MEMORY ERROR", "Not enough memory for operation"),
-    7: ("USER ERROR", "Command line option error"),
-    6: ("OPEN ERROR", "Open file error"),
-    5: ("WRITE ERROR", "Write to disk error"),
-    4: ("LOCKED ARCHIVE", "Attempt to modify an archive previously locked by the 'k' command"),
-    3: ("CRC ERROR", "A CRC error occurred when unpacking"),
-    2: ("FATAL ERROR", "A fatal error occurred"),
-    1: ("WARNING", "Non fatal error(s) occurred"),
-    0: ("SUCCESS", "Successful operation (User exit)"),
-}
-
-class UnknownError(Exception):
-    pass
-
-class NoFilesError(Exception):
-    pass
-
-class WrongPasswordError(Exception):
-    pass
-
-class LowRamError(Exception):
-    pass
-
-class CommandError(Exception):
-    def __init__(self, ret=None, stdout=None, stderr=None):
-        self.ret = ret
-        self.stdout = stdout
-        self.stderr = stderr
-    
-    def __str__(self):
-        return "%s %s %s" % (EXITMAP[self.ret], self.stdout, self.stderr)
-    
-    def __repr__(self):
-        try:
-            return "<CommandError %s (%s)>" % (EXITMAP[self.ret][0], EXITMAP[self.ret][1])
-        except:
-            return "<CommandError>"
-    
-    def getExitCode(self):
-        return self.ret
-    
-    def getMappedExitCode(self):
-        return EXITMAP[self.ret]
-
-class Unrar():
-    def __init__(self, archive, tmpdir=None, ramSize=0, cpu=0):
-        """
-            archive should be be first or only part
-        """
-        self.archive = archive
-        self.pattern = None
-        m = re.match("^(.*).part(\d+).rar$", archive)
-        if m:
-            self.pattern = "%s.part*.rar" % m.group(1)
-        else: #old style
-            self.pattern = "%s.r*" % archive.replace(".rar", "")
-        if os.name == "nt":
-            self.cmd = join(pypath, "UnRAR.exe")
-        else:
-            self.cmd = "unrar"
-        self.encrypted = None
-        self.headerEncrypted = None
-        self.smallestFiles = None
-        self.biggestFiles = {"size" : 0}
-        self.password = None
-        if not tmpdir:
-            self.tmpdir = mkdtemp()
-        else:
-            self.tmpdir = tmpdir +"_" + basename(archive).replace(".rar", "").replace(".","")
-
-        self.ram = ramSize
-        self.cpu = cpu
-
-
-    def renice(self, p):
-        """ renice process """
-        if os.name != "nt" and self.cpu:
-            try:
-                Popen(["renice", str(self.cpu), str(p.pid)], stdout=PIPE, stderr=PIPE,bufsize=-1)
-            except:
-                print "Renice failed"
-
-    def listContent(self, password=None):
-        """
-            returns a list with all infos to the files in the archive
-            dict keys: name, version, method, crc, attributes, time, date, ratio, size_packed, size
-            @return list(dict, dict, ...)
-        """
-        f = self.archive
-        if self.pattern:
-            f = self.pattern
-        args = [self.cmd, "v"]
-        if password:
-            args.append("-p%s" % password)
-        else:
-            args.append("-p-")
-        args.append(f)
-        p = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE, bufsize=-1)
-        ret = p.wait() #@TODO blocks for big archives with many files
-        if ret == 3:
-            self.headerEncrypted = True
-            raise WrongPasswordError()
-        elif ret in (0,1) and password:
-            self.headerEncrypted = False
-        o = p.stdout.read()
-        inList = False
-        infos = {}
-        nameLine = False
-        name = ""
-        for line in o.splitlines():
-            if line == "-"*79:
-                inList = not inList
-                continue
-            if inList:
-                nameLine = not nameLine
-                if nameLine:
-                    name = line
-                    if name[0] == "*": #check for pw indicator
-                        name = name[1:]
-                        self.encrypted = True
-                    name = name.strip()
-                    continue
-                s = line.split(" ")
-                s = [e for e in s if e]
-                s.reverse()
-                d = {}
-                for k, v in zip(["version", "method", "crc", "attributes", "time", "date", "ratio", "size_packed", "size"], s[0:9]):
-                    d[k] = v
-                #if d["crc"] == "00000000" and len(d["method"]) == 2:
-                if re.search("d", d["attributes"].lower()): #directory
-                    continue
-                d["name"] = name
-                d["size_packed"] = int(d["size_packed"])
-                d["size"] = int(d["size"])
-                if infos.has_key(name):
-                    infos[name]["size_packed"] = infos[name]["size_packed"] + d["size_packed"]
-                    infos[name]["crc"].append(d["crc"])
-                else:
-                    infos[name] = d
-                    infos[name]["crc"] = [d["crc"]]
-        infos = infos.values()
-        return infos
-    
-    def listSimple(self, password=None):
-        """
-            return a list with full path to all files
-            @return list
-        """
-        l = self.listContent(password=password)
-        return [e["name"] for e in l]
-    
-    def getSmallestFile(self, password=None):
-        """
-            return the file info for the smallest file
-            @return dict
-        """
-        files = self.listContent(password=password)
-        smallest = (-1, -1)
-        biggest = (-1, -1)
-        for i, f in enumerate(files):
-            if f["size"] < smallest[1] or smallest[1] == -1:
-                smallest = (i, f["size"])
-            if f["size"] > biggest[1] or biggest[1] == -1:
-                biggest = (i, f["size"])
-        if smallest[0] == -1 or biggest[0] == -1:
-            raise UnknownError()
-        self.smallestFiles = files[smallest[0]]
-        self.biggestFiles = files[biggest[0]]
-        return files[smallest[0]]
-    
-    def needPassword(self):
-        """
-            do we need a password?
-            @return bool
-        """
-        if not self.smallestFiles:
-            try:
-                self.getSmallestFile()
-            except WrongPasswordError:
-                return True
-        return self.headerEncrypted or self.encrypted
-    
-    def checkPassword(self, password, statusFunction=None):
-        """
-            check if password is okay
-            @return bool
-        """
-        if not self.needPassword():
-            return True
-        f = self.archive
-        if self.pattern:
-            f = self.pattern
-        args = [self.cmd, "t", "-p%s" % password, f]
-        try:
-            args.append(self.getSmallestFile(password)["name"])
-        except WrongPasswordError:
-            return False
-        p = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE, bufsize=-1)
-        self.renice(p)
-        (ret, out) = self.processOutput(p, statusFunction)
-        if ret == 3:
-            raise False
-        elif ret in (0,1):
-            return True
-        else:
-            raise UnknownError()
-    
-    def extract(self, password=None, fullPath=True, files=[], exclude=[], destination=None, overwrite=False, statusFunction=None):
-        """
-            extract the archive
-            @return bool: extract okay?
-            raises WrongPasswordError or CommandError
-        """
-        f = self.archive
-        if self.pattern:
-            f = self.pattern
-        args = [self.cmd]
-        if fullPath:
-            args.append("x")
-        else:
-            args.append("e")
-        if not password:
-            password = "-"
-        if overwrite:
-            args.append("-o+")
-        else:
-            args.append("-o-")
-        args.append("-p%s" % password)
-        args.append(f)
-        if files:
-            args.extend([e for e in files])
-        if exclude:
-            args.extend(["-x%s" % e for e in exclude])
-        if destination:
-            args.append(destination)
-        p = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE, bufsize=-1)
-        self.renice(p)
-        (ret, out) = self.processOutput(p, statusFunction)
-        if ret == 3:
-            raise WrongPasswordError()
-        elif ret in (0,1):
-            return True
-        else:
-            raise CommandError(ret=ret, stdout=out, stderr=p.stderr.read())
-    
-    def crackPassword(self, passwords=[], fullPath=True, destination=None, overwrite=False, statusFunction=None, exclude=[]):
-        """
-            check password list until the right one is found and extract the archive
-            @return bool: password found?
-        """
-        correctPassword = None
-        if self.needPassword():
-            for password in passwords:
-                sf = []
-                try:
-                    sf.append(self.getSmallestFile(password)["name"])
-                except WrongPasswordError:
-                    continue
-
-                if self.ram:
-                    size = self.biggestFiles["size"] / 1024 ** 2
-                    if self.ram < 127 and size > 500:
-                        raise LowRamError
-                    elif self.ram < 256 and size > 1000:
-                        raise LowRamError
-                    elif self.ram < 512 and size > 5000:
-                        raise LowRamError
-
-                tdir = self.tmpdir
-                if not exists(tdir):
-                    makedirs(tdir)
-                try:
-                    self.extract(password=password, fullPath=fullPath, destination=tdir, overwrite=overwrite, statusFunction=statusFunction, files=sf)
-                except WrongPasswordError:
-                    continue
-                else:
-                    if not destination:
-                        destination = "."
-                    if overwrite:
-                        try:
-                            remove(abspath( join(destination, sf[0])))
-                        except OSError, e:
-                            if not e.errno == 2:
-                                raise e
-                    f = sf[0]
-                    d = destination
-                    if fullPath:
-                        try:
-                            makedirs(dirname(join(abspath(destination), sf[0])))
-                        except OSError, e:
-                            if not e.errno == 17:
-                                raise e
-                        d = join(destination, dirname(f))
-                    else:
-                        f = basename(f)
-                    try:
-                        move(join(tdir, f), abspath(d))
-                    except FileError:
-                        pass
-                    exclude.append(sf[0])
-                    correctPassword = password
-                    break
-                finally:
-                    rmtree(tdir)
-        try:
-
-            if self.ram:
-                size = self.biggestFiles["size"] / 1024 ** 2
-                if self.ram < 127 and size > 150:
-                    raise LowRamError
-                elif self.ram < 256 and size > 500:
-                    raise LowRamError
-                elif self.ram < 512 and size > 2000:
-                    raise LowRamError
-
-            self.extract(password=correctPassword, fullPath=fullPath, destination=destination, overwrite=overwrite, statusFunction=statusFunction, exclude=exclude)
-            self.password = correctPassword
-            return True
-        except WrongPasswordError:
-            return False
-    
-    def processOutput(self, p, statusFunction=None):
-        """
-            internal method
-            parse the progress output of the rar/unrar command
-            @return int: exitcode
-                    string: command output
-        """
-        ret = None
-        out = ""
-        tmp = None
-        count = 0
-        perc = 0
-        tperc = "0"
-        last = None
-        digits = "1 2 3 4 5 6 7 8 9 0".split(" ")
-        if not statusFunction:
-            statusFunction = lambda p: None
-        statusFunction(0)
-        while ret is None or tmp:
-            tmp = p.stdout.read(1)
-            if tmp:
-                out += tmp
-                if tmp == chr(8):
-                    if last == tmp:
-                        count += 1
-                        tperc = "0"
-                    else:
-                        count = 0
-                        if perc < int(tperc):
-                            perc = int(tperc)
-                            statusFunction(perc)
-                elif count >= 3:
-                    if tmp in ("\r","\n","\r\n"):
-                        count = 0
-                    elif tmp in digits:
-                        tperc += tmp
-                last = tmp
-            else:
-                sleep(0.01)
-            ret = p.poll()
-        statusFunction(100)
-        return ret, out
-    
-    def getPassword(self):
-        """
-            return the correct password
-            works only in conjunction with 'crackPassword'
-            @return string: password
-        """
-        return self.password
-
-if __name__ == "__main__":
-    from pprint import pprint
-    u = Unrar("archive.part1.rar", multi=True)
-    u = Unrar("parchive.part1.rar", multi=True)
-    pprint(u.listContent())
-    u = Unrar("pharchive.part1.rar", multi=True)
-    pprint(u.listContent(password="test"))
-    u = Unrar("bigarchive.rar")
-    pprint(u.listContent())
-    print u.getSmallestFile()
-    try:
-        def s(p):
-            print p
-        print u.crackPassword(passwords=["test1", "ggfd", "423r", "test"], destination=".", statusFunction=s, overwrite=True)
-    except CommandError, e:
-        print e
-- 
cgit v1.2.3