1<?php 2/** 3 * DokuWiki Plugin cleanup (Helper Component) 4 * 5 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 6 * @author Andreas Gohr <gohr@cosmocode.de> 7 */ 8 9// must be run within Dokuwiki 10if(!defined('DOKU_INC')) die(); 11 12class helper_plugin_cleanup extends DokuWiki_Plugin { 13 /** @var int log file pointer */ 14 private $log = 0; 15 16 /** @var bool do no actually delete */ 17 private $dryrun = true; 18 19 /** @var array list of files */ 20 public $list = array(); 21 22 /** @var int sum of deleted files */ 23 public $size = 0; 24 25 26 27 /** 28 * Runs all the checks 29 */ 30 public function run($run=false) { 31 global $conf; 32 $data = array(); 33 34 $this->dryrun = !$run; 35 36 @set_time_limit(0); 37 38 search( 39 $data, 40 $conf['cachedir'], 41 array($this, 'cb_check_cache'), 42 array( 43 'maxage' => $this->getConf('cacheage'), 44 'useatime' => $this->supportsatime() 45 ) 46 ); 47 48 search( 49 $data, 50 $conf['olddir'], 51 array($this, 'cb_check_attic'), 52 array( 53 'maxage' => $this->getConf('atticage'), 54 'nonexonly' => $this->getConf('atticnoexonly') 55 ) 56 ); 57 58 search( 59 $data, 60 $conf['mediaolddir'], 61 array($this, 'cb_check_mediaattic'), 62 array( 63 'maxage' => $this->getConf('mediaatticage'), 64 'nonexonly' => $this->getConf('mediaatticnoexonly') 65 ) 66 ); 67 68 search( 69 $data, 70 $conf['metadir'], 71 array($this, 'cb_check_meta'), 72 array( 73 'maxage' => $this->getConf('metaage'), 74 ) 75 ); 76 77 search( 78 $data, 79 $conf['lockdir'], 80 array($this, 'cb_check_locks'), 81 array( 82 'maxage' => $this->getConf('lockage'), 83 ) 84 ); 85 } 86 87 /** 88 * Deletes the given file if $this->dryrun isn't set 89 * 90 * @param string $file file to delete 91 * @param string $type type of file to delete 92 */ 93 public function delete($file, $type) { 94 global $conf; 95 96 $size = filesize($file); 97 $time = time(); 98 99 // delete the file 100 if(!$this->dryrun){ 101 if(@unlink($file)){ 102 // log to file 103 if(!$this->log) $this->log = fopen($conf['cachedir'] . '/cleanup.log', 'a'); 104 if($this->log) { 105 fwrite($this->log, "$time\t$size\t$type\t$file\n"); 106 } 107 108 $this->size += $size; 109 $this->list[] = $file; 110 } 111 }else{ 112 $this->size += $size; 113 $this->list[] = $file; 114 } 115 } 116 117 /** 118 * Checks if the filesystem supports atimes 119 * 120 * @return bool 121 */ 122 protected function supportsatime(){ 123 global $conf; 124 125 $testfile = $conf['cachedir'].'/atime'; 126 io_saveFile($testfile, 'x'); 127 $mtime = filemtime($testfile); 128 sleep(1); 129 io_readFile($testfile); 130 clearstatcache(false, $testfile); 131 $atime = @fileatime($testfile); 132 @unlink($testfile); 133 134 return ($mtime != $atime); 135 } 136 137 /** 138 * Callback for checking the cache directories 139 */ 140 public function cb_check_cache(&$data, $base, $file, $type, $lvl, $opts) { 141 if($type == 'd') { 142 // we only recurse into our known cache key directories 143 if($lvl == 1 && !preg_match('/^\/[a-f0-9]$/', $file)) return false; 144 return true; 145 } 146 if($lvl == 1) return false; // ignore all files in top directory 147 148 $time = $opts['useatime'] ? fileatime($base . $file) : filemtime($base . $file); 149 150 if(time() - $time > $opts['maxage']) { 151 $this->delete($base.$file, 'cache'); 152 } 153 return true; 154 } 155 156 /** 157 * Callback for checking the page attic directories 158 */ 159 public function cb_check_attic(&$data, $base, $file, $type, $lvl, $opts) { 160 if($type == 'd') { 161 return true; 162 } 163 164 $time = filemtime($base . $file); 165 if(time() - $time > $opts['maxage']) { 166 // skip existing? 167 if($opts['nonexonly']) { 168 $path = preg_replace('/\.\d+\.txt(\.gz)?$/', '', $file); 169 $id = pathID($path, true); 170 if(page_exists($id)) return false; 171 } 172 173 $this->delete($base.$file, 'attic'); 174 } 175 return true; 176 } 177 178 /** 179 * Callback for checking the media attic directories 180 */ 181 public function cb_check_mediaattic(&$data, $base, $file, $type, $lvl, $opts) { 182 if($type == 'd') { 183 return true; 184 } 185 186 $time = filemtime($base . $file); 187 if(time() - $time > $opts['maxage']) { 188 // skip existing? 189 if($opts['nonexonly']) { 190 list($ext) = mimetype($file); 191 $ext = preg_quote($ext, '/'); 192 $path = preg_replace('/\.\d+\.' . $ext . '?$/', ".$ext", $file); 193 $id = pathID($path, true); 194 195 if(file_exists(mediaFN($id))) return false; 196 } 197 198 $this->delete($base.$file, 'mediattic'); 199 } 200 return true; 201 } 202 203 /** 204 * Callback for checking the page meta directories 205 */ 206 public function cb_check_meta(&$data, $base, $file, $type, $lvl, $opts) { 207 if($type == 'd') { 208 return true; 209 } 210 211 // only handle known extensions 212 if(!preg_match('/\.(meta|changes|indexed)$/', $file, $m)) return false; 213 $type = $m[1]; 214 215 $time = filemtime($base . $file); 216 if(time() - $time > $opts['maxage']) { 217 $path = substr($file, 0, -1 * (strlen($type) + 1)); 218 $id = pathID($path); 219 if(page_exists($id)) return false; 220 221 $this->delete($base.$file, 'meta'); 222 } 223 return true; 224 } 225 226 /** 227 * Callback for checking the media meta directories 228 */ 229 public function cb_check_mediameta(&$data, $base, $file, $type, $lvl, $opts) { 230 if($type == 'd') { 231 return true; 232 } 233 234 // only handle known extensions 235 if(!preg_match('/\.(changes)$/', $file, $m)) return false; 236 $type = $m[1]; 237 238 $time = filemtime($base . $file); 239 if(time() - $time > $opts['maxage']) { 240 $path = substr($file, 0, -1 * (strlen($type) + 1)); 241 $id = pathID($path); 242 if(file_exists(mediaFN($id))) return false; 243 244 $this->delete($base.$file, 'mediameta'); 245 } 246 return true; 247 } 248 249 /** 250 * Callback for checking the locks directories 251 */ 252 public function cb_check_locks(&$data, $base, $file, $type, $lvl, $opts) { 253 if($type == 'd') { 254 return true; 255 } 256 257 // only handle known extensions 258 if(!preg_match('/\.(lock)$/', $file, $m)) return false; 259 $type = $m[1]; 260 261 $time = filemtime($base . $file); 262 if(time() - $time > $opts['maxage']) { 263 $this->delete($base.$file, 'lock'); 264 } 265 return true; 266 } 267 268} 269 270// vim:ts=4:sw=4:et: 271