xref: /dokuwiki/inc/common.php (revision 71726d7801bdcbf41dfdc79d244f09a0988529c0)
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/**
24d5197206Schris * Wrapper around htmlspecialchars()
25d5197206Schris *
26d5197206Schris * @author Andreas Gohr <andi@splitbrain.org>
27d5197206Schris * @see    htmlspecialchars()
28d5197206Schris */
29d5197206Schrisfunction hsc($string){
30d5197206Schris  return htmlspecialchars($string, ENT_QUOTES, 'UTF-8');
31d5197206Schris}
32d5197206Schris
33d5197206Schris/**
34d5197206Schris * print a newline terminated string
35d5197206Schris *
36d5197206Schris * You can give an indention as optional parameter
37d5197206Schris *
38d5197206Schris * @author Andreas Gohr <andi@splitbrain.org>
39d5197206Schris */
40d5197206Schrisfunction ptln($string,$intend=0){
41d5197206Schris  for($i=0; $i<$intend; $i++) print ' ';
42d5197206Schris  print"$string\n";
43d5197206Schris}
44d5197206Schris
45d5197206Schris/**
4615fae107Sandi * Return info about the current document as associative
47f3f0262cSandi * array.
4815fae107Sandi *
4915fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
50f3f0262cSandi */
51f3f0262cSandifunction pageinfo(){
52f3f0262cSandi  global $ID;
53f3f0262cSandi  global $REV;
54f3f0262cSandi  global $USERINFO;
55f3f0262cSandi  global $conf;
56f3f0262cSandi
57f3f0262cSandi  if($_SERVER['REMOTE_USER']){
58f3f0262cSandi    $info['userinfo']   = $USERINFO;
59f3f0262cSandi    $info['perm']       = auth_quickaclcheck($ID);
601380fc45SAndreas Gohr    $info['subscribed'] = is_subscribed($ID,$_SERVER['REMOTE_USER']);
61ee4c4a1bSAndreas Gohr    $info['client']     = $_SERVER['REMOTE_USER'];
6217ee7f66SAndreas Gohr
6317ee7f66SAndreas Gohr    // if some outside auth were used only REMOTE_USER is set
6417ee7f66SAndreas Gohr    if(!$info['userinfo']['name']){
6517ee7f66SAndreas Gohr      $info['userinfo']['name'] = $_SERVER['REMOTE_USER'];
6617ee7f66SAndreas Gohr    }
67ee4c4a1bSAndreas Gohr
68f3f0262cSandi  }else{
69f3f0262cSandi    $info['perm']       = auth_aclcheck($ID,'',null);
701380fc45SAndreas Gohr    $info['subscribed'] = false;
71ee4c4a1bSAndreas Gohr    $info['client']     = clientIP(true);
72f3f0262cSandi  }
73f3f0262cSandi
74f3f0262cSandi  $info['namespace'] = getNS($ID);
75f3f0262cSandi  $info['locked']    = checklock($ID);
76f3f0262cSandi  $info['filepath']  = realpath(wikiFN($ID,$REV));
77f3f0262cSandi  $info['exists']    = @file_exists($info['filepath']);
78f3f0262cSandi  if($REV && !$info['exists']){
79f3f0262cSandi    //check if current revision was meant
80f3f0262cSandi    $cur = wikiFN($ID);
81f3f0262cSandi    if(@file_exists($cur) && (@filemtime($cur) == $REV)){
82f3f0262cSandi      $info['filepath'] = realpath($cur);
83f3f0262cSandi      $info['exists']   = true;
84f3f0262cSandi      $REV = '';
85f3f0262cSandi    }
86f3f0262cSandi  }
87c112d578Sandi  $info['rev'] = $REV;
88f3f0262cSandi  if($info['exists']){
89f3f0262cSandi    $info['writable'] = (is_writable($info['filepath']) &&
90f3f0262cSandi                         ($info['perm'] >= AUTH_EDIT));
91f3f0262cSandi  }else{
92f3f0262cSandi    $info['writable'] = ($info['perm'] >= AUTH_CREATE);
93f3f0262cSandi  }
94f3f0262cSandi  $info['editable']  = ($info['writable'] && empty($info['lock']));
95f3f0262cSandi  $info['lastmod']   = @filemtime($info['filepath']);
96f3f0262cSandi
97*71726d78SBen Coburn  //load page meta data
98*71726d78SBen Coburn  $info['meta'] = p_get_metadata($ID);
99*71726d78SBen Coburn
100652610a2Sandi  //who's the editor
101652610a2Sandi  if($REV){
102*71726d78SBen Coburn    $revinfo = getRevisionInfo($ID, $REV, 1024);
103652610a2Sandi  }else{
104*71726d78SBen Coburn    $revinfo = $info['meta']['last_change'];
105652610a2Sandi  }
106652610a2Sandi  $info['ip']     = $revinfo['ip'];
107652610a2Sandi  $info['user']   = $revinfo['user'];
108652610a2Sandi  $info['sum']    = $revinfo['sum'];
109*71726d78SBen Coburn  // See also $INFO['meta']['last_change'] which is the most recent log line for page $ID.
110*71726d78SBen Coburn  // Use $INFO['meta']['last_change']['type']==='e' in place of $info['minor'].
11159f257aeSchris
11288f522e9Sandi  if($revinfo['user']){
11388f522e9Sandi    $info['editor'] = $revinfo['user'];
11488f522e9Sandi  }else{
11588f522e9Sandi    $info['editor'] = $revinfo['ip'];
11688f522e9Sandi  }
117652610a2Sandi
118ee4c4a1bSAndreas Gohr  // draft
119ee4c4a1bSAndreas Gohr  $draft = getCacheName($info['client'].$ID,'.draft');
120ee4c4a1bSAndreas Gohr  if(@file_exists($draft)){
121ee4c4a1bSAndreas Gohr    if(@filemtime($draft) < @filemtime(wikiFN($ID))){
122ee4c4a1bSAndreas Gohr      // remove stale draft
123ee4c4a1bSAndreas Gohr      @unlink($draft);
124ee4c4a1bSAndreas Gohr    }else{
125ee4c4a1bSAndreas Gohr      $info['draft'] = $draft;
126ee4c4a1bSAndreas Gohr    }
127ee4c4a1bSAndreas Gohr  }
128ee4c4a1bSAndreas Gohr
129f3f0262cSandi  return $info;
130f3f0262cSandi}
131f3f0262cSandi
132f3f0262cSandi/**
1332684e50aSAndreas Gohr * Build an string of URL parameters
1342684e50aSAndreas Gohr *
1352684e50aSAndreas Gohr * @author Andreas Gohr
1362684e50aSAndreas Gohr */
137b174aeaeSchrisfunction buildURLparams($params, $sep='&amp;'){
1382684e50aSAndreas Gohr  $url = '';
1392684e50aSAndreas Gohr  $amp = false;
1402684e50aSAndreas Gohr  foreach($params as $key => $val){
141b174aeaeSchris    if($amp) $url .= $sep;
1422684e50aSAndreas Gohr
1432684e50aSAndreas Gohr    $url .= $key.'=';
144b6c6979fSAndreas Gohr    $url .= rawurlencode($val);
1452684e50aSAndreas Gohr    $amp = true;
1462684e50aSAndreas Gohr  }
1472684e50aSAndreas Gohr  return $url;
1482684e50aSAndreas Gohr}
1492684e50aSAndreas Gohr
1502684e50aSAndreas Gohr/**
1512684e50aSAndreas Gohr * Build an string of html tag attributes
1522684e50aSAndreas Gohr *
1532684e50aSAndreas Gohr * @author Andreas Gohr
1542684e50aSAndreas Gohr */
1552684e50aSAndreas Gohrfunction buildAttributes($params){
1562684e50aSAndreas Gohr  $url = '';
1572684e50aSAndreas Gohr  foreach($params as $key => $val){
1582684e50aSAndreas Gohr    $url .= $key.'="';
1592684e50aSAndreas Gohr    $url .= htmlspecialchars ($val);
1602684e50aSAndreas Gohr    $url .= '" ';
1612684e50aSAndreas Gohr  }
1622684e50aSAndreas Gohr  return $url;
1632684e50aSAndreas Gohr}
1642684e50aSAndreas Gohr
1652684e50aSAndreas Gohr
1662684e50aSAndreas Gohr/**
1670396becbSandi * print a message
1680396becbSandi *
1690396becbSandi * If HTTP headers were not sent yet the message is added
1700396becbSandi * to the global message array else it's printed directly
1710396becbSandi * using html_msgarea()
1720396becbSandi *
173f3f0262cSandi *
174f3f0262cSandi * Levels can be:
175f3f0262cSandi *
176f3f0262cSandi * -1 error
177f3f0262cSandi *  0 info
178f3f0262cSandi *  1 success
17915fae107Sandi *
18015fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
1810396becbSandi * @see    html_msgarea
182f3f0262cSandi */
1830d58d74eSAndreas Gohrfunction msg($message,$lvl=0,$line='',$file=''){
184f3f0262cSandi  global $MSG;
185f3f0262cSandi  $errors[-1] = 'error';
186f3f0262cSandi  $errors[0]  = 'info';
187f3f0262cSandi  $errors[1]  = 'success';
188f3f0262cSandi
1890d58d74eSAndreas Gohr  if($line || $file) $message.=' ['.basename($file).':'.$line.']';
1900d58d74eSAndreas Gohr
191cc20ad51Sandi  if(!headers_sent()){
192f3f0262cSandi    if(!isset($MSG)) $MSG = array();
193f3f0262cSandi    $MSG[]=array('lvl' => $errors[$lvl], 'msg' => $message);
1940396becbSandi  }else{
1950396becbSandi    $MSG = array();
1960396becbSandi    $MSG[]=array('lvl' => $errors[$lvl], 'msg' => $message);
197f62ea8a1Sandi    if(function_exists('html_msgarea')){
1980396becbSandi      html_msgarea();
199f62ea8a1Sandi    }else{
200f62ea8a1Sandi      print "ERROR($lvl) $message";
201f62ea8a1Sandi    }
2020396becbSandi  }
203f3f0262cSandi}
204f3f0262cSandi
205f3f0262cSandi/**
20615fae107Sandi * This builds the breadcrumb trail and returns it as array
20715fae107Sandi *
20815fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
209f3f0262cSandi */
210f3f0262cSandifunction breadcrumbs(){
2118746e727Sandi  // we prepare the breadcrumbs early for quick session closing
2128746e727Sandi  static $crumbs = null;
2138746e727Sandi  if($crumbs != null) return $crumbs;
2148746e727Sandi
215f3f0262cSandi  global $ID;
216f3f0262cSandi  global $ACT;
217f3f0262cSandi  global $conf;
218f3f0262cSandi  $crumbs = $_SESSION[$conf['title']]['bc'];
219f3f0262cSandi
220f3f0262cSandi  //first visit?
221f3f0262cSandi  if (!is_array($crumbs)){
222f3f0262cSandi    $crumbs = array();
223f3f0262cSandi  }
224f3f0262cSandi  //we only save on show and existing wiki documents
225a77f5846Sjan  $file = wikiFN($ID);
226a77f5846Sjan  if($ACT != 'show' || !@file_exists($file)){
227f3f0262cSandi    $_SESSION[$conf['title']]['bc'] = $crumbs;
228f3f0262cSandi    return $crumbs;
229f3f0262cSandi  }
230a77f5846Sjan
231a77f5846Sjan  // page names
232a77f5846Sjan  $name = noNS($ID);
233a77f5846Sjan  if ($conf['useheading']) {
234a77f5846Sjan    // get page title
235bb0a59d4Sjan    $title = p_get_first_heading($ID);
236a77f5846Sjan    if ($title) {
237a77f5846Sjan      $name = $title;
238a77f5846Sjan    }
239a77f5846Sjan  }
240a77f5846Sjan
241f3f0262cSandi  //remove ID from array
242a77f5846Sjan  if (isset($crumbs[$ID])) {
243a77f5846Sjan    unset($crumbs[$ID]);
244f3f0262cSandi  }
245f3f0262cSandi
246f3f0262cSandi  //add to array
247a77f5846Sjan  $crumbs[$ID] = $name;
248f3f0262cSandi  //reduce size
249f3f0262cSandi  while(count($crumbs) > $conf['breadcrumbs']){
250f3f0262cSandi    array_shift($crumbs);
251f3f0262cSandi  }
252f3f0262cSandi  //save to session
253f3f0262cSandi  $_SESSION[$conf['title']]['bc'] = $crumbs;
254f3f0262cSandi  return $crumbs;
255f3f0262cSandi}
256f3f0262cSandi
257f3f0262cSandi/**
25815fae107Sandi * Filter for page IDs
25915fae107Sandi *
260f3f0262cSandi * This is run on a ID before it is outputted somewhere
261f3f0262cSandi * currently used to replace the colon with something else
262f3f0262cSandi * on Windows systems and to have proper URL encoding
26315fae107Sandi *
26449c713a3Sandi * Urlencoding is ommitted when the second parameter is false
26549c713a3Sandi *
26615fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
267f3f0262cSandi */
26849c713a3Sandifunction idfilter($id,$ue=true){
269f3f0262cSandi  global $conf;
270f3f0262cSandi  if ($conf['useslash'] && $conf['userewrite']){
271f3f0262cSandi    $id = strtr($id,':','/');
272f3f0262cSandi  }elseif (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' &&
273f3f0262cSandi      $conf['userewrite']) {
274f3f0262cSandi    $id = strtr($id,':',';');
275f3f0262cSandi  }
27649c713a3Sandi  if($ue){
277b6c6979fSAndreas Gohr    $id = rawurlencode($id);
278f3f0262cSandi    $id = str_replace('%3A',':',$id); //keep as colon
279f3f0262cSandi    $id = str_replace('%2F','/',$id); //keep as slash
28049c713a3Sandi  }
281f3f0262cSandi  return $id;
282f3f0262cSandi}
283f3f0262cSandi
284f3f0262cSandi/**
285ed7b5f09Sandi * This builds a link to a wikipage
28615fae107Sandi *
2876c7843b5Sandi * It handles URL rewriting and adds additional parameter if
2886c7843b5Sandi * given in $more
2896c7843b5Sandi *
29015fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
291f3f0262cSandi */
292b174aeaeSchrisfunction wl($id='',$more='',$abs=false,$sep='&amp;'){
293f3f0262cSandi  global $conf;
2946de3759aSAndreas Gohr  if(is_array($more)){
295b174aeaeSchris    $more = buildURLparams($more,$sep);
2966de3759aSAndreas Gohr  }else{
297b174aeaeSchris    $more = str_replace(',',$sep,$more);
2986de3759aSAndreas Gohr  }
299f3f0262cSandi
300f3f0262cSandi  $id    = idfilter($id);
301ed7b5f09Sandi  if($abs){
302ed7b5f09Sandi    $xlink = DOKU_URL;
303ed7b5f09Sandi  }else{
304ed7b5f09Sandi    $xlink = DOKU_BASE;
305ed7b5f09Sandi  }
306f3f0262cSandi
3076c7843b5Sandi  if($conf['userewrite'] == 2){
3086c7843b5Sandi    $xlink .= DOKU_SCRIPT.'/'.$id;
3096c7843b5Sandi    if($more) $xlink .= '?'.$more;
3106c7843b5Sandi  }elseif($conf['userewrite']){
311f3f0262cSandi    $xlink .= $id;
312f3f0262cSandi    if($more) $xlink .= '?'.$more;
3136c7843b5Sandi  }else{
3146c7843b5Sandi    $xlink .= DOKU_SCRIPT.'?id='.$id;
315b174aeaeSchris    if($more) $xlink .= $sep.$more;
316f3f0262cSandi  }
317f3f0262cSandi
318f3f0262cSandi  return $xlink;
319f3f0262cSandi}
320f3f0262cSandi
321f3f0262cSandi/**
322f5c2808fSBen Coburn * This builds a link to an alternate page format
323f5c2808fSBen Coburn *
324f5c2808fSBen Coburn * Handles URL rewriting if enabled. Follows the style of wl().
325f5c2808fSBen Coburn *
326f5c2808fSBen Coburn * @author Ben Coburn <btcoburn@silicodon.net>
327f5c2808fSBen Coburn */
328f5c2808fSBen Coburnfunction exportlink($id='',$format='raw',$more='',$abs=false,$sep='&amp;'){
329f5c2808fSBen Coburn  global $conf;
330f5c2808fSBen Coburn  if(is_array($more)){
331f5c2808fSBen Coburn    $more = buildURLparams($more,$sep);
332f5c2808fSBen Coburn  }else{
333f5c2808fSBen Coburn    $more = str_replace(',',$sep,$more);
334f5c2808fSBen Coburn  }
335f5c2808fSBen Coburn
336f5c2808fSBen Coburn  $format = rawurlencode($format);
337f5c2808fSBen Coburn  $id = idfilter($id);
338f5c2808fSBen Coburn  if($abs){
339f5c2808fSBen Coburn    $xlink = DOKU_URL;
340f5c2808fSBen Coburn  }else{
341f5c2808fSBen Coburn    $xlink = DOKU_BASE;
342f5c2808fSBen Coburn  }
343f5c2808fSBen Coburn
344f5c2808fSBen Coburn  if($conf['userewrite'] == 2){
345f5c2808fSBen Coburn    $xlink .= DOKU_SCRIPT.'/'.$id.'?do=export_'.$format;
346f5c2808fSBen Coburn    if($more) $xlink .= $sep.$more;
347f5c2808fSBen Coburn  }elseif($conf['userewrite'] == 1){
348f5c2808fSBen Coburn    $xlink .= '_export/'.$format.'/'.$id;
349f5c2808fSBen Coburn    if($more) $xlink .= '?'.$more;
350f5c2808fSBen Coburn  }else{
351f5c2808fSBen Coburn    $xlink .= DOKU_SCRIPT.'?do=export_'.$format.$sep.'id='.$id;
352f5c2808fSBen Coburn    if($more) $xlink .= $sep.$more;
353f5c2808fSBen Coburn  }
354f5c2808fSBen Coburn
355f5c2808fSBen Coburn  return $xlink;
356f5c2808fSBen Coburn}
357f5c2808fSBen Coburn
358f5c2808fSBen Coburn/**
3596de3759aSAndreas Gohr * Build a link to a media file
3606de3759aSAndreas Gohr *
3616de3759aSAndreas Gohr * Will return a link to the detail page if $direct is false
3626de3759aSAndreas Gohr */
363b174aeaeSchrisfunction ml($id='',$more='',$direct=true,$sep='&amp;'){
3646de3759aSAndreas Gohr  global $conf;
3656de3759aSAndreas Gohr  if(is_array($more)){
366b174aeaeSchris    $more = buildURLparams($more,$sep);
3676de3759aSAndreas Gohr  }else{
368b174aeaeSchris    $more = str_replace(',',$sep,$more);
3696de3759aSAndreas Gohr  }
3706de3759aSAndreas Gohr
3716de3759aSAndreas Gohr  $xlink = DOKU_BASE;
3726de3759aSAndreas Gohr
3736de3759aSAndreas Gohr  // external URLs are always direct without rewriting
3746de3759aSAndreas Gohr  if(preg_match('#^(https?|ftp)://#i',$id)){
3756de3759aSAndreas Gohr    $xlink .= 'lib/exe/fetch.php';
3766de3759aSAndreas Gohr    if($more){
3776de3759aSAndreas Gohr      $xlink .= '?'.$more;
378b174aeaeSchris      $xlink .= $sep.'media='.rawurlencode($id);
3796de3759aSAndreas Gohr    }else{
380b6c6979fSAndreas Gohr      $xlink .= '?media='.rawurlencode($id);
3816de3759aSAndreas Gohr    }
3826de3759aSAndreas Gohr    return $xlink;
3836de3759aSAndreas Gohr  }
3846de3759aSAndreas Gohr
3856de3759aSAndreas Gohr  $id = idfilter($id);
3866de3759aSAndreas Gohr
3876de3759aSAndreas Gohr  // decide on scriptname
3886de3759aSAndreas Gohr  if($direct){
3896de3759aSAndreas Gohr    if($conf['userewrite'] == 1){
3906de3759aSAndreas Gohr      $script = '_media';
3916de3759aSAndreas Gohr    }else{
3926de3759aSAndreas Gohr      $script = 'lib/exe/fetch.php';
3936de3759aSAndreas Gohr    }
3946de3759aSAndreas Gohr  }else{
3956de3759aSAndreas Gohr    if($conf['userewrite'] == 1){
3966de3759aSAndreas Gohr      $script = '_detail';
3976de3759aSAndreas Gohr    }else{
3986de3759aSAndreas Gohr      $script = 'lib/exe/detail.php';
3996de3759aSAndreas Gohr    }
4006de3759aSAndreas Gohr  }
4016de3759aSAndreas Gohr
4026de3759aSAndreas Gohr  // build URL based on rewrite mode
4036de3759aSAndreas Gohr   if($conf['userewrite']){
4046de3759aSAndreas Gohr     $xlink .= $script.'/'.$id;
4056de3759aSAndreas Gohr     if($more) $xlink .= '?'.$more;
4066de3759aSAndreas Gohr   }else{
4076de3759aSAndreas Gohr     if($more){
408a99d3236SEsther Brunner       $xlink .= $script.'?'.$more;
409b174aeaeSchris       $xlink .= $sep.'media='.$id;
4106de3759aSAndreas Gohr     }else{
411a99d3236SEsther Brunner       $xlink .= $script.'?media='.$id;
4126de3759aSAndreas Gohr     }
4136de3759aSAndreas Gohr   }
4146de3759aSAndreas Gohr
4156de3759aSAndreas Gohr  return $xlink;
4166de3759aSAndreas Gohr}
4176de3759aSAndreas Gohr
4186de3759aSAndreas Gohr
4196de3759aSAndreas Gohr
4206de3759aSAndreas Gohr/**
421f3f0262cSandi * Just builds a link to a script
42215fae107Sandi *
423ed7b5f09Sandi * @todo   maybe obsolete
42415fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
425f3f0262cSandi */
426f3f0262cSandifunction script($script='doku.php'){
427ed7b5f09Sandi#  $link = getBaseURL();
428ed7b5f09Sandi#  $link .= $script;
429ed7b5f09Sandi#  return $link;
430ed7b5f09Sandi  return DOKU_BASE.DOKU_SCRIPT;
431f3f0262cSandi}
432f3f0262cSandi
433f3f0262cSandi/**
43415fae107Sandi * Spamcheck against wordlist
43515fae107Sandi *
436f3f0262cSandi * Checks the wikitext against a list of blocked expressions
437f3f0262cSandi * returns true if the text contains any bad words
43815fae107Sandi *
43915fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
440f3f0262cSandi */
441f3f0262cSandifunction checkwordblock(){
442f3f0262cSandi  global $TEXT;
443f3f0262cSandi  global $conf;
444f3f0262cSandi
445f3f0262cSandi  if(!$conf['usewordblock']) return false;
446f3f0262cSandi
447b9ac8716Schris  $wordblocks = getWordblocks();
4483e2965d7Sandi  //how many lines to read at once (to work around some PCRE limits)
4493e2965d7Sandi  if(version_compare(phpversion(),'4.3.0','<')){
4503e2965d7Sandi    //old versions of PCRE define a maximum of parenthesises even if no
4513e2965d7Sandi    //backreferences are used - the maximum is 99
4523e2965d7Sandi    //this is very bad performancewise and may even be too high still
4533e2965d7Sandi    $chunksize = 40;
4543e2965d7Sandi  }else{
455703f6fdeSandi    //read file in chunks of 600 - this should work around the
4563e2965d7Sandi    //MAX_PATTERN_SIZE in modern PCRE
457444b87a5SAndreas Gohr    $chunksize = 400;
4583e2965d7Sandi  }
459b9ac8716Schris  while($blocks = array_splice($wordblocks,0,$chunksize)){
460f3f0262cSandi    $re = array();
461f3f0262cSandi    #build regexp from blocks
462f3f0262cSandi    foreach($blocks as $block){
463f3f0262cSandi      $block = preg_replace('/#.*$/','',$block);
464f3f0262cSandi      $block = trim($block);
465f3f0262cSandi      if(empty($block)) continue;
466f3f0262cSandi      $re[]  = $block;
467f3f0262cSandi    }
468b9ac8716Schris    if(preg_match('#('.join('|',$re).')#si',$TEXT, $match=array())) {
469b9ac8716Schris      return true;
470b9ac8716Schris    }
471703f6fdeSandi  }
472f3f0262cSandi  return false;
473f3f0262cSandi}
474f3f0262cSandi
475f3f0262cSandi/**
47615fae107Sandi * Return the IP of the client
47715fae107Sandi *
4786d8affe6SAndreas Gohr * Honours X-Forwarded-For and X-Real-IP Proxy Headers
47915fae107Sandi *
4806d8affe6SAndreas Gohr * It returns a comma separated list of IPs if the above mentioned
4816d8affe6SAndreas Gohr * headers are set. If the single parameter is set, it tries to return
4826d8affe6SAndreas Gohr * a routable public address, prefering the ones suplied in the X
4836d8affe6SAndreas Gohr * headers
4846d8affe6SAndreas Gohr *
4856d8affe6SAndreas Gohr * @param  boolean $single If set only a single IP is returned
48615fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
487f3f0262cSandi */
4886d8affe6SAndreas Gohrfunction clientIP($single=false){
4896d8affe6SAndreas Gohr  $ip = array();
4906d8affe6SAndreas Gohr  $ip[] = $_SERVER['REMOTE_ADDR'];
4916d8affe6SAndreas Gohr  if($_SERVER['HTTP_X_FORWARDED_FOR'])
4926d8affe6SAndreas Gohr    $ip = array_merge($ip,explode(',',$_SERVER['HTTP_X_FORWARDED_FOR']));
4936d8affe6SAndreas Gohr  if($_SERVER['HTTP_X_REAL_IP'])
4946d8affe6SAndreas Gohr    $ip = array_merge($ip,explode(',',$_SERVER['HTTP_X_REAL_IP']));
4956d8affe6SAndreas Gohr
4966d8affe6SAndreas Gohr  // remove any non-IP stuff
4976d8affe6SAndreas Gohr  $cnt = count($ip);
4986d8affe6SAndreas Gohr  for($i=0; $i<$cnt; $i++){
4996d8affe6SAndreas Gohr    $ip[$i] = preg_replace('/[^0-9\.]+/','',$ip[$i]);
5006d8affe6SAndreas Gohr    if(!preg_match('/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/',$ip[$i])) $ip[$i] = '';
5016d8affe6SAndreas Gohr    if(empty($ip[$i])) unset($ip[$i]);
502f3f0262cSandi  }
5036d8affe6SAndreas Gohr  $ip = array_values(array_unique($ip));
5046d8affe6SAndreas Gohr  if(!$ip[0]) $ip[0] = '0.0.0.0'; // for some strange reason we don't have a IP
5056d8affe6SAndreas Gohr
5066d8affe6SAndreas Gohr  if(!$single) return join(',',$ip);
5076d8affe6SAndreas Gohr
5086d8affe6SAndreas Gohr  // decide which IP to use, trying to avoid local addresses
5096d8affe6SAndreas Gohr  $ip = array_reverse($ip);
5106d8affe6SAndreas Gohr  foreach($ip as $i){
5116d8affe6SAndreas Gohr    if(preg_match('/^(127\.|10\.|192\.168\.|172\.((1[6-9])|(2[0-9])|(3[0-1]))\.)/',$i)){
5126d8affe6SAndreas Gohr      continue;
5136d8affe6SAndreas Gohr    }else{
5146d8affe6SAndreas Gohr      return $i;
5156d8affe6SAndreas Gohr    }
5166d8affe6SAndreas Gohr  }
5176d8affe6SAndreas Gohr  // still here? just use the first (last) address
5186d8affe6SAndreas Gohr  return $ip[0];
519f3f0262cSandi}
520f3f0262cSandi
521f3f0262cSandi/**
52215fae107Sandi * Checks if a given page is currently locked.
52315fae107Sandi *
524f3f0262cSandi * removes stale lockfiles
52515fae107Sandi *
52615fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
527f3f0262cSandi */
528f3f0262cSandifunction checklock($id){
529f3f0262cSandi  global $conf;
530c9b4bd1eSBen Coburn  $lock = wikiLockFN($id);
531f3f0262cSandi
532f3f0262cSandi  //no lockfile
533f3f0262cSandi  if(!@file_exists($lock)) return false;
534f3f0262cSandi
535f3f0262cSandi  //lockfile expired
536f3f0262cSandi  if((time() - filemtime($lock)) > $conf['locktime']){
537f3f0262cSandi    unlink($lock);
538f3f0262cSandi    return false;
539f3f0262cSandi  }
540f3f0262cSandi
541f3f0262cSandi  //my own lock
542f3f0262cSandi  $ip = io_readFile($lock);
543f3f0262cSandi  if( ($ip == clientIP()) || ($ip == $_SERVER['REMOTE_USER']) ){
544f3f0262cSandi    return false;
545f3f0262cSandi  }
546f3f0262cSandi
547f3f0262cSandi  return $ip;
548f3f0262cSandi}
549f3f0262cSandi
550f3f0262cSandi/**
55115fae107Sandi * Lock a page for editing
55215fae107Sandi *
55315fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
554f3f0262cSandi */
555f3f0262cSandifunction lock($id){
556c9b4bd1eSBen Coburn  $lock = wikiLockFN($id);
557f3f0262cSandi  if($_SERVER['REMOTE_USER']){
558f3f0262cSandi    io_saveFile($lock,$_SERVER['REMOTE_USER']);
559f3f0262cSandi  }else{
560f3f0262cSandi    io_saveFile($lock,clientIP());
561f3f0262cSandi  }
562f3f0262cSandi}
563f3f0262cSandi
564f3f0262cSandi/**
56515fae107Sandi * Unlock a page if it was locked by the user
566f3f0262cSandi *
56715fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
56815fae107Sandi * @return bool true if a lock was removed
569f3f0262cSandi */
570f3f0262cSandifunction unlock($id){
571c9b4bd1eSBen Coburn  $lock = wikiLockFN($id);
572f3f0262cSandi  if(@file_exists($lock)){
573f3f0262cSandi    $ip = io_readFile($lock);
574f3f0262cSandi    if( ($ip == clientIP()) || ($ip == $_SERVER['REMOTE_USER']) ){
575f3f0262cSandi      @unlink($lock);
576f3f0262cSandi      return true;
577f3f0262cSandi    }
578f3f0262cSandi  }
579f3f0262cSandi  return false;
580f3f0262cSandi}
581f3f0262cSandi
582f3f0262cSandi/**
583f3f0262cSandi * convert line ending to unix format
584f3f0262cSandi *
58515fae107Sandi * @see    formText() for 2crlf conversion
58615fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
587f3f0262cSandi */
588f3f0262cSandifunction cleanText($text){
589f3f0262cSandi  $text = preg_replace("/(\015\012)|(\015)/","\012",$text);
590f3f0262cSandi  return $text;
591f3f0262cSandi}
592f3f0262cSandi
593f3f0262cSandi/**
594f3f0262cSandi * Prepares text for print in Webforms by encoding special chars.
595f3f0262cSandi * It also converts line endings to Windows format which is
596f3f0262cSandi * pseudo standard for webforms.
597f3f0262cSandi *
59815fae107Sandi * @see    cleanText() for 2unix conversion
59915fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
600f3f0262cSandi */
601f3f0262cSandifunction formText($text){
602f3f0262cSandi  $text = preg_replace("/\012/","\015\012",$text);
603f3f0262cSandi  return htmlspecialchars($text);
604f3f0262cSandi}
605f3f0262cSandi
606f3f0262cSandi/**
60715fae107Sandi * Returns the specified local text in raw format
60815fae107Sandi *
60915fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
610f3f0262cSandi */
611f3f0262cSandifunction rawLocale($id){
612f3f0262cSandi  return io_readFile(localeFN($id));
613f3f0262cSandi}
614f3f0262cSandi
615f3f0262cSandi/**
616f3f0262cSandi * Returns the raw WikiText
61715fae107Sandi *
61815fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
619f3f0262cSandi */
620f3f0262cSandifunction rawWiki($id,$rev=''){
621cc7d0c94SBen Coburn  return io_readWikiPage(wikiFN($id, $rev), $id, $rev);
622f3f0262cSandi}
623f3f0262cSandi
624f3f0262cSandi/**
6257146cee2SAndreas Gohr * Returns the pagetemplate contents for the ID's namespace
6267146cee2SAndreas Gohr *
6277146cee2SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
6287146cee2SAndreas Gohr */
6297146cee2SAndreas Gohrfunction pageTemplate($id){
630a15ce62dSEsther Brunner  global $conf;
631a15ce62dSEsther Brunner  global $INFO;
632a15ce62dSEsther Brunner  $tpl = io_readFile(dirname(wikiFN($id)).'/_template.txt');
633a15ce62dSEsther Brunner  $tpl = str_replace('@ID@',$id,$tpl);
634a15ce62dSEsther Brunner  $tpl = str_replace('@NS@',getNS($id),$tpl);
635a15ce62dSEsther Brunner  $tpl = str_replace('@PAGE@',strtr(noNS($id),'_',' '),$tpl);
636a15ce62dSEsther Brunner  $tpl = str_replace('@USER@',$_SERVER['REMOTE_USER'],$tpl);
637a15ce62dSEsther Brunner  $tpl = str_replace('@NAME@',$INFO['userinfo']['name'],$tpl);
638a15ce62dSEsther Brunner  $tpl = str_replace('@MAIL@',$INFO['userinfo']['mail'],$tpl);
639a15ce62dSEsther Brunner  $tpl = str_replace('@DATE@',date($conf['dformat']),$tpl);
640a15ce62dSEsther Brunner  return $tpl;
6417146cee2SAndreas Gohr}
6427146cee2SAndreas Gohr
6437146cee2SAndreas Gohr
6447146cee2SAndreas Gohr/**
64515fae107Sandi * Returns the raw Wiki Text in three slices.
64615fae107Sandi *
64715fae107Sandi * The range parameter needs to have the form "from-to"
64815cfe303Sandi * and gives the range of the section in bytes - no
64915cfe303Sandi * UTF-8 awareness is needed.
650f3f0262cSandi * The returned order is prefix, section and suffix.
65115fae107Sandi *
65215fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
653f3f0262cSandi */
654f3f0262cSandifunction rawWikiSlices($range,$id,$rev=''){
655f3f0262cSandi  list($from,$to) = split('-',$range,2);
656cc7d0c94SBen Coburn  $text = io_readWikiPage(wikiFN($id, $rev), $id, $rev);
657f3f0262cSandi  if(!$from) $from = 0;
658c3d8e19bSandi  if(!$to)   $to   = strlen($text)+1;
659f3f0262cSandi
66015cfe303Sandi  $slices[0] = substr($text,0,$from-1);
66115cfe303Sandi  $slices[1] = substr($text,$from-1,$to-$from);
66215cfe303Sandi  $slices[2] = substr($text,$to);
663f3f0262cSandi
664f3f0262cSandi  return $slices;
665f3f0262cSandi}
666f3f0262cSandi
667f3f0262cSandi/**
66815fae107Sandi * Joins wiki text slices
66915fae107Sandi *
670f3f0262cSandi * function to join the text slices with correct lineendings again.
671f3f0262cSandi * When the pretty parameter is set to true it adds additional empty
672f3f0262cSandi * lines between sections if needed (used on saving).
67315fae107Sandi *
67415fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
675f3f0262cSandi */
676f3f0262cSandifunction con($pre,$text,$suf,$pretty=false){
677f3f0262cSandi
678f3f0262cSandi  if($pretty){
679f3f0262cSandi    if($pre && substr($pre,-1) != "\n") $pre .= "\n";
680f3f0262cSandi    if($suf && substr($text,-1) != "\n") $text .= "\n";
681f3f0262cSandi  }
682f3f0262cSandi
683f3f0262cSandi  if($pre) $pre .= "\n";
684f3f0262cSandi  if($suf) $text .= "\n";
685f3f0262cSandi  return $pre.$text.$suf;
686f3f0262cSandi}
687f3f0262cSandi
688f3f0262cSandi/**
68915fae107Sandi * print debug messages
69015fae107Sandi *
691f3f0262cSandi * little function to print the content of a var
69215fae107Sandi *
69315fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
694f3f0262cSandi */
695f3f0262cSandifunction dbg($msg,$hidden=false){
696f3f0262cSandi  (!$hidden) ? print '<pre class="dbg">' : print "<!--\n";
697f3f0262cSandi  print_r($msg);
698f3f0262cSandi  (!$hidden) ? print '</pre>' : print "\n-->";
699f3f0262cSandi}
700f3f0262cSandi
701f3f0262cSandi/**
70263cb5853SAndreas Gohr * Print info to a log file
70363cb5853SAndreas Gohr *
70463cb5853SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
70563cb5853SAndreas Gohr */
70663cb5853SAndreas Gohrfunction dbglog($msg){
70763cb5853SAndreas Gohr  global $conf;
70863cb5853SAndreas Gohr  $file = $conf['cachedir'].'/debug.log';
70963cb5853SAndreas Gohr  $fh = fopen($file,'a');
71063cb5853SAndreas Gohr  if($fh){
71163cb5853SAndreas Gohr    fwrite($fh,date('H:i:s ').$_SERVER['REMOTE_ADDR'].': '.$msg."\n");
71263cb5853SAndreas Gohr    fclose($fh);
71363cb5853SAndreas Gohr  }
71463cb5853SAndreas Gohr}
71563cb5853SAndreas Gohr
71663cb5853SAndreas Gohr/**
717*71726d78SBen Coburn * Add's an entry to the changelog and saves the metadata for the page
71815fae107Sandi *
71915fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
720*71726d78SBen Coburn * @author Esther Brunner <wikidesign@gmail.com>
721*71726d78SBen Coburn * @author Ben Coburn <btcoburn@silicodon.net>
722f3f0262cSandi */
723*71726d78SBen Coburnfunction addLogEntry($date, $id, $type='E', $summary='', $extra=''){
724*71726d78SBen Coburn  global $conf, $INFO;
725c1049928Sandi
726*71726d78SBen Coburn  $id = cleanid($id);
727*71726d78SBen Coburn  $file = wikiFN($id);
728*71726d78SBen Coburn  $created = @filectime($file);
729*71726d78SBen Coburn  $minor = ($type==='e');
730*71726d78SBen Coburn  $wasRemoved = ($type==='D');
731c1049928Sandi
732652610a2Sandi  if(!$date) $date = time(); //use current time if none supplied
733f3f0262cSandi  $remote = $_SERVER['REMOTE_ADDR'];
734f3f0262cSandi  $user   = $_SERVER['REMOTE_USER'];
735f3f0262cSandi
736*71726d78SBen Coburn  $logline = array(
737*71726d78SBen Coburn    'date'  => $date,
738*71726d78SBen Coburn    'ip'    => $remote,
739*71726d78SBen Coburn    'type'  => $type,
740*71726d78SBen Coburn    'id'    => $id,
741*71726d78SBen Coburn    'user'  => $user,
742*71726d78SBen Coburn    'sum'   => $summary,
743*71726d78SBen Coburn    'extra' => $extra
744*71726d78SBen Coburn  );
745*71726d78SBen Coburn
746*71726d78SBen Coburn  // update metadata
747*71726d78SBen Coburn  if (!$wasRemoved) {
748*71726d78SBen Coburn    $meta = array();
749*71726d78SBen Coburn    if (!$INFO['exists']){ // newly created
750*71726d78SBen Coburn      $meta['date']['created'] = $created;
751*71726d78SBen Coburn      if ($user) $meta['creator'] = $INFO['userinfo']['name'];
752*71726d78SBen Coburn    } elseif (!$minor) {   // non-minor modification
753*71726d78SBen Coburn      $meta['date']['modified'] = $date;
754*71726d78SBen Coburn      if ($user) $meta['contributor'][$user] = $INFO['userinfo']['name'];
755*71726d78SBen Coburn    }
756*71726d78SBen Coburn    $meta['last_change'] = $logline;
757*71726d78SBen Coburn    p_set_metadata($id, $meta, true);
758b6912aeaSAndreas Gohr  }
759b6912aeaSAndreas Gohr
760*71726d78SBen Coburn  // add changelog lines
761*71726d78SBen Coburn  $logline = implode("\t", $logline)."\n";
762*71726d78SBen Coburn  io_saveFile(metaFN($id,'.changes'),$logline,true); //page changelog
763*71726d78SBen Coburn  io_saveFile($conf['changelog'],$logline,true); //global changelog cache
764b6912aeaSAndreas Gohr}
765b6912aeaSAndreas Gohr
766b6912aeaSAndreas Gohr/**
767d437bcc4SAndreas Gohr * Internal function used by getRecents
768d437bcc4SAndreas Gohr *
769d437bcc4SAndreas Gohr * don't call directly
770d437bcc4SAndreas Gohr *
771d437bcc4SAndreas Gohr * @see getRecents()
772d437bcc4SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
773*71726d78SBen Coburn * @author Ben Coburn <btcoburn@silicodon.net>
774d437bcc4SAndreas Gohr */
775b6912aeaSAndreas Gohrfunction _handleRecent($line,$ns,$flags){
776d437bcc4SAndreas Gohr  static $seen  = array();         //caches seen pages and skip them
777d437bcc4SAndreas Gohr  if(empty($line)) return false;   //skip empty lines
778d437bcc4SAndreas Gohr
779d437bcc4SAndreas Gohr  // split the line into parts
780*71726d78SBen Coburn  $recent = parseChangelogLine($line);
781*71726d78SBen Coburn  if ($recent===false) { return false; }
782d437bcc4SAndreas Gohr
783d437bcc4SAndreas Gohr  // skip seen ones
784*71726d78SBen Coburn  if(isset($seen[$recent['id']])) return false;
785b6912aeaSAndreas Gohr
786b6912aeaSAndreas Gohr  // skip minors
787*71726d78SBen Coburn  if($recent['type']==='e' && ($flags & RECENTS_SKIP_MINORS)) return false;
788d437bcc4SAndreas Gohr
789d437bcc4SAndreas Gohr  // remember in seen to skip additional sights
790*71726d78SBen Coburn  $seen[$recent['id']] = 1;
791d437bcc4SAndreas Gohr
7920dc92c6fSAndreas Gohr  // check if it's a hidden page
793*71726d78SBen Coburn  if(isHiddenPage($recent['id'])) return false;
7940dc92c6fSAndreas Gohr
795d437bcc4SAndreas Gohr  // filter namespace
796*71726d78SBen Coburn  if (($ns) && (strpos($recent['id'],$ns.':') !== 0)) return false;
797d437bcc4SAndreas Gohr
798d437bcc4SAndreas Gohr  // exclude subnamespaces
799*71726d78SBen Coburn  if (($flags & RECENTS_SKIP_SUBSPACES) && (getNS($recent['id']) != $ns)) return false;
800d437bcc4SAndreas Gohr
801ae56bfb6SAndreas Gohr  // check ACL
802*71726d78SBen Coburn  if (auth_quickaclcheck($recent['id']) < AUTH_READ) return false;
803ae56bfb6SAndreas Gohr
804d437bcc4SAndreas Gohr  // check existance
805*71726d78SBen Coburn  if((!@file_exists(wikiFN($recent['id']))) && ($flags & RECENTS_SKIP_DELETED)) return false;
806d437bcc4SAndreas Gohr
807d437bcc4SAndreas Gohr  return $recent;
808d437bcc4SAndreas Gohr}
809d437bcc4SAndreas Gohr
810b6912aeaSAndreas Gohr
811d437bcc4SAndreas Gohr/**
812f3f0262cSandi * returns an array of recently changed files using the
813f3f0262cSandi * changelog
814d437bcc4SAndreas Gohr *
815b6912aeaSAndreas Gohr * The following constants can be used to control which changes are
816b6912aeaSAndreas Gohr * included. Add them together as needed.
817b6912aeaSAndreas Gohr *
818b6912aeaSAndreas Gohr * RECENTS_SKIP_DELETED   - don't include deleted pages
819b6912aeaSAndreas Gohr * RECENTS_SKIP_MINORS    - don't include minor changes
820b6912aeaSAndreas Gohr * RECENTS_SKIP_SUBSPACES - don't include subspaces
821b6912aeaSAndreas Gohr *
822d437bcc4SAndreas Gohr * @param int    $first   number of first entry returned (for paginating
823d437bcc4SAndreas Gohr * @param int    $num     return $num entries
824d437bcc4SAndreas Gohr * @param string $ns      restrict to given namespace
825b6912aeaSAndreas Gohr * @param bool   $flags   see above
82615fae107Sandi *
827*71726d78SBen Coburn * @author Ben Coburn <btcoburn@silicodon.net>
828f3f0262cSandi */
829b6912aeaSAndreas Gohrfunction getRecents($first,$num,$ns='',$flags=0){
830f3f0262cSandi  global $conf;
831f3f0262cSandi  $recent = array();
832d437bcc4SAndreas Gohr  $count  = 0;
8335749f1ceSmatthiasgrimm
8345749f1ceSmatthiasgrimm  if(!$num)
8355749f1ceSmatthiasgrimm    return $recent;
836f3f0262cSandi
837*71726d78SBen Coburn  // read all recent changes. (kept short)
838*71726d78SBen Coburn  $lines = file($conf['changelog']);
839d437bcc4SAndreas Gohr
840d437bcc4SAndreas Gohr  // handle lines
841*71726d78SBen Coburn  for($i = count($lines)-1; $i >= 0; $i--){
842b6912aeaSAndreas Gohr    $rec = _handleRecent($lines[$i], $ns, $flags);
843d437bcc4SAndreas Gohr    if($rec !== false) {
844d437bcc4SAndreas Gohr      if(--$first >= 0) continue; // skip first entries
845d437bcc4SAndreas Gohr      $recent[] = $rec;
846d437bcc4SAndreas Gohr      $count++;
847*71726d78SBen Coburn      // break when we have enough entries
848*71726d78SBen Coburn      if($count >= $num){ break; }
849*71726d78SBen Coburn    }
850*71726d78SBen Coburn  }
851d437bcc4SAndreas Gohr
852f3f0262cSandi  return $recent;
853f3f0262cSandi}
854f3f0262cSandi
855f3f0262cSandi/**
856*71726d78SBen Coburn * parses a changelog line into it's components
85751815db0SYann *
858fb53bfe2SBen Coburn * @author Ben Coburn <btcoburn@silicodon.net>
859652610a2Sandi */
860*71726d78SBen Coburnfunction parseChangelogLine($line) {
861*71726d78SBen Coburn  $tmp = explode("\t", $line);
862*71726d78SBen Coburn    if ($tmp!==false && count($tmp)>1) {
863*71726d78SBen Coburn      $info = array();
864*71726d78SBen Coburn      $info['date']  = $tmp[0]; // unix timestamp
865*71726d78SBen Coburn      $info['ip']    = $tmp[1]; // IPv4 address (127.0.0.1)
866*71726d78SBen Coburn      $info['type']  = $tmp[2]; // log line type
867*71726d78SBen Coburn      $info['id']    = $tmp[3]; // page id
868*71726d78SBen Coburn      $info['user']  = $tmp[4]; // user name
869*71726d78SBen Coburn      $info['sum']   = $tmp[5]; // edit summary (or action reason)
870*71726d78SBen Coburn      $info['extra'] = rtrim($tmp[6], "\n"); // extra data (varies by line type)
871*71726d78SBen Coburn      return $info;
872*71726d78SBen Coburn  } else { return false; }
873*71726d78SBen Coburn}
874*71726d78SBen Coburn
875*71726d78SBen Coburn/**
876*71726d78SBen Coburn * Get the changelog information for a specific page id
877*71726d78SBen Coburn * and revision (timestamp). Adjacent changelog lines
878*71726d78SBen Coburn * are optimistically parsed and cached to speed up
879*71726d78SBen Coburn * consecutive calls to getRevisionInfo. For large
880*71726d78SBen Coburn * changelog files, only the chunk containing the
881*71726d78SBen Coburn * requested changelog line is read.
882*71726d78SBen Coburn *
883*71726d78SBen Coburn * @author Ben Coburn <btcoburn@silicodon.net>
884*71726d78SBen Coburn */
885*71726d78SBen Coburnfunction getRevisionInfo($id, $rev, $chunk_size=8192) {
886*71726d78SBen Coburn  global $cache_revinfo;
887*71726d78SBen Coburn  $cache =& $cache_revinfo;
888*71726d78SBen Coburn  if (!isset($cache[$id])) { $cache[$id] = array(); }
889*71726d78SBen Coburn  $rev = max($rev, 0);
890258641c6Sandi
891fb53bfe2SBen Coburn  // check if it's already in the memory cache
892*71726d78SBen Coburn  if (isset($cache[$id]) && isset($cache[$id][$rev])) {
893fb53bfe2SBen Coburn    return $cache[$id][$rev];
894fb53bfe2SBen Coburn  }
895fb53bfe2SBen Coburn
896*71726d78SBen Coburn  $file = metaFN($id, '.changes');
897*71726d78SBen Coburn  if (!file_exists($file)) { return false; }
898*71726d78SBen Coburn  if (filesize($file)<$chunk_size || $chunk_size==0) {
899*71726d78SBen Coburn    // read whole file
900*71726d78SBen Coburn    $lines = file($file);
901*71726d78SBen Coburn    if ($lines===false) { return false; }
902fb53bfe2SBen Coburn  } else {
903*71726d78SBen Coburn    // read by chunk
904*71726d78SBen Coburn    $fp = fopen($file, 'rb'); // "file pointer"
905*71726d78SBen Coburn    if ($fp===false) { return false; }
906*71726d78SBen Coburn    $head = 0;
907*71726d78SBen Coburn    fseek($fp, 0, SEEK_END);
908*71726d78SBen Coburn    $tail = ftell($fp);
909*71726d78SBen Coburn    $finger = 0;
910*71726d78SBen Coburn    $finger_rev = 0;
911*71726d78SBen Coburn
912*71726d78SBen Coburn    // find chunk
913*71726d78SBen Coburn    while ($tail-$head>$chunk_size) {
914*71726d78SBen Coburn      $finger = $head+floor(($tail-$head)/2.0);
915*71726d78SBen Coburn      fseek($fp, $finger);
916*71726d78SBen Coburn      fgets($fp); // slip the finger forward to a new line
917*71726d78SBen Coburn      $finger = ftell($fp);
918*71726d78SBen Coburn      $tmp = fgets($fp); // then read at that location
919*71726d78SBen Coburn      $tmp = parseChangelogLine($tmp);
920*71726d78SBen Coburn      $finger_rev = $tmp['date'];
921*71726d78SBen Coburn      if ($finger==$head || $finger==$tail) { break; }
922*71726d78SBen Coburn      if ($finger_rev>$rev) {
923*71726d78SBen Coburn        $tail = $finger;
924*71726d78SBen Coburn      } else {
925*71726d78SBen Coburn        $head = $finger;
926*71726d78SBen Coburn      }
927*71726d78SBen Coburn    }
928*71726d78SBen Coburn
929*71726d78SBen Coburn    if ($tail-$head<1) {
930*71726d78SBen Coburn      // cound not find chunk, assume requested rev is missing
931*71726d78SBen Coburn      fclose($fp);
932*71726d78SBen Coburn      return false;
933*71726d78SBen Coburn    }
934*71726d78SBen Coburn
935*71726d78SBen Coburn    // read chunk
936*71726d78SBen Coburn    $chunk = '';
937*71726d78SBen Coburn    $chunk_size = max($tail-$head, 0); // found chunk size
938*71726d78SBen Coburn    $got = 0;
939*71726d78SBen Coburn    fseek($fp, $head);
940*71726d78SBen Coburn    while ($got<$chunk_size && !feof($fp)) {
941*71726d78SBen Coburn      $tmp = fread($fp, max($chunk_size-$got, 0));
942*71726d78SBen Coburn      if ($tmp===false) { break; } //error state
943*71726d78SBen Coburn      $got += strlen($tmp);
944*71726d78SBen Coburn      $chunk .= $tmp;
945*71726d78SBen Coburn    }
946*71726d78SBen Coburn    $lines = explode("\n", $chunk);
947*71726d78SBen Coburn    array_pop($lines); // remove trailing newline
948*71726d78SBen Coburn    fclose($fp);
949*71726d78SBen Coburn  }
950*71726d78SBen Coburn
951*71726d78SBen Coburn  // parse and cache changelog lines
952*71726d78SBen Coburn  foreach ($lines as $value) {
953*71726d78SBen Coburn    $tmp = parseChangelogLine($value);
954*71726d78SBen Coburn    if ($tmp!==false) {
955*71726d78SBen Coburn      $cache[$id][$tmp['date']] = $tmp;
956*71726d78SBen Coburn    }
957*71726d78SBen Coburn  }
958*71726d78SBen Coburn  if (!isset($cache[$id][$rev])) { return false; }
959*71726d78SBen Coburn  return $cache[$id][$rev];
960*71726d78SBen Coburn}
961*71726d78SBen Coburn
962*71726d78SBen Coburn/**
963*71726d78SBen Coburn * Return a list of page revisions numbers
964*71726d78SBen Coburn * Does not guarantee that the revision exists in the attic,
965*71726d78SBen Coburn * only that a line with the date exists in the changelog.
966*71726d78SBen Coburn * By default the current revision is skipped.
967*71726d78SBen Coburn *
968*71726d78SBen Coburn * id:    the page of interest
969*71726d78SBen Coburn * first: skip the first n changelog lines
970*71726d78SBen Coburn * num:   number of revisions to return
971*71726d78SBen Coburn *
972*71726d78SBen Coburn * The current revision is automatically skipped when the page exists.
973*71726d78SBen Coburn * See $INFO['meta']['last_change'] for the current revision.
974*71726d78SBen Coburn *
975*71726d78SBen Coburn * For efficiency, the log lines are parsed and cached for later
976*71726d78SBen Coburn * calls to getRevisionInfo. Large changelog files are read
977*71726d78SBen Coburn * backwards in chunks untill the requested number of changelog
978*71726d78SBen Coburn * lines are recieved.
979*71726d78SBen Coburn *
980*71726d78SBen Coburn * @author Ben Coburn <btcoburn@silicodon.net>
981*71726d78SBen Coburn */
982*71726d78SBen Coburnfunction getRevisions($id, $first, $num, $chunk_size=8192) {
983*71726d78SBen Coburn  global $cache_revinfo;
984*71726d78SBen Coburn  $cache =& $cache_revinfo;
985fb53bfe2SBen Coburn  if (!isset($cache[$id])) { $cache[$id] = array(); }
986*71726d78SBen Coburn
987*71726d78SBen Coburn  $revs = array();
988*71726d78SBen Coburn  $lines = array();
989*71726d78SBen Coburn  $count  = 0;
990*71726d78SBen Coburn  $file = metaFN($id, '.changes');
991*71726d78SBen Coburn  $num = max($num, 0);
992*71726d78SBen Coburn  $chunk_size = max($chunk_size, 0);
993*71726d78SBen Coburn  if ($first<0) { $first = 0; }
994*71726d78SBen Coburn  else if (file_exists(wikiFN($id))) {
995*71726d78SBen Coburn     // skip current revision if the page exists
996*71726d78SBen Coburn    $first = max($first+1, 0);
997fb53bfe2SBen Coburn  }
998fb53bfe2SBen Coburn
999*71726d78SBen Coburn  if (!file_exists($file)) { return $revs; }
1000*71726d78SBen Coburn  if (filesize($file)<$chunk_size || $chunk_size==0) {
1001*71726d78SBen Coburn    // read whole file
1002*71726d78SBen Coburn    $lines = file($file);
1003*71726d78SBen Coburn    if ($lines===false) { return $revs; }
1004*71726d78SBen Coburn  } else {
1005*71726d78SBen Coburn    // read chunks backwards
1006*71726d78SBen Coburn    $fp = fopen($file, 'rb'); // "file pointer"
1007*71726d78SBen Coburn    if ($fp===false) { return $revs; }
1008*71726d78SBen Coburn    fseek($fp, 0, SEEK_END);
1009*71726d78SBen Coburn    $tail = ftell($fp);
1010*71726d78SBen Coburn
1011*71726d78SBen Coburn    // chunk backwards
1012*71726d78SBen Coburn    $finger = max($tail-$chunk_size, 0);
1013*71726d78SBen Coburn    while ($count<$num+$first) {
1014*71726d78SBen Coburn      fseek($fp, $finger);
1015*71726d78SBen Coburn      if ($finger>0) {
1016*71726d78SBen Coburn        fgets($fp); // slip the finger forward to a new line
1017*71726d78SBen Coburn        $finger = ftell($fp);
1018652610a2Sandi      }
1019652610a2Sandi
1020*71726d78SBen Coburn      // read chunk
1021*71726d78SBen Coburn      if ($tail<=$finger) { break; }
1022*71726d78SBen Coburn      $chunk = '';
1023*71726d78SBen Coburn      $read_size = max($tail-$finger, 0); // found chunk size
1024*71726d78SBen Coburn      $got = 0;
1025*71726d78SBen Coburn      while ($got<$read_size && !feof($fp)) {
1026*71726d78SBen Coburn        $tmp = fread($fp, max($read_size-$got, 0));
1027*71726d78SBen Coburn        if ($tmp===false) { break; } //error state
1028*71726d78SBen Coburn        $got += strlen($tmp);
1029*71726d78SBen Coburn        $chunk .= $tmp;
1030*71726d78SBen Coburn      }
1031*71726d78SBen Coburn      $tmp = explode("\n", $chunk);
1032*71726d78SBen Coburn      array_pop($tmp); // remove trailing newline
1033*71726d78SBen Coburn
1034*71726d78SBen Coburn      // combine with previous chunk
1035*71726d78SBen Coburn      $count += count($tmp);
1036*71726d78SBen Coburn      $lines = array_merge($tmp, $lines);
1037*71726d78SBen Coburn
1038*71726d78SBen Coburn      // next chunk
1039*71726d78SBen Coburn      if ($finger==0) { break; } // already read all the lines
1040*71726d78SBen Coburn      else {
1041*71726d78SBen Coburn        $tail = $finger;
1042*71726d78SBen Coburn        $finger = max($tail-$chunk_size, 0);
1043*71726d78SBen Coburn      }
1044*71726d78SBen Coburn    }
1045*71726d78SBen Coburn    fclose($fp);
1046*71726d78SBen Coburn  }
1047*71726d78SBen Coburn
1048*71726d78SBen Coburn  // skip parsing extra lines
1049*71726d78SBen Coburn  $num = max(min(count($lines)-$first, $num), 0);
1050*71726d78SBen Coburn  if      ($first>0 && $num>0)  { $lines = array_slice($lines, max(count($lines)-$first-$num, 0), $num); }
1051*71726d78SBen Coburn  else if ($first>0 && $num==0) { $lines = array_slice($lines, 0, max(count($lines)-$first, 0)); }
1052*71726d78SBen Coburn  else if ($first==0 && $num>0) { $lines = array_slice($lines, max(count($lines)-$num, 0)); }
1053*71726d78SBen Coburn
1054*71726d78SBen Coburn  // handle lines in reverse order
1055*71726d78SBen Coburn  for ($i = count($lines)-1; $i >= 0; $i--) {
1056*71726d78SBen Coburn    $tmp = parseChangelogLine($lines[$i]);
1057*71726d78SBen Coburn    if ($tmp!==false) {
1058*71726d78SBen Coburn      $cache[$id][$tmp['date']] = $tmp;
1059*71726d78SBen Coburn      $revs[] = $tmp['date'];
1060*71726d78SBen Coburn    }
1061*71726d78SBen Coburn  }
1062*71726d78SBen Coburn
1063*71726d78SBen Coburn  return $revs;
1064*71726d78SBen Coburn}
106551815db0SYann
1066652610a2Sandi/**
1067cc7d0c94SBen Coburn * Saves a wikitext by calling io_writeWikiPage
106815fae107Sandi *
106915fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
1070*71726d78SBen Coburn * @author Ben Coburn <btcoburn@silicodon.net>
1071f3f0262cSandi */
1072b6912aeaSAndreas Gohrfunction saveWikiText($id,$text,$summary,$minor=false){
1073f3f0262cSandi  global $conf;
1074f3f0262cSandi  global $lang;
1075*71726d78SBen Coburn  global $REV;
1076f3f0262cSandi  // ignore if no changes were made
1077f3f0262cSandi  if($text == rawWiki($id,'')){
1078f3f0262cSandi    return;
1079f3f0262cSandi  }
1080f3f0262cSandi
1081f3f0262cSandi  $file = wikiFN($id);
1082f3f0262cSandi  $old  = saveOldRevision($id);
1083*71726d78SBen Coburn  $wasRemoved = empty($text);
1084*71726d78SBen Coburn  $wasCreated = !file_exists($file);
1085*71726d78SBen Coburn  $wasReverted = ($REV==true);
1086f3f0262cSandi
1087*71726d78SBen Coburn  if ($wasRemoved){
1088e1f3d9e1SEsther Brunner    // remove empty file
1089f3f0262cSandi    @unlink($file);
1090*71726d78SBen Coburn    // remove old meta info...
1091e1f3d9e1SEsther Brunner    $mfiles = metaFiles($id);
1092*71726d78SBen Coburn    $changelog = metaFN($id, '.changes');
1093e1f3d9e1SEsther Brunner    foreach ($mfiles as $mfile) {
1094*71726d78SBen Coburn      // but keep per-page changelog to preserve page history
1095*71726d78SBen Coburn      if (file_exists($mfile) && $mfile!==$changelog) { @unlink($mfile); }
1096b158d625SSteven Danz    }
1097f3f0262cSandi    $del = true;
10983ce054b3Sandi    // autoset summary on deletion
10993ce054b3Sandi    if(empty($summary)) $summary = $lang['deleted'];
110053d6ccfeSandi    // remove empty namespaces
1101cc7d0c94SBen Coburn    io_sweepNS($id, 'datadir');
1102cc7d0c94SBen Coburn    io_sweepNS($id, 'mediadir');
1103f3f0262cSandi  }else{
1104cc7d0c94SBen Coburn    // save file (namespace dir is created in io_writeWikiPage)
1105cc7d0c94SBen Coburn    io_writeWikiPage($file, $text, $id);
1106f3f0262cSandi    $del = false;
1107f3f0262cSandi  }
1108f3f0262cSandi
1109*71726d78SBen Coburn  // select changelog line type
1110*71726d78SBen Coburn  $extra = '';
1111*71726d78SBen Coburn  $type = 'E';
1112*71726d78SBen Coburn  if ($wasReverted) {
1113*71726d78SBen Coburn    $type = 'R';
1114*71726d78SBen Coburn    $extra = $REV;
1115*71726d78SBen Coburn  }
1116*71726d78SBen Coburn  else if ($wasCreated) { $type = 'C'; }
1117*71726d78SBen Coburn  else if ($wasRemoved) { $type = 'D'; }
1118*71726d78SBen Coburn  else if ($minor && $conf['useacl'] && $_SERVER['REMOTE_USER']) { $type = 'e'; } //minor edits only for logged in users
1119*71726d78SBen Coburn
1120*71726d78SBen Coburn  addLogEntry(@filemtime($file), $id, $type, $summary, $extra);
112126a0801fSAndreas Gohr  // send notify mails
112290033e9dSAndreas Gohr  notify($id,'admin',$old,$summary,$minor);
112390033e9dSAndreas Gohr  notify($id,'subscribers',$old,$summary,$minor);
1124f3f0262cSandi
1125f3f0262cSandi  //purge cache on add by updating the purgefile
1126f3f0262cSandi  if($conf['purgeonadd'] && (!$old || $del)){
112798407a7aSandi    io_saveFile($conf['cachedir'].'/purgefile',time());
1128f3f0262cSandi  }
1129f3f0262cSandi}
1130f3f0262cSandi
1131f3f0262cSandi/**
1132f3f0262cSandi * moves the current version to the attic and returns its
1133f3f0262cSandi * revision date
113415fae107Sandi *
113515fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
1136f3f0262cSandi */
1137f3f0262cSandifunction saveOldRevision($id){
1138f3f0262cSandi  global $conf;
1139f3f0262cSandi  $oldf = wikiFN($id);
1140f3f0262cSandi  if(!@file_exists($oldf)) return '';
1141f3f0262cSandi  $date = filemtime($oldf);
1142f3f0262cSandi  $newf = wikiFN($id,$date);
1143cc7d0c94SBen Coburn  io_writeWikiPage($newf, rawWiki($id), $id, $date);
1144f3f0262cSandi  return $date;
1145f3f0262cSandi}
1146f3f0262cSandi
1147f3f0262cSandi/**
114826a0801fSAndreas Gohr * Sends a notify mail on page change
114926a0801fSAndreas Gohr *
115026a0801fSAndreas Gohr * @param  string  $id       The changed page
115126a0801fSAndreas Gohr * @param  string  $who      Who to notify (admin|subscribers)
115226a0801fSAndreas Gohr * @param  int     $rev      Old page revision
115326a0801fSAndreas Gohr * @param  string  $summary  What changed
115490033e9dSAndreas Gohr * @param  boolean $minor    Is this a minor edit?
115502a498e7Schris * @param  array   $replace  Additional string substitutions, @KEY@ to be replaced by value
115615fae107Sandi *
115715fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
1158f3f0262cSandi */
115902a498e7Schrisfunction notify($id,$who,$rev='',$summary='',$minor=false,$replace=array()){
1160f3f0262cSandi  global $lang;
1161f3f0262cSandi  global $conf;
1162b158d625SSteven Danz
116326a0801fSAndreas Gohr  // decide if there is something to do
116426a0801fSAndreas Gohr  if($who == 'admin'){
116526a0801fSAndreas Gohr    if(empty($conf['notify'])) return; //notify enabled?
1166f3f0262cSandi    $text = rawLocale('mailtext');
116726a0801fSAndreas Gohr    $to   = $conf['notify'];
116826a0801fSAndreas Gohr    $bcc  = '';
116926a0801fSAndreas Gohr  }elseif($who == 'subscribers'){
117026a0801fSAndreas Gohr    if(!$conf['subscribers']) return; //subscribers enabled?
117190033e9dSAndreas Gohr    if($conf['useacl'] && $_SERVER['REMOTE_USER'] && $minor) return; //skip minors
117226a0801fSAndreas Gohr    $bcc  = subscriber_addresslist($id);
117326a0801fSAndreas Gohr    if(empty($bcc)) return;
117426a0801fSAndreas Gohr    $to   = '';
117526a0801fSAndreas Gohr    $text = rawLocale('subscribermail');
1176a06e4bdbSSebastian Harl  }elseif($who == 'register'){
1177a06e4bdbSSebastian Harl    if(empty($conf['registernotify'])) return;
1178a06e4bdbSSebastian Harl    $text = rawLocale('registermail');
1179a06e4bdbSSebastian Harl    $to   = $conf['registernotify'];
1180a06e4bdbSSebastian Harl    $bcc  = '';
118126a0801fSAndreas Gohr  }else{
118226a0801fSAndreas Gohr    return; //just to be safe
118326a0801fSAndreas Gohr  }
118426a0801fSAndreas Gohr
1185f3f0262cSandi  $text = str_replace('@DATE@',date($conf['dformat']),$text);
1186f3f0262cSandi  $text = str_replace('@BROWSER@',$_SERVER['HTTP_USER_AGENT'],$text);
1187f3f0262cSandi  $text = str_replace('@IPADDRESS@',$_SERVER['REMOTE_ADDR'],$text);
1188f3f0262cSandi  $text = str_replace('@HOSTNAME@',gethostbyaddr($_SERVER['REMOTE_ADDR']),$text);
1189ed7b5f09Sandi  $text = str_replace('@NEWPAGE@',wl($id,'',true),$text);
119026a0801fSAndreas Gohr  $text = str_replace('@PAGE@',$id,$text);
119126a0801fSAndreas Gohr  $text = str_replace('@TITLE@',$conf['title'],$text);
1192ed7b5f09Sandi  $text = str_replace('@DOKUWIKIURL@',DOKU_URL,$text);
1193f3f0262cSandi  $text = str_replace('@SUMMARY@',$summary,$text);
11947a82afdcSandi  $text = str_replace('@USER@',$_SERVER['REMOTE_USER'],$text);
1195f3f0262cSandi
119602a498e7Schris  foreach ($replace as $key => $substitution) {
119702a498e7Schris    $text = str_replace('@'.strtoupper($key).'@',$substitution, $text);
119802a498e7Schris  }
119902a498e7Schris
1200a06e4bdbSSebastian Harl  if($who == 'register'){
1201a06e4bdbSSebastian Harl    $subject = $lang['mail_new_user'].' '.$summary;
1202a06e4bdbSSebastian Harl  }elseif($rev){
1203f3f0262cSandi    $subject = $lang['mail_changed'].' '.$id;
1204ed7b5f09Sandi    $text = str_replace('@OLDPAGE@',wl($id,"rev=$rev",true),$text);
1205ccdfa6c0SAndreas Gohr    require_once(DOKU_INC.'inc/DifferenceEngine.php');
1206f3f0262cSandi    $df  = new Diff(split("\n",rawWiki($id,$rev)),
1207f3f0262cSandi                    split("\n",rawWiki($id)));
1208f3f0262cSandi    $dformat = new UnifiedDiffFormatter();
1209f3f0262cSandi    $diff    = $dformat->format($df);
1210f3f0262cSandi  }else{
1211f3f0262cSandi    $subject=$lang['mail_newpage'].' '.$id;
1212f3f0262cSandi    $text = str_replace('@OLDPAGE@','none',$text);
1213f3f0262cSandi    $diff = rawWiki($id);
1214f3f0262cSandi  }
1215f3f0262cSandi  $text = str_replace('@DIFF@',$diff,$text);
1216241f3a36Sandi  $subject = '['.$conf['title'].'] '.$subject;
1217f3f0262cSandi
121826a0801fSAndreas Gohr  mail_send($to,$subject,$text,$conf['mailfrom'],'',$bcc);
1219f3f0262cSandi}
1220f3f0262cSandi
122115fae107Sandi/**
1222f3f0262cSandi * extracts the query from a google referer
122315fae107Sandi *
12246b13307fSandi * @todo   should be more generic and support yahoo et al
122515fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
1226f3f0262cSandi */
1227f3f0262cSandifunction getGoogleQuery(){
1228f3f0262cSandi  $url = parse_url($_SERVER['HTTP_REFERER']);
12295c3f206fSandi  if(!$url) return '';
1230f3f0262cSandi
1231f3f0262cSandi  if(!preg_match("#google\.#i",$url['host'])) return '';
1232f3f0262cSandi  $query = array();
1233f3f0262cSandi  parse_str($url['query'],$query);
1234f3f0262cSandi
1235f3f0262cSandi  return $query['q'];
1236f3f0262cSandi}
1237f3f0262cSandi
1238f3f0262cSandi/**
123915fae107Sandi * Try to set correct locale
124015fae107Sandi *
1241095bfd5cSandi * @deprecated No longer used
124215fae107Sandi * @author     Andreas Gohr <andi@splitbrain.org>
1243f3f0262cSandi */
1244f3f0262cSandifunction setCorrectLocale(){
1245f3f0262cSandi  global $conf;
1246f3f0262cSandi  global $lang;
1247f3f0262cSandi
1248f3f0262cSandi  $enc = strtoupper($lang['encoding']);
1249f3f0262cSandi  foreach ($lang['locales'] as $loc){
1250f3f0262cSandi    //try locale
1251f3f0262cSandi    if(@setlocale(LC_ALL,$loc)) return;
1252f3f0262cSandi    //try loceale with encoding
1253f3f0262cSandi    if(@setlocale(LC_ALL,"$loc.$enc")) return;
1254f3f0262cSandi  }
1255f3f0262cSandi  //still here? try to set from environment
1256f3f0262cSandi  @setlocale(LC_ALL,"");
1257f3f0262cSandi}
1258f3f0262cSandi
1259f3f0262cSandi/**
1260f3f0262cSandi * Return the human readable size of a file
1261f3f0262cSandi *
1262f3f0262cSandi * @param       int    $size   A file size
1263f3f0262cSandi * @param       int    $dec    A number of decimal places
1264f3f0262cSandi * @author      Martin Benjamin <b.martin@cybernet.ch>
1265f3f0262cSandi * @author      Aidan Lister <aidan@php.net>
1266f3f0262cSandi * @version     1.0.0
1267f3f0262cSandi */
1268f31d5b73Sandifunction filesize_h($size, $dec = 1){
1269f3f0262cSandi  $sizes = array('B', 'KB', 'MB', 'GB');
1270f3f0262cSandi  $count = count($sizes);
1271f3f0262cSandi  $i = 0;
1272f3f0262cSandi
1273f3f0262cSandi  while ($size >= 1024 && ($i < $count - 1)) {
1274f3f0262cSandi    $size /= 1024;
1275f3f0262cSandi    $i++;
1276f3f0262cSandi  }
1277f3f0262cSandi
1278f3f0262cSandi  return round($size, $dec) . ' ' . $sizes[$i];
1279f3f0262cSandi}
1280f3f0262cSandi
128115fae107Sandi/**
128200a7b5adSEsther Brunner * return an obfuscated email address in line with $conf['mailguard'] setting
128300a7b5adSEsther Brunner *
128400a7b5adSEsther Brunner * @author Harry Fuecks <hfuecks@gmail.com>
128500a7b5adSEsther Brunner * @author Christopher Smith <chris@jalakai.co.uk>
128600a7b5adSEsther Brunner */
128700a7b5adSEsther Brunnerfunction obfuscate($email) {
128800a7b5adSEsther Brunner  global $conf;
128900a7b5adSEsther Brunner
129000a7b5adSEsther Brunner  switch ($conf['mailguard']) {
129100a7b5adSEsther Brunner    case 'visible' :
129200a7b5adSEsther Brunner      $obfuscate = array('@' => ' [at] ', '.' => ' [dot] ', '-' => ' [dash] ');
129300a7b5adSEsther Brunner      return strtr($email, $obfuscate);
129400a7b5adSEsther Brunner
129500a7b5adSEsther Brunner    case 'hex' :
129600a7b5adSEsther Brunner      $encode = '';
129700a7b5adSEsther Brunner      for ($x=0; $x < strlen($email); $x++) $encode .= '&#x' . bin2hex($email{$x}).';';
129800a7b5adSEsther Brunner      return $encode;
129900a7b5adSEsther Brunner
130000a7b5adSEsther Brunner    case 'none' :
130100a7b5adSEsther Brunner    default :
130200a7b5adSEsther Brunner      return $email;
130300a7b5adSEsther Brunner  }
130400a7b5adSEsther Brunner}
130500a7b5adSEsther Brunner
130600a7b5adSEsther Brunner/**
1307dc57ef04Sandi * Return DokuWikis version
130815fae107Sandi *
130915fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
131015fae107Sandi */
1311f31d5b73Sandifunction getVersion(){
1312f31d5b73Sandi  //import version string
1313f31d5b73Sandi  if(@file_exists('VERSION')){
1314f31d5b73Sandi    //official release
13150647ce3bSAndreas Gohr    return 'Release '.trim(io_readfile(DOKU_INC.'/VERSION'));
1316f31d5b73Sandi  }elseif(is_dir('_darcs')){
1317f31d5b73Sandi    //darcs checkout
1318f31d5b73Sandi    $inv = file('_darcs/inventory');
1319ae41559bSAndreas Gohr    $inv = preg_grep('#\*\*\d{14}[\]$]#',$inv);
1320f31d5b73Sandi    $cur = array_pop($inv);
1321f31d5b73Sandi    preg_match('#\*\*(\d{4})(\d{2})(\d{2})#',$cur,$matches);
1322f31d5b73Sandi    return 'Darcs '.$matches[1].'-'.$matches[2].'-'.$matches[3];
1323f31d5b73Sandi  }else{
1324f31d5b73Sandi    return 'snapshot?';
1325f31d5b73Sandi  }
1326f31d5b73Sandi}
1327f31d5b73Sandi
1328f31d5b73Sandi/**
1329f31d5b73Sandi * Run a few sanity checks
1330f31d5b73Sandi *
1331f31d5b73Sandi * @author Andreas Gohr <andi@splitbrain.org>
1332f31d5b73Sandi */
1333f3f0262cSandifunction check(){
1334f3f0262cSandi  global $conf;
1335f3f0262cSandi  global $INFO;
1336f3f0262cSandi
1337f31d5b73Sandi  msg('DokuWiki version: '.getVersion(),1);
1338f31d5b73Sandi
133949022a38Sandi  if(version_compare(phpversion(),'4.3.0','<')){
134049022a38Sandi    msg('Your PHP version is too old ('.phpversion().' vs. 4.3.+ recommended)',-1);
134149022a38Sandi  }elseif(version_compare(phpversion(),'4.3.10','<')){
134249022a38Sandi    msg('Consider upgrading PHP to 4.3.10 or higher for security reasons (your version: '.phpversion().')',0);
134349022a38Sandi  }else{
134449022a38Sandi    msg('PHP version '.phpversion(),1);
134549022a38Sandi  }
134649022a38Sandi
1347f3f0262cSandi  if(is_writable($conf['changelog'])){
1348f3f0262cSandi    msg('Changelog is writable',1);
1349f3f0262cSandi  }else{
1350*71726d78SBen Coburn    if (file_exists($conf['changelog'])) {
1351f3f0262cSandi      msg('Changelog is not writable',-1);
1352f3f0262cSandi    }
1353*71726d78SBen Coburn  }
1354*71726d78SBen Coburn
1355*71726d78SBen Coburn  if (isset($conf['changelog_old']) && file_exists($conf['changelog_old'])) {
1356*71726d78SBen Coburn    msg('Old changelog exists.', 0);
1357*71726d78SBen Coburn  }
1358*71726d78SBen Coburn
1359*71726d78SBen Coburn  if (file_exists($conf['changelog'].'_failed')) {
1360*71726d78SBen Coburn    msg('Importing old changelog failed.', -1);
1361*71726d78SBen Coburn  } else if (file_exists($conf['changelog'].'_importing')) {
1362*71726d78SBen Coburn    msg('Importing old changelog now.', 0);
1363*71726d78SBen Coburn  } else if (file_exists($conf['changelog'].'_import_ok')) {
1364*71726d78SBen Coburn    msg('Old changelog imported.', 1);
1365*71726d78SBen Coburn  }
1366f3f0262cSandi
1367f3f0262cSandi  if(is_writable($conf['datadir'])){
1368f3f0262cSandi    msg('Datadir is writable',1);
1369f3f0262cSandi  }else{
1370f3f0262cSandi    msg('Datadir is not writable',-1);
1371f3f0262cSandi  }
1372f3f0262cSandi
1373f3f0262cSandi  if(is_writable($conf['olddir'])){
1374f3f0262cSandi    msg('Attic is writable',1);
1375f3f0262cSandi  }else{
1376f3f0262cSandi    msg('Attic is not writable',-1);
1377f3f0262cSandi  }
1378f3f0262cSandi
1379f3f0262cSandi  if(is_writable($conf['mediadir'])){
1380f3f0262cSandi    msg('Mediadir is writable',1);
1381f3f0262cSandi  }else{
1382f3f0262cSandi    msg('Mediadir is not writable',-1);
1383f3f0262cSandi  }
1384f3f0262cSandi
138598407a7aSandi  if(is_writable($conf['cachedir'])){
138698407a7aSandi    msg('Cachedir is writable',1);
138798407a7aSandi  }else{
138898407a7aSandi    msg('Cachedir is not writable',-1);
138998407a7aSandi  }
139098407a7aSandi
13917de6c234SAndreas Gohr  if(is_writable($conf['lockdir'])){
13927de6c234SAndreas Gohr    msg('Lockdir is writable',1);
13937de6c234SAndreas Gohr  }else{
13947de6c234SAndreas Gohr    msg('Lockdir is not writable',-1);
13957de6c234SAndreas Gohr  }
13967de6c234SAndreas Gohr
1397e7cb32dcSAndreas Gohr  if(is_writable(DOKU_CONF.'users.auth.php')){
13988c4f28e8Sjan    msg('conf/users.auth.php is writable',1);
1399f3f0262cSandi  }else{
14008c4f28e8Sjan    msg('conf/users.auth.php is not writable',0);
1401f3f0262cSandi  }
140293a9e835Sandi
140393a9e835Sandi  if(function_exists('mb_strpos')){
140493a9e835Sandi    if(defined('UTF8_NOMBSTRING')){
140593a9e835Sandi      msg('mb_string extension is available but will not be used',0);
140693a9e835Sandi    }else{
140793a9e835Sandi      msg('mb_string extension is available and will be used',1);
140893a9e835Sandi    }
140993a9e835Sandi  }else{
141093a9e835Sandi    msg('mb_string extension not available - PHP only replacements will be used',0);
141193a9e835Sandi  }
1412f42d1c75SAndreas Gohr
1413f42d1c75SAndreas Gohr  if($conf['allowdebug']){
1414f42d1c75SAndreas Gohr    msg('Debugging support is enabled. If you don\'t need it you should set $conf[\'allowdebug\'] = 0',-1);
1415f42d1c75SAndreas Gohr  }else{
1416f42d1c75SAndreas Gohr    msg('Debugging support is disabled',1);
1417f42d1c75SAndreas Gohr  }
1418f3f0262cSandi
1419f3f0262cSandi  msg('Your current permission for this page is '.$INFO['perm'],0);
1420f3f0262cSandi
1421f3f0262cSandi  if(is_writable($INFO['filepath'])){
1422f3f0262cSandi    msg('The current page is writable by the webserver',0);
1423f3f0262cSandi  }else{
1424f3f0262cSandi    msg('The current page is not writable by the webserver',0);
1425f3f0262cSandi  }
1426f3f0262cSandi
1427f3f0262cSandi  if($INFO['writable']){
1428f3f0262cSandi    msg('The current page is writable by you',0);
1429f3f0262cSandi  }else{
1430f3f0262cSandi    msg('The current page is not writable you',0);
1431f3f0262cSandi  }
1432f3f0262cSandi}
1433340756e4Sandi
1434b158d625SSteven Danz/**
1435b158d625SSteven Danz * Let us know if a user is tracking a page
1436b158d625SSteven Danz *
14371380fc45SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
1438b158d625SSteven Danz */
14391380fc45SAndreas Gohrfunction is_subscribed($id,$uid){
14401380fc45SAndreas Gohr  $file=metaFN($id,'.mlist');
14411380fc45SAndreas Gohr  if (@file_exists($file)) {
1442b158d625SSteven Danz    $mlist = file($file);
14431380fc45SAndreas Gohr    $pos = array_search($uid."\n",$mlist);
14441380fc45SAndreas Gohr    return is_int($pos);
1445b158d625SSteven Danz  }
14461380fc45SAndreas Gohr
1447b158d625SSteven Danz  return false;
1448b158d625SSteven Danz}
1449340756e4Sandi
1450f9eb5648Ssteven-danz/**
1451f9eb5648Ssteven-danz * Return a string with the email addresses of all the
1452f9eb5648Ssteven-danz * users subscribed to a page
1453f9eb5648Ssteven-danz *
145426a0801fSAndreas Gohr * @author Steven Danz <steven-danz@kc.rr.com>
1455f9eb5648Ssteven-danz */
1456f9eb5648Ssteven-danzfunction subscriber_addresslist($id){
1457f9eb5648Ssteven-danz  global $conf;
1458cd52f92dSchris  global $auth;
1459f9eb5648Ssteven-danz
1460f9eb5648Ssteven-danz  $emails = '';
1461f9eb5648Ssteven-danz
146226a0801fSAndreas Gohr  if (!$conf['subscribers']) return;
146326a0801fSAndreas Gohr
1464f9eb5648Ssteven-danz  $mlist = array();
1465f9eb5648Ssteven-danz  $file=metaFN($id,'.mlist');
1466f9eb5648Ssteven-danz  if (file_exists($file)) {
1467f9eb5648Ssteven-danz    $mlist = file($file);
1468f9eb5648Ssteven-danz  }
1469f9eb5648Ssteven-danz  if(count($mlist) > 0) {
1470f9eb5648Ssteven-danz    foreach ($mlist as $who) {
1471f9eb5648Ssteven-danz      $who = rtrim($who);
1472cd52f92dSchris      $info = $auth->getUserData($who);
1473f9eb5648Ssteven-danz      $level = auth_aclcheck($id,$who,$info['grps']);
1474f9eb5648Ssteven-danz      if ($level >= AUTH_READ) {
1475f9eb5648Ssteven-danz        if (strcasecmp($info['mail'],$conf['notify']) != 0) {
1476f9eb5648Ssteven-danz          if (empty($emails)) {
1477f9eb5648Ssteven-danz            $emails = $info['mail'];
1478f9eb5648Ssteven-danz          } else {
1479f9eb5648Ssteven-danz            $emails = "$emails,".$info['mail'];
1480f9eb5648Ssteven-danz          }
1481f9eb5648Ssteven-danz        }
1482f9eb5648Ssteven-danz      }
1483f9eb5648Ssteven-danz    }
1484f9eb5648Ssteven-danz  }
1485f9eb5648Ssteven-danz
1486f9eb5648Ssteven-danz  return $emails;
1487f9eb5648Ssteven-danz}
1488f9eb5648Ssteven-danz
148989541d4bSAndreas Gohr/**
149089541d4bSAndreas Gohr * Removes quoting backslashes
149189541d4bSAndreas Gohr *
149289541d4bSAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
149389541d4bSAndreas Gohr */
149489541d4bSAndreas Gohrfunction unslash($string,$char="'"){
149589541d4bSAndreas Gohr  return str_replace('\\'.$char,$char,$string);
149689541d4bSAndreas Gohr}
149789541d4bSAndreas Gohr
1498340756e4Sandi//Setup VIM: ex: et ts=2 enc=utf-8 :
1499