1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* @author Bart Visscher <[email protected]> |
4
|
|
|
* @author Christopher Schäpers <[email protected]> |
5
|
|
|
* @author Felix Moeller <[email protected]> |
6
|
|
|
* @author Frank Karlitschek <[email protected]> |
7
|
|
|
* @author Joas Schilling <[email protected]> |
8
|
|
|
* @author Jörn Friedrich Dreyer <[email protected]> |
9
|
|
|
* @author Morris Jobke <[email protected]> |
10
|
|
|
* @author Robin Appelman <[email protected]> |
11
|
|
|
* @author Thomas Müller <[email protected]> |
12
|
|
|
* |
13
|
|
|
* @copyright Copyright (c) 2016, ownCloud, Inc. |
14
|
|
|
* @license AGPL-3.0 |
15
|
|
|
* |
16
|
|
|
* This code is free software: you can redistribute it and/or modify |
17
|
|
|
* it under the terms of the GNU Affero General Public License, version 3, |
18
|
|
|
* as published by the Free Software Foundation. |
19
|
|
|
* |
20
|
|
|
* This program is distributed in the hope that it will be useful, |
21
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
22
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
23
|
|
|
* GNU Affero General Public License for more details. |
24
|
|
|
* |
25
|
|
|
* You should have received a copy of the GNU Affero General Public License, version 3, |
26
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/> |
27
|
|
|
* |
28
|
|
|
*/ |
29
|
|
|
|
30
|
|
|
namespace OC\Archive; |
31
|
|
|
|
32
|
|
|
class ZIP extends Archive{ |
33
|
|
|
/** |
34
|
|
|
* @var \ZipArchive zip |
35
|
|
|
*/ |
36
|
|
|
private $zip=null; |
37
|
|
|
private $path; |
38
|
|
|
|
39
|
|
|
/** |
40
|
|
|
* @param string $source |
41
|
|
|
*/ |
42
|
|
|
function __construct($source) { |
43
|
|
|
$this->path=$source; |
44
|
|
|
$this->zip=new \ZipArchive(); |
45
|
|
|
if($this->zip->open($source, \ZipArchive::CREATE)) { |
46
|
|
|
}else{ |
47
|
|
|
\OCP\Util::writeLog('files_archive', 'Error while opening archive '.$source, \OCP\Util::WARN); |
48
|
|
|
} |
49
|
|
|
} |
50
|
|
|
/** |
51
|
|
|
* add an empty folder to the archive |
52
|
|
|
* @param string $path |
53
|
|
|
* @return bool |
54
|
|
|
*/ |
55
|
|
|
function addFolder($path) { |
56
|
|
|
return $this->zip->addEmptyDir($path); |
57
|
|
|
} |
58
|
|
|
/** |
59
|
|
|
* add a file to the archive |
60
|
|
|
* @param string $path |
61
|
|
|
* @param string $source either a local file or string data |
62
|
|
|
* @return bool |
63
|
|
|
*/ |
64
|
|
|
function addFile($path, $source='') { |
65
|
|
|
if($source and $source[0]=='/' and file_exists($source)) { |
66
|
|
|
$result=$this->zip->addFile($source, $path); |
67
|
|
|
}else{ |
68
|
|
|
$result=$this->zip->addFromString($path, $source); |
69
|
|
|
} |
70
|
|
|
if($result) { |
71
|
|
|
$this->zip->close();//close and reopen to save the zip |
72
|
|
|
$this->zip->open($this->path); |
73
|
|
|
} |
74
|
|
|
return $result; |
75
|
|
|
} |
76
|
|
|
/** |
77
|
|
|
* rename a file or folder in the archive |
78
|
|
|
* @param string $source |
79
|
|
|
* @param string $dest |
80
|
|
|
* @return boolean|null |
81
|
|
|
*/ |
82
|
|
|
function rename($source, $dest) { |
83
|
|
|
$source=$this->stripPath($source); |
84
|
|
|
$dest=$this->stripPath($dest); |
85
|
|
|
$this->zip->renameName($source, $dest); |
86
|
|
|
} |
87
|
|
|
/** |
88
|
|
|
* get the uncompressed size of a file in the archive |
89
|
|
|
* @param string $path |
90
|
|
|
* @return int |
91
|
|
|
*/ |
92
|
|
|
function filesize($path) { |
93
|
|
|
$stat=$this->zip->statName($path); |
94
|
|
|
return $stat['size']; |
95
|
|
|
} |
96
|
|
|
/** |
97
|
|
|
* get the last modified time of a file in the archive |
98
|
|
|
* @param string $path |
99
|
|
|
* @return int |
100
|
|
|
*/ |
101
|
|
|
function mtime($path) { |
102
|
|
|
return filemtime($this->path); |
103
|
|
|
} |
104
|
|
|
/** |
105
|
|
|
* get the files in a folder |
106
|
|
|
* @param string $path |
107
|
|
|
* @return array |
108
|
|
|
*/ |
109
|
|
|
function getFolder($path) { |
110
|
|
|
$files=$this->getFiles(); |
111
|
|
|
$folderContent=array(); |
112
|
|
|
$pathLength=strlen($path); |
113
|
|
|
foreach($files as $file) { |
114
|
|
|
if(substr($file, 0, $pathLength)==$path and $file!=$path) { |
115
|
|
|
if(strrpos(substr($file, 0, -1), '/')<=$pathLength) { |
116
|
|
|
$folderContent[]=substr($file, $pathLength); |
117
|
|
|
} |
118
|
|
|
} |
119
|
|
|
} |
120
|
|
|
return $folderContent; |
121
|
|
|
} |
122
|
|
|
/** |
123
|
|
|
* get all files in the archive |
124
|
|
|
* @return array |
125
|
|
|
*/ |
126
|
|
|
function getFiles() { |
127
|
|
|
$fileCount=$this->zip->numFiles; |
128
|
|
|
$files=array(); |
129
|
|
|
for($i=0;$i<$fileCount;$i++) { |
130
|
|
|
$files[]=$this->zip->getNameIndex($i); |
131
|
|
|
} |
132
|
|
|
return $files; |
133
|
|
|
} |
134
|
|
|
/** |
135
|
|
|
* get the content of a file |
136
|
|
|
* @param string $path |
137
|
|
|
* @return string |
138
|
|
|
*/ |
139
|
|
|
function getFile($path) { |
140
|
|
|
return $this->zip->getFromName($path); |
141
|
|
|
} |
142
|
|
|
/** |
143
|
|
|
* extract a single file from the archive |
144
|
|
|
* @param string $path |
145
|
|
|
* @param string $dest |
146
|
|
|
* @return boolean|null |
147
|
|
|
*/ |
148
|
|
|
function extractFile($path, $dest) { |
149
|
|
|
$fp = $this->zip->getStream($path); |
150
|
|
|
file_put_contents($dest, $fp); |
151
|
|
|
} |
152
|
|
|
/** |
153
|
|
|
* extract the archive |
154
|
|
|
* @param string $dest |
155
|
|
|
* @return bool |
156
|
|
|
*/ |
157
|
|
|
function extract($dest) { |
158
|
|
|
return $this->zip->extractTo($dest); |
159
|
|
|
} |
160
|
|
|
/** |
161
|
|
|
* check if a file or folder exists in the archive |
162
|
|
|
* @param string $path |
163
|
|
|
* @return bool |
164
|
|
|
*/ |
165
|
|
|
function fileExists($path) { |
166
|
|
|
return ($this->zip->locateName($path)!==false) or ($this->zip->locateName($path.'/')!==false); |
167
|
|
|
} |
168
|
|
|
/** |
169
|
|
|
* remove a file or folder from the archive |
170
|
|
|
* @param string $path |
171
|
|
|
* @return bool |
172
|
|
|
*/ |
173
|
|
|
function remove($path) { |
174
|
|
|
if($this->fileExists($path.'/')) { |
175
|
|
|
return $this->zip->deleteName($path.'/'); |
176
|
|
|
}else{ |
177
|
|
|
return $this->zip->deleteName($path); |
178
|
|
|
} |
179
|
|
|
} |
180
|
|
|
/** |
181
|
|
|
* get a file handler |
182
|
|
|
* @param string $path |
183
|
|
|
* @param string $mode |
184
|
|
|
* @return resource |
185
|
|
|
*/ |
186
|
|
|
function getStream($path, $mode) { |
187
|
|
|
if($mode=='r' or $mode=='rb') { |
188
|
|
|
return $this->zip->getStream($path); |
189
|
|
|
} else { |
190
|
|
|
//since we can't directly get a writable stream, |
191
|
|
|
//make a temp copy of the file and put it back |
192
|
|
|
//in the archive when the stream is closed |
193
|
|
|
if(strrpos($path, '.')!==false) { |
194
|
|
|
$ext=substr($path, strrpos($path, '.')); |
195
|
|
|
}else{ |
196
|
|
|
$ext=''; |
197
|
|
|
} |
198
|
|
|
$tmpFile=\OCP\Files::tmpFile($ext); |
199
|
|
|
\OC\Files\Stream\Close::registerCallback($tmpFile, array($this, 'writeBack')); |
200
|
|
|
if($this->fileExists($path)) { |
201
|
|
|
$this->extractFile($path, $tmpFile); |
202
|
|
|
} |
203
|
|
|
self::$tempFiles[$tmpFile]=$path; |
204
|
|
|
return fopen('close://'.$tmpFile, $mode); |
205
|
|
|
} |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
private static $tempFiles=array(); |
209
|
|
|
/** |
210
|
|
|
* write back temporary files |
211
|
|
|
*/ |
212
|
|
|
function writeBack($tmpFile) { |
213
|
|
View Code Duplication |
if(isset(self::$tempFiles[$tmpFile])) { |
|
|
|
|
214
|
|
|
$this->addFile(self::$tempFiles[$tmpFile], $tmpFile); |
215
|
|
|
unlink($tmpFile); |
216
|
|
|
} |
217
|
|
|
} |
218
|
|
|
|
219
|
|
|
/** |
220
|
|
|
* @param string $path |
221
|
|
|
* @return string |
222
|
|
|
*/ |
223
|
|
|
private function stripPath($path) { |
224
|
|
|
if(!$path || $path[0]=='/') { |
225
|
|
|
return substr($path, 1); |
226
|
|
|
}else{ |
227
|
|
|
return $path; |
228
|
|
|
} |
229
|
|
|
} |
230
|
|
|
} |
231
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.