1<?php 2/** 3 * Action Plugin ArchiveUpload 4 * 5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6 * @author Michael Klier chi@chimeric.de 7 */ 8 9/** 10 * DokuWiki Action Plugin Archive Upload 11 * 12 * @author Michael Klier <chi@chimeric.de> 13 */ 14class action_plugin_archiveupload extends DokuWiki_Action_Plugin { 15 16 var $tmpdir = ''; 17 18 /** 19 * return some info 20 */ 21 function getInfo() { 22 return array( 23 'author' => 'Michael Klier', 24 'email' => 'chi@chimeric.de', 25 'date' => @file_get_contents(DOKU_PLUGIN.'archiveupload/VERSION'), 26 'name' => 'ArchiveUpload', 27 'desc' => 'Allows you to unpack uploaded archives.', 28 'url' => 'http://dokuwiki.org/plugin:archiveupload' 29 ); 30 } 31 32 /** 33 * Registers our callback functions 34 */ 35 function register(Doku_Event_Handler $controller) { 36 $controller->register_hook('HTML_UPLOADFORM_OUTPUT', 'BEFORE', $this, 'handle_form_output'); 37 $controller->register_hook('MEDIA_UPLOAD_FINISH', 'BEFORE', $this, 'handle_media_upload'); 38 $controller->register_hook('TPL_METAHEADER_OUTPUT', 'AFTER', $this, 'metaheaders_after'); 39 } 40 /** 41 * Disables new uploader in favor of old 42 * 43 * @author Myron Turner <turnermm02@shaw.ca> 44 */ 45 function metaheaders_after(&$event, $param) { 46 ptln( "\n<script type='text/javascript'>\n //<![CDATA[\n"); 47 ptln("qq = {};\n //]]>\n</script>"); 48 } 49 /** 50 * Adds a checkbox 51 * 52 * @author Michael Klier <chi@chimeric.de> 53 */ 54 function handle_form_output(&$event, $param) { 55 global $INFO; 56 if($this->getConf('manageronly')) { 57 if(!$INFO['isadmin'] && !$INFO['ismanager']) return; 58 } 59 $event->data->addElement(form_makeCheckboxField('unpack', 0, $this->getLang('unpack'))); 60 } 61 62 /** 63 * MEDIA_UPLOAD_FINISH handler 64 * 65 * @author Michael Klier <chi@chimeric.de> 66 */ 67 function handle_media_upload(&$event, $param) { 68 global $INFO; 69 70 // nothing todo 71 if(!isset($_REQUEST['unpack'])) return; 72 73 if($this->getConf('manageronly')) { 74 if(!$INFO['isadmin'] && !$INFO['ismanager']) return; 75 } 76 77 // our turn - prevent default action 78 $event->preventDefault(); 79 80 call_user_func_array(array($this,'extract_archive'), $event->data); 81 } 82 83 /** 84 * Uploads an extracts an archive 85 * FIXME add bz and bz2 support 86 * 87 * @author Michael Klier <chi@chimeric.de> 88 */ 89 function extract_archive($fn_tmp, $fn, $id, $imime) { 90 global $lang; 91 global $conf; 92 93 $dir = io_mktmpdir(); 94 if($dir) { 95 $this->tmpdir = $dir; 96 } else { 97 msg('Failed to create tmp dir, check permissions of cache/ directory', -1); 98 return false; 99 } 100 101 // failed to create tmp dir stop here 102 if(!$this->tmpdir) return false; 103 104 $ext = substr($fn, strrpos($fn,'.')+1); 105 106 if(in_array($ext, array('tar','gz','tgz','zip'))) { 107 108 //prepare directory 109 //FIXME needed? do it later? 110 io_createNamespace($id, 'media'); 111 112 if(move_uploaded_file($fn_tmp, $fn)) { 113 114 chmod($fn, $conf['fmode']); 115 116 if($this->decompress($fn, dirname($fn))) { 117 msg($this->getLang('decompr_succ'), 1); 118 } else { 119 msg($this->getLang('decompr_err'), -1); 120 } 121 122 // delete archive after decompression 123 // fixme check for success? 124 unlink($fn); 125 126 } else { 127 msg($lang['uploadfail'], -1); 128 } 129 130 } else { 131 msg($this->getLang('unsupported_ftype'), -1); 132 return false; 133 } 134 135 // remove tmpdir in any case 136 rmdir($this->tmpdir); 137 138 // fixme do a sweepNS here, just in case? 139 } 140 141 /** 142 * Decompress an archive (adopted from plugin manager) 143 * 144 * @author Christopher Smith <chris@jalakai.co.uk> 145 * @author Michael Klier <chi@chimeric.de> 146 */ 147 function decompress($file, $target) { 148 149 // need to source plugin manager because otherwise the ZipLib doesn't work 150 // FIXME fix ZipLib.class.php 151 require_once(DOKU_INC.'lib/plugins/plugin/admin.php'); 152 153 // decompression library doesn't like target folders ending in "/" 154 if(substr($target, -1) == "/") $target = substr($target, 0, -1); 155 156 $ext = substr($file, strrpos($file,'.')+1); 157 158 if(in_array($ext, array('tar','gz','tgz'))) { 159 160 require_once(DOKU_INC."inc/TarLib.class.php"); 161 162 if(strpos($ext, 'gz') !== false) $compress_type = COMPRESS_GZIP; 163 //else if (strpos($ext,'bz') !== false) $compress_type = COMPRESS_BZIP; // FIXME bz2 support 164 else $compress_type = COMPRESS_NONE; 165 166 $tar = new TarLib($file, $compress_type); 167 $ok = $tar->Extract(FULL_ARCHIVE, $this->tmpdir, '', 0777); 168 169 if($ok) { 170 $files = $tar->ListContents(); 171 $this->postProcessFiles($target, $files); 172 return true; 173 } else { 174 return false; 175 } 176 177 } else if ($ext == 'zip') { 178 179 require_once(DOKU_INC."inc/ZipLib.class.php"); 180 181 $zip = new ZipLib(); 182 $ok = $zip->Extract($file, $this->tmpdir); 183 184 if($ok) { 185 $files = $zip->get_List($file); 186 $this->postProcessFiles($target, $files); 187 return true; 188 } else { 189 return false; 190 } 191 192 } 193 194 // unsupported file type 195 return false; 196 } 197 198 /** 199 * Checks the mime type and fixes the permission and filenames of the 200 * extracted files and sends a notification email per uploaded file 201 * 202 * @author Michael Klier <chi@chimeric.de> 203 */ 204 function postProcessFiles($dir, $files) { 205 global $conf; 206 global $lang; 207 208 require_once(DOKU_INC.'inc/media.php'); 209 $reldir = preg_replace("#".$conf['mediadir']."#", '/', $dir) . '/'; 210 211 // get filetype regexp 212 $types = array_keys(getMimeTypes()); 213 $types = array_map(create_function('$q','return preg_quote($q,"/");'),$types); 214 $regex = join('|',$types); 215 216 $dirs = array(); 217 $tmp_dirs = array(); 218 219 foreach($files as $file) { 220 $fn_old = $file['filename']; // original filename 221 $fn_new = str_replace('/',':',$fn_old); // target filename 222 $fn_new = str_replace(':', '/', cleanID($fn_new)); 223 224 if(substr($fn_old, -1) == '/') { 225 // given file is a directory 226 io_mkdir_p($dir.'/'.$fn_new); 227 chmod($dir.'/'.$fn_new, $conf['dmode']); 228 array_push($dirs, $dir.'/'.$fn_new); 229 array_push($tmp_dirs, $this->tmpdir.'/'.$fn_old); 230 } else { 231 list($ext, $imime) = mimetype($this->tmpdir.'/'.$fn_old); 232 233 if(preg_match('/\.('.$regex.')$/i',$fn_old)){ 234 // check for overwrite 235 if(@file_exists($dir.'/'.$fn_new) && (!$_POST['ow'] || $auth < AUTH_DELETE)){ 236 msg($lang['uploadexist'],0); 237 continue; 238 } 239 240 // check for valid content 241 $ok = media_contentcheck($this->tmpdir.'/'.$fn_old,$imime); 242 if($ok == -1){ 243 msg(sprintf($lang['uploadbadcontent'],".$ext"),-1); 244 unlink($this->tmpdir.'/'.$fn_old); 245 continue; 246 }elseif($ok == -2){ 247 msg($lang['uploadspam'],-1); 248 unlink($this->tmpdir.'/'.$fn_old); 249 continue; 250 }elseif($ok == -3){ 251 msg($lang['uploadxss'],-1); 252 unlink($this->tmpdir.'/'.$fn_old); 253 continue; 254 } 255 256 // everything's ok - lets move the file 257 // FIXME check for success ?? 258 rename($this->tmpdir.'/'.$fn_old, $dir.'/'.$fn_new); 259 chmod($dir.'/'.$fn_new, $conf['fmode']); 260 261 // send notification mail 262 $id = cleanID(str_replace('/',':',$reldir.'/'.$fn_new)); 263 media_notify($id, $dir.'/'.$fn_new, $imime); 264 msg($lang['uploadsucc'], 1); 265 266 } else { 267 msg($lang['uploadwrong'],-1); 268 @unlink($this->tmpdir.'/'.$fn_old); 269 continue; 270 } 271 } 272 } 273 274 // done - remove eventually left over empty dirs in destination directory 275 natsort($dirs); 276 $dirs = array_reverse($dirs); 277 foreach($dirs as $dir) { 278 @rmdir($dir); 279 } 280 281 // do the same for the tmp dir 282 natsort($tmp_dirs); 283 $dirs = array_reverse($tmp_dirs); 284 foreach($dirs as $dir) { 285 @rmdir($dir); 286 } 287 } 288} 289// vim:ts=4:sw=4:et:enc=utf8: 290