xref: /dokuwiki/inc/common.php (revision 6d8affe6a4c62d13d1cd6051c23ab305145f9db6)
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']);
3917ee7f66SAndreas Gohr
4017ee7f66SAndreas Gohr    // if some outside auth were used only REMOTE_USER is set
4117ee7f66SAndreas Gohr    if(!$info['userinfo']['name']){
4217ee7f66SAndreas Gohr      $info['userinfo']['name'] = $_SERVER['REMOTE_USER'];
4317ee7f66SAndreas Gohr    }
44f3f0262cSandi  }else{
45f3f0262cSandi    $info['perm']       = auth_aclcheck($ID,'',null);
461380fc45SAndreas Gohr    $info['subscribed'] = false;
47f3f0262cSandi  }
48f3f0262cSandi
49f3f0262cSandi  $info['namespace'] = getNS($ID);
50f3f0262cSandi  $info['locked']    = checklock($ID);
51f3f0262cSandi  $info['filepath']  = realpath(wikiFN($ID,$REV));
52f3f0262cSandi  $info['exists']    = @file_exists($info['filepath']);
53f3f0262cSandi  if($REV && !$info['exists']){
54f3f0262cSandi    //check if current revision was meant
55f3f0262cSandi    $cur = wikiFN($ID);
56f3f0262cSandi    if(@file_exists($cur) && (@filemtime($cur) == $REV)){
57f3f0262cSandi      $info['filepath'] = realpath($cur);
58f3f0262cSandi      $info['exists']   = true;
59f3f0262cSandi      $REV = '';
60f3f0262cSandi    }
61f3f0262cSandi  }
62c112d578Sandi  $info['rev'] = $REV;
63f3f0262cSandi  if($info['exists']){
64f3f0262cSandi    $info['writable'] = (is_writable($info['filepath']) &&
65f3f0262cSandi                         ($info['perm'] >= AUTH_EDIT));
66f3f0262cSandi  }else{
67f3f0262cSandi    $info['writable'] = ($info['perm'] >= AUTH_CREATE);
68f3f0262cSandi  }
69f3f0262cSandi  $info['editable']  = ($info['writable'] && empty($info['lock']));
70f3f0262cSandi  $info['lastmod']   = @filemtime($info['filepath']);
71f3f0262cSandi
72652610a2Sandi  //who's the editor
73652610a2Sandi  if($REV){
74652610a2Sandi    $revinfo = getRevisionInfo($ID,$REV);
75652610a2Sandi  }else{
76652610a2Sandi    $revinfo = getRevisionInfo($ID,$info['lastmod']);
77652610a2Sandi  }
78652610a2Sandi  $info['ip']     = $revinfo['ip'];
79652610a2Sandi  $info['user']   = $revinfo['user'];
80652610a2Sandi  $info['sum']    = $revinfo['sum'];
81b6912aeaSAndreas Gohr  $info['minor']  = $revinfo['minor'];
8259f257aeSchris
8388f522e9Sandi  if($revinfo['user']){
8488f522e9Sandi    $info['editor'] = $revinfo['user'];
8588f522e9Sandi  }else{
8688f522e9Sandi    $info['editor'] = $revinfo['ip'];
8788f522e9Sandi  }
88652610a2Sandi
89f3f0262cSandi  return $info;
90f3f0262cSandi}
91f3f0262cSandi
92f3f0262cSandi/**
932684e50aSAndreas Gohr * Build an string of URL parameters
942684e50aSAndreas Gohr *
952684e50aSAndreas Gohr * @author Andreas Gohr
962684e50aSAndreas Gohr */
97b174aeaeSchrisfunction buildURLparams($params, $sep='&amp;'){
982684e50aSAndreas Gohr  $url = '';
992684e50aSAndreas Gohr  $amp = false;
1002684e50aSAndreas Gohr  foreach($params as $key => $val){
101b174aeaeSchris    if($amp) $url .= $sep;
1022684e50aSAndreas Gohr
1032684e50aSAndreas Gohr    $url .= $key.'=';
104b6c6979fSAndreas Gohr    $url .= rawurlencode($val);
1052684e50aSAndreas Gohr    $amp = true;
1062684e50aSAndreas Gohr  }
1072684e50aSAndreas Gohr  return $url;
1082684e50aSAndreas Gohr}
1092684e50aSAndreas Gohr
1102684e50aSAndreas Gohr/**
1112684e50aSAndreas Gohr * Build an string of html tag attributes
1122684e50aSAndreas Gohr *
1132684e50aSAndreas Gohr * @author Andreas Gohr
1142684e50aSAndreas Gohr */
1152684e50aSAndreas Gohrfunction buildAttributes($params){
1162684e50aSAndreas Gohr  $url = '';
1172684e50aSAndreas Gohr  foreach($params as $key => $val){
1182684e50aSAndreas Gohr    $url .= $key.'="';
1192684e50aSAndreas Gohr    $url .= htmlspecialchars ($val);
1202684e50aSAndreas Gohr    $url .= '" ';
1212684e50aSAndreas Gohr  }
1222684e50aSAndreas Gohr  return $url;
1232684e50aSAndreas Gohr}
1242684e50aSAndreas Gohr
1252684e50aSAndreas Gohr
1262684e50aSAndreas Gohr/**
1270396becbSandi * print a message
1280396becbSandi *
1290396becbSandi * If HTTP headers were not sent yet the message is added
1300396becbSandi * to the global message array else it's printed directly
1310396becbSandi * using html_msgarea()
1320396becbSandi *
133f3f0262cSandi *
134f3f0262cSandi * Levels can be:
135f3f0262cSandi *
136f3f0262cSandi * -1 error
137f3f0262cSandi *  0 info
138f3f0262cSandi *  1 success
13915fae107Sandi *
14015fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
1410396becbSandi * @see    html_msgarea
142f3f0262cSandi */
1430d58d74eSAndreas Gohrfunction msg($message,$lvl=0,$line='',$file=''){
144f3f0262cSandi  global $MSG;
145f3f0262cSandi  $errors[-1] = 'error';
146f3f0262cSandi  $errors[0]  = 'info';
147f3f0262cSandi  $errors[1]  = 'success';
148f3f0262cSandi
1490d58d74eSAndreas Gohr  if($line || $file) $message.=' ['.basename($file).':'.$line.']';
1500d58d74eSAndreas Gohr
151cc20ad51Sandi  if(!headers_sent()){
152f3f0262cSandi    if(!isset($MSG)) $MSG = array();
153f3f0262cSandi    $MSG[]=array('lvl' => $errors[$lvl], 'msg' => $message);
1540396becbSandi  }else{
1550396becbSandi    $MSG = array();
1560396becbSandi    $MSG[]=array('lvl' => $errors[$lvl], 'msg' => $message);
157f62ea8a1Sandi    if(function_exists('html_msgarea')){
1580396becbSandi      html_msgarea();
159f62ea8a1Sandi    }else{
160f62ea8a1Sandi      print "ERROR($lvl) $message";
161f62ea8a1Sandi    }
1620396becbSandi  }
163f3f0262cSandi}
164f3f0262cSandi
165f3f0262cSandi/**
16615fae107Sandi * This builds the breadcrumb trail and returns it as array
16715fae107Sandi *
16815fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
169f3f0262cSandi */
170f3f0262cSandifunction breadcrumbs(){
1718746e727Sandi  // we prepare the breadcrumbs early for quick session closing
1728746e727Sandi  static $crumbs = null;
1738746e727Sandi  if($crumbs != null) return $crumbs;
1748746e727Sandi
175f3f0262cSandi  global $ID;
176f3f0262cSandi  global $ACT;
177f3f0262cSandi  global $conf;
178f3f0262cSandi  $crumbs = $_SESSION[$conf['title']]['bc'];
179f3f0262cSandi
180f3f0262cSandi  //first visit?
181f3f0262cSandi  if (!is_array($crumbs)){
182f3f0262cSandi    $crumbs = array();
183f3f0262cSandi  }
184f3f0262cSandi  //we only save on show and existing wiki documents
185a77f5846Sjan  $file = wikiFN($ID);
186a77f5846Sjan  if($ACT != 'show' || !@file_exists($file)){
187f3f0262cSandi    $_SESSION[$conf['title']]['bc'] = $crumbs;
188f3f0262cSandi    return $crumbs;
189f3f0262cSandi  }
190a77f5846Sjan
191a77f5846Sjan  // page names
192a77f5846Sjan  $name = noNS($ID);
193a77f5846Sjan  if ($conf['useheading']) {
194a77f5846Sjan    // get page title
195bb0a59d4Sjan    $title = p_get_first_heading($ID);
196a77f5846Sjan    if ($title) {
197a77f5846Sjan      $name = $title;
198a77f5846Sjan    }
199a77f5846Sjan  }
200a77f5846Sjan
201f3f0262cSandi  //remove ID from array
202a77f5846Sjan  if (isset($crumbs[$ID])) {
203a77f5846Sjan    unset($crumbs[$ID]);
204f3f0262cSandi  }
205f3f0262cSandi
206f3f0262cSandi  //add to array
207a77f5846Sjan  $crumbs[$ID] = $name;
208f3f0262cSandi  //reduce size
209f3f0262cSandi  while(count($crumbs) > $conf['breadcrumbs']){
210f3f0262cSandi    array_shift($crumbs);
211f3f0262cSandi  }
212f3f0262cSandi  //save to session
213f3f0262cSandi  $_SESSION[$conf['title']]['bc'] = $crumbs;
214f3f0262cSandi  return $crumbs;
215f3f0262cSandi}
216f3f0262cSandi
217f3f0262cSandi/**
21815fae107Sandi * Filter for page IDs
21915fae107Sandi *
220f3f0262cSandi * This is run on a ID before it is outputted somewhere
221f3f0262cSandi * currently used to replace the colon with something else
222f3f0262cSandi * on Windows systems and to have proper URL encoding
22315fae107Sandi *
22449c713a3Sandi * Urlencoding is ommitted when the second parameter is false
22549c713a3Sandi *
22615fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
227f3f0262cSandi */
22849c713a3Sandifunction idfilter($id,$ue=true){
229f3f0262cSandi  global $conf;
230f3f0262cSandi  if ($conf['useslash'] && $conf['userewrite']){
231f3f0262cSandi    $id = strtr($id,':','/');
232f3f0262cSandi  }elseif (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' &&
233f3f0262cSandi      $conf['userewrite']) {
234f3f0262cSandi    $id = strtr($id,':',';');
235f3f0262cSandi  }
23649c713a3Sandi  if($ue){
237b6c6979fSAndreas Gohr    $id = rawurlencode($id);
238f3f0262cSandi    $id = str_replace('%3A',':',$id); //keep as colon
239f3f0262cSandi    $id = str_replace('%2F','/',$id); //keep as slash
24049c713a3Sandi  }
241f3f0262cSandi  return $id;
242f3f0262cSandi}
243f3f0262cSandi
244f3f0262cSandi/**
245ed7b5f09Sandi * This builds a link to a wikipage
24615fae107Sandi *
2476c7843b5Sandi * It handles URL rewriting and adds additional parameter if
2486c7843b5Sandi * given in $more
2496c7843b5Sandi *
25015fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
251f3f0262cSandi */
252b174aeaeSchrisfunction wl($id='',$more='',$abs=false,$sep='&amp;'){
253f3f0262cSandi  global $conf;
2546de3759aSAndreas Gohr  if(is_array($more)){
255b174aeaeSchris    $more = buildURLparams($more,$sep);
2566de3759aSAndreas Gohr  }else{
257b174aeaeSchris    $more = str_replace(',',$sep,$more);
2586de3759aSAndreas Gohr  }
259f3f0262cSandi
260f3f0262cSandi  $id    = idfilter($id);
261ed7b5f09Sandi  if($abs){
262ed7b5f09Sandi    $xlink = DOKU_URL;
263ed7b5f09Sandi  }else{
264ed7b5f09Sandi    $xlink = DOKU_BASE;
265ed7b5f09Sandi  }
266f3f0262cSandi
2676c7843b5Sandi  if($conf['userewrite'] == 2){
2686c7843b5Sandi    $xlink .= DOKU_SCRIPT.'/'.$id;
2696c7843b5Sandi    if($more) $xlink .= '?'.$more;
2706c7843b5Sandi  }elseif($conf['userewrite']){
271f3f0262cSandi    $xlink .= $id;
272f3f0262cSandi    if($more) $xlink .= '?'.$more;
2736c7843b5Sandi  }else{
2746c7843b5Sandi    $xlink .= DOKU_SCRIPT.'?id='.$id;
275b174aeaeSchris    if($more) $xlink .= $sep.$more;
276f3f0262cSandi  }
277f3f0262cSandi
278f3f0262cSandi  return $xlink;
279f3f0262cSandi}
280f3f0262cSandi
281f3f0262cSandi/**
2826de3759aSAndreas Gohr * Build a link to a media file
2836de3759aSAndreas Gohr *
2846de3759aSAndreas Gohr * Will return a link to the detail page if $direct is false
2856de3759aSAndreas Gohr */
286b174aeaeSchrisfunction ml($id='',$more='',$direct=true,$sep='&amp;'){
2876de3759aSAndreas Gohr  global $conf;
2886de3759aSAndreas Gohr  if(is_array($more)){
289b174aeaeSchris    $more = buildURLparams($more,$sep);
2906de3759aSAndreas Gohr  }else{
291b174aeaeSchris    $more = str_replace(',',$sep,$more);
2926de3759aSAndreas Gohr  }
2936de3759aSAndreas Gohr
2946de3759aSAndreas Gohr  $xlink = DOKU_BASE;
2956de3759aSAndreas Gohr
2966de3759aSAndreas Gohr  // external URLs are always direct without rewriting
2976de3759aSAndreas Gohr  if(preg_match('#^(https?|ftp)://#i',$id)){
2986de3759aSAndreas Gohr    $xlink .= 'lib/exe/fetch.php';
2996de3759aSAndreas Gohr    if($more){
3006de3759aSAndreas Gohr      $xlink .= '?'.$more;
301b174aeaeSchris      $xlink .= $sep.'media='.rawurlencode($id);
3026de3759aSAndreas Gohr    }else{
303b6c6979fSAndreas Gohr      $xlink .= '?media='.rawurlencode($id);
3046de3759aSAndreas Gohr    }
3056de3759aSAndreas Gohr    return $xlink;
3066de3759aSAndreas Gohr  }
3076de3759aSAndreas Gohr
3086de3759aSAndreas Gohr  $id = idfilter($id);
3096de3759aSAndreas Gohr
3106de3759aSAndreas Gohr  // decide on scriptname
3116de3759aSAndreas Gohr  if($direct){
3126de3759aSAndreas Gohr    if($conf['userewrite'] == 1){
3136de3759aSAndreas Gohr      $script = '_media';
3146de3759aSAndreas Gohr    }else{
3156de3759aSAndreas Gohr      $script = 'lib/exe/fetch.php';
3166de3759aSAndreas Gohr    }
3176de3759aSAndreas Gohr  }else{
3186de3759aSAndreas Gohr    if($conf['userewrite'] == 1){
3196de3759aSAndreas Gohr      $script = '_detail';
3206de3759aSAndreas Gohr    }else{
3216de3759aSAndreas Gohr      $script = 'lib/exe/detail.php';
3226de3759aSAndreas Gohr    }
3236de3759aSAndreas Gohr  }
3246de3759aSAndreas Gohr
3256de3759aSAndreas Gohr  // build URL based on rewrite mode
3266de3759aSAndreas Gohr   if($conf['userewrite']){
3276de3759aSAndreas Gohr     $xlink .= $script.'/'.$id;
3286de3759aSAndreas Gohr     if($more) $xlink .= '?'.$more;
3296de3759aSAndreas Gohr   }else{
3306de3759aSAndreas Gohr     if($more){
331a99d3236SEsther Brunner       $xlink .= $script.'?'.$more;
332b174aeaeSchris       $xlink .= $sep.'media='.$id;
3336de3759aSAndreas Gohr     }else{
334a99d3236SEsther Brunner       $xlink .= $script.'?media='.$id;
3356de3759aSAndreas Gohr     }
3366de3759aSAndreas Gohr   }
3376de3759aSAndreas Gohr
3386de3759aSAndreas Gohr  return $xlink;
3396de3759aSAndreas Gohr}
3406de3759aSAndreas Gohr
3416de3759aSAndreas Gohr
3426de3759aSAndreas Gohr
3436de3759aSAndreas Gohr/**
344f3f0262cSandi * Just builds a link to a script
34515fae107Sandi *
346ed7b5f09Sandi * @todo   maybe obsolete
34715fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
348f3f0262cSandi */
349f3f0262cSandifunction script($script='doku.php'){
350ed7b5f09Sandi#  $link = getBaseURL();
351ed7b5f09Sandi#  $link .= $script;
352ed7b5f09Sandi#  return $link;
353ed7b5f09Sandi  return DOKU_BASE.DOKU_SCRIPT;
354f3f0262cSandi}
355f3f0262cSandi
356f3f0262cSandi/**
35715fae107Sandi * Spamcheck against wordlist
35815fae107Sandi *
359f3f0262cSandi * Checks the wikitext against a list of blocked expressions
360f3f0262cSandi * returns true if the text contains any bad words
36115fae107Sandi *
36215fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
363f3f0262cSandi */
364f3f0262cSandifunction checkwordblock(){
365f3f0262cSandi  global $TEXT;
366f3f0262cSandi  global $conf;
367f3f0262cSandi
368f3f0262cSandi  if(!$conf['usewordblock']) return false;
369f3f0262cSandi
370b9ac8716Schris  $wordblocks = getWordblocks();
3713e2965d7Sandi  //how many lines to read at once (to work around some PCRE limits)
3723e2965d7Sandi  if(version_compare(phpversion(),'4.3.0','<')){
3733e2965d7Sandi    //old versions of PCRE define a maximum of parenthesises even if no
3743e2965d7Sandi    //backreferences are used - the maximum is 99
3753e2965d7Sandi    //this is very bad performancewise and may even be too high still
3763e2965d7Sandi    $chunksize = 40;
3773e2965d7Sandi  }else{
378703f6fdeSandi    //read file in chunks of 600 - this should work around the
3793e2965d7Sandi    //MAX_PATTERN_SIZE in modern PCRE
380444b87a5SAndreas Gohr    $chunksize = 400;
3813e2965d7Sandi  }
382b9ac8716Schris  while($blocks = array_splice($wordblocks,0,$chunksize)){
383f3f0262cSandi    $re = array();
384f3f0262cSandi    #build regexp from blocks
385f3f0262cSandi    foreach($blocks as $block){
386f3f0262cSandi      $block = preg_replace('/#.*$/','',$block);
387f3f0262cSandi      $block = trim($block);
388f3f0262cSandi      if(empty($block)) continue;
389f3f0262cSandi      $re[]  = $block;
390f3f0262cSandi    }
391b9ac8716Schris    if(preg_match('#('.join('|',$re).')#si',$TEXT, $match=array())) {
392b9ac8716Schris      return true;
393b9ac8716Schris    }
394703f6fdeSandi  }
395f3f0262cSandi  return false;
396f3f0262cSandi}
397f3f0262cSandi
398f3f0262cSandi/**
39915fae107Sandi * Return the IP of the client
40015fae107Sandi *
401*6d8affe6SAndreas Gohr * Honours X-Forwarded-For and X-Real-IP Proxy Headers
40215fae107Sandi *
403*6d8affe6SAndreas Gohr * It returns a comma separated list of IPs if the above mentioned
404*6d8affe6SAndreas Gohr * headers are set. If the single parameter is set, it tries to return
405*6d8affe6SAndreas Gohr * a routable public address, prefering the ones suplied in the X
406*6d8affe6SAndreas Gohr * headers
407*6d8affe6SAndreas Gohr *
408*6d8affe6SAndreas Gohr * @param  boolean $single If set only a single IP is returned
40915fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
410f3f0262cSandi */
411*6d8affe6SAndreas Gohrfunction clientIP($single=false){
412*6d8affe6SAndreas Gohr  $ip = array();
413*6d8affe6SAndreas Gohr  $ip[] = $_SERVER['REMOTE_ADDR'];
414*6d8affe6SAndreas Gohr  if($_SERVER['HTTP_X_FORWARDED_FOR'])
415*6d8affe6SAndreas Gohr    $ip = array_merge($ip,explode(',',$_SERVER['HTTP_X_FORWARDED_FOR']));
416*6d8affe6SAndreas Gohr  if($_SERVER['HTTP_X_REAL_IP'])
417*6d8affe6SAndreas Gohr    $ip = array_merge($ip,explode(',',$_SERVER['HTTP_X_REAL_IP']));
418*6d8affe6SAndreas Gohr
419*6d8affe6SAndreas Gohr  // remove any non-IP stuff
420*6d8affe6SAndreas Gohr  $cnt = count($ip);
421*6d8affe6SAndreas Gohr  for($i=0; $i<$cnt; $i++){
422*6d8affe6SAndreas Gohr    $ip[$i] = preg_replace('/[^0-9\.]+/','',$ip[$i]);
423*6d8affe6SAndreas Gohr    if(!preg_match('/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/',$ip[$i])) $ip[$i] = '';
424*6d8affe6SAndreas Gohr    if(empty($ip[$i])) unset($ip[$i]);
425f3f0262cSandi  }
426*6d8affe6SAndreas Gohr  $ip = array_values(array_unique($ip));
427*6d8affe6SAndreas Gohr  if(!$ip[0]) $ip[0] = '0.0.0.0'; // for some strange reason we don't have a IP
428*6d8affe6SAndreas Gohr
429*6d8affe6SAndreas Gohr  if(!$single) return join(',',$ip);
430*6d8affe6SAndreas Gohr
431*6d8affe6SAndreas Gohr  // decide which IP to use, trying to avoid local addresses
432*6d8affe6SAndreas Gohr  $ip = array_reverse($ip);
433*6d8affe6SAndreas Gohr  foreach($ip as $i){
434*6d8affe6SAndreas Gohr    if(preg_match('/^(127\.|10\.|192\.168\.|172\.((1[6-9])|(2[0-9])|(3[0-1]))\.)/',$i)){
435*6d8affe6SAndreas Gohr      continue;
436*6d8affe6SAndreas Gohr    }else{
437*6d8affe6SAndreas Gohr      return $i;
438*6d8affe6SAndreas Gohr    }
439*6d8affe6SAndreas Gohr  }
440*6d8affe6SAndreas Gohr  // still here? just use the first (last) address
441*6d8affe6SAndreas Gohr  return $ip[0];
442f3f0262cSandi}
443f3f0262cSandi
444f3f0262cSandi/**
44515fae107Sandi * Checks if a given page is currently locked.
44615fae107Sandi *
447f3f0262cSandi * removes stale lockfiles
44815fae107Sandi *
44915fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
450f3f0262cSandi */
451f3f0262cSandifunction checklock($id){
452f3f0262cSandi  global $conf;
453f3f0262cSandi  $lock = wikiFN($id).'.lock';
454f3f0262cSandi
455f3f0262cSandi  //no lockfile
456f3f0262cSandi  if(!@file_exists($lock)) return false;
457f3f0262cSandi
458f3f0262cSandi  //lockfile expired
459f3f0262cSandi  if((time() - filemtime($lock)) > $conf['locktime']){
460f3f0262cSandi    unlink($lock);
461f3f0262cSandi    return false;
462f3f0262cSandi  }
463f3f0262cSandi
464f3f0262cSandi  //my own lock
465f3f0262cSandi  $ip = io_readFile($lock);
466f3f0262cSandi  if( ($ip == clientIP()) || ($ip == $_SERVER['REMOTE_USER']) ){
467f3f0262cSandi    return false;
468f3f0262cSandi  }
469f3f0262cSandi
470f3f0262cSandi  return $ip;
471f3f0262cSandi}
472f3f0262cSandi
473f3f0262cSandi/**
47415fae107Sandi * Lock a page for editing
47515fae107Sandi *
47615fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
477f3f0262cSandi */
478f3f0262cSandifunction lock($id){
479f3f0262cSandi  $lock = wikiFN($id).'.lock';
480f3f0262cSandi  if($_SERVER['REMOTE_USER']){
481f3f0262cSandi    io_saveFile($lock,$_SERVER['REMOTE_USER']);
482f3f0262cSandi  }else{
483f3f0262cSandi    io_saveFile($lock,clientIP());
484f3f0262cSandi  }
485f3f0262cSandi}
486f3f0262cSandi
487f3f0262cSandi/**
48815fae107Sandi * Unlock a page if it was locked by the user
489f3f0262cSandi *
49015fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
49115fae107Sandi * @return bool true if a lock was removed
492f3f0262cSandi */
493f3f0262cSandifunction unlock($id){
494f3f0262cSandi  $lock = wikiFN($id).'.lock';
495f3f0262cSandi  if(@file_exists($lock)){
496f3f0262cSandi    $ip = io_readFile($lock);
497f3f0262cSandi    if( ($ip == clientIP()) || ($ip == $_SERVER['REMOTE_USER']) ){
498f3f0262cSandi      @unlink($lock);
499f3f0262cSandi      return true;
500f3f0262cSandi    }
501f3f0262cSandi  }
502f3f0262cSandi  return false;
503f3f0262cSandi}
504f3f0262cSandi
505f3f0262cSandi/**
506f3f0262cSandi * convert line ending to unix format
507f3f0262cSandi *
50815fae107Sandi * @see    formText() for 2crlf conversion
50915fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
510f3f0262cSandi */
511f3f0262cSandifunction cleanText($text){
512f3f0262cSandi  $text = preg_replace("/(\015\012)|(\015)/","\012",$text);
513f3f0262cSandi  return $text;
514f3f0262cSandi}
515f3f0262cSandi
516f3f0262cSandi/**
517f3f0262cSandi * Prepares text for print in Webforms by encoding special chars.
518f3f0262cSandi * It also converts line endings to Windows format which is
519f3f0262cSandi * pseudo standard for webforms.
520f3f0262cSandi *
52115fae107Sandi * @see    cleanText() for 2unix conversion
52215fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
523f3f0262cSandi */
524f3f0262cSandifunction formText($text){
525f3f0262cSandi  $text = preg_replace("/\012/","\015\012",$text);
526f3f0262cSandi  return htmlspecialchars($text);
527f3f0262cSandi}
528f3f0262cSandi
529f3f0262cSandi/**
53015fae107Sandi * Returns the specified local text in raw format
53115fae107Sandi *
53215fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
533f3f0262cSandi */
534f3f0262cSandifunction rawLocale($id){
535f3f0262cSandi  return io_readFile(localeFN($id));
536f3f0262cSandi}
537f3f0262cSandi
538f3f0262cSandi/**
539f3f0262cSandi * Returns the raw WikiText
54015fae107Sandi *
54115fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
542f3f0262cSandi */
543f3f0262cSandifunction rawWiki($id,$rev=''){
544f3f0262cSandi  return io_readFile(wikiFN($id,$rev));
545f3f0262cSandi}
546f3f0262cSandi
547f3f0262cSandi/**
5487146cee2SAndreas Gohr * Returns the pagetemplate contents for the ID's namespace
5497146cee2SAndreas Gohr *
5507146cee2SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
5517146cee2SAndreas Gohr */
5527146cee2SAndreas Gohrfunction pageTemplate($id){
553a15ce62dSEsther Brunner  global $conf;
554a15ce62dSEsther Brunner  global $INFO;
555a15ce62dSEsther Brunner  $tpl = io_readFile(dirname(wikiFN($id)).'/_template.txt');
556a15ce62dSEsther Brunner  $tpl = str_replace('@ID@',$id,$tpl);
557a15ce62dSEsther Brunner  $tpl = str_replace('@NS@',getNS($id),$tpl);
558a15ce62dSEsther Brunner  $tpl = str_replace('@PAGE@',strtr(noNS($id),'_',' '),$tpl);
559a15ce62dSEsther Brunner  $tpl = str_replace('@USER@',$_SERVER['REMOTE_USER'],$tpl);
560a15ce62dSEsther Brunner  $tpl = str_replace('@NAME@',$INFO['userinfo']['name'],$tpl);
561a15ce62dSEsther Brunner  $tpl = str_replace('@MAIL@',$INFO['userinfo']['mail'],$tpl);
562a15ce62dSEsther Brunner  $tpl = str_replace('@DATE@',date($conf['dformat']),$tpl);
563a15ce62dSEsther Brunner  return $tpl;
5647146cee2SAndreas Gohr}
5657146cee2SAndreas Gohr
5667146cee2SAndreas Gohr
5677146cee2SAndreas Gohr/**
56815fae107Sandi * Returns the raw Wiki Text in three slices.
56915fae107Sandi *
57015fae107Sandi * The range parameter needs to have the form "from-to"
57115cfe303Sandi * and gives the range of the section in bytes - no
57215cfe303Sandi * UTF-8 awareness is needed.
573f3f0262cSandi * The returned order is prefix, section and suffix.
57415fae107Sandi *
57515fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
576f3f0262cSandi */
577f3f0262cSandifunction rawWikiSlices($range,$id,$rev=''){
578f3f0262cSandi  list($from,$to) = split('-',$range,2);
579f3f0262cSandi  $text = io_readFile(wikiFN($id,$rev));
580f3f0262cSandi  if(!$from) $from = 0;
581c3d8e19bSandi  if(!$to)   $to   = strlen($text)+1;
582f3f0262cSandi
58315cfe303Sandi  $slices[0] = substr($text,0,$from-1);
58415cfe303Sandi  $slices[1] = substr($text,$from-1,$to-$from);
58515cfe303Sandi  $slices[2] = substr($text,$to);
586f3f0262cSandi
587f3f0262cSandi  return $slices;
588f3f0262cSandi}
589f3f0262cSandi
590f3f0262cSandi/**
59115fae107Sandi * Joins wiki text slices
59215fae107Sandi *
593f3f0262cSandi * function to join the text slices with correct lineendings again.
594f3f0262cSandi * When the pretty parameter is set to true it adds additional empty
595f3f0262cSandi * lines between sections if needed (used on saving).
59615fae107Sandi *
59715fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
598f3f0262cSandi */
599f3f0262cSandifunction con($pre,$text,$suf,$pretty=false){
600f3f0262cSandi
601f3f0262cSandi  if($pretty){
602f3f0262cSandi    if($pre && substr($pre,-1) != "\n") $pre .= "\n";
603f3f0262cSandi    if($suf && substr($text,-1) != "\n") $text .= "\n";
604f3f0262cSandi  }
605f3f0262cSandi
606f3f0262cSandi  if($pre) $pre .= "\n";
607f3f0262cSandi  if($suf) $text .= "\n";
608f3f0262cSandi  return $pre.$text.$suf;
609f3f0262cSandi}
610f3f0262cSandi
611f3f0262cSandi/**
61215fae107Sandi * print debug messages
61315fae107Sandi *
614f3f0262cSandi * little function to print the content of a var
61515fae107Sandi *
61615fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
617f3f0262cSandi */
618f3f0262cSandifunction dbg($msg,$hidden=false){
619f3f0262cSandi  (!$hidden) ? print '<pre class="dbg">' : print "<!--\n";
620f3f0262cSandi  print_r($msg);
621f3f0262cSandi  (!$hidden) ? print '</pre>' : print "\n-->";
622f3f0262cSandi}
623f3f0262cSandi
624f3f0262cSandi/**
625f3f0262cSandi * Add's an entry to the changelog
62615fae107Sandi *
62715fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
628f3f0262cSandi */
629b6912aeaSAndreas Gohrfunction addLogEntry($date,$id,$summary='',$minor=false){
630f3f0262cSandi  global $conf;
631c1049928Sandi
632c1049928Sandi  if(!@is_writable($conf['changelog'])){
633c1049928Sandi    msg($conf['changelog'].' is not writable!',-1);
634c1049928Sandi    return;
635c1049928Sandi  }
636c1049928Sandi
637652610a2Sandi  if(!$date) $date = time(); //use current time if none supplied
638f3f0262cSandi  $remote = $_SERVER['REMOTE_ADDR'];
639f3f0262cSandi  $user   = $_SERVER['REMOTE_USER'];
640f3f0262cSandi
641b6912aeaSAndreas Gohr  if($conf['useacl'] && $user && $minor){
642b6912aeaSAndreas Gohr    $summary = '*'.$summary;
643b6912aeaSAndreas Gohr  }else{
644b6912aeaSAndreas Gohr    $summary = ' '.$summary;
645b6912aeaSAndreas Gohr  }
646b6912aeaSAndreas Gohr
647f3f0262cSandi  $logline = join("\t",array($date,$remote,$id,$user,$summary))."\n";
648dbb00abcSEsther Brunner  io_saveFile($conf['changelog'],$logline,true);
649f3f0262cSandi}
650f3f0262cSandi
651f3f0262cSandi/**
652b6912aeaSAndreas Gohr * Checks an summary entry if it was a minor edit
653b6912aeaSAndreas Gohr *
654b6912aeaSAndreas Gohr * The summary is cleaned of the marker char
655b6912aeaSAndreas Gohr *
656b6912aeaSAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
657b6912aeaSAndreas Gohr */
658b6912aeaSAndreas Gohrfunction isMinor(&$summary){
659b6912aeaSAndreas Gohr  if(substr($summary,0,1) == '*'){
660b6912aeaSAndreas Gohr    $summary = substr($summary,1);
661b6912aeaSAndreas Gohr    return true;
662b6912aeaSAndreas Gohr  }
663b6912aeaSAndreas Gohr  $summary = trim($summary);
664b6912aeaSAndreas Gohr  return false;
665b6912aeaSAndreas Gohr}
666b6912aeaSAndreas Gohr
667b6912aeaSAndreas Gohr/**
668d437bcc4SAndreas Gohr * Internal function used by getRecents
669d437bcc4SAndreas Gohr *
670d437bcc4SAndreas Gohr * don't call directly
671d437bcc4SAndreas Gohr *
672d437bcc4SAndreas Gohr * @see getRecents()
673d437bcc4SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
674d437bcc4SAndreas Gohr */
675b6912aeaSAndreas Gohrfunction _handleRecent($line,$ns,$flags){
676d437bcc4SAndreas Gohr  static $seen  = array();         //caches seen pages and skip them
677d437bcc4SAndreas Gohr  if(empty($line)) return false;   //skip empty lines
678d437bcc4SAndreas Gohr
679d437bcc4SAndreas Gohr  // split the line into parts
680d437bcc4SAndreas Gohr  list($dt,$ip,$id,$usr,$sum) = explode("\t",$line);
681d437bcc4SAndreas Gohr
682d437bcc4SAndreas Gohr  // skip seen ones
683d437bcc4SAndreas Gohr  if($seen[$id]) return false;
684b6912aeaSAndreas Gohr  $recent = array();
685b6912aeaSAndreas Gohr
686b6912aeaSAndreas Gohr  // check minors
687b6912aeaSAndreas Gohr  if(isMinor($sum)){
688b6912aeaSAndreas Gohr    // skip minors
689b6912aeaSAndreas Gohr    if($flags & RECENTS_SKIP_MINORS) return false;
690b6912aeaSAndreas Gohr    $recent['minor'] = true;
691b6912aeaSAndreas Gohr  }else{
692b6912aeaSAndreas Gohr    $recent['minor'] = false;
693b6912aeaSAndreas Gohr  }
694d437bcc4SAndreas Gohr
695d437bcc4SAndreas Gohr  // remember in seen to skip additional sights
696d437bcc4SAndreas Gohr  $seen[$id] = 1;
697d437bcc4SAndreas Gohr
6980dc92c6fSAndreas Gohr  // check if it's a hidden page
6990dc92c6fSAndreas Gohr  if(isHiddenPage($id)) return false;
7000dc92c6fSAndreas Gohr
701d437bcc4SAndreas Gohr  // filter namespace
702d437bcc4SAndreas Gohr  if (($ns) && (strpos($id,$ns.':') !== 0)) return false;
703d437bcc4SAndreas Gohr
704d437bcc4SAndreas Gohr  // exclude subnamespaces
705b6912aeaSAndreas Gohr  if (($flags & RECENTS_SKIP_SUBSPACES) && (getNS($id) != $ns)) return false;
706d437bcc4SAndreas Gohr
707ae56bfb6SAndreas Gohr  // check ACL
708ae56bfb6SAndreas Gohr  if (auth_quickaclcheck($id) < AUTH_READ) return false;
709ae56bfb6SAndreas Gohr
710d437bcc4SAndreas Gohr  // check existance
711d437bcc4SAndreas Gohr  if(!@file_exists(wikiFN($id))){
712b6912aeaSAndreas Gohr    if($flags & RECENTS_SKIP_DELETED){
713d437bcc4SAndreas Gohr      return false;
714d437bcc4SAndreas Gohr    }else{
715d437bcc4SAndreas Gohr      $recent['del'] = true;
716d437bcc4SAndreas Gohr    }
717d437bcc4SAndreas Gohr  }else{
718d437bcc4SAndreas Gohr    $recent['del'] = false;
719d437bcc4SAndreas Gohr  }
720d437bcc4SAndreas Gohr
721d437bcc4SAndreas Gohr  $recent['id']   = $id;
722d437bcc4SAndreas Gohr  $recent['date'] = $dt;
723d437bcc4SAndreas Gohr  $recent['ip']   = $ip;
724d437bcc4SAndreas Gohr  $recent['user'] = $usr;
725d437bcc4SAndreas Gohr  $recent['sum']  = $sum;
726d437bcc4SAndreas Gohr
727d437bcc4SAndreas Gohr  return $recent;
728d437bcc4SAndreas Gohr}
729d437bcc4SAndreas Gohr
730b6912aeaSAndreas Gohr
731d437bcc4SAndreas Gohr/**
732f3f0262cSandi * returns an array of recently changed files using the
733f3f0262cSandi * changelog
734d437bcc4SAndreas Gohr *
735b6912aeaSAndreas Gohr * The following constants can be used to control which changes are
736b6912aeaSAndreas Gohr * included. Add them together as needed.
737b6912aeaSAndreas Gohr *
738b6912aeaSAndreas Gohr * RECENTS_SKIP_DELETED   - don't include deleted pages
739b6912aeaSAndreas Gohr * RECENTS_SKIP_MINORS    - don't include minor changes
740b6912aeaSAndreas Gohr * RECENTS_SKIP_SUBSPACES - don't include subspaces
741b6912aeaSAndreas Gohr *
742d437bcc4SAndreas Gohr * @param int    $first   number of first entry returned (for paginating
743d437bcc4SAndreas Gohr * @param int    $num     return $num entries
744d437bcc4SAndreas Gohr * @param string $ns      restrict to given namespace
745b6912aeaSAndreas Gohr * @param bool   $flags   see above
74615fae107Sandi *
74715fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
748f3f0262cSandi */
749b6912aeaSAndreas Gohrfunction getRecents($first,$num,$ns='',$flags=0){
750f3f0262cSandi  global $conf;
751f3f0262cSandi  $recent = array();
752d437bcc4SAndreas Gohr  $count  = 0;
7535749f1ceSmatthiasgrimm
7545749f1ceSmatthiasgrimm  if(!$num)
7555749f1ceSmatthiasgrimm    return $recent;
756f3f0262cSandi
757c1049928Sandi  if(!@is_readable($conf['changelog'])){
758c1049928Sandi    msg($conf['changelog'].' is not readable',-1);
759c1049928Sandi    return $recent;
760c1049928Sandi  }
761c1049928Sandi
762d437bcc4SAndreas Gohr  $fh  = fopen($conf['changelog'],'r');
763d437bcc4SAndreas Gohr  $buf = '';
764d437bcc4SAndreas Gohr  $csz = 4096;                              //chunksize
765d437bcc4SAndreas Gohr  fseek($fh,0,SEEK_END);                    // jump to the end
766d437bcc4SAndreas Gohr  $pos = ftell($fh);                        // position pointer
767f3f0262cSandi
768d437bcc4SAndreas Gohr  // now read backwards into buffer
769d437bcc4SAndreas Gohr  while($pos > 0){
770d437bcc4SAndreas Gohr    $pos -= $csz;                           // seek to previous chunk...
771f6c1156dSWolfgang Ocker    if($pos < 0) {                          // ...or rest of file
772f6c1156dSWolfgang Ocker      $csz += $pos;
773f6c1156dSWolfgang Ocker      $pos = 0;
774f6c1156dSWolfgang Ocker    }
775f6c1156dSWolfgang Ocker
776d437bcc4SAndreas Gohr    fseek($fh,$pos);
777dbb00abcSEsther Brunner
778d437bcc4SAndreas Gohr    $buf = fread($fh,$csz).$buf;            // prepend to buffer
7798f1d587cSEsther Brunner
780d437bcc4SAndreas Gohr    $lines = explode("\n",$buf);            // split buffer into lines
7815749f1ceSmatthiasgrimm
782d437bcc4SAndreas Gohr    if($pos > 0){
783d437bcc4SAndreas Gohr      $buf = array_shift($lines);           // first one may be still incomplete
784f3f0262cSandi    }
785d437bcc4SAndreas Gohr
786d437bcc4SAndreas Gohr    $cnt = count($lines);
787d437bcc4SAndreas Gohr    if(!$cnt) continue;                     // no lines yet
788d437bcc4SAndreas Gohr
789d437bcc4SAndreas Gohr    // handle lines
790d437bcc4SAndreas Gohr    for($i = $cnt-1; $i >= 0; $i--){
791b6912aeaSAndreas Gohr      $rec = _handleRecent($lines[$i],$ns,$flags);
792d437bcc4SAndreas Gohr      if($rec !== false){
793d437bcc4SAndreas Gohr        if(--$first >= 0) continue;         // skip first entries
794d437bcc4SAndreas Gohr        $recent[] = $rec;
795d437bcc4SAndreas Gohr        $count++;
796d437bcc4SAndreas Gohr
797d437bcc4SAndreas Gohr        // break while when we have enough entries
798d437bcc4SAndreas Gohr        if($count >= $num){
799d437bcc4SAndreas Gohr          $pos = 0; // will break the while loop
800d437bcc4SAndreas Gohr          break;    // will break the for loop
801f3f0262cSandi        }
802f3f0262cSandi      }
803d437bcc4SAndreas Gohr    }
804d437bcc4SAndreas Gohr  }// end of while
805d437bcc4SAndreas Gohr
806d437bcc4SAndreas Gohr  fclose($fh);
807f3f0262cSandi  return $recent;
808f3f0262cSandi}
809f3f0262cSandi
810f3f0262cSandi/**
811652610a2Sandi * gets additonal informations for a certain pagerevison
812652610a2Sandi * from the changelog
813652610a2Sandi *
814652610a2Sandi * @author Andreas Gohr <andi@splitbrain.org>
815652610a2Sandi */
816652610a2Sandifunction getRevisionInfo($id,$rev){
817652610a2Sandi  global $conf;
818258641c6Sandi
819258641c6Sandi  if(!$rev) return(null);
820258641c6Sandi
821c1049928Sandi  $info = array();
822c1049928Sandi  if(!@is_readable($conf['changelog'])){
823c1049928Sandi    msg($conf['changelog'].' is not readable',-1);
824c1049928Sandi    return $recent;
825c1049928Sandi  }
826652610a2Sandi  $loglines = file($conf['changelog']);
827652610a2Sandi  $loglines = preg_grep("/$rev\t\d+\.\d+\.\d+\.\d+\t$id\t/",$loglines);
828dc42ff59Sandi  $loglines = array_reverse($loglines); //reverse sort on timestamp (shouldn't be needed)
829652610a2Sandi  $line = split("\t",$loglines[0]);
830652610a2Sandi  $info['date']  = $line[0];
831652610a2Sandi  $info['ip']    = $line[1];
832652610a2Sandi  $info['user']  = $line[3];
833652610a2Sandi  $info['sum']   = $line[4];
834b6912aeaSAndreas Gohr  $info['minor'] = isMinor($info['sum']);
835652610a2Sandi  return $info;
836652610a2Sandi}
837652610a2Sandi
838652610a2Sandi/**
839f3f0262cSandi * Saves a wikitext by calling io_saveFile
84015fae107Sandi *
84115fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
842f3f0262cSandi */
843b6912aeaSAndreas Gohrfunction saveWikiText($id,$text,$summary,$minor=false){
844f3f0262cSandi  global $conf;
845f3f0262cSandi  global $lang;
846f3f0262cSandi  // ignore if no changes were made
847f3f0262cSandi  if($text == rawWiki($id,'')){
848f3f0262cSandi    return;
849f3f0262cSandi  }
850f3f0262cSandi
851f3f0262cSandi  $file = wikiFN($id);
852f3f0262cSandi  $old  = saveOldRevision($id);
853f3f0262cSandi
854f3f0262cSandi  if (empty($text)){
855e1f3d9e1SEsther Brunner    // remove empty file
856f3f0262cSandi    @unlink($file);
857e1f3d9e1SEsther Brunner    // remove any meta info
858e1f3d9e1SEsther Brunner    $mfiles = metaFiles($id);
859e1f3d9e1SEsther Brunner    foreach ($mfiles as $mfile) {
860e1f3d9e1SEsther Brunner      if (file_exists($mfile)) @unlink($mfile);
861b158d625SSteven Danz    }
862f3f0262cSandi    $del = true;
8633ce054b3Sandi    // autoset summary on deletion
8643ce054b3Sandi    if(empty($summary)) $summary = $lang['deleted'];
865f864871eSAndreas Gohr    // unlock early
866f864871eSAndreas Gohr    unlock($id);
86753d6ccfeSandi    // remove empty namespaces
86853d6ccfeSandi    io_sweepNS($id);
869f3f0262cSandi  }else{
870f3f0262cSandi    // save file (datadir is created in io_saveFile)
871f3f0262cSandi    io_saveFile($file,$text);
872f3f0262cSandi    $del = false;
873f3f0262cSandi  }
874f3f0262cSandi
875b6912aeaSAndreas Gohr  addLogEntry(@filemtime($file),$id,$summary,$minor);
87626a0801fSAndreas Gohr  // send notify mails
87790033e9dSAndreas Gohr  notify($id,'admin',$old,$summary,$minor);
87890033e9dSAndreas Gohr  notify($id,'subscribers',$old,$summary,$minor);
879f3f0262cSandi
880f3f0262cSandi  //purge cache on add by updating the purgefile
881f3f0262cSandi  if($conf['purgeonadd'] && (!$old || $del)){
88298407a7aSandi    io_saveFile($conf['cachedir'].'/purgefile',time());
883f3f0262cSandi  }
884f3f0262cSandi}
885f3f0262cSandi
886f3f0262cSandi/**
887f3f0262cSandi * moves the current version to the attic and returns its
888f3f0262cSandi * revision date
88915fae107Sandi *
89015fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
891f3f0262cSandi */
892f3f0262cSandifunction saveOldRevision($id){
893f3f0262cSandi  global $conf;
894f3f0262cSandi  $oldf = wikiFN($id);
895f3f0262cSandi  if(!@file_exists($oldf)) return '';
896f3f0262cSandi  $date = filemtime($oldf);
897f3f0262cSandi  $newf = wikiFN($id,$date);
898f3f0262cSandi  if(substr($newf,-3)=='.gz'){
899f3f0262cSandi    io_saveFile($newf,rawWiki($id));
900f3f0262cSandi  }else{
901f3f0262cSandi    io_makeFileDir($newf);
902f3f0262cSandi    copy($oldf, $newf);
903f3f0262cSandi  }
904f3f0262cSandi  return $date;
905f3f0262cSandi}
906f3f0262cSandi
907f3f0262cSandi/**
90826a0801fSAndreas Gohr * Sends a notify mail on page change
90926a0801fSAndreas Gohr *
91026a0801fSAndreas Gohr * @param  string  $id       The changed page
91126a0801fSAndreas Gohr * @param  string  $who      Who to notify (admin|subscribers)
91226a0801fSAndreas Gohr * @param  int     $rev      Old page revision
91326a0801fSAndreas Gohr * @param  string  $summary  What changed
91490033e9dSAndreas Gohr * @param  boolean $minor    Is this a minor edit?
91515fae107Sandi *
91615fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
917f3f0262cSandi */
91890033e9dSAndreas Gohrfunction notify($id,$who,$rev='',$summary='',$minor=false){
919f3f0262cSandi  global $lang;
920f3f0262cSandi  global $conf;
921b158d625SSteven Danz
92226a0801fSAndreas Gohr  // decide if there is something to do
92326a0801fSAndreas Gohr  if($who == 'admin'){
92426a0801fSAndreas Gohr    if(empty($conf['notify'])) return; //notify enabled?
925f3f0262cSandi    $text = rawLocale('mailtext');
92626a0801fSAndreas Gohr    $to   = $conf['notify'];
92726a0801fSAndreas Gohr    $bcc  = '';
92826a0801fSAndreas Gohr  }elseif($who == 'subscribers'){
92926a0801fSAndreas Gohr    if(!$conf['subscribers']) return; //subscribers enabled?
93090033e9dSAndreas Gohr    if($conf['useacl'] && $_SERVER['REMOTE_USER'] && $minor) return; //skip minors
93126a0801fSAndreas Gohr    $bcc  = subscriber_addresslist($id);
93226a0801fSAndreas Gohr    if(empty($bcc)) return;
93326a0801fSAndreas Gohr    $to   = '';
93426a0801fSAndreas Gohr    $text = rawLocale('subscribermail');
93526a0801fSAndreas Gohr  }else{
93626a0801fSAndreas Gohr    return; //just to be safe
93726a0801fSAndreas Gohr  }
93826a0801fSAndreas Gohr
939f3f0262cSandi  $text = str_replace('@DATE@',date($conf['dformat']),$text);
940f3f0262cSandi  $text = str_replace('@BROWSER@',$_SERVER['HTTP_USER_AGENT'],$text);
941f3f0262cSandi  $text = str_replace('@IPADDRESS@',$_SERVER['REMOTE_ADDR'],$text);
942f3f0262cSandi  $text = str_replace('@HOSTNAME@',gethostbyaddr($_SERVER['REMOTE_ADDR']),$text);
943ed7b5f09Sandi  $text = str_replace('@NEWPAGE@',wl($id,'',true),$text);
94426a0801fSAndreas Gohr  $text = str_replace('@PAGE@',$id,$text);
94526a0801fSAndreas Gohr  $text = str_replace('@TITLE@',$conf['title'],$text);
946ed7b5f09Sandi  $text = str_replace('@DOKUWIKIURL@',DOKU_URL,$text);
947f3f0262cSandi  $text = str_replace('@SUMMARY@',$summary,$text);
9487a82afdcSandi  $text = str_replace('@USER@',$_SERVER['REMOTE_USER'],$text);
949f3f0262cSandi
950f3f0262cSandi  if($rev){
951f3f0262cSandi    $subject = $lang['mail_changed'].' '.$id;
952ed7b5f09Sandi    $text = str_replace('@OLDPAGE@',wl($id,"rev=$rev",true),$text);
953ccdfa6c0SAndreas Gohr    require_once(DOKU_INC.'inc/DifferenceEngine.php');
954f3f0262cSandi    $df  = new Diff(split("\n",rawWiki($id,$rev)),
955f3f0262cSandi                    split("\n",rawWiki($id)));
956f3f0262cSandi    $dformat = new UnifiedDiffFormatter();
957f3f0262cSandi    $diff    = $dformat->format($df);
958f3f0262cSandi  }else{
959f3f0262cSandi    $subject=$lang['mail_newpage'].' '.$id;
960f3f0262cSandi    $text = str_replace('@OLDPAGE@','none',$text);
961f3f0262cSandi    $diff = rawWiki($id);
962f3f0262cSandi  }
963f3f0262cSandi  $text = str_replace('@DIFF@',$diff,$text);
964241f3a36Sandi  $subject = '['.$conf['title'].'] '.$subject;
965f3f0262cSandi
96626a0801fSAndreas Gohr  mail_send($to,$subject,$text,$conf['mailfrom'],'',$bcc);
967f3f0262cSandi}
968f3f0262cSandi
96915fae107Sandi/**
97015fae107Sandi * Return a list of available page revisons
97115fae107Sandi *
97215fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
97315fae107Sandi */
974f3f0262cSandifunction getRevisions($id){
975f3f0262cSandi  $revd = dirname(wikiFN($id,'foo'));
976f3f0262cSandi  $revs = array();
977f3f0262cSandi  $clid = cleanID($id);
978f3f0262cSandi  if(strrpos($clid,':')) $clid = substr($clid,strrpos($clid,':')+1); //remove path
979493a6929SKobaYY  $clid = utf8_encodeFN($clid);
980f3f0262cSandi
981f3f0262cSandi  if (is_dir($revd) && $dh = opendir($revd)) {
982f3f0262cSandi    while (($file = readdir($dh)) !== false) {
983f3f0262cSandi      if (is_dir($revd.'/'.$file)) continue;
984f3f0262cSandi      if (preg_match('/^'.$clid.'\.(\d+)\.txt(\.gz)?$/',$file,$match)){
985f3f0262cSandi        $revs[]=$match[1];
986f3f0262cSandi      }
987f3f0262cSandi    }
988f3f0262cSandi    closedir($dh);
989f3f0262cSandi  }
990f3f0262cSandi  rsort($revs);
991f3f0262cSandi  return $revs;
992f3f0262cSandi}
993f3f0262cSandi
994f3f0262cSandi/**
995f3f0262cSandi * extracts the query from a google referer
99615fae107Sandi *
9976b13307fSandi * @todo   should be more generic and support yahoo et al
99815fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
999f3f0262cSandi */
1000f3f0262cSandifunction getGoogleQuery(){
1001f3f0262cSandi  $url = parse_url($_SERVER['HTTP_REFERER']);
10025c3f206fSandi  if(!$url) return '';
1003f3f0262cSandi
1004f3f0262cSandi  if(!preg_match("#google\.#i",$url['host'])) return '';
1005f3f0262cSandi  $query = array();
1006f3f0262cSandi  parse_str($url['query'],$query);
1007f3f0262cSandi
1008f3f0262cSandi  return $query['q'];
1009f3f0262cSandi}
1010f3f0262cSandi
1011f3f0262cSandi/**
101215fae107Sandi * Try to set correct locale
101315fae107Sandi *
1014095bfd5cSandi * @deprecated No longer used
101515fae107Sandi * @author     Andreas Gohr <andi@splitbrain.org>
1016f3f0262cSandi */
1017f3f0262cSandifunction setCorrectLocale(){
1018f3f0262cSandi  global $conf;
1019f3f0262cSandi  global $lang;
1020f3f0262cSandi
1021f3f0262cSandi  $enc = strtoupper($lang['encoding']);
1022f3f0262cSandi  foreach ($lang['locales'] as $loc){
1023f3f0262cSandi    //try locale
1024f3f0262cSandi    if(@setlocale(LC_ALL,$loc)) return;
1025f3f0262cSandi    //try loceale with encoding
1026f3f0262cSandi    if(@setlocale(LC_ALL,"$loc.$enc")) return;
1027f3f0262cSandi  }
1028f3f0262cSandi  //still here? try to set from environment
1029f3f0262cSandi  @setlocale(LC_ALL,"");
1030f3f0262cSandi}
1031f3f0262cSandi
1032f3f0262cSandi/**
1033f3f0262cSandi * Return the human readable size of a file
1034f3f0262cSandi *
1035f3f0262cSandi * @param       int    $size   A file size
1036f3f0262cSandi * @param       int    $dec    A number of decimal places
1037f3f0262cSandi * @author      Martin Benjamin <b.martin@cybernet.ch>
1038f3f0262cSandi * @author      Aidan Lister <aidan@php.net>
1039f3f0262cSandi * @version     1.0.0
1040f3f0262cSandi */
1041f31d5b73Sandifunction filesize_h($size, $dec = 1){
1042f3f0262cSandi  $sizes = array('B', 'KB', 'MB', 'GB');
1043f3f0262cSandi  $count = count($sizes);
1044f3f0262cSandi  $i = 0;
1045f3f0262cSandi
1046f3f0262cSandi  while ($size >= 1024 && ($i < $count - 1)) {
1047f3f0262cSandi    $size /= 1024;
1048f3f0262cSandi    $i++;
1049f3f0262cSandi  }
1050f3f0262cSandi
1051f3f0262cSandi  return round($size, $dec) . ' ' . $sizes[$i];
1052f3f0262cSandi}
1053f3f0262cSandi
105415fae107Sandi/**
105500a7b5adSEsther Brunner * return an obfuscated email address in line with $conf['mailguard'] setting
105600a7b5adSEsther Brunner *
105700a7b5adSEsther Brunner * @author Harry Fuecks <hfuecks@gmail.com>
105800a7b5adSEsther Brunner * @author Christopher Smith <chris@jalakai.co.uk>
105900a7b5adSEsther Brunner */
106000a7b5adSEsther Brunnerfunction obfuscate($email) {
106100a7b5adSEsther Brunner  global $conf;
106200a7b5adSEsther Brunner
106300a7b5adSEsther Brunner  switch ($conf['mailguard']) {
106400a7b5adSEsther Brunner    case 'visible' :
106500a7b5adSEsther Brunner      $obfuscate = array('@' => ' [at] ', '.' => ' [dot] ', '-' => ' [dash] ');
106600a7b5adSEsther Brunner      return strtr($email, $obfuscate);
106700a7b5adSEsther Brunner
106800a7b5adSEsther Brunner    case 'hex' :
106900a7b5adSEsther Brunner      $encode = '';
107000a7b5adSEsther Brunner      for ($x=0; $x < strlen($email); $x++) $encode .= '&#x' . bin2hex($email{$x}).';';
107100a7b5adSEsther Brunner      return $encode;
107200a7b5adSEsther Brunner
107300a7b5adSEsther Brunner    case 'none' :
107400a7b5adSEsther Brunner    default :
107500a7b5adSEsther Brunner      return $email;
107600a7b5adSEsther Brunner  }
107700a7b5adSEsther Brunner}
107800a7b5adSEsther Brunner
107900a7b5adSEsther Brunner/**
1080dc57ef04Sandi * Return DokuWikis version
108115fae107Sandi *
108215fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
108315fae107Sandi */
1084f31d5b73Sandifunction getVersion(){
1085f31d5b73Sandi  //import version string
1086f31d5b73Sandi  if(@file_exists('VERSION')){
1087f31d5b73Sandi    //official release
10880647ce3bSAndreas Gohr    return 'Release '.trim(io_readfile(DOKU_INC.'/VERSION'));
1089f31d5b73Sandi  }elseif(is_dir('_darcs')){
1090f31d5b73Sandi    //darcs checkout
1091f31d5b73Sandi    $inv = file('_darcs/inventory');
1092ae41559bSAndreas Gohr    $inv = preg_grep('#\*\*\d{14}[\]$]#',$inv);
1093f31d5b73Sandi    $cur = array_pop($inv);
1094f31d5b73Sandi    preg_match('#\*\*(\d{4})(\d{2})(\d{2})#',$cur,$matches);
1095f31d5b73Sandi    return 'Darcs '.$matches[1].'-'.$matches[2].'-'.$matches[3];
1096f31d5b73Sandi  }else{
1097f31d5b73Sandi    return 'snapshot?';
1098f31d5b73Sandi  }
1099f31d5b73Sandi}
1100f31d5b73Sandi
1101f31d5b73Sandi/**
1102f31d5b73Sandi * Run a few sanity checks
1103f31d5b73Sandi *
1104f31d5b73Sandi * @author Andreas Gohr <andi@splitbrain.org>
1105f31d5b73Sandi */
1106f3f0262cSandifunction check(){
1107f3f0262cSandi  global $conf;
1108f3f0262cSandi  global $INFO;
1109f3f0262cSandi
1110f31d5b73Sandi  msg('DokuWiki version: '.getVersion(),1);
1111f31d5b73Sandi
111249022a38Sandi  if(version_compare(phpversion(),'4.3.0','<')){
111349022a38Sandi    msg('Your PHP version is too old ('.phpversion().' vs. 4.3.+ recommended)',-1);
111449022a38Sandi  }elseif(version_compare(phpversion(),'4.3.10','<')){
111549022a38Sandi    msg('Consider upgrading PHP to 4.3.10 or higher for security reasons (your version: '.phpversion().')',0);
111649022a38Sandi  }else{
111749022a38Sandi    msg('PHP version '.phpversion(),1);
111849022a38Sandi  }
111949022a38Sandi
1120f3f0262cSandi  if(is_writable($conf['changelog'])){
1121f3f0262cSandi    msg('Changelog is writable',1);
1122f3f0262cSandi  }else{
1123f3f0262cSandi    msg('Changelog is not writable',-1);
1124f3f0262cSandi  }
1125f3f0262cSandi
1126f3f0262cSandi  if(is_writable($conf['datadir'])){
1127f3f0262cSandi    msg('Datadir is writable',1);
1128f3f0262cSandi  }else{
1129f3f0262cSandi    msg('Datadir is not writable',-1);
1130f3f0262cSandi  }
1131f3f0262cSandi
1132f3f0262cSandi  if(is_writable($conf['olddir'])){
1133f3f0262cSandi    msg('Attic is writable',1);
1134f3f0262cSandi  }else{
1135f3f0262cSandi    msg('Attic is not writable',-1);
1136f3f0262cSandi  }
1137f3f0262cSandi
1138f3f0262cSandi  if(is_writable($conf['mediadir'])){
1139f3f0262cSandi    msg('Mediadir is writable',1);
1140f3f0262cSandi  }else{
1141f3f0262cSandi    msg('Mediadir is not writable',-1);
1142f3f0262cSandi  }
1143f3f0262cSandi
114498407a7aSandi  if(is_writable($conf['cachedir'])){
114598407a7aSandi    msg('Cachedir is writable',1);
114698407a7aSandi  }else{
114798407a7aSandi    msg('Cachedir is not writable',-1);
114898407a7aSandi  }
114998407a7aSandi
1150e7cb32dcSAndreas Gohr  if(is_writable(DOKU_CONF.'users.auth.php')){
11518c4f28e8Sjan    msg('conf/users.auth.php is writable',1);
1152f3f0262cSandi  }else{
11538c4f28e8Sjan    msg('conf/users.auth.php is not writable',0);
1154f3f0262cSandi  }
115593a9e835Sandi
115693a9e835Sandi  if(function_exists('mb_strpos')){
115793a9e835Sandi    if(defined('UTF8_NOMBSTRING')){
115893a9e835Sandi      msg('mb_string extension is available but will not be used',0);
115993a9e835Sandi    }else{
116093a9e835Sandi      msg('mb_string extension is available and will be used',1);
116193a9e835Sandi    }
116293a9e835Sandi  }else{
116393a9e835Sandi    msg('mb_string extension not available - PHP only replacements will be used',0);
116493a9e835Sandi  }
1165f42d1c75SAndreas Gohr
1166f42d1c75SAndreas Gohr  if($conf['allowdebug']){
1167f42d1c75SAndreas Gohr    msg('Debugging support is enabled. If you don\'t need it you should set $conf[\'allowdebug\'] = 0',-1);
1168f42d1c75SAndreas Gohr  }else{
1169f42d1c75SAndreas Gohr    msg('Debugging support is disabled',1);
1170f42d1c75SAndreas Gohr  }
1171f3f0262cSandi
1172f3f0262cSandi  msg('Your current permission for this page is '.$INFO['perm'],0);
1173f3f0262cSandi
1174f3f0262cSandi  if(is_writable($INFO['filepath'])){
1175f3f0262cSandi    msg('The current page is writable by the webserver',0);
1176f3f0262cSandi  }else{
1177f3f0262cSandi    msg('The current page is not writable by the webserver',0);
1178f3f0262cSandi  }
1179f3f0262cSandi
1180f3f0262cSandi  if($INFO['writable']){
1181f3f0262cSandi    msg('The current page is writable by you',0);
1182f3f0262cSandi  }else{
1183f3f0262cSandi    msg('The current page is not writable you',0);
1184f3f0262cSandi  }
1185f3f0262cSandi}
1186340756e4Sandi
1187b158d625SSteven Danz/**
1188b158d625SSteven Danz * Let us know if a user is tracking a page
1189b158d625SSteven Danz *
11901380fc45SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
1191b158d625SSteven Danz */
11921380fc45SAndreas Gohrfunction is_subscribed($id,$uid){
11931380fc45SAndreas Gohr  $file=metaFN($id,'.mlist');
11941380fc45SAndreas Gohr  if (@file_exists($file)) {
1195b158d625SSteven Danz    $mlist = file($file);
11961380fc45SAndreas Gohr    $pos = array_search($uid."\n",$mlist);
11971380fc45SAndreas Gohr    return is_int($pos);
1198b158d625SSteven Danz  }
11991380fc45SAndreas Gohr
1200b158d625SSteven Danz  return false;
1201b158d625SSteven Danz}
1202340756e4Sandi
1203f9eb5648Ssteven-danz/**
1204f9eb5648Ssteven-danz * Return a string with the email addresses of all the
1205f9eb5648Ssteven-danz * users subscribed to a page
1206f9eb5648Ssteven-danz *
120726a0801fSAndreas Gohr * @author Steven Danz <steven-danz@kc.rr.com>
1208f9eb5648Ssteven-danz */
1209f9eb5648Ssteven-danzfunction subscriber_addresslist($id){
1210f9eb5648Ssteven-danz  global $conf;
1211cd52f92dSchris  global $auth;
1212f9eb5648Ssteven-danz
1213f9eb5648Ssteven-danz  $emails = '';
1214f9eb5648Ssteven-danz
121526a0801fSAndreas Gohr  if (!$conf['subscribers']) return;
121626a0801fSAndreas Gohr
1217f9eb5648Ssteven-danz  $mlist = array();
1218f9eb5648Ssteven-danz  $file=metaFN($id,'.mlist');
1219f9eb5648Ssteven-danz  if (file_exists($file)) {
1220f9eb5648Ssteven-danz    $mlist = file($file);
1221f9eb5648Ssteven-danz  }
1222f9eb5648Ssteven-danz  if(count($mlist) > 0) {
1223f9eb5648Ssteven-danz    foreach ($mlist as $who) {
1224f9eb5648Ssteven-danz      $who = rtrim($who);
1225cd52f92dSchris      $info = $auth->getUserData($who);
1226f9eb5648Ssteven-danz      $level = auth_aclcheck($id,$who,$info['grps']);
1227f9eb5648Ssteven-danz      if ($level >= AUTH_READ) {
1228f9eb5648Ssteven-danz        if (strcasecmp($info['mail'],$conf['notify']) != 0) {
1229f9eb5648Ssteven-danz          if (empty($emails)) {
1230f9eb5648Ssteven-danz            $emails = $info['mail'];
1231f9eb5648Ssteven-danz          } else {
1232f9eb5648Ssteven-danz            $emails = "$emails,".$info['mail'];
1233f9eb5648Ssteven-danz          }
1234f9eb5648Ssteven-danz        }
1235f9eb5648Ssteven-danz      }
1236f9eb5648Ssteven-danz    }
1237f9eb5648Ssteven-danz  }
1238f9eb5648Ssteven-danz
1239f9eb5648Ssteven-danz  return $emails;
1240f9eb5648Ssteven-danz}
1241f9eb5648Ssteven-danz
124289541d4bSAndreas Gohr/**
124389541d4bSAndreas Gohr * Removes quoting backslashes
124489541d4bSAndreas Gohr *
124589541d4bSAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
124689541d4bSAndreas Gohr */
124789541d4bSAndreas Gohrfunction unslash($string,$char="'"){
124889541d4bSAndreas Gohr  return str_replace('\\'.$char,$char,$string);
124989541d4bSAndreas Gohr}
125089541d4bSAndreas Gohr
1251340756e4Sandi//Setup VIM: ex: et ts=2 enc=utf-8 :
1252