forked from pyload/pyload
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathUnRar.py
More file actions
253 lines (176 loc) · 6.76 KB
/
UnRar.py
File metadata and controls
253 lines (176 loc) · 6.76 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
# -*- coding: utf-8 -*-
import os
import re
from glob import glob
from os.path import basename, dirname, join
from string import digits
from subprocess import Popen, PIPE
from module.plugins.internal.Extractor import Extractor, ArchiveError, CRCError, PasswordError
from module.utils import save_join, decode
def renice(pid, value):
if os.name != "nt" and value:
try:
Popen(["renice", str(value), str(pid)], stdout=PIPE, stderr=PIPE, bufsize=-1)
except:
print "Renice failed"
class UnRar(Extractor):
__name__ = "UnRar"
__version__ = "1.01"
__description__ = """Rar extractor plugin"""
__license__ = "GPLv3"
__authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
CMD = "unrar"
EXTENSIONS = ["rar", "zip", "cab", "arj", "lzh", "tar", "gz", "bz2", "ace", "uue", "jar", "iso", "7z", "xz", "z"]
#@NOTE: there are some more uncovered rar formats
re_rarpart = re.compile(r'(.*)\.part(\d+)\.rar$', re.I)
re_rarfile = re.compile(r'.*\.(rar|r\d+)$', re.I)
re_filelist = re.compile(r'(.+)\s+(\d+)\s+(\d+)\s+|(.+)\s+(\d+)\s+\d\d-\d\d-\d\d\s+\d\d:\d\d\s+(.+)')
re_wrongpwd = re.compile(r'password', re.I)
re_wrongcrc = re.compile(r'encrypted|damaged|CRC failed|checksum error', re.I)
@classmethod
def checkDeps(cls):
if os.name == "nt":
cls.CMD = join(pypath, "UnRAR.exe")
p = Popen([cls.CMD], stdout=PIPE, stderr=PIPE)
p.communicate()
else:
try:
p = Popen([cls.CMD], stdout=PIPE, stderr=PIPE)
p.communicate()
except OSError:
# fallback to rar
cls.CMD = "rar"
p = Popen([cls.CMD], stdout=PIPE, stderr=PIPE)
p.communicate()
return True
@classmethod
def isArchive(cls, file):
f = basename(file).lower()
return any(f.endswith('.%s' % ext) for ext in cls.EXTENSIONS)
@classmethod
def getTargets(cls, files_ids):
targets = []
for file, id in files_ids:
if not cls.isArchive(file):
continue
m = cls.re_rarpart.findall(file)
if m:
# only add first parts
if int(m[0][1]) == 1:
targets.append((file, id))
else:
targets.append((file, id))
return targets
def check(self, out="", err=""):
if not out or not err:
return
if err.strip():
if self.re_wrongpwd.search(err):
raise PasswordError
elif self.re_wrongcrc.search(err):
raise CRCError
else: #: raise error if anything is on stderr
raise ArchiveError(err.strip())
# output only used to check if passworded files are present
for attr in self.re_filelist.findall(out):
if attr[0].startswith("*"):
raise PasswordError
def verify(self):
p = self.call_cmd("l", "-v", self.file, password=self.password)
self.check(*p.communicate())
if p and p.returncode:
raise ArchiveError("Process terminated")
if not self.list():
raise ArchiveError("Empty archive")
def isPassword(self, password):
if isinstance(password, basestring):
p = self.call_cmd("l", "-v", self.file, password=password)
out, err = p.communicate()
if not self.re_wrongpwd.search(err):
return True
return False
def repair(self):
p = self.call_cmd("rc", self.file)
out, err = p.communicate()
if p.returncode or err.strip():
p = self.call_cmd("r", self.file)
out, err = p.communicate()
if p.returncode or err.strip():
return False
else:
self.file = join(dirname(self.file), re.search(r'(fixed|rebuild)\.%s' % basename(self.file), out).group(0))
return True
def extract(self, progress=lambda x: None):
self.verify()
progress(0)
command = "x" if self.fullpath else "e"
p = self.call_cmd(command, self.file, self.out, password=self.password)
renice(p.pid, self.renice)
progressstring = ""
while True:
c = p.stdout.read(1)
# quit loop on eof
if not c:
break
# reading a percentage sign -> set progress and restart
if c is '%':
progress(int(progressstring))
progressstring = ""
# not reading a digit -> therefore restart
elif c not in digits:
progressstring = ""
# add digit to progressstring
else:
progressstring += c
progress(100)
self.files = self.list()
# retrieve stderr
self.check(err=p.stderr.read())
if p.returncode:
raise ArchiveError("Process terminated")
def getDeleteFiles(self):
if ".part" in basename(self.file):
return glob(re.sub("(?<=\.part)([01]+)", "*", self.file, re.I))
# get files which matches .r* and filter unsuited files out
parts = glob(re.sub(r"(?<=\.r)ar$", "*", self.file, re.I))
return filter(lambda x: self.re_rarfile.match(x), parts)
def list(self):
command = "vb" if self.fullpath else "lb"
p = self.call_cmd(command, "-v", self.file, password=self.password)
out, err = p.communicate()
if err.strip():
self.m.logError(err)
if "Cannot open" in err:
return list()
if p.returncode:
self.m.logError("Process terminated")
return list()
result = set()
for f in decode(out).splitlines():
f = f.strip()
result.add(save_join(self.out, f))
return list(result)
def call_cmd(self, command, *xargs, **kwargs):
args = []
# overwrite flag
if self.overwrite:
args.append("-o+")
else:
args.append("-o-")
if self.delete:
args.append("-or")
for word in self.excludefiles:
args.append("-x%s" % word.strip())
# assume yes on all queries
args.append("-y")
# set a password
if "password" in kwargs and kwargs['password']:
args.append("-p%s" % kwargs['password'])
else:
args.append("-p-")
if self.keepbroken:
args.append("-kb")
# NOTE: return codes are not reliable, some kind of threading, cleanup whatever issue
call = [self.CMD, command] + args + list(xargs)
self.m.logDebug(" ".join(call))
return Popen(call, stdout=PIPE, stderr=PIPE)