summaryrefslogtreecommitdiffstats
path: root/module/pyunrar.py
diff options
context:
space:
mode:
authorGravatar RaNaN <Mast3rRaNaN@hotmail.de> 2010-08-25 18:22:27 +0200
committerGravatar RaNaN <Mast3rRaNaN@hotmail.de> 2010-08-25 18:22:27 +0200
commit29f9dc8fb3396b03d732ebcbeb1cc8f00fe13897 (patch)
treef2a910cbea747a7b0c0a50d6c66691e54f5ef47f /module/pyunrar.py
parentmerged gui (diff)
downloadpyload-29f9dc8fb3396b03d732ebcbeb1cc8f00fe13897.tar.xz
new dirs
Diffstat (limited to 'module/pyunrar.py')
-rw-r--r--module/pyunrar.py370
1 files changed, 370 insertions, 0 deletions
diff --git a/module/pyunrar.py b/module/pyunrar.py
new file mode 100644
index 000000000..1d17486a4
--- /dev/null
+++ b/module/pyunrar.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: 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
+from os.path import join, abspath, basename, dirname
+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 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):
+ """
+ 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", "")
+ self.cmd = "unrar"
+ self.encrypted = None
+ self.headerEncrypted = None
+ self.smallestFiles = None
+ self.password = None
+
+ 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)
+ ret = p.wait()
+ if ret == 3:
+ self.headerEncrypted = True
+ raise WrongPasswordError()
+ elif ret == 0 and password:
+ self.headerEncrypted = False
+ o = p.stdout.read()
+ inList = False
+ infos = {}
+ nameLine = False
+ name = ""
+ for line in o.split("\n"):
+ 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)
+ for i, f in enumerate(files):
+ if f["size"] < smallest[1] or smallest[1] == -1:
+ smallest = (i, f["size"])
+ if smallest[0] == -1:
+ raise UnknownError()
+ self.smallestFiles = files[smallest[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)
+ (ret, out) = self.processOutput(p, statusFunction)
+ if ret == 3:
+ raise False
+ elif ret == 0:
+ 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)
+ (ret, out) = self.processOutput(p, statusFunction)
+ if ret == 3:
+ raise WrongPasswordError()
+ elif ret == 0:
+ 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
+ tdir = mkdtemp(prefix="rar")
+ 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)
+ pass
+ try:
+ 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 == 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 == "\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