[JOIN] Fix removal of protected files
[rarslave2.git] / PAR2Set / Join.py
1 #!/usr/bin/env python
2 # vim: set ts=4 sts=4 sw=4 textwidth=92:
3
4 """
5 Holds the Join class.
6
7 This module works with normal joined sets.
8
9 It will detect sets like the following:
10 X.par2
11 X.vol0+1.par2
12 ...
13
14 X.001
15 X.002
16 ...
17
18 Where the PAR2 files protect a file named X.avi, or similar other types.
19 It will not work where the PAR2 files are protecting the X.001, etc files
20 directly.
21 """
22
23 __author__    = "Ira W. Snyder (devel@irasnyder.com)"
24 __copyright__ = "Copyright (c) 2006,2007 Ira W. Snyder (devel@irasnyder.com)"
25 __license__   = "GNU GPL v2 (or, at your option, any later version)"
26
27 #    Join.py
28 #
29 #    Copyright (C) 2006,2007  Ira W. Snyder (devel@irasnyder.com)
30 #
31 #    This program is free software; you can redistribute it and/or modify
32 #    it under the terms of the GNU General Public License as published by
33 #    the Free Software Foundation; either version 2 of the License, or
34 #    (at your option) any later version.
35 #
36 #    This program is distributed in the hope that it will be useful,
37 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
38 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
39 #    GNU General Public License for more details.
40 #
41 #    You should have received a copy of the GNU General Public License
42 #    along with this program; if not, write to the Free Software
43 #    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
44
45 import re
46 import os
47 import logging
48 import PAR2Set.Base
49 import rsutil.common
50
51
52 def detector (name_files, prot_files):
53         """Detects a Join set"""
54
55         return rsutil.common.has_a_match ('^.*\.\d\d\d$', name_files) \
56                         and not rsutil.common.has_a_match ('^.*\.\d\d\d$', prot_files)
57
58
59 class Join (PAR2Set.Base.Base):
60
61         """Class for normal joined-file sets"""
62
63         def __repr__ (self):
64                 return 'JOIN'
65
66         def find_joinfiles (self):
67                 """Finds files which contain data to be joined together"""
68
69                 return rsutil.common.find_matches ('^.*\.\d\d\d$', self.name_matched_files)
70
71         def runVerifyAndRepair (self):
72                 """Verify and Repair a PAR2Set. This version extends the PAR2Set.Base.Base
73                    version by adding the datafiles to be joined at the end of the command
74                    line.
75
76                    This is done using the par2repair command by default"""
77
78                 PAR2_CMD = rsutil.common.config_get_value ('commands', 'par2repair')
79
80                 # assemble the command
81                 # par2repair -- PAR2 PAR2_EXTRA [JOIN_FILES]
82                 command = "%s \"%s\" " % (PAR2_CMD, self.p2file)
83
84                 for f in self.all_p2files:
85                         if f != self.p2file:
86                                 command += "\"%s\" " % os.path.split (f)[1]
87
88                 for f in self.find_joinfiles ():
89                         command += "\"%s\" " % os.path.split (f)[1]
90
91                 # run the command
92                 ret = rsutil.common.run_command (command, self.dir)
93
94                 # check the result
95                 if ret != 0:
96                         logging.critical ('PAR2 Check / Repair failed: %s' % self.p2file)
97                         return -rsutil.common.ECHECK
98
99                 return rsutil.common.SUCCESS
100
101         def find_deleteable_files (self):
102                 """Find all files which are deletable by using the regular expression from the
103                    configuration file"""
104
105                 DELETE_REGEX = rsutil.common.config_get_value ('regular expressions', 'delete_regex')
106                 dregex = re.compile (DELETE_REGEX, re.IGNORECASE)
107
108                 return [f for f in self.all_files if dregex.match (f) and \
109                                 f not in self.prot_matched_files]
110
111         def find_extraction_heads (self):
112                 """Find the extraction heads. Since this should not be an extractable set,
113                    we return the files which are protected directly by the PAR2 files."""
114
115                 return self.prot_matched_files
116
117         def extraction_function (self, file, todir):
118                 """Extract a single file of the Join type.
119
120                    file -- the file to extract
121                    todir -- the directory to extract to
122
123                    This command ignores the extraction if file and todir+file are the same
124                    file. This keeps things like mv working smoothly."""
125
126                 NOEXTRACT_CMD = rsutil.common.config_get_value ('commands', 'noextract')
127
128                 # Make sure that both files are not the same file. If they are, don't run at all.
129                 if os.path.samefile (file, os.path.join (todir, file)):
130                         return rsutil.common.SUCCESS
131
132                 cmd = NOEXTRACT_CMD % (file, todir)
133                 ret = rsutil.common.run_command (cmd)
134
135                 # Check error code
136                 if ret != 0:
137                         return -rsutil.common.EEXTRACT
138
139                 return rsutil.common.SUCCESS
140