xref: /dokuwiki/inc/common.php (revision ee4c4a1b5a5840c1b9d2d8c74b3f4298dd52928b)
1ed7b5f09Sandi<?php
215fae107Sandi/**
315fae107Sandi * Common DokuWiki functions
415fae107Sandi *
515fae107Sandi * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
615fae107Sandi * @author     Andreas Gohr <andi@splitbrain.org>
715fae107Sandi */
815fae107Sandi
9ed7b5f09Sandi  if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../').'/');
10e7cb32dcSAndreas Gohr  require_once(DOKU_CONF.'dokuwiki.php');
11ed7b5f09Sandi  require_once(DOKU_INC.'inc/io.php');
12ed7b5f09Sandi  require_once(DOKU_INC.'inc/utf8.php');
13ed7b5f09Sandi  require_once(DOKU_INC.'inc/mail.php');
14c112d578Sandi  require_once(DOKU_INC.'inc/parserutils.php');
15f3f0262cSandi
16f3f0262cSandi/**
17b6912aeaSAndreas Gohr * These constants are used with the recents function
18b6912aeaSAndreas Gohr */
19b6912aeaSAndreas Gohrdefine('RECENTS_SKIP_DELETED',2);
20b6912aeaSAndreas Gohrdefine('RECENTS_SKIP_MINORS',4);
21b6912aeaSAndreas Gohrdefine('RECENTS_SKIP_SUBSPACES',8);
22b6912aeaSAndreas Gohr
23b6912aeaSAndreas Gohr/**
2415fae107Sandi * Return info about the current document as associative
25f3f0262cSandi * array.
2615fae107Sandi *
2715fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
28f3f0262cSandi */
29f3f0262cSandifunction pageinfo(){
30f3f0262cSandi  global $ID;
31f3f0262cSandi  global $REV;
32f3f0262cSandi  global $USERINFO;
33f3f0262cSandi  global $conf;
34f3f0262cSandi
35f3f0262cSandi  if($_SERVER['REMOTE_USER']){
36f3f0262cSandi    $info['userinfo']   = $USERINFO;
37f3f0262cSandi    $info['perm']       = auth_quickaclcheck($ID);
381380fc45SAndreas Gohr    $info['subscribed'] = is_subscribed($ID,$_SERVER['REMOTE_USER']);
39*ee4c4a1bSAndreas Gohr    $info['client']     = $_SERVER['REMOTE_USER'];
4017ee7f66SAndreas Gohr
4117ee7f66SAndreas Gohr    // if some outside auth were used only REMOTE_USER is set
4217ee7f66SAndreas Gohr    if(!$info['userinfo']['name']){
4317ee7f66SAndreas Gohr      $info['userinfo']['name'] = $_SERVER['REMOTE_USER'];
4417ee7f66SAndreas Gohr    }
45*ee4c4a1bSAndreas Gohr
46f3f0262cSandi  }else{
47f3f0262cSandi    $info['perm']       = auth_aclcheck($ID,'',null);
481380fc45SAndreas Gohr    $info['subscribed'] = false;
49*ee4c4a1bSAndreas Gohr    $info['client']     = clientIP(true);
50f3f0262cSandi  }
51f3f0262cSandi
52f3f0262cSandi  $info['namespace'] = getNS($ID);
53f3f0262cSandi  $info['locked']    = checklock($ID);
54f3f0262cSandi  $info['filepath']  = realpath(wikiFN($ID,$REV));
55f3f0262cSandi  $info['exists']    = @file_exists($info['filepath']);
56f3f0262cSandi  if($REV && !$info['exists']){
57f3f0262cSandi    //check if current revision was meant
58f3f0262cSandi    $cur = wikiFN($ID);
59f3f0262cSandi    if(@file_exists($cur) && (@filemtime($cur) == $REV)){
60f3f0262cSandi      $info['filepath'] = realpath($cur);
61f3f0262cSandi      $info['exists']   = true;
62f3f0262cSandi      $REV = '';
63f3f0262cSandi    }
64f3f0262cSandi  }
65c112d578Sandi  $info['rev'] = $REV;
66f3f0262cSandi  if($info['exists']){
67f3f0262cSandi    $info['writable'] = (is_writable($info['filepath']) &&
68f3f0262cSandi                         ($info['perm'] >= AUTH_EDIT));
69f3f0262cSandi  }else{
70f3f0262cSandi    $info['writable'] = ($info['perm'] >= AUTH_CREATE);
71f3f0262cSandi  }
72f3f0262cSandi  $info['editable']  = ($info['writable'] && empty($info['lock']));
73f3f0262cSandi  $info['lastmod']   = @filemtime($info['filepath']);
74f3f0262cSandi
75652610a2Sandi  //who's the editor
76652610a2Sandi  if($REV){
77652610a2Sandi    $revinfo = getRevisionInfo($ID,$REV);
78652610a2Sandi  }else{
79652610a2Sandi    $revinfo = getRevisionInfo($ID,$info['lastmod']);
80652610a2Sandi  }
81652610a2Sandi  $info['ip']     = $revinfo['ip'];
82652610a2Sandi  $info['user']   = $revinfo['user'];
83652610a2Sandi  $info['sum']    = $revinfo['sum'];
84b6912aeaSAndreas Gohr  $info['minor']  = $revinfo['minor'];
8559f257aeSchris
8688f522e9Sandi  if($revinfo['user']){
8788f522e9Sandi    $info['editor'] = $revinfo['user'];
8888f522e9Sandi  }else{
8988f522e9Sandi    $info['editor'] = $revinfo['ip'];
9088f522e9Sandi  }
91652610a2Sandi
92*ee4c4a1bSAndreas Gohr  // draft
93*ee4c4a1bSAndreas Gohr  $draft = getCacheName($info['client'].$ID,'.draft');
94*ee4c4a1bSAndreas Gohr  if(@file_exists($draft)){
95*ee4c4a1bSAndreas Gohr    if(@filemtime($draft) < @filemtime(wikiFN($ID))){
96*ee4c4a1bSAndreas Gohr      // remove stale draft
97*ee4c4a1bSAndreas Gohr      @unlink($draft);
98*ee4c4a1bSAndreas Gohr    }else{
99*ee4c4a1bSAndreas Gohr      $info['draft'] = $draft;
100*ee4c4a1bSAndreas Gohr    }
101*ee4c4a1bSAndreas Gohr  }
102*ee4c4a1bSAndreas Gohr
103f3f0262cSandi  return $info;
104f3f0262cSandi}
105f3f0262cSandi
106f3f0262cSandi/**
1072684e50aSAndreas Gohr * Build an string of URL parameters
1082684e50aSAndreas Gohr *
1092684e50aSAndreas Gohr * @author Andreas Gohr
1102684e50aSAndreas Gohr */
111b174aeaeSchrisfunction buildURLparams($params, $sep='&amp;'){
1122684e50aSAndreas Gohr  $url = '';
1132684e50aSAndreas Gohr  $amp = false;
1142684e50aSAndreas Gohr  foreach($params as $key => $val){
115b174aeaeSchris    if($amp) $url .= $sep;
1162684e50aSAndreas Gohr
1172684e50aSAndreas Gohr    $url .= $key.'=';
118b6c6979fSAndreas Gohr    $url .= rawurlencode($val);
1192684e50aSAndreas Gohr    $amp = true;
1202684e50aSAndreas Gohr  }
1212684e50aSAndreas Gohr  return $url;
1222684e50aSAndreas Gohr}
1232684e50aSAndreas Gohr
1242684e50aSAndreas Gohr/**
1252684e50aSAndreas Gohr * Build an string of html tag attributes
1262684e50aSAndreas Gohr *
1272684e50aSAndreas Gohr * @author Andreas Gohr
1282684e50aSAndreas Gohr */
1292684e50aSAndreas Gohrfunction buildAttributes($params){
1302684e50aSAndreas Gohr  $url = '';
1312684e50aSAndreas Gohr  foreach($params as $key => $val){
1322684e50aSAndreas Gohr    $url .= $key.'="';
1332684e50aSAndreas Gohr    $url .= htmlspecialchars ($val);
1342684e50aSAndreas Gohr    $url .= '" ';
1352684e50aSAndreas Gohr  }
1362684e50aSAndreas Gohr  return $url;
1372684e50aSAndreas Gohr}
1382684e50aSAndreas Gohr
1392684e50aSAndreas Gohr
1402684e50aSAndreas Gohr/**
1410396becbSandi * print a message
1420396becbSandi *
1430396becbSandi * If HTTP headers were not sent yet the message is added
1440396becbSandi * to the global message array else it's printed directly
1450396becbSandi * using html_msgarea()
1460396becbSandi *
147f3f0262cSandi *
148f3f0262cSandi * Levels can be:
149f3f0262cSandi *
150f3f0262cSandi * -1 error
151f3f0262cSandi *  0 info
152f3f0262cSandi *  1 success
15315fae107Sandi *
15415fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
1550396becbSandi * @see    html_msgarea
156f3f0262cSandi */
1570d58d74eSAndreas Gohrfunction msg($message,$lvl=0,$line='',$file=''){
158f3f0262cSandi  global $MSG;
159f3f0262cSandi  $errors[-1] = 'error';
160f3f0262cSandi  $errors[0]  = 'info';
161f3f0262cSandi  $errors[1]  = 'success';
162f3f0262cSandi
1630d58d74eSAndreas Gohr  if($line || $file) $message.=' ['.basename($file).':'.$line.']';
1640d58d74eSAndreas Gohr
165cc20ad51Sandi  if(!headers_sent()){
166f3f0262cSandi    if(!isset($MSG)) $MSG = array();
167f3f0262cSandi    $MSG[]=array('lvl' => $errors[$lvl], 'msg' => $message);
1680396becbSandi  }else{
1690396becbSandi    $MSG = array();
1700396becbSandi    $MSG[]=array('lvl' => $errors[$lvl], 'msg' => $message);
171f62ea8a1Sandi    if(function_exists('html_msgarea')){
1720396becbSandi      html_msgarea();
173f62ea8a1Sandi    }else{
174f62ea8a1Sandi      print "ERROR($lvl) $message";
175f62ea8a1Sandi    }
1760396becbSandi  }
177f3f0262cSandi}
178f3f0262cSandi
179f3f0262cSandi/**
18015fae107Sandi * This builds the breadcrumb trail and returns it as array
18115fae107Sandi *
18215fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
183f3f0262cSandi */
184f3f0262cSandifunction breadcrumbs(){
1858746e727Sandi  // we prepare the breadcrumbs early for quick session closing
1868746e727Sandi  static $crumbs = null;
1878746e727Sandi  if($crumbs != null) return $crumbs;
1888746e727Sandi
189f3f0262cSandi  global $ID;
190f3f0262cSandi  global $ACT;
191f3f0262cSandi  global $conf;
192f3f0262cSandi  $crumbs = $_SESSION[$conf['title']]['bc'];
193f3f0262cSandi
194f3f0262cSandi  //first visit?
195f3f0262cSandi  if (!is_array($crumbs)){
196f3f0262cSandi    $crumbs = array();
197f3f0262cSandi  }
198f3f0262cSandi  //we only save on show and existing wiki documents
199a77f5846Sjan  $file = wikiFN($ID);
200a77f5846Sjan  if($ACT != 'show' || !@file_exists($file)){
201f3f0262cSandi    $_SESSION[$conf['title']]['bc'] = $crumbs;
202f3f0262cSandi    return $crumbs;
203f3f0262cSandi  }
204a77f5846Sjan
205a77f5846Sjan  // page names
206a77f5846Sjan  $name = noNS($ID);
207a77f5846Sjan  if ($conf['useheading']) {
208a77f5846Sjan    // get page title
209bb0a59d4Sjan    $title = p_get_first_heading($ID);
210a77f5846Sjan    if ($title) {
211a77f5846Sjan      $name = $title;
212a77f5846Sjan    }
213a77f5846Sjan  }
214a77f5846Sjan
215f3f0262cSandi  //remove ID from array
216a77f5846Sjan  if (isset($crumbs[$ID])) {
217a77f5846Sjan    unset($crumbs[$ID]);
218f3f0262cSandi  }
219f3f0262cSandi
220f3f0262cSandi  //add to array
221a77f5846Sjan  $crumbs[$ID] = $name;
222f3f0262cSandi  //reduce size
223f3f0262cSandi  while(count($crumbs) > $conf['breadcrumbs']){
224f3f0262cSandi    array_shift($crumbs);
225f3f0262cSandi  }
226f3f0262cSandi  //save to session
227f3f0262cSandi  $_SESSION[$conf['title']]['bc'] = $crumbs;
228f3f0262cSandi  return $crumbs;
229f3f0262cSandi}
230f3f0262cSandi
231f3f0262cSandi/**
23215fae107Sandi * Filter for page IDs
23315fae107Sandi *
234f3f0262cSandi * This is run on a ID before it is outputted somewhere
235f3f0262cSandi * currently used to replace the colon with something else
236f3f0262cSandi * on Windows systems and to have proper URL encoding
23715fae107Sandi *
23849c713a3Sandi * Urlencoding is ommitted when the second parameter is false
23949c713a3Sandi *
24015fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
241f3f0262cSandi */
24249c713a3Sandifunction idfilter($id,$ue=true){
243f3f0262cSandi  global $conf;
244f3f0262cSandi  if ($conf['useslash'] && $conf['userewrite']){
245f3f0262cSandi    $id = strtr($id,':','/');
246f3f0262cSandi  }elseif (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' &&
247f3f0262cSandi      $conf['userewrite']) {
248f3f0262cSandi    $id = strtr($id,':',';');
249f3f0262cSandi  }
25049c713a3Sandi  if($ue){
251b6c6979fSAndreas Gohr    $id = rawurlencode($id);
252f3f0262cSandi    $id = str_replace('%3A',':',$id); //keep as colon
253f3f0262cSandi    $id = str_replace('%2F','/',$id); //keep as slash
25449c713a3Sandi  }
255f3f0262cSandi  return $id;
256f3f0262cSandi}
257f3f0262cSandi
258f3f0262cSandi/**
259ed7b5f09Sandi * This builds a link to a wikipage
26015fae107Sandi *
2616c7843b5Sandi * It handles URL rewriting and adds additional parameter if
2626c7843b5Sandi * given in $more
2636c7843b5Sandi *
26415fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
265f3f0262cSandi */
266b174aeaeSchrisfunction wl($id='',$more='',$abs=false,$sep='&amp;'){
267f3f0262cSandi  global $conf;
2686de3759aSAndreas Gohr  if(is_array($more)){
269b174aeaeSchris    $more = buildURLparams($more,$sep);
2706de3759aSAndreas Gohr  }else{
271b174aeaeSchris    $more = str_replace(',',$sep,$more);
2726de3759aSAndreas Gohr  }
273f3f0262cSandi
274f3f0262cSandi  $id    = idfilter($id);
275ed7b5f09Sandi  if($abs){
276ed7b5f09Sandi    $xlink = DOKU_URL;
277ed7b5f09Sandi  }else{
278ed7b5f09Sandi    $xlink = DOKU_BASE;
279ed7b5f09Sandi  }
280f3f0262cSandi
2816c7843b5Sandi  if($conf['userewrite'] == 2){
2826c7843b5Sandi    $xlink .= DOKU_SCRIPT.'/'.$id;
2836c7843b5Sandi    if($more) $xlink .= '?'.$more;
2846c7843b5Sandi  }elseif($conf['userewrite']){
285f3f0262cSandi    $xlink .= $id;
286f3f0262cSandi    if($more) $xlink .= '?'.$more;
2876c7843b5Sandi  }else{
2886c7843b5Sandi    $xlink .= DOKU_SCRIPT.'?id='.$id;
289b174aeaeSchris    if($more) $xlink .= $sep.$more;
290f3f0262cSandi  }
291f3f0262cSandi
292f3f0262cSandi  return $xlink;
293f3f0262cSandi}
294f3f0262cSandi
295f3f0262cSandi/**
2966de3759aSAndreas Gohr * Build a link to a media file
2976de3759aSAndreas Gohr *
2986de3759aSAndreas Gohr * Will return a link to the detail page if $direct is false
2996de3759aSAndreas Gohr */
300b174aeaeSchrisfunction ml($id='',$more='',$direct=true,$sep='&amp;'){
3016de3759aSAndreas Gohr  global $conf;
3026de3759aSAndreas Gohr  if(is_array($more)){
303b174aeaeSchris    $more = buildURLparams($more,$sep);
3046de3759aSAndreas Gohr  }else{
305b174aeaeSchris    $more = str_replace(',',$sep,$more);
3066de3759aSAndreas Gohr  }
3076de3759aSAndreas Gohr
3086de3759aSAndreas Gohr  $xlink = DOKU_BASE;
3096de3759aSAndreas Gohr
3106de3759aSAndreas Gohr  // external URLs are always direct without rewriting
3116de3759aSAndreas Gohr  if(preg_match('#^(https?|ftp)://#i',$id)){
3126de3759aSAndreas Gohr    $xlink .= 'lib/exe/fetch.php';
3136de3759aSAndreas Gohr    if($more){
3146de3759aSAndreas Gohr      $xlink .= '?'.$more;
315b174aeaeSchris      $xlink .= $sep.'media='.rawurlencode($id);
3166de3759aSAndreas Gohr    }else{
317b6c6979fSAndreas Gohr      $xlink .= '?media='.rawurlencode($id);
3186de3759aSAndreas Gohr    }
3196de3759aSAndreas Gohr    return $xlink;
3206de3759aSAndreas Gohr  }
3216de3759aSAndreas Gohr
3226de3759aSAndreas Gohr  $id = idfilter($id);
3236de3759aSAndreas Gohr
3246de3759aSAndreas Gohr  // decide on scriptname
3256de3759aSAndreas Gohr  if($direct){
3266de3759aSAndreas Gohr    if($conf['userewrite'] == 1){
3276de3759aSAndreas Gohr      $script = '_media';
3286de3759aSAndreas Gohr    }else{
3296de3759aSAndreas Gohr      $script = 'lib/exe/fetch.php';
3306de3759aSAndreas Gohr    }
3316de3759aSAndreas Gohr  }else{
3326de3759aSAndreas Gohr    if($conf['userewrite'] == 1){
3336de3759aSAndreas Gohr      $script = '_detail';
3346de3759aSAndreas Gohr    }else{
3356de3759aSAndreas Gohr      $script = 'lib/exe/detail.php';
3366de3759aSAndreas Gohr    }
3376de3759aSAndreas Gohr  }
3386de3759aSAndreas Gohr
3396de3759aSAndreas Gohr  // build URL based on rewrite mode
3406de3759aSAndreas Gohr   if($conf['userewrite']){
3416de3759aSAndreas Gohr     $xlink .= $script.'/'.$id;
3426de3759aSAndreas Gohr     if($more) $xlink .= '?'.$more;
3436de3759aSAndreas Gohr   }else{
3446de3759aSAndreas Gohr     if($more){
345a99d3236SEsther Brunner       $xlink .= $script.'?'.$more;
346b174aeaeSchris       $xlink .= $sep.'media='.$id;
3476de3759aSAndreas Gohr     }else{
348a99d3236SEsther Brunner       $xlink .= $script.'?media='.$id;
3496de3759aSAndreas Gohr     }
3506de3759aSAndreas Gohr   }
3516de3759aSAndreas Gohr
3526de3759aSAndreas Gohr  return $xlink;
3536de3759aSAndreas Gohr}
3546de3759aSAndreas Gohr
3556de3759aSAndreas Gohr
3566de3759aSAndreas Gohr
3576de3759aSAndreas Gohr/**
358f3f0262cSandi * Just builds a link to a script
35915fae107Sandi *
360ed7b5f09Sandi * @todo   maybe obsolete
36115fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
362f3f0262cSandi */
363f3f0262cSandifunction script($script='doku.php'){
364ed7b5f09Sandi#  $link = getBaseURL();
365ed7b5f09Sandi#  $link .= $script;
366ed7b5f09Sandi#  return $link;
367ed7b5f09Sandi  return DOKU_BASE.DOKU_SCRIPT;
368f3f0262cSandi}
369f3f0262cSandi
370f3f0262cSandi/**
37115fae107Sandi * Spamcheck against wordlist
37215fae107Sandi *
373f3f0262cSandi * Checks the wikitext against a list of blocked expressions
374f3f0262cSandi * returns true if the text contains any bad words
37515fae107Sandi *
37615fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
377f3f0262cSandi */
378f3f0262cSandifunction checkwordblock(){
379f3f0262cSandi  global $TEXT;
380f3f0262cSandi  global $conf;
381f3f0262cSandi
382f3f0262cSandi  if(!$conf['usewordblock']) return false;
383f3f0262cSandi
384b9ac8716Schris  $wordblocks = getWordblocks();
3853e2965d7Sandi  //how many lines to read at once (to work around some PCRE limits)
3863e2965d7Sandi  if(version_compare(phpversion(),'4.3.0','<')){
3873e2965d7Sandi    //old versions of PCRE define a maximum of parenthesises even if no
3883e2965d7Sandi    //backreferences are used - the maximum is 99
3893e2965d7Sandi    //this is very bad performancewise and may even be too high still
3903e2965d7Sandi    $chunksize = 40;
3913e2965d7Sandi  }else{
392703f6fdeSandi    //read file in chunks of 600 - this should work around the
3933e2965d7Sandi    //MAX_PATTERN_SIZE in modern PCRE
394444b87a5SAndreas Gohr    $chunksize = 400;
3953e2965d7Sandi  }
396b9ac8716Schris  while($blocks = array_splice($wordblocks,0,$chunksize)){
397f3f0262cSandi    $re = array();
398f3f0262cSandi    #build regexp from blocks
399f3f0262cSandi    foreach($blocks as $block){
400f3f0262cSandi      $block = preg_replace('/#.*$/','',$block);
401f3f0262cSandi      $block = trim($block);
402f3f0262cSandi      if(empty($block)) continue;
403f3f0262cSandi      $re[]  = $block;
404f3f0262cSandi    }
405b9ac8716Schris    if(preg_match('#('.join('|',$re).')#si',$TEXT, $match=array())) {
406b9ac8716Schris      return true;
407b9ac8716Schris    }
408703f6fdeSandi  }
409f3f0262cSandi  return false;
410f3f0262cSandi}
411f3f0262cSandi
412f3f0262cSandi/**
41315fae107Sandi * Return the IP of the client
41415fae107Sandi *
4156d8affe6SAndreas Gohr * Honours X-Forwarded-For and X-Real-IP Proxy Headers
41615fae107Sandi *
4176d8affe6SAndreas Gohr * It returns a comma separated list of IPs if the above mentioned
4186d8affe6SAndreas Gohr * headers are set. If the single parameter is set, it tries to return
4196d8affe6SAndreas Gohr * a routable public address, prefering the ones suplied in the X
4206d8affe6SAndreas Gohr * headers
4216d8affe6SAndreas Gohr *
4226d8affe6SAndreas Gohr * @param  boolean $single If set only a single IP is returned
42315fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
424f3f0262cSandi */
4256d8affe6SAndreas Gohrfunction clientIP($single=false){
4266d8affe6SAndreas Gohr  $ip = array();
4276d8affe6SAndreas Gohr  $ip[] = $_SERVER['REMOTE_ADDR'];
4286d8affe6SAndreas Gohr  if($_SERVER['HTTP_X_FORWARDED_FOR'])
4296d8affe6SAndreas Gohr    $ip = array_merge($ip,explode(',',$_SERVER['HTTP_X_FORWARDED_FOR']));
4306d8affe6SAndreas Gohr  if($_SERVER['HTTP_X_REAL_IP'])
4316d8affe6SAndreas Gohr    $ip = array_merge($ip,explode(',',$_SERVER['HTTP_X_REAL_IP']));
4326d8affe6SAndreas Gohr
4336d8affe6SAndreas Gohr  // remove any non-IP stuff
4346d8affe6SAndreas Gohr  $cnt = count($ip);
4356d8affe6SAndreas Gohr  for($i=0; $i<$cnt; $i++){
4366d8affe6SAndreas Gohr    $ip[$i] = preg_replace('/[^0-9\.]+/','',$ip[$i]);
4376d8affe6SAndreas Gohr    if(!preg_match('/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/',$ip[$i])) $ip[$i] = '';
4386d8affe6SAndreas Gohr    if(empty($ip[$i])) unset($ip[$i]);
439f3f0262cSandi  }
4406d8affe6SAndreas Gohr  $ip = array_values(array_unique($ip));
4416d8affe6SAndreas Gohr  if(!$ip[0]) $ip[0] = '0.0.0.0'; // for some strange reason we don't have a IP
4426d8affe6SAndreas Gohr
4436d8affe6SAndreas Gohr  if(!$single) return join(',',$ip);
4446d8affe6SAndreas Gohr
4456d8affe6SAndreas Gohr  // decide which IP to use, trying to avoid local addresses
4466d8affe6SAndreas Gohr  $ip = array_reverse($ip);
4476d8affe6SAndreas Gohr  foreach($ip as $i){
4486d8affe6SAndreas Gohr    if(preg_match('/^(127\.|10\.|192\.168\.|172\.((1[6-9])|(2[0-9])|(3[0-1]))\.)/',$i)){
4496d8affe6SAndreas Gohr      continue;
4506d8affe6SAndreas Gohr    }else{
4516d8affe6SAndreas Gohr      return $i;
4526d8affe6SAndreas Gohr    }
4536d8affe6SAndreas Gohr  }
4546d8affe6SAndreas Gohr  // still here? just use the first (last) address
4556d8affe6SAndreas Gohr  return $ip[0];
456f3f0262cSandi}
457f3f0262cSandi
458f3f0262cSandi/**
45915fae107Sandi * Checks if a given page is currently locked.
46015fae107Sandi *
461f3f0262cSandi * removes stale lockfiles
46215fae107Sandi *
46315fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
464f3f0262cSandi */
465f3f0262cSandifunction checklock($id){
466f3f0262cSandi  global $conf;
467f3f0262cSandi  $lock = wikiFN($id).'.lock';
468f3f0262cSandi
469f3f0262cSandi  //no lockfile
470f3f0262cSandi  if(!@file_exists($lock)) return false;
471f3f0262cSandi
472f3f0262cSandi  //lockfile expired
473f3f0262cSandi  if((time() - filemtime($lock)) > $conf['locktime']){
474f3f0262cSandi    unlink($lock);
475f3f0262cSandi    return false;
476f3f0262cSandi  }
477f3f0262cSandi
478f3f0262cSandi  //my own lock
479f3f0262cSandi  $ip = io_readFile($lock);
480f3f0262cSandi  if( ($ip == clientIP()) || ($ip == $_SERVER['REMOTE_USER']) ){
481f3f0262cSandi    return false;
482f3f0262cSandi  }
483f3f0262cSandi
484f3f0262cSandi  return $ip;
485f3f0262cSandi}
486f3f0262cSandi
487f3f0262cSandi/**
48815fae107Sandi * Lock a page for editing
48915fae107Sandi *
49015fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
491f3f0262cSandi */
492f3f0262cSandifunction lock($id){
493f3f0262cSandi  $lock = wikiFN($id).'.lock';
494f3f0262cSandi  if($_SERVER['REMOTE_USER']){
495f3f0262cSandi    io_saveFile($lock,$_SERVER['REMOTE_USER']);
496f3f0262cSandi  }else{
497f3f0262cSandi    io_saveFile($lock,clientIP());
498f3f0262cSandi  }
499f3f0262cSandi}
500f3f0262cSandi
501f3f0262cSandi/**
50215fae107Sandi * Unlock a page if it was locked by the user
503f3f0262cSandi *
50415fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
50515fae107Sandi * @return bool true if a lock was removed
506f3f0262cSandi */
507f3f0262cSandifunction unlock($id){
508f3f0262cSandi  $lock = wikiFN($id).'.lock';
509f3f0262cSandi  if(@file_exists($lock)){
510f3f0262cSandi    $ip = io_readFile($lock);
511f3f0262cSandi    if( ($ip == clientIP()) || ($ip == $_SERVER['REMOTE_USER']) ){
512f3f0262cSandi      @unlink($lock);
513f3f0262cSandi      return true;
514f3f0262cSandi    }
515f3f0262cSandi  }
516f3f0262cSandi  return false;
517f3f0262cSandi}
518f3f0262cSandi
519f3f0262cSandi/**
520f3f0262cSandi * convert line ending to unix format
521f3f0262cSandi *
52215fae107Sandi * @see    formText() for 2crlf conversion
52315fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
524f3f0262cSandi */
525f3f0262cSandifunction cleanText($text){
526f3f0262cSandi  $text = preg_replace("/(\015\012)|(\015)/","\012",$text);
527f3f0262cSandi  return $text;
528f3f0262cSandi}
529f3f0262cSandi
530f3f0262cSandi/**
531f3f0262cSandi * Prepares text for print in Webforms by encoding special chars.
532f3f0262cSandi * It also converts line endings to Windows format which is
533f3f0262cSandi * pseudo standard for webforms.
534f3f0262cSandi *
53515fae107Sandi * @see    cleanText() for 2unix conversion
53615fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
537f3f0262cSandi */
538f3f0262cSandifunction formText($text){
539f3f0262cSandi  $text = preg_replace("/\012/","\015\012",$text);
540f3f0262cSandi  return htmlspecialchars($text);
541f3f0262cSandi}
542f3f0262cSandi
543f3f0262cSandi/**
54415fae107Sandi * Returns the specified local text in raw format
54515fae107Sandi *
54615fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
547f3f0262cSandi */
548f3f0262cSandifunction rawLocale($id){
549f3f0262cSandi  return io_readFile(localeFN($id));
550f3f0262cSandi}
551f3f0262cSandi
552f3f0262cSandi/**
553f3f0262cSandi * Returns the raw WikiText
55415fae107Sandi *
55515fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
556f3f0262cSandi */
557f3f0262cSandifunction rawWiki($id,$rev=''){
558f3f0262cSandi  return io_readFile(wikiFN($id,$rev));
559f3f0262cSandi}
560f3f0262cSandi
561f3f0262cSandi/**
5627146cee2SAndreas Gohr * Returns the pagetemplate contents for the ID's namespace
5637146cee2SAndreas Gohr *
5647146cee2SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
5657146cee2SAndreas Gohr */
5667146cee2SAndreas Gohrfunction pageTemplate($id){
567a15ce62dSEsther Brunner  global $conf;
568a15ce62dSEsther Brunner  global $INFO;
569a15ce62dSEsther Brunner  $tpl = io_readFile(dirname(wikiFN($id)).'/_template.txt');
570a15ce62dSEsther Brunner  $tpl = str_replace('@ID@',$id,$tpl);
571a15ce62dSEsther Brunner  $tpl = str_replace('@NS@',getNS($id),$tpl);
572a15ce62dSEsther Brunner  $tpl = str_replace('@PAGE@',strtr(noNS($id),'_',' '),$tpl);
573a15ce62dSEsther Brunner  $tpl = str_replace('@USER@',$_SERVER['REMOTE_USER'],$tpl);
574a15ce62dSEsther Brunner  $tpl = str_replace('@NAME@',$INFO['userinfo']['name'],$tpl);
575a15ce62dSEsther Brunner  $tpl = str_replace('@MAIL@',$INFO['userinfo']['mail'],$tpl);
576a15ce62dSEsther Brunner  $tpl = str_replace('@DATE@',date($conf['dformat']),$tpl);
577a15ce62dSEsther Brunner  return $tpl;
5787146cee2SAndreas Gohr}
5797146cee2SAndreas Gohr
5807146cee2SAndreas Gohr
5817146cee2SAndreas Gohr/**
58215fae107Sandi * Returns the raw Wiki Text in three slices.
58315fae107Sandi *
58415fae107Sandi * The range parameter needs to have the form "from-to"
58515cfe303Sandi * and gives the range of the section in bytes - no
58615cfe303Sandi * UTF-8 awareness is needed.
587f3f0262cSandi * The returned order is prefix, section and suffix.
58815fae107Sandi *
58915fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
590f3f0262cSandi */
591f3f0262cSandifunction rawWikiSlices($range,$id,$rev=''){
592f3f0262cSandi  list($from,$to) = split('-',$range,2);
593f3f0262cSandi  $text = io_readFile(wikiFN($id,$rev));
594f3f0262cSandi  if(!$from) $from = 0;
595c3d8e19bSandi  if(!$to)   $to   = strlen($text)+1;
596f3f0262cSandi
59715cfe303Sandi  $slices[0] = substr($text,0,$from-1);
59815cfe303Sandi  $slices[1] = substr($text,$from-1,$to-$from);
59915cfe303Sandi  $slices[2] = substr($text,$to);
600f3f0262cSandi
601f3f0262cSandi  return $slices;
602f3f0262cSandi}
603f3f0262cSandi
604f3f0262cSandi/**
60515fae107Sandi * Joins wiki text slices
60615fae107Sandi *
607f3f0262cSandi * function to join the text slices with correct lineendings again.
608f3f0262cSandi * When the pretty parameter is set to true it adds additional empty
609f3f0262cSandi * lines between sections if needed (used on saving).
61015fae107Sandi *
61115fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
612f3f0262cSandi */
613f3f0262cSandifunction con($pre,$text,$suf,$pretty=false){
614f3f0262cSandi
615f3f0262cSandi  if($pretty){
616f3f0262cSandi    if($pre && substr($pre,-1) != "\n") $pre .= "\n";
617f3f0262cSandi    if($suf && substr($text,-1) != "\n") $text .= "\n";
618f3f0262cSandi  }
619f3f0262cSandi
620f3f0262cSandi  if($pre) $pre .= "\n";
621f3f0262cSandi  if($suf) $text .= "\n";
622f3f0262cSandi  return $pre.$text.$suf;
623f3f0262cSandi}
624f3f0262cSandi
625f3f0262cSandi/**
62615fae107Sandi * print debug messages
62715fae107Sandi *
628f3f0262cSandi * little function to print the content of a var
62915fae107Sandi *
63015fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
631f3f0262cSandi */
632f3f0262cSandifunction dbg($msg,$hidden=false){
633f3f0262cSandi  (!$hidden) ? print '<pre class="dbg">' : print "<!--\n";
634f3f0262cSandi  print_r($msg);
635f3f0262cSandi  (!$hidden) ? print '</pre>' : print "\n-->";
636f3f0262cSandi}
637f3f0262cSandi
638f3f0262cSandi/**
639f3f0262cSandi * Add's an entry to the changelog
64015fae107Sandi *
64115fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
642f3f0262cSandi */
643b6912aeaSAndreas Gohrfunction addLogEntry($date,$id,$summary='',$minor=false){
644f3f0262cSandi  global $conf;
645c1049928Sandi
646c1049928Sandi  if(!@is_writable($conf['changelog'])){
647c1049928Sandi    msg($conf['changelog'].' is not writable!',-1);
648c1049928Sandi    return;
649c1049928Sandi  }
650c1049928Sandi
651652610a2Sandi  if(!$date) $date = time(); //use current time if none supplied
652f3f0262cSandi  $remote = $_SERVER['REMOTE_ADDR'];
653f3f0262cSandi  $user   = $_SERVER['REMOTE_USER'];
654f3f0262cSandi
655b6912aeaSAndreas Gohr  if($conf['useacl'] && $user && $minor){
656b6912aeaSAndreas Gohr    $summary = '*'.$summary;
657b6912aeaSAndreas Gohr  }else{
658b6912aeaSAndreas Gohr    $summary = ' '.$summary;
659b6912aeaSAndreas Gohr  }
660b6912aeaSAndreas Gohr
661f3f0262cSandi  $logline = join("\t",array($date,$remote,$id,$user,$summary))."\n";
662dbb00abcSEsther Brunner  io_saveFile($conf['changelog'],$logline,true);
663f3f0262cSandi}
664f3f0262cSandi
665f3f0262cSandi/**
666b6912aeaSAndreas Gohr * Checks an summary entry if it was a minor edit
667b6912aeaSAndreas Gohr *
668b6912aeaSAndreas Gohr * The summary is cleaned of the marker char
669b6912aeaSAndreas Gohr *
670b6912aeaSAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
671b6912aeaSAndreas Gohr */
672b6912aeaSAndreas Gohrfunction isMinor(&$summary){
673b6912aeaSAndreas Gohr  if(substr($summary,0,1) == '*'){
674b6912aeaSAndreas Gohr    $summary = substr($summary,1);
675b6912aeaSAndreas Gohr    return true;
676b6912aeaSAndreas Gohr  }
677b6912aeaSAndreas Gohr  $summary = trim($summary);
678b6912aeaSAndreas Gohr  return false;
679b6912aeaSAndreas Gohr}
680b6912aeaSAndreas Gohr
681b6912aeaSAndreas Gohr/**
682d437bcc4SAndreas Gohr * Internal function used by getRecents
683d437bcc4SAndreas Gohr *
684d437bcc4SAndreas Gohr * don't call directly
685d437bcc4SAndreas Gohr *
686d437bcc4SAndreas Gohr * @see getRecents()
687d437bcc4SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
688d437bcc4SAndreas Gohr */
689b6912aeaSAndreas Gohrfunction _handleRecent($line,$ns,$flags){
690d437bcc4SAndreas Gohr  static $seen  = array();         //caches seen pages and skip them
691d437bcc4SAndreas Gohr  if(empty($line)) return false;   //skip empty lines
692d437bcc4SAndreas Gohr
693d437bcc4SAndreas Gohr  // split the line into parts
694d437bcc4SAndreas Gohr  list($dt,$ip,$id,$usr,$sum) = explode("\t",$line);
695d437bcc4SAndreas Gohr
696d437bcc4SAndreas Gohr  // skip seen ones
697d437bcc4SAndreas Gohr  if($seen[$id]) return false;
698b6912aeaSAndreas Gohr  $recent = array();
699b6912aeaSAndreas Gohr
700b6912aeaSAndreas Gohr  // check minors
701b6912aeaSAndreas Gohr  if(isMinor($sum)){
702b6912aeaSAndreas Gohr    // skip minors
703b6912aeaSAndreas Gohr    if($flags & RECENTS_SKIP_MINORS) return false;
704b6912aeaSAndreas Gohr    $recent['minor'] = true;
705b6912aeaSAndreas Gohr  }else{
706b6912aeaSAndreas Gohr    $recent['minor'] = false;
707b6912aeaSAndreas Gohr  }
708d437bcc4SAndreas Gohr
709d437bcc4SAndreas Gohr  // remember in seen to skip additional sights
710d437bcc4SAndreas Gohr  $seen[$id] = 1;
711d437bcc4SAndreas Gohr
7120dc92c6fSAndreas Gohr  // check if it's a hidden page
7130dc92c6fSAndreas Gohr  if(isHiddenPage($id)) return false;
7140dc92c6fSAndreas Gohr
715d437bcc4SAndreas Gohr  // filter namespace
716d437bcc4SAndreas Gohr  if (($ns) && (strpos($id,$ns.':') !== 0)) return false;
717d437bcc4SAndreas Gohr
718d437bcc4SAndreas Gohr  // exclude subnamespaces
719b6912aeaSAndreas Gohr  if (($flags & RECENTS_SKIP_SUBSPACES) && (getNS($id) != $ns)) return false;
720d437bcc4SAndreas Gohr
721ae56bfb6SAndreas Gohr  // check ACL
722ae56bfb6SAndreas Gohr  if (auth_quickaclcheck($id) < AUTH_READ) return false;
723ae56bfb6SAndreas Gohr
724d437bcc4SAndreas Gohr  // check existance
725d437bcc4SAndreas Gohr  if(!@file_exists(wikiFN($id))){
726b6912aeaSAndreas Gohr    if($flags & RECENTS_SKIP_DELETED){
727d437bcc4SAndreas Gohr      return false;
728d437bcc4SAndreas Gohr    }else{
729d437bcc4SAndreas Gohr      $recent['del'] = true;
730d437bcc4SAndreas Gohr    }
731d437bcc4SAndreas Gohr  }else{
732d437bcc4SAndreas Gohr    $recent['del'] = false;
733d437bcc4SAndreas Gohr  }
734d437bcc4SAndreas Gohr
735d437bcc4SAndreas Gohr  $recent['id']   = $id;
736d437bcc4SAndreas Gohr  $recent['date'] = $dt;
737d437bcc4SAndreas Gohr  $recent['ip']   = $ip;
738d437bcc4SAndreas Gohr  $recent['user'] = $usr;
739d437bcc4SAndreas Gohr  $recent['sum']  = $sum;
740d437bcc4SAndreas Gohr
741d437bcc4SAndreas Gohr  return $recent;
742d437bcc4SAndreas Gohr}
743d437bcc4SAndreas Gohr
744b6912aeaSAndreas Gohr
745d437bcc4SAndreas Gohr/**
746f3f0262cSandi * returns an array of recently changed files using the
747f3f0262cSandi * changelog
748d437bcc4SAndreas Gohr *
749b6912aeaSAndreas Gohr * The following constants can be used to control which changes are
750b6912aeaSAndreas Gohr * included. Add them together as needed.
751b6912aeaSAndreas Gohr *
752b6912aeaSAndreas Gohr * RECENTS_SKIP_DELETED   - don't include deleted pages
753b6912aeaSAndreas Gohr * RECENTS_SKIP_MINORS    - don't include minor changes
754b6912aeaSAndreas Gohr * RECENTS_SKIP_SUBSPACES - don't include subspaces
755b6912aeaSAndreas Gohr *
756d437bcc4SAndreas Gohr * @param int    $first   number of first entry returned (for paginating
757d437bcc4SAndreas Gohr * @param int    $num     return $num entries
758d437bcc4SAndreas Gohr * @param string $ns      restrict to given namespace
759b6912aeaSAndreas Gohr * @param bool   $flags   see above
76015fae107Sandi *
76115fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
762f3f0262cSandi */
763b6912aeaSAndreas Gohrfunction getRecents($first,$num,$ns='',$flags=0){
764f3f0262cSandi  global $conf;
765f3f0262cSandi  $recent = array();
766d437bcc4SAndreas Gohr  $count  = 0;
7675749f1ceSmatthiasgrimm
7685749f1ceSmatthiasgrimm  if(!$num)
7695749f1ceSmatthiasgrimm    return $recent;
770f3f0262cSandi
771c1049928Sandi  if(!@is_readable($conf['changelog'])){
772c1049928Sandi    msg($conf['changelog'].' is not readable',-1);
773c1049928Sandi    return $recent;
774c1049928Sandi  }
775c1049928Sandi
776d437bcc4SAndreas Gohr  $fh  = fopen($conf['changelog'],'r');
777d437bcc4SAndreas Gohr  $buf = '';
778d437bcc4SAndreas Gohr  $csz = 4096;                              //chunksize
779d437bcc4SAndreas Gohr  fseek($fh,0,SEEK_END);                    // jump to the end
780d437bcc4SAndreas Gohr  $pos = ftell($fh);                        // position pointer
781f3f0262cSandi
782d437bcc4SAndreas Gohr  // now read backwards into buffer
783d437bcc4SAndreas Gohr  while($pos > 0){
784d437bcc4SAndreas Gohr    $pos -= $csz;                           // seek to previous chunk...
785f6c1156dSWolfgang Ocker    if($pos < 0) {                          // ...or rest of file
786f6c1156dSWolfgang Ocker      $csz += $pos;
787f6c1156dSWolfgang Ocker      $pos = 0;
788f6c1156dSWolfgang Ocker    }
789f6c1156dSWolfgang Ocker
790d437bcc4SAndreas Gohr    fseek($fh,$pos);
791dbb00abcSEsther Brunner
792d437bcc4SAndreas Gohr    $buf = fread($fh,$csz).$buf;            // prepend to buffer
7938f1d587cSEsther Brunner
794d437bcc4SAndreas Gohr    $lines = explode("\n",$buf);            // split buffer into lines
7955749f1ceSmatthiasgrimm
796d437bcc4SAndreas Gohr    if($pos > 0){
797d437bcc4SAndreas Gohr      $buf = array_shift($lines);           // first one may be still incomplete
798f3f0262cSandi    }
799d437bcc4SAndreas Gohr
800d437bcc4SAndreas Gohr    $cnt = count($lines);
801d437bcc4SAndreas Gohr    if(!$cnt) continue;                     // no lines yet
802d437bcc4SAndreas Gohr
803d437bcc4SAndreas Gohr    // handle lines
804d437bcc4SAndreas Gohr    for($i = $cnt-1; $i >= 0; $i--){
805b6912aeaSAndreas Gohr      $rec = _handleRecent($lines[$i],$ns,$flags);
806d437bcc4SAndreas Gohr      if($rec !== false){
807d437bcc4SAndreas Gohr        if(--$first >= 0) continue;         // skip first entries
808d437bcc4SAndreas Gohr        $recent[] = $rec;
809d437bcc4SAndreas Gohr        $count++;
810d437bcc4SAndreas Gohr
811d437bcc4SAndreas Gohr        // break while when we have enough entries
812d437bcc4SAndreas Gohr        if($count >= $num){
813d437bcc4SAndreas Gohr          $pos = 0; // will break the while loop
814d437bcc4SAndreas Gohr          break;    // will break the for loop
815f3f0262cSandi        }
816f3f0262cSandi      }
817d437bcc4SAndreas Gohr    }
818d437bcc4SAndreas Gohr  }// end of while
819d437bcc4SAndreas Gohr
820d437bcc4SAndreas Gohr  fclose($fh);
821f3f0262cSandi  return $recent;
822f3f0262cSandi}
823f3f0262cSandi
824f3f0262cSandi/**
825652610a2Sandi * gets additonal informations for a certain pagerevison
826652610a2Sandi * from the changelog
827652610a2Sandi *
828652610a2Sandi * @author Andreas Gohr <andi@splitbrain.org>
829652610a2Sandi */
830652610a2Sandifunction getRevisionInfo($id,$rev){
831652610a2Sandi  global $conf;
832258641c6Sandi
833258641c6Sandi  if(!$rev) return(null);
834258641c6Sandi
835c1049928Sandi  $info = array();
836c1049928Sandi  if(!@is_readable($conf['changelog'])){
837c1049928Sandi    msg($conf['changelog'].' is not readable',-1);
838c1049928Sandi    return $recent;
839c1049928Sandi  }
840652610a2Sandi  $loglines = file($conf['changelog']);
841652610a2Sandi  $loglines = preg_grep("/$rev\t\d+\.\d+\.\d+\.\d+\t$id\t/",$loglines);
842dc42ff59Sandi  $loglines = array_reverse($loglines); //reverse sort on timestamp (shouldn't be needed)
843652610a2Sandi  $line = split("\t",$loglines[0]);
844652610a2Sandi  $info['date']  = $line[0];
845652610a2Sandi  $info['ip']    = $line[1];
846652610a2Sandi  $info['user']  = $line[3];
847652610a2Sandi  $info['sum']   = $line[4];
848b6912aeaSAndreas Gohr  $info['minor'] = isMinor($info['sum']);
849652610a2Sandi  return $info;
850652610a2Sandi}
851652610a2Sandi
852652610a2Sandi/**
853f3f0262cSandi * Saves a wikitext by calling io_saveFile
85415fae107Sandi *
85515fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
856f3f0262cSandi */
857b6912aeaSAndreas Gohrfunction saveWikiText($id,$text,$summary,$minor=false){
858f3f0262cSandi  global $conf;
859f3f0262cSandi  global $lang;
860f3f0262cSandi  // ignore if no changes were made
861f3f0262cSandi  if($text == rawWiki($id,'')){
862f3f0262cSandi    return;
863f3f0262cSandi  }
864f3f0262cSandi
865f3f0262cSandi  $file = wikiFN($id);
866f3f0262cSandi  $old  = saveOldRevision($id);
867f3f0262cSandi
868f3f0262cSandi  if (empty($text)){
869e1f3d9e1SEsther Brunner    // remove empty file
870f3f0262cSandi    @unlink($file);
871e1f3d9e1SEsther Brunner    // remove any meta info
872e1f3d9e1SEsther Brunner    $mfiles = metaFiles($id);
873e1f3d9e1SEsther Brunner    foreach ($mfiles as $mfile) {
874e1f3d9e1SEsther Brunner      if (file_exists($mfile)) @unlink($mfile);
875b158d625SSteven Danz    }
876f3f0262cSandi    $del = true;
8773ce054b3Sandi    // autoset summary on deletion
8783ce054b3Sandi    if(empty($summary)) $summary = $lang['deleted'];
879f864871eSAndreas Gohr    // unlock early
880f864871eSAndreas Gohr    unlock($id);
88153d6ccfeSandi    // remove empty namespaces
88253d6ccfeSandi    io_sweepNS($id);
883f3f0262cSandi  }else{
884f3f0262cSandi    // save file (datadir is created in io_saveFile)
885f3f0262cSandi    io_saveFile($file,$text);
886f3f0262cSandi    $del = false;
887f3f0262cSandi  }
888f3f0262cSandi
889b6912aeaSAndreas Gohr  addLogEntry(@filemtime($file),$id,$summary,$minor);
89026a0801fSAndreas Gohr  // send notify mails
89190033e9dSAndreas Gohr  notify($id,'admin',$old,$summary,$minor);
89290033e9dSAndreas Gohr  notify($id,'subscribers',$old,$summary,$minor);
893f3f0262cSandi
894f3f0262cSandi  //purge cache on add by updating the purgefile
895f3f0262cSandi  if($conf['purgeonadd'] && (!$old || $del)){
89698407a7aSandi    io_saveFile($conf['cachedir'].'/purgefile',time());
897f3f0262cSandi  }
898f3f0262cSandi}
899f3f0262cSandi
900f3f0262cSandi/**
901f3f0262cSandi * moves the current version to the attic and returns its
902f3f0262cSandi * revision date
90315fae107Sandi *
90415fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
905f3f0262cSandi */
906f3f0262cSandifunction saveOldRevision($id){
907f3f0262cSandi  global $conf;
908f3f0262cSandi  $oldf = wikiFN($id);
909f3f0262cSandi  if(!@file_exists($oldf)) return '';
910f3f0262cSandi  $date = filemtime($oldf);
911f3f0262cSandi  $newf = wikiFN($id,$date);
912f3f0262cSandi  if(substr($newf,-3)=='.gz'){
913f3f0262cSandi    io_saveFile($newf,rawWiki($id));
914f3f0262cSandi  }else{
915f3f0262cSandi    io_makeFileDir($newf);
916f3f0262cSandi    copy($oldf, $newf);
917f3f0262cSandi  }
918f3f0262cSandi  return $date;
919f3f0262cSandi}
920f3f0262cSandi
921f3f0262cSandi/**
92226a0801fSAndreas Gohr * Sends a notify mail on page change
92326a0801fSAndreas Gohr *
92426a0801fSAndreas Gohr * @param  string  $id       The changed page
92526a0801fSAndreas Gohr * @param  string  $who      Who to notify (admin|subscribers)
92626a0801fSAndreas Gohr * @param  int     $rev      Old page revision
92726a0801fSAndreas Gohr * @param  string  $summary  What changed
92890033e9dSAndreas Gohr * @param  boolean $minor    Is this a minor edit?
92915fae107Sandi *
93015fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
931f3f0262cSandi */
93290033e9dSAndreas Gohrfunction notify($id,$who,$rev='',$summary='',$minor=false){
933f3f0262cSandi  global $lang;
934f3f0262cSandi  global $conf;
935b158d625SSteven Danz
93626a0801fSAndreas Gohr  // decide if there is something to do
93726a0801fSAndreas Gohr  if($who == 'admin'){
93826a0801fSAndreas Gohr    if(empty($conf['notify'])) return; //notify enabled?
939f3f0262cSandi    $text = rawLocale('mailtext');
94026a0801fSAndreas Gohr    $to   = $conf['notify'];
94126a0801fSAndreas Gohr    $bcc  = '';
94226a0801fSAndreas Gohr  }elseif($who == 'subscribers'){
94326a0801fSAndreas Gohr    if(!$conf['subscribers']) return; //subscribers enabled?
94490033e9dSAndreas Gohr    if($conf['useacl'] && $_SERVER['REMOTE_USER'] && $minor) return; //skip minors
94526a0801fSAndreas Gohr    $bcc  = subscriber_addresslist($id);
94626a0801fSAndreas Gohr    if(empty($bcc)) return;
94726a0801fSAndreas Gohr    $to   = '';
94826a0801fSAndreas Gohr    $text = rawLocale('subscribermail');
94926a0801fSAndreas Gohr  }else{
95026a0801fSAndreas Gohr    return; //just to be safe
95126a0801fSAndreas Gohr  }
95226a0801fSAndreas Gohr
953f3f0262cSandi  $text = str_replace('@DATE@',date($conf['dformat']),$text);
954f3f0262cSandi  $text = str_replace('@BROWSER@',$_SERVER['HTTP_USER_AGENT'],$text);
955f3f0262cSandi  $text = str_replace('@IPADDRESS@',$_SERVER['REMOTE_ADDR'],$text);
956f3f0262cSandi  $text = str_replace('@HOSTNAME@',gethostbyaddr($_SERVER['REMOTE_ADDR']),$text);
957ed7b5f09Sandi  $text = str_replace('@NEWPAGE@',wl($id,'',true),$text);
95826a0801fSAndreas Gohr  $text = str_replace('@PAGE@',$id,$text);
95926a0801fSAndreas Gohr  $text = str_replace('@TITLE@',$conf['title'],$text);
960ed7b5f09Sandi  $text = str_replace('@DOKUWIKIURL@',DOKU_URL,$text);
961f3f0262cSandi  $text = str_replace('@SUMMARY@',$summary,$text);
9627a82afdcSandi  $text = str_replace('@USER@',$_SERVER['REMOTE_USER'],$text);
963f3f0262cSandi
964f3f0262cSandi  if($rev){
965f3f0262cSandi    $subject = $lang['mail_changed'].' '.$id;
966ed7b5f09Sandi    $text = str_replace('@OLDPAGE@',wl($id,"rev=$rev",true),$text);
967ccdfa6c0SAndreas Gohr    require_once(DOKU_INC.'inc/DifferenceEngine.php');
968f3f0262cSandi    $df  = new Diff(split("\n",rawWiki($id,$rev)),
969f3f0262cSandi                    split("\n",rawWiki($id)));
970f3f0262cSandi    $dformat = new UnifiedDiffFormatter();
971f3f0262cSandi    $diff    = $dformat->format($df);
972f3f0262cSandi  }else{
973f3f0262cSandi    $subject=$lang['mail_newpage'].' '.$id;
974f3f0262cSandi    $text = str_replace('@OLDPAGE@','none',$text);
975f3f0262cSandi    $diff = rawWiki($id);
976f3f0262cSandi  }
977f3f0262cSandi  $text = str_replace('@DIFF@',$diff,$text);
978241f3a36Sandi  $subject = '['.$conf['title'].'] '.$subject;
979f3f0262cSandi
98026a0801fSAndreas Gohr  mail_send($to,$subject,$text,$conf['mailfrom'],'',$bcc);
981f3f0262cSandi}
982f3f0262cSandi
98315fae107Sandi/**
98415fae107Sandi * Return a list of available page revisons
98515fae107Sandi *
98615fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
98715fae107Sandi */
988f3f0262cSandifunction getRevisions($id){
989f3f0262cSandi  $revd = dirname(wikiFN($id,'foo'));
990f3f0262cSandi  $revs = array();
991f3f0262cSandi  $clid = cleanID($id);
992f3f0262cSandi  if(strrpos($clid,':')) $clid = substr($clid,strrpos($clid,':')+1); //remove path
993493a6929SKobaYY  $clid = utf8_encodeFN($clid);
994f3f0262cSandi
995f3f0262cSandi  if (is_dir($revd) && $dh = opendir($revd)) {
996f3f0262cSandi    while (($file = readdir($dh)) !== false) {
997f3f0262cSandi      if (is_dir($revd.'/'.$file)) continue;
998f3f0262cSandi      if (preg_match('/^'.$clid.'\.(\d+)\.txt(\.gz)?$/',$file,$match)){
999f3f0262cSandi        $revs[]=$match[1];
1000f3f0262cSandi      }
1001f3f0262cSandi    }
1002f3f0262cSandi    closedir($dh);
1003f3f0262cSandi  }
1004f3f0262cSandi  rsort($revs);
1005f3f0262cSandi  return $revs;
1006f3f0262cSandi}
1007f3f0262cSandi
1008f3f0262cSandi/**
1009f3f0262cSandi * extracts the query from a google referer
101015fae107Sandi *
10116b13307fSandi * @todo   should be more generic and support yahoo et al
101215fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
1013f3f0262cSandi */
1014f3f0262cSandifunction getGoogleQuery(){
1015f3f0262cSandi  $url = parse_url($_SERVER['HTTP_REFERER']);
10165c3f206fSandi  if(!$url) return '';
1017f3f0262cSandi
1018f3f0262cSandi  if(!preg_match("#google\.#i",$url['host'])) return '';
1019f3f0262cSandi  $query = array();
1020f3f0262cSandi  parse_str($url['query'],$query);
1021f3f0262cSandi
1022f3f0262cSandi  return $query['q'];
1023f3f0262cSandi}
1024f3f0262cSandi
1025f3f0262cSandi/**
102615fae107Sandi * Try to set correct locale
102715fae107Sandi *
1028095bfd5cSandi * @deprecated No longer used
102915fae107Sandi * @author     Andreas Gohr <andi@splitbrain.org>
1030f3f0262cSandi */
1031f3f0262cSandifunction setCorrectLocale(){
1032f3f0262cSandi  global $conf;
1033f3f0262cSandi  global $lang;
1034f3f0262cSandi
1035f3f0262cSandi  $enc = strtoupper($lang['encoding']);
1036f3f0262cSandi  foreach ($lang['locales'] as $loc){
1037f3f0262cSandi    //try locale
1038f3f0262cSandi    if(@setlocale(LC_ALL,$loc)) return;
1039f3f0262cSandi    //try loceale with encoding
1040f3f0262cSandi    if(@setlocale(LC_ALL,"$loc.$enc")) return;
1041f3f0262cSandi  }
1042f3f0262cSandi  //still here? try to set from environment
1043f3f0262cSandi  @setlocale(LC_ALL,"");
1044f3f0262cSandi}
1045f3f0262cSandi
1046f3f0262cSandi/**
1047f3f0262cSandi * Return the human readable size of a file
1048f3f0262cSandi *
1049f3f0262cSandi * @param       int    $size   A file size
1050f3f0262cSandi * @param       int    $dec    A number of decimal places
1051f3f0262cSandi * @author      Martin Benjamin <b.martin@cybernet.ch>
1052f3f0262cSandi * @author      Aidan Lister <aidan@php.net>
1053f3f0262cSandi * @version     1.0.0
1054f3f0262cSandi */
1055f31d5b73Sandifunction filesize_h($size, $dec = 1){
1056f3f0262cSandi  $sizes = array('B', 'KB', 'MB', 'GB');
1057f3f0262cSandi  $count = count($sizes);
1058f3f0262cSandi  $i = 0;
1059f3f0262cSandi
1060f3f0262cSandi  while ($size >= 1024 && ($i < $count - 1)) {
1061f3f0262cSandi    $size /= 1024;
1062f3f0262cSandi    $i++;
1063f3f0262cSandi  }
1064f3f0262cSandi
1065f3f0262cSandi  return round($size, $dec) . ' ' . $sizes[$i];
1066f3f0262cSandi}
1067f3f0262cSandi
106815fae107Sandi/**
106900a7b5adSEsther Brunner * return an obfuscated email address in line with $conf['mailguard'] setting
107000a7b5adSEsther Brunner *
107100a7b5adSEsther Brunner * @author Harry Fuecks <hfuecks@gmail.com>
107200a7b5adSEsther Brunner * @author Christopher Smith <chris@jalakai.co.uk>
107300a7b5adSEsther Brunner */
107400a7b5adSEsther Brunnerfunction obfuscate($email) {
107500a7b5adSEsther Brunner  global $conf;
107600a7b5adSEsther Brunner
107700a7b5adSEsther Brunner  switch ($conf['mailguard']) {
107800a7b5adSEsther Brunner    case 'visible' :
107900a7b5adSEsther Brunner      $obfuscate = array('@' => ' [at] ', '.' => ' [dot] ', '-' => ' [dash] ');
108000a7b5adSEsther Brunner      return strtr($email, $obfuscate);
108100a7b5adSEsther Brunner
108200a7b5adSEsther Brunner    case 'hex' :
108300a7b5adSEsther Brunner      $encode = '';
108400a7b5adSEsther Brunner      for ($x=0; $x < strlen($email); $x++) $encode .= '&#x' . bin2hex($email{$x}).';';
108500a7b5adSEsther Brunner      return $encode;
108600a7b5adSEsther Brunner
108700a7b5adSEsther Brunner    case 'none' :
108800a7b5adSEsther Brunner    default :
108900a7b5adSEsther Brunner      return $email;
109000a7b5adSEsther Brunner  }
109100a7b5adSEsther Brunner}
109200a7b5adSEsther Brunner
109300a7b5adSEsther Brunner/**
1094dc57ef04Sandi * Return DokuWikis version
109515fae107Sandi *
109615fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
109715fae107Sandi */
1098f31d5b73Sandifunction getVersion(){
1099f31d5b73Sandi  //import version string
1100f31d5b73Sandi  if(@file_exists('VERSION')){
1101f31d5b73Sandi    //official release
11020647ce3bSAndreas Gohr    return 'Release '.trim(io_readfile(DOKU_INC.'/VERSION'));
1103f31d5b73Sandi  }elseif(is_dir('_darcs')){
1104f31d5b73Sandi    //darcs checkout
1105f31d5b73Sandi    $inv = file('_darcs/inventory');
1106ae41559bSAndreas Gohr    $inv = preg_grep('#\*\*\d{14}[\]$]#',$inv);
1107f31d5b73Sandi    $cur = array_pop($inv);
1108f31d5b73Sandi    preg_match('#\*\*(\d{4})(\d{2})(\d{2})#',$cur,$matches);
1109f31d5b73Sandi    return 'Darcs '.$matches[1].'-'.$matches[2].'-'.$matches[3];
1110f31d5b73Sandi  }else{
1111f31d5b73Sandi    return 'snapshot?';
1112f31d5b73Sandi  }
1113f31d5b73Sandi}
1114f31d5b73Sandi
1115f31d5b73Sandi/**
1116f31d5b73Sandi * Run a few sanity checks
1117f31d5b73Sandi *
1118f31d5b73Sandi * @author Andreas Gohr <andi@splitbrain.org>
1119f31d5b73Sandi */
1120f3f0262cSandifunction check(){
1121f3f0262cSandi  global $conf;
1122f3f0262cSandi  global $INFO;
1123f3f0262cSandi
1124f31d5b73Sandi  msg('DokuWiki version: '.getVersion(),1);
1125f31d5b73Sandi
112649022a38Sandi  if(version_compare(phpversion(),'4.3.0','<')){
112749022a38Sandi    msg('Your PHP version is too old ('.phpversion().' vs. 4.3.+ recommended)',-1);
112849022a38Sandi  }elseif(version_compare(phpversion(),'4.3.10','<')){
112949022a38Sandi    msg('Consider upgrading PHP to 4.3.10 or higher for security reasons (your version: '.phpversion().')',0);
113049022a38Sandi  }else{
113149022a38Sandi    msg('PHP version '.phpversion(),1);
113249022a38Sandi  }
113349022a38Sandi
1134f3f0262cSandi  if(is_writable($conf['changelog'])){
1135f3f0262cSandi    msg('Changelog is writable',1);
1136f3f0262cSandi  }else{
1137f3f0262cSandi    msg('Changelog is not writable',-1);
1138f3f0262cSandi  }
1139f3f0262cSandi
1140f3f0262cSandi  if(is_writable($conf['datadir'])){
1141f3f0262cSandi    msg('Datadir is writable',1);
1142f3f0262cSandi  }else{
1143f3f0262cSandi    msg('Datadir is not writable',-1);
1144f3f0262cSandi  }
1145f3f0262cSandi
1146f3f0262cSandi  if(is_writable($conf['olddir'])){
1147f3f0262cSandi    msg('Attic is writable',1);
1148f3f0262cSandi  }else{
1149f3f0262cSandi    msg('Attic is not writable',-1);
1150f3f0262cSandi  }
1151f3f0262cSandi
1152f3f0262cSandi  if(is_writable($conf['mediadir'])){
1153f3f0262cSandi    msg('Mediadir is writable',1);
1154f3f0262cSandi  }else{
1155f3f0262cSandi    msg('Mediadir is not writable',-1);
1156f3f0262cSandi  }
1157f3f0262cSandi
115898407a7aSandi  if(is_writable($conf['cachedir'])){
115998407a7aSandi    msg('Cachedir is writable',1);
116098407a7aSandi  }else{
116198407a7aSandi    msg('Cachedir is not writable',-1);
116298407a7aSandi  }
116398407a7aSandi
1164e7cb32dcSAndreas Gohr  if(is_writable(DOKU_CONF.'users.auth.php')){
11658c4f28e8Sjan    msg('conf/users.auth.php is writable',1);
1166f3f0262cSandi  }else{
11678c4f28e8Sjan    msg('conf/users.auth.php is not writable',0);
1168f3f0262cSandi  }
116993a9e835Sandi
117093a9e835Sandi  if(function_exists('mb_strpos')){
117193a9e835Sandi    if(defined('UTF8_NOMBSTRING')){
117293a9e835Sandi      msg('mb_string extension is available but will not be used',0);
117393a9e835Sandi    }else{
117493a9e835Sandi      msg('mb_string extension is available and will be used',1);
117593a9e835Sandi    }
117693a9e835Sandi  }else{
117793a9e835Sandi    msg('mb_string extension not available - PHP only replacements will be used',0);
117893a9e835Sandi  }
1179f42d1c75SAndreas Gohr
1180f42d1c75SAndreas Gohr  if($conf['allowdebug']){
1181f42d1c75SAndreas Gohr    msg('Debugging support is enabled. If you don\'t need it you should set $conf[\'allowdebug\'] = 0',-1);
1182f42d1c75SAndreas Gohr  }else{
1183f42d1c75SAndreas Gohr    msg('Debugging support is disabled',1);
1184f42d1c75SAndreas Gohr  }
1185f3f0262cSandi
1186f3f0262cSandi  msg('Your current permission for this page is '.$INFO['perm'],0);
1187f3f0262cSandi
1188f3f0262cSandi  if(is_writable($INFO['filepath'])){
1189f3f0262cSandi    msg('The current page is writable by the webserver',0);
1190f3f0262cSandi  }else{
1191f3f0262cSandi    msg('The current page is not writable by the webserver',0);
1192f3f0262cSandi  }
1193f3f0262cSandi
1194f3f0262cSandi  if($INFO['writable']){
1195f3f0262cSandi    msg('The current page is writable by you',0);
1196f3f0262cSandi  }else{
1197f3f0262cSandi    msg('The current page is not writable you',0);
1198f3f0262cSandi  }
1199f3f0262cSandi}
1200340756e4Sandi
1201b158d625SSteven Danz/**
1202b158d625SSteven Danz * Let us know if a user is tracking a page
1203b158d625SSteven Danz *
12041380fc45SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
1205b158d625SSteven Danz */
12061380fc45SAndreas Gohrfunction is_subscribed($id,$uid){
12071380fc45SAndreas Gohr  $file=metaFN($id,'.mlist');
12081380fc45SAndreas Gohr  if (@file_exists($file)) {
1209b158d625SSteven Danz    $mlist = file($file);
12101380fc45SAndreas Gohr    $pos = array_search($uid."\n",$mlist);
12111380fc45SAndreas Gohr    return is_int($pos);
1212b158d625SSteven Danz  }
12131380fc45SAndreas Gohr
1214b158d625SSteven Danz  return false;
1215b158d625SSteven Danz}
1216340756e4Sandi
1217f9eb5648Ssteven-danz/**
1218f9eb5648Ssteven-danz * Return a string with the email addresses of all the
1219f9eb5648Ssteven-danz * users subscribed to a page
1220f9eb5648Ssteven-danz *
122126a0801fSAndreas Gohr * @author Steven Danz <steven-danz@kc.rr.com>
1222f9eb5648Ssteven-danz */
1223f9eb5648Ssteven-danzfunction subscriber_addresslist($id){
1224f9eb5648Ssteven-danz  global $conf;
1225cd52f92dSchris  global $auth;
1226f9eb5648Ssteven-danz
1227f9eb5648Ssteven-danz  $emails = '';
1228f9eb5648Ssteven-danz
122926a0801fSAndreas Gohr  if (!$conf['subscribers']) return;
123026a0801fSAndreas Gohr
1231f9eb5648Ssteven-danz  $mlist = array();
1232f9eb5648Ssteven-danz  $file=metaFN($id,'.mlist');
1233f9eb5648Ssteven-danz  if (file_exists($file)) {
1234f9eb5648Ssteven-danz    $mlist = file($file);
1235f9eb5648Ssteven-danz  }
1236f9eb5648Ssteven-danz  if(count($mlist) > 0) {
1237f9eb5648Ssteven-danz    foreach ($mlist as $who) {
1238f9eb5648Ssteven-danz      $who = rtrim($who);
1239cd52f92dSchris      $info = $auth->getUserData($who);
1240f9eb5648Ssteven-danz      $level = auth_aclcheck($id,$who,$info['grps']);
1241f9eb5648Ssteven-danz      if ($level >= AUTH_READ) {
1242f9eb5648Ssteven-danz        if (strcasecmp($info['mail'],$conf['notify']) != 0) {
1243f9eb5648Ssteven-danz          if (empty($emails)) {
1244f9eb5648Ssteven-danz            $emails = $info['mail'];
1245f9eb5648Ssteven-danz          } else {
1246f9eb5648Ssteven-danz            $emails = "$emails,".$info['mail'];
1247f9eb5648Ssteven-danz          }
1248f9eb5648Ssteven-danz        }
1249f9eb5648Ssteven-danz      }
1250f9eb5648Ssteven-danz    }
1251f9eb5648Ssteven-danz  }
1252f9eb5648Ssteven-danz
1253f9eb5648Ssteven-danz  return $emails;
1254f9eb5648Ssteven-danz}
1255f9eb5648Ssteven-danz
125689541d4bSAndreas Gohr/**
125789541d4bSAndreas Gohr * Removes quoting backslashes
125889541d4bSAndreas Gohr *
125989541d4bSAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
126089541d4bSAndreas Gohr */
126189541d4bSAndreas Gohrfunction unslash($string,$char="'"){
126289541d4bSAndreas Gohr  return str_replace('\\'.$char,$char,$string);
126389541d4bSAndreas Gohr}
126489541d4bSAndreas Gohr
1265340756e4Sandi//Setup VIM: ex: et ts=2 enc=utf-8 :
1266