Creating repository for dokuwiki modifications for sudaraka.org
[sudaraka-org:dokuwiki-mods.git] / inc / infoutils.php
1 <?php
2 /**
3  * Information and debugging functions
4  *
5  * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6  * @author     Andreas Gohr <andi@splitbrain.org>
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 <andi@splitbrain.org>
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_SCRIPT)){
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 <andi@splitbrain.org>
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 <anika@selfthinker.org>
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 <andi@splitbrain.org>
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(is_writable(DOKU_CONF)){
179         msg('conf directory is writable',1);
180     }else{
181         msg('conf directory is not writable',-1);
182     }
183
184     if($conf['authtype'] == 'plain'){
185         global $config_cascade;
186         if(is_writable($config_cascade['plainauth.users']['default'])){
187             msg('conf/users.auth.php is writable',1);
188         }else{
189             msg('conf/users.auth.php is not writable',0);
190         }
191     }
192
193     if(function_exists('mb_strpos')){
194         if(defined('UTF8_NOMBSTRING')){
195             msg('mb_string extension is available but will not be used',0);
196         }else{
197             msg('mb_string extension is available and will be used',1);
198             if(ini_get('mbstring.func_overload') != 0){
199                 msg('mb_string function overloading is enabled, this will cause problems and should be disabled',-1);
200             }
201         }
202     }else{
203         msg('mb_string extension not available - PHP only replacements will be used',0);
204     }
205
206     if($conf['allowdebug']){
207         msg('Debugging support is enabled. If you don\'t need it you should set $conf[\'allowdebug\'] = 0',-1);
208     }else{
209         msg('Debugging support is disabled',1);
210     }
211
212     if($INFO['userinfo']['name']){
213         msg('You are currently logged in as '.$_SERVER['REMOTE_USER'].' ('.$INFO['userinfo']['name'].')',0);
214         msg('You are part of the groups '.join($INFO['userinfo']['grps'],', '),0);
215     }else{
216         msg('You are currently not logged in',0);
217     }
218
219     msg('Your current permission for this page is '.$INFO['perm'],0);
220
221     if(is_writable($INFO['filepath'])){
222         msg('The current page is writable by the webserver',0);
223     }else{
224         msg('The current page is not writable by the webserver',0);
225     }
226
227     if($INFO['writable']){
228         msg('The current page is writable by you',0);
229     }else{
230         msg('The current page is not writable by you',0);
231     }
232
233     $check = wl('','',true).'data/_dummy';
234     $http = new DokuHTTPClient();
235     $http->timeout = 6;
236     $res = $http->get($check);
237     if(strpos($res,'data directory') !== false){
238         msg('It seems like 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     }elseif($http->status == 404 || $http->status == 403){
242         msg('The data directory seems to be properly protected',1);
243     }else{
244         msg('Failed to check if the data directory is accessible from the web.
245                 Make sure this directory is properly protected
246                 (See <a href="http://www.dokuwiki.org/security">security</a>)',-1);
247     }
248
249     // Check for corrupted search index
250     $lengths = idx_listIndexLengths();
251     $index_corrupted = false;
252     foreach ($lengths as $length) {
253         if (count(idx_getIndex('w', $length)) != count(idx_getIndex('i', $length))) {
254             $index_corrupted = true;
255             break;
256         }
257     }
258
259     foreach (idx_getIndex('metadata', '') as $index) {
260         if (count(idx_getIndex($index.'_w', '')) != count(idx_getIndex($index.'_i', ''))) {
261             $index_corrupted = true;
262             break;
263         }
264     }
265
266     if ($index_corrupted)
267         msg('The search index is corrupted. It might produce wrong results and most
268                 probably needs to be rebuilt. See
269                 <a href="http://www.dokuwiki.org/faq:searchindex">faq:searchindex</a>
270                 for ways to rebuild the search index.', -1);
271     elseif (!empty($lengths))
272         msg('The search index seems to be working', 1);
273     else
274         msg('The search index is empty. See
275                 <a href="http://www.dokuwiki.org/faq:searchindex">faq:searchindex</a>
276                 for help on how to fix the search index. If the default indexer
277                 isn\'t used or the wiki is actually empty this is normal.');
278 }
279
280 /**
281  * print a message
282  *
283  * If HTTP headers were not sent yet the message is added
284  * to the global message array else it's printed directly
285  * using html_msgarea()
286  *
287  *
288  * Levels can be:
289  *
290  * -1 error
291  *  0 info
292  *  1 success
293  *
294  * @author Andreas Gohr <andi@splitbrain.org>
295  * @see    html_msgarea
296  */
297 function msg($message,$lvl=0,$line='',$file=''){
298     global $MSG, $MSG_shown;
299     $errors[-1] = 'error';
300     $errors[0]  = 'info';
301     $errors[1]  = 'success';
302     $errors[2]  = 'notify';
303
304     if($line || $file) $message.=' ['.basename($file).':'.$line.']';
305
306     if(!isset($MSG)) $MSG = array();
307     $MSG[]=array('lvl' => $errors[$lvl], 'msg' => $message);
308     if(isset($MSG_shown) || headers_sent()){
309         if(function_exists('html_msgarea')){
310             html_msgarea();
311         }else{
312             print "ERROR($lvl) $message";
313         }
314         unset($GLOBALS['MSG']);
315     }
316 }
317
318 /**
319  * print debug messages
320  *
321  * little function to print the content of a var
322  *
323  * @author Andreas Gohr <andi@splitbrain.org>
324  */
325 function dbg($msg,$hidden=false){
326     if($hidden){
327         echo "<!--\n";
328         print_r($msg);
329         echo "\n-->";
330     }else{
331         echo '<pre class="dbg">';
332         echo hsc(print_r($msg,true));
333         echo '</pre>';
334     }
335 }
336
337 /**
338  * Print info to a log file
339  *
340  * @author Andreas Gohr <andi@splitbrain.org>
341  */
342 function dbglog($msg,$header=''){
343     global $conf;
344     // The debug log isn't automatically cleaned thus only write it when
345     // debugging has been enabled by the user.
346     if($conf['allowdebug'] !== 1) return;
347     if(is_object($msg) || is_array($msg)){
348         $msg = print_r($msg,true);
349     }
350
351     if($header) $msg = "$header\n$msg";
352
353     $file = $conf['cachedir'].'/debug.log';
354     $fh = fopen($file,'a');
355     if($fh){
356         fwrite($fh,date('H:i:s ').$_SERVER['REMOTE_ADDR'].': '.$msg."\n");
357         fclose($fh);
358     }
359 }
360
361 /**
362  * Print a reversed, prettyprinted backtrace
363  *
364  * @author Gary Owen <gary_owen@bigfoot.com>
365  */
366 function dbg_backtrace(){
367     // Get backtrace
368     $backtrace = debug_backtrace();
369
370     // Unset call to debug_print_backtrace
371     array_shift($backtrace);
372
373     // Iterate backtrace
374     $calls = array();
375     $depth = count($backtrace) - 1;
376     foreach ($backtrace as $i => $call) {
377         $location = $call['file'] . ':' . $call['line'];
378         $function = (isset($call['class'])) ?
379             $call['class'] . $call['type'] . $call['function'] : $call['function'];
380
381         $params = array();
382         if (isset($call['args'])){
383             foreach($call['args'] as $arg){
384                 if(is_object($arg)){
385                     $params[] = '[Object '.get_class($arg).']';
386                 }elseif(is_array($arg)){
387                     $params[] = '[Array]';
388                 }elseif(is_null($arg)){
389                     $param[] = '[NULL]';
390                 }else{
391                     $params[] = (string) '"'.$arg.'"';
392                 }
393             }
394         }
395         $params = implode(', ',$params);
396
397         $calls[$depth - $i] = sprintf('%s(%s) called at %s',
398                 $function,
399                 str_replace("\n", '\n', $params),
400                 $location);
401     }
402     ksort($calls);
403
404     return implode("\n", $calls);
405 }
406
407 /**
408  * Remove all data from an array where the key seems to point to sensitive data
409  *
410  * This is used to remove passwords, mail addresses and similar data from the
411  * debug output
412  *
413  * @author Andreas Gohr <andi@splitbrain.org>
414  */
415 function debug_guard(&$data){
416     foreach($data as $key => $value){
417         if(preg_match('/(notify|pass|auth|secret|ftp|userinfo|token|buid|mail|proxy)/i',$key)){
418             $data[$key] = '***';
419             continue;
420         }
421         if(is_array($value)) debug_guard($data[$key]);
422     }
423 }