1 | <?php
|
---|
2 | /**
|
---|
3 | * Information and debugging functions
|
---|
4 | *
|
---|
5 | * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
|
---|
6 | * @author Andreas Gohr <[email protected]>
|
---|
7 | */
|
---|
8 | if(!defined('DOKU_INC')) die('meh.');
|
---|
9 | if(!defined('DOKU_MESSAGEURL')) define('DOKU_MESSAGEURL','http://update.dokuwiki.org/check/');
|
---|
10 |
|
---|
11 | /**
|
---|
12 | * Check for new messages from upstream
|
---|
13 | *
|
---|
14 | * @author Andreas Gohr <[email protected]>
|
---|
15 | */
|
---|
16 | function checkUpdateMessages(){
|
---|
17 | global $conf;
|
---|
18 | global $INFO;
|
---|
19 | global $updateVersion;
|
---|
20 | if(!$conf['updatecheck']) return;
|
---|
21 | if($conf['useacl'] && !$INFO['ismanager']) return;
|
---|
22 |
|
---|
23 | $cf = $conf['cachedir'].'/messages.txt';
|
---|
24 | $lm = @filemtime($cf);
|
---|
25 |
|
---|
26 | // check if new messages needs to be fetched
|
---|
27 | if($lm < time()-(60*60*24) || $lm < @filemtime(DOKU_INC.'doku.php')){
|
---|
28 | $http = new DokuHTTPClient();
|
---|
29 | $http->timeout = 8;
|
---|
30 | $data = $http->get(DOKU_MESSAGEURL.$updateVersion);
|
---|
31 | io_saveFile($cf,$data);
|
---|
32 | }else{
|
---|
33 | $data = io_readFile($cf);
|
---|
34 | }
|
---|
35 |
|
---|
36 | // show messages through the usual message mechanism
|
---|
37 | $msgs = explode("\n%\n",$data);
|
---|
38 | foreach($msgs as $msg){
|
---|
39 | if($msg) msg($msg,2);
|
---|
40 | }
|
---|
41 | }
|
---|
42 |
|
---|
43 |
|
---|
44 | /**
|
---|
45 | * Return DokuWiki's version (split up in date and type)
|
---|
46 | *
|
---|
47 | * @author Andreas Gohr <[email protected]>
|
---|
48 | */
|
---|
49 | function getVersionData(){
|
---|
50 | $version = array();
|
---|
51 | //import version string
|
---|
52 | if(@file_exists(DOKU_INC.'VERSION')){
|
---|
53 | //official release
|
---|
54 | $version['date'] = trim(io_readfile(DOKU_INC.'VERSION'));
|
---|
55 | $version['type'] = 'Release';
|
---|
56 | }elseif(is_dir(DOKU_INC.'.git')){
|
---|
57 | $version['type'] = 'Git';
|
---|
58 | $version['date'] = 'unknown';
|
---|
59 |
|
---|
60 | $inventory = DOKU_INC.'.git/logs/HEAD';
|
---|
61 | if(is_file($inventory)){
|
---|
62 | $sz = filesize($inventory);
|
---|
63 | $seek = max(0,$sz-2000); // read from back of the file
|
---|
64 | $fh = fopen($inventory,'rb');
|
---|
65 | fseek($fh,$seek);
|
---|
66 | $chunk = fread($fh,2000);
|
---|
67 | fclose($fh);
|
---|
68 | $chunk = trim($chunk);
|
---|
69 | $chunk = array_pop(explode("\n",$chunk)); //last log line
|
---|
70 | $chunk = array_shift(explode("\t",$chunk)); //strip commit msg
|
---|
71 | $chunk = explode(" ",$chunk);
|
---|
72 | array_pop($chunk); //strip timezone
|
---|
73 | $date = date('Y-m-d',array_pop($chunk));
|
---|
74 | if($date) $version['date'] = $date;
|
---|
75 | }
|
---|
76 | }else{
|
---|
77 | $version['date'] = 'unknown';
|
---|
78 | $version['type'] = 'snapshot?';
|
---|
79 | }
|
---|
80 | return $version;
|
---|
81 | }
|
---|
82 |
|
---|
83 | /**
|
---|
84 | * Return DokuWiki's version (as a string)
|
---|
85 | *
|
---|
86 | * @author Anika Henke <[email protected]>
|
---|
87 | */
|
---|
88 | function getVersion(){
|
---|
89 | $version = getVersionData();
|
---|
90 | return $version['type'].' '.$version['date'];
|
---|
91 | }
|
---|
92 |
|
---|
93 | /**
|
---|
94 | * Run a few sanity checks
|
---|
95 | *
|
---|
96 | * @author Andreas Gohr <[email protected]>
|
---|
97 | */
|
---|
98 | function check(){
|
---|
99 | global $conf;
|
---|
100 | global $INFO;
|
---|
101 |
|
---|
102 | if ($INFO['isadmin'] || $INFO['ismanager']){
|
---|
103 | msg('DokuWiki version: '.getVersion(),1);
|
---|
104 | }
|
---|
105 |
|
---|
106 | if(version_compare(phpversion(),'5.1.2','<')){
|
---|
107 | msg('Your PHP version is too old ('.phpversion().' vs. 5.1.2+ needed)',-1);
|
---|
108 | }else{
|
---|
109 | msg('PHP version '.phpversion(),1);
|
---|
110 | }
|
---|
111 |
|
---|
112 | $mem = (int) php_to_byte(ini_get('memory_limit'));
|
---|
113 | if($mem){
|
---|
114 | if($mem < 16777216){
|
---|
115 | msg('PHP is limited to less than 16MB RAM ('.$mem.' bytes). Increase memory_limit in php.ini',-1);
|
---|
116 | }elseif($mem < 20971520){
|
---|
117 | msg('PHP is limited to less than 20MB RAM ('.$mem.' bytes), you might encounter problems with bigger pages. Increase memory_limit in php.ini',-1);
|
---|
118 | }elseif($mem < 33554432){
|
---|
119 | msg('PHP is limited to less than 32MB RAM ('.$mem.' bytes), but that should be enough in most cases. If not, increase memory_limit in php.ini',0);
|
---|
120 | }else{
|
---|
121 | msg('More than 32MB RAM ('.$mem.' bytes) available.',1);
|
---|
122 | }
|
---|
123 | }
|
---|
124 |
|
---|
125 | if(is_writable($conf['changelog'])){
|
---|
126 | msg('Changelog is writable',1);
|
---|
127 | }else{
|
---|
128 | if (@file_exists($conf['changelog'])) {
|
---|
129 | msg('Changelog is not writable',-1);
|
---|
130 | }
|
---|
131 | }
|
---|
132 |
|
---|
133 | if (isset($conf['changelog_old']) && @file_exists($conf['changelog_old'])) {
|
---|
134 | msg('Old changelog exists', 0);
|
---|
135 | }
|
---|
136 |
|
---|
137 | if (@file_exists($conf['changelog'].'_failed')) {
|
---|
138 | msg('Importing old changelog failed', -1);
|
---|
139 | } else if (@file_exists($conf['changelog'].'_importing')) {
|
---|
140 | msg('Importing old changelog now.', 0);
|
---|
141 | } else if (@file_exists($conf['changelog'].'_import_ok')) {
|
---|
142 | msg('Old changelog imported', 1);
|
---|
143 | if (!plugin_isdisabled('importoldchangelog')) {
|
---|
144 | msg('Importoldchangelog plugin not disabled after import', -1);
|
---|
145 | }
|
---|
146 | }
|
---|
147 |
|
---|
148 | if(is_writable($conf['datadir'])){
|
---|
149 | msg('Datadir is writable',1);
|
---|
150 | }else{
|
---|
151 | msg('Datadir is not writable',-1);
|
---|
152 | }
|
---|
153 |
|
---|
154 | if(is_writable($conf['olddir'])){
|
---|
155 | msg('Attic is writable',1);
|
---|
156 | }else{
|
---|
157 | msg('Attic is not writable',-1);
|
---|
158 | }
|
---|
159 |
|
---|
160 | if(is_writable($conf['mediadir'])){
|
---|
161 | msg('Mediadir is writable',1);
|
---|
162 | }else{
|
---|
163 | msg('Mediadir is not writable',-1);
|
---|
164 | }
|
---|
165 |
|
---|
166 | if(is_writable($conf['cachedir'])){
|
---|
167 | msg('Cachedir is writable',1);
|
---|
168 | }else{
|
---|
169 | msg('Cachedir is not writable',-1);
|
---|
170 | }
|
---|
171 |
|
---|
172 | if(is_writable($conf['lockdir'])){
|
---|
173 | msg('Lockdir is writable',1);
|
---|
174 | }else{
|
---|
175 | msg('Lockdir is not writable',-1);
|
---|
176 | }
|
---|
177 |
|
---|
178 | if($conf['authtype'] == 'plain'){
|
---|
179 | global $config_cascade;
|
---|
180 | if(is_writable($config_cascade['plainauth.users']['default'])){
|
---|
181 | msg('conf/users.auth.php is writable',1);
|
---|
182 | }else{
|
---|
183 | msg('conf/users.auth.php is not writable',0);
|
---|
184 | }
|
---|
185 | }
|
---|
186 |
|
---|
187 | if(function_exists('mb_strpos')){
|
---|
188 | if(defined('UTF8_NOMBSTRING')){
|
---|
189 | msg('mb_string extension is available but will not be used',0);
|
---|
190 | }else{
|
---|
191 | msg('mb_string extension is available and will be used',1);
|
---|
192 | if(ini_get('mbstring.func_overload') != 0){
|
---|
193 | msg('mb_string function overloading is enabled, this will cause problems and should be disabled',-1);
|
---|
194 | }
|
---|
195 | }
|
---|
196 | }else{
|
---|
197 | msg('mb_string extension not available - PHP only replacements will be used',0);
|
---|
198 | }
|
---|
199 |
|
---|
200 | if($conf['allowdebug']){
|
---|
201 | msg('Debugging support is enabled. If you don\'t need it you should set $conf[\'allowdebug\'] = 0',-1);
|
---|
202 | }else{
|
---|
203 | msg('Debugging support is disabled',1);
|
---|
204 | }
|
---|
205 |
|
---|
206 | if($INFO['userinfo']['name']){
|
---|
207 | msg('You are currently logged in as '.$_SERVER['REMOTE_USER'].' ('.$INFO['userinfo']['name'].')',0);
|
---|
208 | msg('You are part of the groups '.join($INFO['userinfo']['grps'],', '),0);
|
---|
209 | }else{
|
---|
210 | msg('You are currently not logged in',0);
|
---|
211 | }
|
---|
212 |
|
---|
213 | msg('Your current permission for this page is '.$INFO['perm'],0);
|
---|
214 |
|
---|
215 | if(is_writable($INFO['filepath'])){
|
---|
216 | msg('The current page is writable by the webserver',0);
|
---|
217 | }else{
|
---|
218 | msg('The current page is not writable by the webserver',0);
|
---|
219 | }
|
---|
220 |
|
---|
221 | if($INFO['writable']){
|
---|
222 | msg('The current page is writable by you',0);
|
---|
223 | }else{
|
---|
224 | msg('The current page is not writable by you',0);
|
---|
225 | }
|
---|
226 |
|
---|
227 | $check = wl('','',true).'data/_dummy';
|
---|
228 | $http = new DokuHTTPClient();
|
---|
229 | $http->timeout = 6;
|
---|
230 | $res = $http->get($check);
|
---|
231 | if(strpos($res,'data directory') !== false){
|
---|
232 | msg('It seems like the data directory is accessible from the web.
|
---|
233 | Make sure this directory is properly protected
|
---|
234 | (See <a href="http://www.dokuwiki.org/security">security</a>)',-1);
|
---|
235 | }elseif($http->status == 404 || $http->status == 403){
|
---|
236 | msg('The data directory seems to be properly protected',1);
|
---|
237 | }else{
|
---|
238 | msg('Failed to check if the data directory is accessible from the web.
|
---|
239 | Make sure this directory is properly protected
|
---|
240 | (See <a href="http://www.dokuwiki.org/security">security</a>)',-1);
|
---|
241 | }
|
---|
242 |
|
---|
243 | // Check for corrupted search index
|
---|
244 | $lengths = idx_listIndexLengths();
|
---|
245 | $index_corrupted = false;
|
---|
246 | foreach ($lengths as $length) {
|
---|
247 | if (count(idx_getIndex('w', $length)) != count(idx_getIndex('i', $length))) {
|
---|
248 | $index_corrupted = true;
|
---|
249 | break;
|
---|
250 | }
|
---|
251 | }
|
---|
252 |
|
---|
253 | foreach (idx_getIndex('metadata', '') as $index) {
|
---|
254 | if (count(idx_getIndex($index.'_w', '')) != count(idx_getIndex($index.'_i', ''))) {
|
---|
255 | $index_corrupted = true;
|
---|
256 | break;
|
---|
257 | }
|
---|
258 | }
|
---|
259 |
|
---|
260 | if ($index_corrupted)
|
---|
261 | msg('The search index is corrupted. It might produce wrong results and most
|
---|
262 | probably needs to be rebuilt. See
|
---|
263 | <a href="http://www.dokuwiki.org/faq:searchindex">faq:searchindex</a>
|
---|
264 | for ways to rebuild the search index.', -1);
|
---|
265 | elseif (!empty($lengths))
|
---|
266 | msg('The search index seems to be working', 1);
|
---|
267 | else
|
---|
268 | msg('The search index is empty. See
|
---|
269 | <a href="http://www.dokuwiki.org/faq:searchindex">faq:searchindex</a>
|
---|
270 | for help on how to fix the search index. If the default indexer
|
---|
271 | isn\'t used or the wiki is actually empty this is normal.');
|
---|
272 | }
|
---|
273 |
|
---|
274 | /**
|
---|
275 | * print a message
|
---|
276 | *
|
---|
277 | * If HTTP headers were not sent yet the message is added
|
---|
278 | * to the global message array else it's printed directly
|
---|
279 | * using html_msgarea()
|
---|
280 | *
|
---|
281 | *
|
---|
282 | * Levels can be:
|
---|
283 | *
|
---|
284 | * -1 error
|
---|
285 | * 0 info
|
---|
286 | * 1 success
|
---|
287 | *
|
---|
288 | * @author Andreas Gohr <[email protected]>
|
---|
289 | * @see html_msgarea
|
---|
290 | */
|
---|
291 | function msg($message,$lvl=0,$line='',$file=''){
|
---|
292 | global $MSG, $MSG_shown;
|
---|
293 | $errors[-1] = 'error';
|
---|
294 | $errors[0] = 'info';
|
---|
295 | $errors[1] = 'success';
|
---|
296 | $errors[2] = 'notify';
|
---|
297 |
|
---|
298 | if($line || $file) $message.=' ['.basename($file).':'.$line.']';
|
---|
299 |
|
---|
300 | if(!isset($MSG)) $MSG = array();
|
---|
301 | $MSG[]=array('lvl' => $errors[$lvl], 'msg' => $message);
|
---|
302 | if(isset($MSG_shown) || headers_sent()){
|
---|
303 | if(function_exists('html_msgarea')){
|
---|
304 | html_msgarea();
|
---|
305 | }else{
|
---|
306 | print "ERROR($lvl) $message";
|
---|
307 | }
|
---|
308 | unset($GLOBALS['MSG']);
|
---|
309 | }
|
---|
310 | }
|
---|
311 |
|
---|
312 | /**
|
---|
313 | * print debug messages
|
---|
314 | *
|
---|
315 | * little function to print the content of a var
|
---|
316 | *
|
---|
317 | * @author Andreas Gohr <[email protected]>
|
---|
318 | */
|
---|
319 | function dbg($msg,$hidden=false){
|
---|
320 | if($hidden){
|
---|
321 | echo "<!--\n";
|
---|
322 | print_r($msg);
|
---|
323 | echo "\n-->";
|
---|
324 | }else{
|
---|
325 | echo '<pre class="dbg">';
|
---|
326 | echo hsc(print_r($msg,true));
|
---|
327 | echo '</pre>';
|
---|
328 | }
|
---|
329 | }
|
---|
330 |
|
---|
331 | /**
|
---|
332 | * Print info to a log file
|
---|
333 | *
|
---|
334 | * @author Andreas Gohr <[email protected]>
|
---|
335 | */
|
---|
336 | function dbglog($msg,$header=''){
|
---|
337 | global $conf;
|
---|
338 | // The debug log isn't automatically cleaned thus only write it when
|
---|
339 | // debugging has been enabled by the user.
|
---|
340 | if($conf['allowdebug'] !== 1) return;
|
---|
341 | if(is_object($msg) || is_array($msg)){
|
---|
342 | $msg = print_r($msg,true);
|
---|
343 | }
|
---|
344 |
|
---|
345 | if($header) $msg = "$header\n$msg";
|
---|
346 |
|
---|
347 | $file = $conf['cachedir'].'/debug.log';
|
---|
348 | $fh = fopen($file,'a');
|
---|
349 | if($fh){
|
---|
350 | fwrite($fh,date('H:i:s ').$_SERVER['REMOTE_ADDR'].': '.$msg."\n");
|
---|
351 | fclose($fh);
|
---|
352 | }
|
---|
353 | }
|
---|
354 |
|
---|
355 | /**
|
---|
356 | * Print a reversed, prettyprinted backtrace
|
---|
357 | *
|
---|
358 | * @author Gary Owen <[email protected]>
|
---|
359 | */
|
---|
360 | function dbg_backtrace(){
|
---|
361 | // Get backtrace
|
---|
362 | $backtrace = debug_backtrace();
|
---|
363 |
|
---|
364 | // Unset call to debug_print_backtrace
|
---|
365 | array_shift($backtrace);
|
---|
366 |
|
---|
367 | // Iterate backtrace
|
---|
368 | $calls = array();
|
---|
369 | $depth = count($backtrace) - 1;
|
---|
370 | foreach ($backtrace as $i => $call) {
|
---|
371 | $location = $call['file'] . ':' . $call['line'];
|
---|
372 | $function = (isset($call['class'])) ?
|
---|
373 | $call['class'] . $call['type'] . $call['function'] : $call['function'];
|
---|
374 |
|
---|
375 | $params = array();
|
---|
376 | if (isset($call['args'])){
|
---|
377 | foreach($call['args'] as $arg){
|
---|
378 | if(is_object($arg)){
|
---|
379 | $params[] = '[Object '.get_class($arg).']';
|
---|
380 | }elseif(is_array($arg)){
|
---|
381 | $params[] = '[Array]';
|
---|
382 | }elseif(is_null($arg)){
|
---|
383 | $param[] = '[NULL]';
|
---|
384 | }else{
|
---|
385 | $params[] = (string) '"'.$arg.'"';
|
---|
386 | }
|
---|
387 | }
|
---|
388 | }
|
---|
389 | $params = implode(', ',$params);
|
---|
390 |
|
---|
391 | $calls[$depth - $i] = sprintf('%s(%s) called at %s',
|
---|
392 | $function,
|
---|
393 | str_replace("\n", '\n', $params),
|
---|
394 | $location);
|
---|
395 | }
|
---|
396 | ksort($calls);
|
---|
397 |
|
---|
398 | return implode("\n", $calls);
|
---|
399 | }
|
---|
400 |
|
---|
401 | /**
|
---|
402 | * Remove all data from an array where the key seems to point to sensitive data
|
---|
403 | *
|
---|
404 | * This is used to remove passwords, mail addresses and similar data from the
|
---|
405 | * debug output
|
---|
406 | *
|
---|
407 | * @author Andreas Gohr <[email protected]>
|
---|
408 | */
|
---|
409 | function debug_guard(&$data){
|
---|
410 | foreach($data as $key => $value){
|
---|
411 | if(preg_match('/(notify|pass|auth|secret|ftp|userinfo|token|buid|mail|proxy)/i',$key)){
|
---|
412 | $data[$key] = '***';
|
---|
413 | continue;
|
---|
414 | }
|
---|
415 | if(is_array($value)) debug_guard($data[$key]);
|
---|
416 | }
|
---|
417 | }
|
---|