first commit. dokuwiki.
[h2N7SspZmY.git] / lib / plugins / plugin / classes / ap_download.class.php
blob90e5de53b22a7f4ff693f2e5811ac2f6ed7fa693
1 <?php
2 class ap_download extends ap_manage {
4 var $overwrite = false;
6 /**
7 * Initiate the plugin download
8 */
9 function process() {
10 global $lang;
12 $plugin_url = $_REQUEST['url'];
13 $this->download($plugin_url, $this->overwrite);
14 return '';
17 /**
18 * Print results of the download
20 function html() {
21 parent::html();
23 ptln('<div class="pm_info">');
24 ptln('<h2>'.$this->lang['downloading'].'</h2>');
26 if ($this->manager->error) {
27 ptln('<div class="error">'.str_replace("\n","<br />",$this->manager->error).'</div>');
28 } else if (count($this->downloaded) == 1) {
29 ptln('<p>'.sprintf($this->lang['downloaded'],$this->downloaded[0]).'</p>');
30 } else if (count($this->downloaded)) { // more than one plugin in the download
31 ptln('<p>'.$this->lang['downloads'].'</p>');
32 ptln('<ul>');
33 foreach ($this->downloaded as $plugin) {
34 ptln('<li><div class="li">'.$plugin.'</div></li>',2);
36 ptln('</ul>');
37 } else { // none found in download
38 ptln('<p>'.$this->lang['download_none'].'</p>');
40 ptln('</div>');
43 /**
44 * Process the downloaded file
46 function download($url, $overwrite=false) {
47 global $lang;
48 // check the url
49 $matches = array();
50 if (!preg_match("/[^\/]*$/", $url, $matches) || !$matches[0]) {
51 $this->manager->error = $this->lang['error_badurl']."\n";
52 return false;
55 $file = $matches[0];
57 if (!($tmp = io_mktmpdir())) {
58 $this->manager->error = $this->lang['error_dircreate']."\n";
59 return false;
62 if (!$file = io_download($url, "$tmp/", true, $file)) {
63 $this->manager->error = sprintf($this->lang['error_download'],$url)."\n";
66 if (!$this->manager->error && !$this->decompress("$tmp/$file", $tmp)) {
67 $this->manager->error = sprintf($this->lang['error_decompress'],$file)."\n";
70 // search $tmp for the folder(s) that has been created
71 // move the folder(s) to lib/plugins/
72 if (!$this->manager->error) {
73 $result = array('old'=>array(), 'new'=>array());
74 if($this->find_folders($result,$tmp)){
75 // choose correct result array
76 if(count($result['new'])){
77 $install = $result['new'];
78 }else{
79 $install = $result['old'];
82 // now install all found items
83 foreach($install as $item){
84 // where to install?
85 if($item['type'] == 'template'){
86 $target = DOKU_INC.'lib/tpl/'.$item['base'];
87 }else{
88 $target = DOKU_INC.'lib/plugins/'.$item['base'];
91 // check to make sure we aren't overwriting anything
92 if (!$overwrite && @file_exists($target)) {
93 // remember our settings, ask the user to confirm overwrite, FIXME
94 continue;
97 $instruction = @file_exists($target) ? 'update' : 'install';
99 // copy action
100 if ($this->dircopy($item['tmp'], $target)) {
101 $this->downloaded[] = $item['base'];
102 $this->plugin_writelog($target, $instruction, array($url));
103 } else {
104 $this->manager->error .= sprintf($this->lang['error_copy']."\n", $item['base']);
108 } else {
109 $this->manager->error = $this->lang['error']."\n";
113 // cleanup
114 if ($tmp) $this->dir_delete($tmp);
116 if (!$this->manager->error) {
117 msg('Plugin package ('.count($this->downloaded).' plugin'.(count($this->downloaded) != 1?'s':'').': '.join(',',$this->downloaded).') successfully installed.',1);
118 $this->refresh();
119 return true;
122 return false;
126 * Find out what was in the extracted directory
128 * Correct folders are searched recursively using the "*.info.txt" configs
129 * as indicator for a root folder. When such a file is found, it's base
130 * setting is used (when set). All folders found by this method are stored
131 * in the 'new' key of the $result array.
133 * For backwards compatibility all found top level folders are stored as
134 * in the 'old' key of the $result array.
136 * When no items are found in 'new' the copy mechanism should fall back
137 * the 'old' list.
139 * @author Andreas Gohr <andi@splitbrain.org>
140 * @param arrayref $result - results are stored here
141 * @param string $base - the temp directory where the package was unpacked to
142 * @param string $dir - a subdirectory. do not set. used by recursion
143 * @return bool - false on error
145 function find_folders(&$result,$base,$dir=''){
146 $dh = @opendir("$base/$dir");
147 if(!$dh) return false;
148 while (false !== ($f = readdir($dh))) {
149 if ($f == '.' || $f == '..' || $f == 'tmp') continue;
151 if(!is_dir("$base/$dir/$f")){
152 // it's a file -> check for config
153 if($f == 'plugin.info.txt'){
154 $info = array();
155 $info['type'] = 'plugin';
156 $info['tmp'] = "$base/$dir";
157 $conf = confToHash("$base/$dir/$f");
158 $info['base'] = basename($conf['base']);
159 if(!$info['base']) $info['base'] = basename("$base/$dir");
160 $result['new'][] = $info;
161 }elseif($f == 'template.info.txt'){
162 $info = array();
163 $info['type'] = 'template';
164 $info['tmp'] = "$base/$dir";
165 $conf = confToHash("$base/$dir/$f");
166 $info['base'] = basename($conf['base']);
167 if(!$info['base']) $info['base'] = basename("$base/$dir");
168 $result['new'][] = $info;
170 }else{
171 // it's a directory -> add to dir list for old method, then recurse
172 if(!$dir){
173 $info = array();
174 $info['type'] = 'plugin';
175 $info['tmp'] = "$base/$dir/$f";
176 $info['base'] = $f;
177 $result['old'][] = $info;
179 $this->find_folders($result,$base,"$dir/$f");
182 closedir($dh);
183 return true;
188 * Decompress a given file to the given target directory
190 * Determines the compression type from the file extension
192 function decompress($file, $target) {
193 global $conf;
195 // decompression library doesn't like target folders ending in "/"
196 if (substr($target, -1) == "/") $target = substr($target, 0, -1);
198 $ext = $this->guess_archive($file);
199 if (in_array($ext, array('tar','bz','gz'))) {
200 require_once(DOKU_INC."inc/TarLib.class.php");
202 switch($ext){
203 case 'bz':
204 $compress_type = COMPRESS_BZIP;
205 break;
206 case 'gz':
207 $compress_type = COMPRESS_GZIP;
208 break;
209 default:
210 $compress_type = COMPRESS_NONE;
213 $tar = new TarLib($file, $compress_type);
214 if($tar->_initerror < 0){
215 if($conf['allowdebug']){
216 msg('TarLib Error: '.$tar->TarErrorStr($tar->_initerror),-1);
218 return false;
220 $ok = $tar->Extract(FULL_ARCHIVE, $target, '', 0777);
222 if($ok<1){
223 if($conf['allowdebug']){
224 msg('TarLib Error: '.$tar->TarErrorStr($ok),-1);
226 return false;
228 return true;
229 } else if ($ext == 'zip') {
230 require_once(DOKU_INC."inc/ZipLib.class.php");
232 $zip = new ZipLib();
233 $ok = $zip->Extract($file, $target);
235 // FIXME sort something out for handling zip error messages meaningfully
236 return ($ok==-1?false:true);
240 // unsupported file type
241 return false;
245 * Determine the archive type of the given file
247 * Reads the first magic bytes of the given file for content type guessing,
248 * if neither bz, gz or zip are recognized, tar is assumed.
250 * @author Andreas Gohr <andi@splitbrain.org>
251 * @returns false if the file can't be read, otherwise an "extension"
253 function guess_archive($file){
254 $fh = fopen($file,'rb');
255 if(!$fh) return false;
256 $magic = fread($fh,5);
257 fclose($fh);
259 if(strpos($magic,"\x42\x5a") === 0) return 'bz';
260 if(strpos($magic,"\x1f\x8b") === 0) return 'gz';
261 if(strpos($magic,"\x50\x4b\x03\x04") === 0) return 'zip';
262 return 'tar';
266 * Copy with recursive sub-directory support
268 function dircopy($src, $dst) {
269 global $conf;
271 if (is_dir($src)) {
272 if (!$dh = @opendir($src)) return false;
274 if ($ok = io_mkdir_p($dst)) {
275 while ($ok && (false !== ($f = readdir($dh)))) {
276 if ($f == '..' || $f == '.') continue;
277 $ok = $this->dircopy("$src/$f", "$dst/$f");
281 closedir($dh);
282 return $ok;
284 } else {
285 $exists = @file_exists($dst);
287 if (!@copy($src,$dst)) return false;
288 if (!$exists && !empty($conf['fperm'])) chmod($dst, $conf['fperm']);
289 @touch($dst,filemtime($src));
292 return true;