xref: /dokuwiki/inc/common.php (revision b6912aeac771ef294377b8af071d28f6acfa7050)
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/**
17*b6912aeaSAndreas Gohr * These constants are used with the recents function
18*b6912aeaSAndreas Gohr */
19*b6912aeaSAndreas Gohrdefine('RECENTS_SKIP_DELETED',2);
20*b6912aeaSAndreas Gohrdefine('RECENTS_SKIP_MINORS',4);
21*b6912aeaSAndreas Gohrdefine('RECENTS_SKIP_SUBSPACES',8);
22*b6912aeaSAndreas Gohr
23*b6912aeaSAndreas 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']);
39f3f0262cSandi  }else{
40f3f0262cSandi    $info['perm']       = auth_aclcheck($ID,'',null);
411380fc45SAndreas Gohr    $info['subscribed'] = false;
42f3f0262cSandi  }
43f3f0262cSandi
44f3f0262cSandi  $info['namespace'] = getNS($ID);
45f3f0262cSandi  $info['locked']    = checklock($ID);
46f3f0262cSandi  $info['filepath']  = realpath(wikiFN($ID,$REV));
47f3f0262cSandi  $info['exists']    = @file_exists($info['filepath']);
48f3f0262cSandi  if($REV && !$info['exists']){
49f3f0262cSandi    //check if current revision was meant
50f3f0262cSandi    $cur = wikiFN($ID);
51f3f0262cSandi    if(@file_exists($cur) && (@filemtime($cur) == $REV)){
52f3f0262cSandi      $info['filepath'] = realpath($cur);
53f3f0262cSandi      $info['exists']   = true;
54f3f0262cSandi      $REV = '';
55f3f0262cSandi    }
56f3f0262cSandi  }
57c112d578Sandi  $info['rev'] = $REV;
58f3f0262cSandi  if($info['exists']){
59f3f0262cSandi    $info['writable'] = (is_writable($info['filepath']) &&
60f3f0262cSandi                         ($info['perm'] >= AUTH_EDIT));
61f3f0262cSandi  }else{
62f3f0262cSandi    $info['writable'] = ($info['perm'] >= AUTH_CREATE);
63f3f0262cSandi  }
64f3f0262cSandi  $info['editable']  = ($info['writable'] && empty($info['lock']));
65f3f0262cSandi  $info['lastmod']   = @filemtime($info['filepath']);
66f3f0262cSandi
67652610a2Sandi  //who's the editor
68652610a2Sandi  if($REV){
69652610a2Sandi    $revinfo = getRevisionInfo($ID,$REV);
70652610a2Sandi  }else{
71652610a2Sandi    $revinfo = getRevisionInfo($ID,$info['lastmod']);
72652610a2Sandi  }
73652610a2Sandi  $info['ip']     = $revinfo['ip'];
74652610a2Sandi  $info['user']   = $revinfo['user'];
75652610a2Sandi  $info['sum']    = $revinfo['sum'];
76*b6912aeaSAndreas Gohr  $info['minor']  = $revinfo['minor'];
7759f257aeSchris
7888f522e9Sandi  if($revinfo['user']){
7988f522e9Sandi    $info['editor'] = $revinfo['user'];
8088f522e9Sandi  }else{
8188f522e9Sandi    $info['editor'] = $revinfo['ip'];
8288f522e9Sandi  }
83652610a2Sandi
84f3f0262cSandi  return $info;
85f3f0262cSandi}
86f3f0262cSandi
87f3f0262cSandi/**
882684e50aSAndreas Gohr * Build an string of URL parameters
892684e50aSAndreas Gohr *
902684e50aSAndreas Gohr * @author Andreas Gohr
912684e50aSAndreas Gohr */
922684e50aSAndreas Gohrfunction buildURLparams($params){
932684e50aSAndreas Gohr  $url = '';
942684e50aSAndreas Gohr  $amp = false;
952684e50aSAndreas Gohr  foreach($params as $key => $val){
962684e50aSAndreas Gohr    if($amp) $url .= '&amp;';
972684e50aSAndreas Gohr
982684e50aSAndreas Gohr    $url .= $key.'=';
992684e50aSAndreas Gohr    $url .= urlencode($val);
1002684e50aSAndreas Gohr    $amp = true;
1012684e50aSAndreas Gohr  }
1022684e50aSAndreas Gohr  return $url;
1032684e50aSAndreas Gohr}
1042684e50aSAndreas Gohr
1052684e50aSAndreas Gohr/**
1062684e50aSAndreas Gohr * Build an string of html tag attributes
1072684e50aSAndreas Gohr *
1082684e50aSAndreas Gohr * @author Andreas Gohr
1092684e50aSAndreas Gohr */
1102684e50aSAndreas Gohrfunction buildAttributes($params){
1112684e50aSAndreas Gohr  $url = '';
1122684e50aSAndreas Gohr  foreach($params as $key => $val){
1132684e50aSAndreas Gohr    $url .= $key.'="';
1142684e50aSAndreas Gohr    $url .= htmlspecialchars ($val);
1152684e50aSAndreas Gohr    $url .= '" ';
1162684e50aSAndreas Gohr  }
1172684e50aSAndreas Gohr  return $url;
1182684e50aSAndreas Gohr}
1192684e50aSAndreas Gohr
1202684e50aSAndreas Gohr
1212684e50aSAndreas Gohr/**
1220396becbSandi * print a message
1230396becbSandi *
1240396becbSandi * If HTTP headers were not sent yet the message is added
1250396becbSandi * to the global message array else it's printed directly
1260396becbSandi * using html_msgarea()
1270396becbSandi *
128f3f0262cSandi *
129f3f0262cSandi * Levels can be:
130f3f0262cSandi *
131f3f0262cSandi * -1 error
132f3f0262cSandi *  0 info
133f3f0262cSandi *  1 success
13415fae107Sandi *
13515fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
1360396becbSandi * @see    html_msgarea
137f3f0262cSandi */
138f3f0262cSandifunction msg($message,$lvl=0){
139f3f0262cSandi  global $MSG;
140f3f0262cSandi  $errors[-1] = 'error';
141f3f0262cSandi  $errors[0]  = 'info';
142f3f0262cSandi  $errors[1]  = 'success';
143f3f0262cSandi
144cc20ad51Sandi  if(!headers_sent()){
145f3f0262cSandi    if(!isset($MSG)) $MSG = array();
146f3f0262cSandi    $MSG[]=array('lvl' => $errors[$lvl], 'msg' => $message);
1470396becbSandi  }else{
1480396becbSandi    $MSG = array();
1490396becbSandi    $MSG[]=array('lvl' => $errors[$lvl], 'msg' => $message);
150f62ea8a1Sandi    if(function_exists('html_msgarea')){
1510396becbSandi      html_msgarea();
152f62ea8a1Sandi    }else{
153f62ea8a1Sandi      print "ERROR($lvl) $message";
154f62ea8a1Sandi    }
1550396becbSandi  }
156f3f0262cSandi}
157f3f0262cSandi
158f3f0262cSandi/**
15915fae107Sandi * This builds the breadcrumb trail and returns it as array
16015fae107Sandi *
16115fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
162f3f0262cSandi */
163f3f0262cSandifunction breadcrumbs(){
1648746e727Sandi  // we prepare the breadcrumbs early for quick session closing
1658746e727Sandi  static $crumbs = null;
1668746e727Sandi  if($crumbs != null) return $crumbs;
1678746e727Sandi
168f3f0262cSandi  global $ID;
169f3f0262cSandi  global $ACT;
170f3f0262cSandi  global $conf;
171f3f0262cSandi  $crumbs = $_SESSION[$conf['title']]['bc'];
172f3f0262cSandi
173f3f0262cSandi  //first visit?
174f3f0262cSandi  if (!is_array($crumbs)){
175f3f0262cSandi    $crumbs = array();
176f3f0262cSandi  }
177f3f0262cSandi  //we only save on show and existing wiki documents
178a77f5846Sjan  $file = wikiFN($ID);
179a77f5846Sjan  if($ACT != 'show' || !@file_exists($file)){
180f3f0262cSandi    $_SESSION[$conf['title']]['bc'] = $crumbs;
181f3f0262cSandi    return $crumbs;
182f3f0262cSandi  }
183a77f5846Sjan
184a77f5846Sjan  // page names
185a77f5846Sjan  $name = noNS($ID);
186a77f5846Sjan  if ($conf['useheading']) {
187a77f5846Sjan    // get page title
188bb0a59d4Sjan    $title = p_get_first_heading($ID);
189a77f5846Sjan    if ($title) {
190a77f5846Sjan      $name = $title;
191a77f5846Sjan    }
192a77f5846Sjan  }
193a77f5846Sjan
194f3f0262cSandi  //remove ID from array
195a77f5846Sjan  if (isset($crumbs[$ID])) {
196a77f5846Sjan    unset($crumbs[$ID]);
197f3f0262cSandi  }
198f3f0262cSandi
199f3f0262cSandi  //add to array
200a77f5846Sjan  $crumbs[$ID] = $name;
201f3f0262cSandi  //reduce size
202f3f0262cSandi  while(count($crumbs) > $conf['breadcrumbs']){
203f3f0262cSandi    array_shift($crumbs);
204f3f0262cSandi  }
205f3f0262cSandi  //save to session
206f3f0262cSandi  $_SESSION[$conf['title']]['bc'] = $crumbs;
207f3f0262cSandi  return $crumbs;
208f3f0262cSandi}
209f3f0262cSandi
210f3f0262cSandi/**
21115fae107Sandi * Filter for page IDs
21215fae107Sandi *
213f3f0262cSandi * This is run on a ID before it is outputted somewhere
214f3f0262cSandi * currently used to replace the colon with something else
215f3f0262cSandi * on Windows systems and to have proper URL encoding
21615fae107Sandi *
21749c713a3Sandi * Urlencoding is ommitted when the second parameter is false
21849c713a3Sandi *
21915fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
220f3f0262cSandi */
22149c713a3Sandifunction idfilter($id,$ue=true){
222f3f0262cSandi  global $conf;
223f3f0262cSandi  if ($conf['useslash'] && $conf['userewrite']){
224f3f0262cSandi    $id = strtr($id,':','/');
225f3f0262cSandi  }elseif (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' &&
226f3f0262cSandi      $conf['userewrite']) {
227f3f0262cSandi    $id = strtr($id,':',';');
228f3f0262cSandi  }
22949c713a3Sandi  if($ue){
230f3f0262cSandi    $id = urlencode($id);
231f3f0262cSandi    $id = str_replace('%3A',':',$id); //keep as colon
232f3f0262cSandi    $id = str_replace('%2F','/',$id); //keep as slash
23349c713a3Sandi  }
234f3f0262cSandi  return $id;
235f3f0262cSandi}
236f3f0262cSandi
237f3f0262cSandi/**
238ed7b5f09Sandi * This builds a link to a wikipage
23915fae107Sandi *
2406c7843b5Sandi * It handles URL rewriting and adds additional parameter if
2416c7843b5Sandi * given in $more
2426c7843b5Sandi *
24315fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
244f3f0262cSandi */
245ed7b5f09Sandifunction wl($id='',$more='',$abs=false){
246f3f0262cSandi  global $conf;
2476de3759aSAndreas Gohr  if(is_array($more)){
2486de3759aSAndreas Gohr    $more = buildURLparams($more);
2496de3759aSAndreas Gohr  }else{
250f3f0262cSandi    $more = str_replace(',','&amp;',$more);
2516de3759aSAndreas Gohr  }
252f3f0262cSandi
253f3f0262cSandi  $id    = idfilter($id);
254ed7b5f09Sandi  if($abs){
255ed7b5f09Sandi    $xlink = DOKU_URL;
256ed7b5f09Sandi  }else{
257ed7b5f09Sandi    $xlink = DOKU_BASE;
258ed7b5f09Sandi  }
259f3f0262cSandi
2606c7843b5Sandi  if($conf['userewrite'] == 2){
2616c7843b5Sandi    $xlink .= DOKU_SCRIPT.'/'.$id;
2626c7843b5Sandi    if($more) $xlink .= '?'.$more;
2636c7843b5Sandi  }elseif($conf['userewrite']){
264f3f0262cSandi    $xlink .= $id;
265f3f0262cSandi    if($more) $xlink .= '?'.$more;
2666c7843b5Sandi  }else{
2676c7843b5Sandi    $xlink .= DOKU_SCRIPT.'?id='.$id;
2686c7843b5Sandi    if($more) $xlink .= '&amp;'.$more;
269f3f0262cSandi  }
270f3f0262cSandi
271f3f0262cSandi  return $xlink;
272f3f0262cSandi}
273f3f0262cSandi
274f3f0262cSandi/**
2756de3759aSAndreas Gohr * Build a link to a media file
2766de3759aSAndreas Gohr *
2776de3759aSAndreas Gohr * Will return a link to the detail page if $direct is false
2786de3759aSAndreas Gohr */
2796de3759aSAndreas Gohrfunction ml($id='',$more='',$direct=true){
2806de3759aSAndreas Gohr  global $conf;
2816de3759aSAndreas Gohr  if(is_array($more)){
2826de3759aSAndreas Gohr    $more = buildURLparams($more);
2836de3759aSAndreas Gohr  }else{
2846de3759aSAndreas Gohr    $more = str_replace(',','&amp;',$more);
2856de3759aSAndreas Gohr  }
2866de3759aSAndreas Gohr
2876de3759aSAndreas Gohr  $xlink = DOKU_BASE;
2886de3759aSAndreas Gohr
2896de3759aSAndreas Gohr  // external URLs are always direct without rewriting
2906de3759aSAndreas Gohr  if(preg_match('#^(https?|ftp)://#i',$id)){
2916de3759aSAndreas Gohr    $xlink .= 'lib/exe/fetch.php';
2926de3759aSAndreas Gohr    if($more){
2936de3759aSAndreas Gohr      $xlink .= '?'.$more;
29448665d38SAndreas Gohr      $xlink .= '&amp;media='.urlencode($id);
2956de3759aSAndreas Gohr    }else{
29648665d38SAndreas Gohr      $xlink .= '?media='.urlencode($id);
2976de3759aSAndreas Gohr    }
2986de3759aSAndreas Gohr    return $xlink;
2996de3759aSAndreas Gohr  }
3006de3759aSAndreas Gohr
3016de3759aSAndreas Gohr  $id = idfilter($id);
3026de3759aSAndreas Gohr
3036de3759aSAndreas Gohr  // decide on scriptname
3046de3759aSAndreas Gohr  if($direct){
3056de3759aSAndreas Gohr    if($conf['userewrite'] == 1){
3066de3759aSAndreas Gohr      $script = '_media';
3076de3759aSAndreas Gohr    }else{
3086de3759aSAndreas Gohr      $script = 'lib/exe/fetch.php';
3096de3759aSAndreas Gohr    }
3106de3759aSAndreas Gohr  }else{
3116de3759aSAndreas Gohr    if($conf['userewrite'] == 1){
3126de3759aSAndreas Gohr      $script = '_detail';
3136de3759aSAndreas Gohr    }else{
3146de3759aSAndreas Gohr      $script = 'lib/exe/detail.php';
3156de3759aSAndreas Gohr    }
3166de3759aSAndreas Gohr  }
3176de3759aSAndreas Gohr
3186de3759aSAndreas Gohr  // build URL based on rewrite mode
3196de3759aSAndreas Gohr   if($conf['userewrite']){
3206de3759aSAndreas Gohr     $xlink .= $script.'/'.$id;
3216de3759aSAndreas Gohr     if($more) $xlink .= '?'.$more;
3226de3759aSAndreas Gohr   }else{
3236de3759aSAndreas Gohr     if($more){
324a99d3236SEsther Brunner       $xlink .= $script.'?'.$more;
3256de3759aSAndreas Gohr       $xlink .= '&amp;media='.$id;
3266de3759aSAndreas Gohr     }else{
327a99d3236SEsther Brunner       $xlink .= $script.'?media='.$id;
3286de3759aSAndreas Gohr     }
3296de3759aSAndreas Gohr   }
3306de3759aSAndreas Gohr
3316de3759aSAndreas Gohr  return $xlink;
3326de3759aSAndreas Gohr}
3336de3759aSAndreas Gohr
3346de3759aSAndreas Gohr
3356de3759aSAndreas Gohr
3366de3759aSAndreas Gohr/**
337f3f0262cSandi * Just builds a link to a script
33815fae107Sandi *
339ed7b5f09Sandi * @todo   maybe obsolete
34015fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
341f3f0262cSandi */
342f3f0262cSandifunction script($script='doku.php'){
343ed7b5f09Sandi#  $link = getBaseURL();
344ed7b5f09Sandi#  $link .= $script;
345ed7b5f09Sandi#  return $link;
346ed7b5f09Sandi  return DOKU_BASE.DOKU_SCRIPT;
347f3f0262cSandi}
348f3f0262cSandi
349f3f0262cSandi/**
35015fae107Sandi * Spamcheck against wordlist
35115fae107Sandi *
352f3f0262cSandi * Checks the wikitext against a list of blocked expressions
353f3f0262cSandi * returns true if the text contains any bad words
35415fae107Sandi *
35515fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
356f3f0262cSandi */
357f3f0262cSandifunction checkwordblock(){
358f3f0262cSandi  global $TEXT;
359f3f0262cSandi  global $conf;
360f3f0262cSandi
361f3f0262cSandi  if(!$conf['usewordblock']) return false;
362f3f0262cSandi
363e7cb32dcSAndreas Gohr  $blockfile = file(DOKU_CONF.'wordblock.conf');
3643e2965d7Sandi  //how many lines to read at once (to work around some PCRE limits)
3653e2965d7Sandi  if(version_compare(phpversion(),'4.3.0','<')){
3663e2965d7Sandi    //old versions of PCRE define a maximum of parenthesises even if no
3673e2965d7Sandi    //backreferences are used - the maximum is 99
3683e2965d7Sandi    //this is very bad performancewise and may even be too high still
3693e2965d7Sandi    $chunksize = 40;
3703e2965d7Sandi  }else{
371703f6fdeSandi    //read file in chunks of 600 - this should work around the
3723e2965d7Sandi    //MAX_PATTERN_SIZE in modern PCRE
3733e2965d7Sandi    $chunksize = 600;
3743e2965d7Sandi  }
3753e2965d7Sandi  while($blocks = array_splice($blockfile,0,$chunksize)){
376f3f0262cSandi    $re = array();
377f3f0262cSandi    #build regexp from blocks
378f3f0262cSandi    foreach($blocks as $block){
379f3f0262cSandi      $block = preg_replace('/#.*$/','',$block);
380f3f0262cSandi      $block = trim($block);
381f3f0262cSandi      if(empty($block)) continue;
382f3f0262cSandi      $re[]  = $block;
383f3f0262cSandi    }
384f3f0262cSandi    if(preg_match('#('.join('|',$re).')#si',$TEXT)) return true;
385703f6fdeSandi  }
386f3f0262cSandi  return false;
387f3f0262cSandi}
388f3f0262cSandi
389f3f0262cSandi/**
39015fae107Sandi * Return the IP of the client
39115fae107Sandi *
39215fae107Sandi * Honours X-Forwarded-For Proxy Headers
39315fae107Sandi *
39415fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
395f3f0262cSandi */
396f3f0262cSandifunction clientIP(){
397f3f0262cSandi  $my = $_SERVER['REMOTE_ADDR'];
398f3f0262cSandi  if($_SERVER['HTTP_X_FORWARDED_FOR']){
399f3f0262cSandi    $my .= ' ('.$_SERVER['HTTP_X_FORWARDED_FOR'].')';
400f3f0262cSandi  }
401f3f0262cSandi  return $my;
402f3f0262cSandi}
403f3f0262cSandi
404f3f0262cSandi/**
40515fae107Sandi * Checks if a given page is currently locked.
40615fae107Sandi *
407f3f0262cSandi * removes stale lockfiles
40815fae107Sandi *
40915fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
410f3f0262cSandi */
411f3f0262cSandifunction checklock($id){
412f3f0262cSandi  global $conf;
413f3f0262cSandi  $lock = wikiFN($id).'.lock';
414f3f0262cSandi
415f3f0262cSandi  //no lockfile
416f3f0262cSandi  if(!@file_exists($lock)) return false;
417f3f0262cSandi
418f3f0262cSandi  //lockfile expired
419f3f0262cSandi  if((time() - filemtime($lock)) > $conf['locktime']){
420f3f0262cSandi    unlink($lock);
421f3f0262cSandi    return false;
422f3f0262cSandi  }
423f3f0262cSandi
424f3f0262cSandi  //my own lock
425f3f0262cSandi  $ip = io_readFile($lock);
426f3f0262cSandi  if( ($ip == clientIP()) || ($ip == $_SERVER['REMOTE_USER']) ){
427f3f0262cSandi    return false;
428f3f0262cSandi  }
429f3f0262cSandi
430f3f0262cSandi  return $ip;
431f3f0262cSandi}
432f3f0262cSandi
433f3f0262cSandi/**
43415fae107Sandi * Lock a page for editing
43515fae107Sandi *
43615fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
437f3f0262cSandi */
438f3f0262cSandifunction lock($id){
439f3f0262cSandi  $lock = wikiFN($id).'.lock';
440f3f0262cSandi  if($_SERVER['REMOTE_USER']){
441f3f0262cSandi    io_saveFile($lock,$_SERVER['REMOTE_USER']);
442f3f0262cSandi  }else{
443f3f0262cSandi    io_saveFile($lock,clientIP());
444f3f0262cSandi  }
445f3f0262cSandi}
446f3f0262cSandi
447f3f0262cSandi/**
44815fae107Sandi * Unlock a page if it was locked by the user
449f3f0262cSandi *
45015fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
45115fae107Sandi * @return bool true if a lock was removed
452f3f0262cSandi */
453f3f0262cSandifunction unlock($id){
454f3f0262cSandi  $lock = wikiFN($id).'.lock';
455f3f0262cSandi  if(@file_exists($lock)){
456f3f0262cSandi    $ip = io_readFile($lock);
457f3f0262cSandi    if( ($ip == clientIP()) || ($ip == $_SERVER['REMOTE_USER']) ){
458f3f0262cSandi      @unlink($lock);
459f3f0262cSandi      return true;
460f3f0262cSandi    }
461f3f0262cSandi  }
462f3f0262cSandi  return false;
463f3f0262cSandi}
464f3f0262cSandi
465f3f0262cSandi/**
466f3f0262cSandi * convert line ending to unix format
467f3f0262cSandi *
46815fae107Sandi * @see    formText() for 2crlf conversion
46915fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
470f3f0262cSandi */
471f3f0262cSandifunction cleanText($text){
472f3f0262cSandi  $text = preg_replace("/(\015\012)|(\015)/","\012",$text);
473f3f0262cSandi  return $text;
474f3f0262cSandi}
475f3f0262cSandi
476f3f0262cSandi/**
477f3f0262cSandi * Prepares text for print in Webforms by encoding special chars.
478f3f0262cSandi * It also converts line endings to Windows format which is
479f3f0262cSandi * pseudo standard for webforms.
480f3f0262cSandi *
48115fae107Sandi * @see    cleanText() for 2unix conversion
48215fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
483f3f0262cSandi */
484f3f0262cSandifunction formText($text){
485f3f0262cSandi  $text = preg_replace("/\012/","\015\012",$text);
486f3f0262cSandi  return htmlspecialchars($text);
487f3f0262cSandi}
488f3f0262cSandi
489f3f0262cSandi/**
49015fae107Sandi * Returns the specified local text in raw format
49115fae107Sandi *
49215fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
493f3f0262cSandi */
494f3f0262cSandifunction rawLocale($id){
495f3f0262cSandi  return io_readFile(localeFN($id));
496f3f0262cSandi}
497f3f0262cSandi
498f3f0262cSandi/**
499f3f0262cSandi * Returns the raw WikiText
50015fae107Sandi *
50115fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
502f3f0262cSandi */
503f3f0262cSandifunction rawWiki($id,$rev=''){
504f3f0262cSandi  return io_readFile(wikiFN($id,$rev));
505f3f0262cSandi}
506f3f0262cSandi
507f3f0262cSandi/**
5087146cee2SAndreas Gohr * Returns the pagetemplate contents for the ID's namespace
5097146cee2SAndreas Gohr *
5107146cee2SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
5117146cee2SAndreas Gohr */
5127146cee2SAndreas Gohrfunction pageTemplate($id){
513a15ce62dSEsther Brunner  global $conf;
514a15ce62dSEsther Brunner  global $INFO;
515a15ce62dSEsther Brunner  $tpl = io_readFile(dirname(wikiFN($id)).'/_template.txt');
516a15ce62dSEsther Brunner  $tpl = str_replace('@ID@',$id,$tpl);
517a15ce62dSEsther Brunner  $tpl = str_replace('@NS@',getNS($id),$tpl);
518a15ce62dSEsther Brunner  $tpl = str_replace('@PAGE@',strtr(noNS($id),'_',' '),$tpl);
519a15ce62dSEsther Brunner  $tpl = str_replace('@USER@',$_SERVER['REMOTE_USER'],$tpl);
520a15ce62dSEsther Brunner  $tpl = str_replace('@NAME@',$INFO['userinfo']['name'],$tpl);
521a15ce62dSEsther Brunner  $tpl = str_replace('@MAIL@',$INFO['userinfo']['mail'],$tpl);
522a15ce62dSEsther Brunner  $tpl = str_replace('@DATE@',date($conf['dformat']),$tpl);
523a15ce62dSEsther Brunner  return $tpl;
5247146cee2SAndreas Gohr}
5257146cee2SAndreas Gohr
5267146cee2SAndreas Gohr
5277146cee2SAndreas Gohr/**
52815fae107Sandi * Returns the raw Wiki Text in three slices.
52915fae107Sandi *
53015fae107Sandi * The range parameter needs to have the form "from-to"
53115cfe303Sandi * and gives the range of the section in bytes - no
53215cfe303Sandi * UTF-8 awareness is needed.
533f3f0262cSandi * The returned order is prefix, section and suffix.
53415fae107Sandi *
53515fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
536f3f0262cSandi */
537f3f0262cSandifunction rawWikiSlices($range,$id,$rev=''){
538f3f0262cSandi  list($from,$to) = split('-',$range,2);
539f3f0262cSandi  $text = io_readFile(wikiFN($id,$rev));
540f3f0262cSandi  if(!$from) $from = 0;
541c3d8e19bSandi  if(!$to)   $to   = strlen($text)+1;
542f3f0262cSandi
54315cfe303Sandi  $slices[0] = substr($text,0,$from-1);
54415cfe303Sandi  $slices[1] = substr($text,$from-1,$to-$from);
54515cfe303Sandi  $slices[2] = substr($text,$to);
546f3f0262cSandi
547f3f0262cSandi  return $slices;
548f3f0262cSandi}
549f3f0262cSandi
550f3f0262cSandi/**
55115fae107Sandi * Joins wiki text slices
55215fae107Sandi *
553f3f0262cSandi * function to join the text slices with correct lineendings again.
554f3f0262cSandi * When the pretty parameter is set to true it adds additional empty
555f3f0262cSandi * lines between sections if needed (used on saving).
55615fae107Sandi *
55715fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
558f3f0262cSandi */
559f3f0262cSandifunction con($pre,$text,$suf,$pretty=false){
560f3f0262cSandi
561f3f0262cSandi  if($pretty){
562f3f0262cSandi    if($pre && substr($pre,-1) != "\n") $pre .= "\n";
563f3f0262cSandi    if($suf && substr($text,-1) != "\n") $text .= "\n";
564f3f0262cSandi  }
565f3f0262cSandi
566f3f0262cSandi  if($pre) $pre .= "\n";
567f3f0262cSandi  if($suf) $text .= "\n";
568f3f0262cSandi  return $pre.$text.$suf;
569f3f0262cSandi}
570f3f0262cSandi
571f3f0262cSandi/**
57215fae107Sandi * print debug messages
57315fae107Sandi *
574f3f0262cSandi * little function to print the content of a var
57515fae107Sandi *
57615fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
577f3f0262cSandi */
578f3f0262cSandifunction dbg($msg,$hidden=false){
579f3f0262cSandi  (!$hidden) ? print '<pre class="dbg">' : print "<!--\n";
580f3f0262cSandi  print_r($msg);
581f3f0262cSandi  (!$hidden) ? print '</pre>' : print "\n-->";
582f3f0262cSandi}
583f3f0262cSandi
584f3f0262cSandi/**
585f3f0262cSandi * Add's an entry to the changelog
58615fae107Sandi *
58715fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
588f3f0262cSandi */
589*b6912aeaSAndreas Gohrfunction addLogEntry($date,$id,$summary='',$minor=false){
590f3f0262cSandi  global $conf;
591c1049928Sandi
592c1049928Sandi  if(!@is_writable($conf['changelog'])){
593c1049928Sandi    msg($conf['changelog'].' is not writable!',-1);
594c1049928Sandi    return;
595c1049928Sandi  }
596c1049928Sandi
597652610a2Sandi  if(!$date) $date = time(); //use current time if none supplied
598f3f0262cSandi  $remote = $_SERVER['REMOTE_ADDR'];
599f3f0262cSandi  $user   = $_SERVER['REMOTE_USER'];
600f3f0262cSandi
601*b6912aeaSAndreas Gohr  if($conf['useacl'] && $user && $minor){
602*b6912aeaSAndreas Gohr    $summary = '*'.$summary;
603*b6912aeaSAndreas Gohr  }else{
604*b6912aeaSAndreas Gohr    $summary = ' '.$summary;
605*b6912aeaSAndreas Gohr  }
606*b6912aeaSAndreas Gohr
607f3f0262cSandi  $logline = join("\t",array($date,$remote,$id,$user,$summary))."\n";
608dbb00abcSEsther Brunner  io_saveFile($conf['changelog'],$logline,true);
609f3f0262cSandi}
610f3f0262cSandi
611f3f0262cSandi/**
612*b6912aeaSAndreas Gohr * Checks an summary entry if it was a minor edit
613*b6912aeaSAndreas Gohr *
614*b6912aeaSAndreas Gohr * The summary is cleaned of the marker char
615*b6912aeaSAndreas Gohr *
616*b6912aeaSAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
617*b6912aeaSAndreas Gohr */
618*b6912aeaSAndreas Gohrfunction isMinor(&$summary){
619*b6912aeaSAndreas Gohr  if(substr($summary,0,1) == '*'){
620*b6912aeaSAndreas Gohr    $summary = substr($summary,1);
621*b6912aeaSAndreas Gohr    return true;
622*b6912aeaSAndreas Gohr  }
623*b6912aeaSAndreas Gohr  $summary = trim($summary);
624*b6912aeaSAndreas Gohr  return false;
625*b6912aeaSAndreas Gohr}
626*b6912aeaSAndreas Gohr
627*b6912aeaSAndreas Gohr/**
628d437bcc4SAndreas Gohr * Internal function used by getRecents
629d437bcc4SAndreas Gohr *
630d437bcc4SAndreas Gohr * don't call directly
631d437bcc4SAndreas Gohr *
632d437bcc4SAndreas Gohr * @see getRecents()
633d437bcc4SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
634d437bcc4SAndreas Gohr */
635*b6912aeaSAndreas Gohrfunction _handleRecent($line,$ns,$flags){
636d437bcc4SAndreas Gohr  static $seen  = array();         //caches seen pages and skip them
637d437bcc4SAndreas Gohr  if(empty($line)) return false;   //skip empty lines
638d437bcc4SAndreas Gohr
639d437bcc4SAndreas Gohr  // split the line into parts
640d437bcc4SAndreas Gohr  list($dt,$ip,$id,$usr,$sum) = explode("\t",$line);
641d437bcc4SAndreas Gohr
642d437bcc4SAndreas Gohr  // skip seen ones
643d437bcc4SAndreas Gohr  if($seen[$id]) return false;
644*b6912aeaSAndreas Gohr  $recent = array();
645*b6912aeaSAndreas Gohr
646*b6912aeaSAndreas Gohr  // check minors
647*b6912aeaSAndreas Gohr  if(isMinor($sum)){
648*b6912aeaSAndreas Gohr    // skip minors
649*b6912aeaSAndreas Gohr    if($flags & RECENTS_SKIP_MINORS) return false;
650*b6912aeaSAndreas Gohr    $recent['minor'] = true;
651*b6912aeaSAndreas Gohr  }else{
652*b6912aeaSAndreas Gohr    $recent['minor'] = false;
653*b6912aeaSAndreas Gohr  }
654d437bcc4SAndreas Gohr
655d437bcc4SAndreas Gohr  // remember in seen to skip additional sights
656d437bcc4SAndreas Gohr  $seen[$id] = 1;
657d437bcc4SAndreas Gohr
658d437bcc4SAndreas Gohr  // filter namespace
659d437bcc4SAndreas Gohr  if (($ns) && (strpos($id,$ns.':') !== 0)) return false;
660d437bcc4SAndreas Gohr
661d437bcc4SAndreas Gohr  // exclude subnamespaces
662*b6912aeaSAndreas Gohr  if (($flags & RECENTS_SKIP_SUBSPACES) && (getNS($id) != $ns)) return false;
663d437bcc4SAndreas Gohr
664ae56bfb6SAndreas Gohr  // check ACL
665ae56bfb6SAndreas Gohr  if (auth_quickaclcheck($id) < AUTH_READ) return false;
666ae56bfb6SAndreas Gohr
667d437bcc4SAndreas Gohr  // check existance
668d437bcc4SAndreas Gohr  if(!@file_exists(wikiFN($id))){
669*b6912aeaSAndreas Gohr    if($flags & RECENTS_SKIP_DELETED){
670d437bcc4SAndreas Gohr      return false;
671d437bcc4SAndreas Gohr    }else{
672d437bcc4SAndreas Gohr      $recent['del'] = true;
673d437bcc4SAndreas Gohr    }
674d437bcc4SAndreas Gohr  }else{
675d437bcc4SAndreas Gohr    $recent['del'] = false;
676d437bcc4SAndreas Gohr  }
677d437bcc4SAndreas Gohr
678d437bcc4SAndreas Gohr  $recent['id']   = $id;
679d437bcc4SAndreas Gohr  $recent['date'] = $dt;
680d437bcc4SAndreas Gohr  $recent['ip']   = $ip;
681d437bcc4SAndreas Gohr  $recent['user'] = $usr;
682d437bcc4SAndreas Gohr  $recent['sum']  = $sum;
683d437bcc4SAndreas Gohr
684d437bcc4SAndreas Gohr  return $recent;
685d437bcc4SAndreas Gohr}
686d437bcc4SAndreas Gohr
687*b6912aeaSAndreas Gohr
688d437bcc4SAndreas Gohr/**
689f3f0262cSandi * returns an array of recently changed files using the
690f3f0262cSandi * changelog
691d437bcc4SAndreas Gohr *
692*b6912aeaSAndreas Gohr * The following constants can be used to control which changes are
693*b6912aeaSAndreas Gohr * included. Add them together as needed.
694*b6912aeaSAndreas Gohr *
695*b6912aeaSAndreas Gohr * RECENTS_SKIP_DELETED   - don't include deleted pages
696*b6912aeaSAndreas Gohr * RECENTS_SKIP_MINORS    - don't include minor changes
697*b6912aeaSAndreas Gohr * RECENTS_SKIP_SUBSPACES - don't include subspaces
698*b6912aeaSAndreas Gohr *
699d437bcc4SAndreas Gohr * @param int    $first   number of first entry returned (for paginating
700d437bcc4SAndreas Gohr * @param int    $num     return $num entries
701d437bcc4SAndreas Gohr * @param string $ns      restrict to given namespace
702*b6912aeaSAndreas Gohr * @param bool   $flags   see above
70315fae107Sandi *
70415fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
705f3f0262cSandi */
706*b6912aeaSAndreas Gohrfunction getRecents($first,$num,$ns='',$flags=0){
707f3f0262cSandi  global $conf;
708f3f0262cSandi  $recent = array();
709d437bcc4SAndreas Gohr  $count  = 0;
7105749f1ceSmatthiasgrimm
7115749f1ceSmatthiasgrimm  if(!$num)
7125749f1ceSmatthiasgrimm    return $recent;
713f3f0262cSandi
714c1049928Sandi  if(!@is_readable($conf['changelog'])){
715c1049928Sandi    msg($conf['changelog'].' is not readable',-1);
716c1049928Sandi    return $recent;
717c1049928Sandi  }
718c1049928Sandi
719d437bcc4SAndreas Gohr  $fh  = fopen($conf['changelog'],'r');
720d437bcc4SAndreas Gohr  $buf = '';
721d437bcc4SAndreas Gohr  $csz = 4096;                              //chunksize
722d437bcc4SAndreas Gohr  fseek($fh,0,SEEK_END);                    // jump to the end
723d437bcc4SAndreas Gohr  $pos = ftell($fh);                        // position pointer
724f3f0262cSandi
725d437bcc4SAndreas Gohr  // now read backwards into buffer
726d437bcc4SAndreas Gohr  while($pos > 0){
727d437bcc4SAndreas Gohr    $pos -= $csz;                           // seek to previous chunk...
728d437bcc4SAndreas Gohr    if($pos < 0) $pos = 0;                  // ...or rest of file
729d437bcc4SAndreas Gohr    fseek($fh,$pos);
730dbb00abcSEsther Brunner
731d437bcc4SAndreas Gohr    $buf = fread($fh,$csz).$buf;            // prepend to buffer
7328f1d587cSEsther Brunner
733d437bcc4SAndreas Gohr    $lines = explode("\n",$buf);            // split buffer into lines
7345749f1ceSmatthiasgrimm
735d437bcc4SAndreas Gohr    if($pos > 0){
736d437bcc4SAndreas Gohr      $buf = array_shift($lines);           // first one may be still incomplete
737f3f0262cSandi    }
738d437bcc4SAndreas Gohr
739d437bcc4SAndreas Gohr    $cnt = count($lines);
740d437bcc4SAndreas Gohr    if(!$cnt) continue;                     // no lines yet
741d437bcc4SAndreas Gohr
742d437bcc4SAndreas Gohr    // handle lines
743d437bcc4SAndreas Gohr    for($i = $cnt-1; $i >= 0; $i--){
744*b6912aeaSAndreas Gohr      $rec = _handleRecent($lines[$i],$ns,$flags);
745d437bcc4SAndreas Gohr      if($rec !== false){
746d437bcc4SAndreas Gohr        if(--$first >= 0) continue;         // skip first entries
747d437bcc4SAndreas Gohr        $recent[] = $rec;
748d437bcc4SAndreas Gohr        $count++;
749d437bcc4SAndreas Gohr
750d437bcc4SAndreas Gohr        // break while when we have enough entries
751d437bcc4SAndreas Gohr        if($count >= $num){
752d437bcc4SAndreas Gohr          $pos = 0; // will break the while loop
753d437bcc4SAndreas Gohr          break;    // will break the for loop
754f3f0262cSandi        }
755f3f0262cSandi      }
756d437bcc4SAndreas Gohr    }
757d437bcc4SAndreas Gohr  }// end of while
758d437bcc4SAndreas Gohr
759d437bcc4SAndreas Gohr  fclose($fh);
760f3f0262cSandi  return $recent;
761f3f0262cSandi}
762f3f0262cSandi
763f3f0262cSandi/**
764652610a2Sandi * gets additonal informations for a certain pagerevison
765652610a2Sandi * from the changelog
766652610a2Sandi *
767652610a2Sandi * @author Andreas Gohr <andi@splitbrain.org>
768652610a2Sandi */
769652610a2Sandifunction getRevisionInfo($id,$rev){
770652610a2Sandi  global $conf;
771258641c6Sandi
772258641c6Sandi  if(!$rev) return(null);
773258641c6Sandi
774c1049928Sandi  $info = array();
775c1049928Sandi  if(!@is_readable($conf['changelog'])){
776c1049928Sandi    msg($conf['changelog'].' is not readable',-1);
777c1049928Sandi    return $recent;
778c1049928Sandi  }
779652610a2Sandi  $loglines = file($conf['changelog']);
780652610a2Sandi  $loglines = preg_grep("/$rev\t\d+\.\d+\.\d+\.\d+\t$id\t/",$loglines);
781dc42ff59Sandi  $loglines = array_reverse($loglines); //reverse sort on timestamp (shouldn't be needed)
782652610a2Sandi  $line = split("\t",$loglines[0]);
783652610a2Sandi  $info['date']  = $line[0];
784652610a2Sandi  $info['ip']    = $line[1];
785652610a2Sandi  $info['user']  = $line[3];
786652610a2Sandi  $info['sum']   = $line[4];
787*b6912aeaSAndreas Gohr  $info['minor'] = isMinor($info['sum']);
788652610a2Sandi  return $info;
789652610a2Sandi}
790652610a2Sandi
791652610a2Sandi/**
792f3f0262cSandi * Saves a wikitext by calling io_saveFile
79315fae107Sandi *
79415fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
795f3f0262cSandi */
796*b6912aeaSAndreas Gohrfunction saveWikiText($id,$text,$summary,$minor=false){
797f3f0262cSandi  global $conf;
798f3f0262cSandi  global $lang;
799f3f0262cSandi  umask($conf['umask']);
800f3f0262cSandi  // ignore if no changes were made
801f3f0262cSandi  if($text == rawWiki($id,'')){
802f3f0262cSandi    return;
803f3f0262cSandi  }
804f3f0262cSandi
805f3f0262cSandi  $file = wikiFN($id);
806f3f0262cSandi  $old  = saveOldRevision($id);
807f3f0262cSandi
808f3f0262cSandi  if (empty($text)){
809e1f3d9e1SEsther Brunner    // remove empty file
810f3f0262cSandi    @unlink($file);
811e1f3d9e1SEsther Brunner    // remove any meta info
812e1f3d9e1SEsther Brunner    $mfiles = metaFiles($id);
813e1f3d9e1SEsther Brunner    foreach ($mfiles as $mfile) {
814e1f3d9e1SEsther Brunner      if (file_exists($mfile)) @unlink($mfile);
815b158d625SSteven Danz    }
816f3f0262cSandi    $del = true;
8173ce054b3Sandi    //autoset summary on deletion
8183ce054b3Sandi    if(empty($summary)) $summary = $lang['deleted'];
81953d6ccfeSandi    //remove empty namespaces
82053d6ccfeSandi    io_sweepNS($id);
821f3f0262cSandi  }else{
822f3f0262cSandi    // save file (datadir is created in io_saveFile)
823f3f0262cSandi    io_saveFile($file,$text);
824f3f0262cSandi    $del = false;
825f3f0262cSandi  }
826f3f0262cSandi
827*b6912aeaSAndreas Gohr  addLogEntry(@filemtime($file),$id,$summary,$minor);
82826a0801fSAndreas Gohr  // send notify mails
82926a0801fSAndreas Gohr  notify($id,'admin',$old,$summary);
83026a0801fSAndreas Gohr  notify($id,'subscribers',$old,$summary);
831f3f0262cSandi
832f3f0262cSandi  //purge cache on add by updating the purgefile
833f3f0262cSandi  if($conf['purgeonadd'] && (!$old || $del)){
83498407a7aSandi    io_saveFile($conf['cachedir'].'/purgefile',time());
835f3f0262cSandi  }
836f3f0262cSandi}
837f3f0262cSandi
838f3f0262cSandi/**
839f3f0262cSandi * moves the current version to the attic and returns its
840f3f0262cSandi * revision date
84115fae107Sandi *
84215fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
843f3f0262cSandi */
844f3f0262cSandifunction saveOldRevision($id){
845f3f0262cSandi	global $conf;
846f3f0262cSandi  umask($conf['umask']);
847f3f0262cSandi  $oldf = wikiFN($id);
848f3f0262cSandi  if(!@file_exists($oldf)) return '';
849f3f0262cSandi  $date = filemtime($oldf);
850f3f0262cSandi  $newf = wikiFN($id,$date);
851f3f0262cSandi  if(substr($newf,-3)=='.gz'){
852f3f0262cSandi    io_saveFile($newf,rawWiki($id));
853f3f0262cSandi  }else{
854f3f0262cSandi    io_makeFileDir($newf);
855f3f0262cSandi    copy($oldf, $newf);
856f3f0262cSandi  }
857f3f0262cSandi  return $date;
858f3f0262cSandi}
859f3f0262cSandi
860f3f0262cSandi/**
86126a0801fSAndreas Gohr * Sends a notify mail on page change
86226a0801fSAndreas Gohr *
86326a0801fSAndreas Gohr * @param  string $id       The changed page
86426a0801fSAndreas Gohr * @param  string $who      Who to notify (admin|subscribers)
86526a0801fSAndreas Gohr * @param  int    $rev      Old page revision
86626a0801fSAndreas Gohr * @param  string $summary  What changed
86715fae107Sandi *
86815fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
869f3f0262cSandi */
87026a0801fSAndreas Gohrfunction notify($id,$who,$rev='',$summary=''){
871f3f0262cSandi  global $lang;
872f3f0262cSandi  global $conf;
873b158d625SSteven Danz
87426a0801fSAndreas Gohr  // decide if there is something to do
87526a0801fSAndreas Gohr  if($who == 'admin'){
87626a0801fSAndreas Gohr    if(empty($conf['notify'])) return; //notify enabled?
877f3f0262cSandi    $text = rawLocale('mailtext');
87826a0801fSAndreas Gohr    $to   = $conf['notify'];
87926a0801fSAndreas Gohr    $bcc  = '';
88026a0801fSAndreas Gohr  }elseif($who == 'subscribers'){
88126a0801fSAndreas Gohr    if(!$conf['subscribers']) return; //subscribers enabled?
88226a0801fSAndreas Gohr    $bcc  = subscriber_addresslist($id);
88326a0801fSAndreas Gohr    if(empty($bcc)) return;
88426a0801fSAndreas Gohr    $to   = '';
88526a0801fSAndreas Gohr    $text = rawLocale('subscribermail');
88626a0801fSAndreas Gohr  }else{
88726a0801fSAndreas Gohr    return; //just to be safe
88826a0801fSAndreas Gohr  }
88926a0801fSAndreas Gohr
890f3f0262cSandi  $text = str_replace('@DATE@',date($conf['dformat']),$text);
891f3f0262cSandi  $text = str_replace('@BROWSER@',$_SERVER['HTTP_USER_AGENT'],$text);
892f3f0262cSandi  $text = str_replace('@IPADDRESS@',$_SERVER['REMOTE_ADDR'],$text);
893f3f0262cSandi  $text = str_replace('@HOSTNAME@',gethostbyaddr($_SERVER['REMOTE_ADDR']),$text);
894ed7b5f09Sandi  $text = str_replace('@NEWPAGE@',wl($id,'',true),$text);
89526a0801fSAndreas Gohr  $text = str_replace('@PAGE@',$id,$text);
89626a0801fSAndreas Gohr  $text = str_replace('@TITLE@',$conf['title'],$text);
897ed7b5f09Sandi  $text = str_replace('@DOKUWIKIURL@',DOKU_URL,$text);
898f3f0262cSandi  $text = str_replace('@SUMMARY@',$summary,$text);
8997a82afdcSandi  $text = str_replace('@USER@',$_SERVER['REMOTE_USER'],$text);
900f3f0262cSandi
901f3f0262cSandi  if($rev){
902f3f0262cSandi    $subject = $lang['mail_changed'].' '.$id;
903ed7b5f09Sandi    $text = str_replace('@OLDPAGE@',wl($id,"rev=$rev",true),$text);
904f3f0262cSandi    require_once("inc/DifferenceEngine.php");
905f3f0262cSandi    $df  = new Diff(split("\n",rawWiki($id,$rev)),
906f3f0262cSandi                    split("\n",rawWiki($id)));
907f3f0262cSandi    $dformat = new UnifiedDiffFormatter();
908f3f0262cSandi    $diff    = $dformat->format($df);
909f3f0262cSandi  }else{
910f3f0262cSandi    $subject=$lang['mail_newpage'].' '.$id;
911f3f0262cSandi    $text = str_replace('@OLDPAGE@','none',$text);
912f3f0262cSandi    $diff = rawWiki($id);
913f3f0262cSandi  }
914f3f0262cSandi  $text = str_replace('@DIFF@',$diff,$text);
915241f3a36Sandi  $subject = '['.$conf['title'].'] '.$subject;
916f3f0262cSandi
91726a0801fSAndreas Gohr  mail_send($to,$subject,$text,$conf['mailfrom'],'',$bcc);
918f3f0262cSandi}
919f3f0262cSandi
92015fae107Sandi/**
92115fae107Sandi * Return a list of available page revisons
92215fae107Sandi *
92315fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
92415fae107Sandi */
925f3f0262cSandifunction getRevisions($id){
926f3f0262cSandi  $revd = dirname(wikiFN($id,'foo'));
927f3f0262cSandi  $revs = array();
928f3f0262cSandi  $clid = cleanID($id);
929f3f0262cSandi  if(strrpos($clid,':')) $clid = substr($clid,strrpos($clid,':')+1); //remove path
930493a6929SKobaYY  $clid = utf8_encodeFN($clid);
931f3f0262cSandi
932f3f0262cSandi  if (is_dir($revd) && $dh = opendir($revd)) {
933f3f0262cSandi    while (($file = readdir($dh)) !== false) {
934f3f0262cSandi      if (is_dir($revd.'/'.$file)) continue;
935f3f0262cSandi      if (preg_match('/^'.$clid.'\.(\d+)\.txt(\.gz)?$/',$file,$match)){
936f3f0262cSandi        $revs[]=$match[1];
937f3f0262cSandi      }
938f3f0262cSandi    }
939f3f0262cSandi    closedir($dh);
940f3f0262cSandi  }
941f3f0262cSandi  rsort($revs);
942f3f0262cSandi  return $revs;
943f3f0262cSandi}
944f3f0262cSandi
945f3f0262cSandi/**
946f3f0262cSandi * extracts the query from a google referer
94715fae107Sandi *
9486b13307fSandi * @todo   should be more generic and support yahoo et al
94915fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
950f3f0262cSandi */
951f3f0262cSandifunction getGoogleQuery(){
952f3f0262cSandi  $url = parse_url($_SERVER['HTTP_REFERER']);
9535c3f206fSandi  if(!$url) return '';
954f3f0262cSandi
955f3f0262cSandi  if(!preg_match("#google\.#i",$url['host'])) return '';
956f3f0262cSandi  $query = array();
957f3f0262cSandi  parse_str($url['query'],$query);
958f3f0262cSandi
959f3f0262cSandi  return $query['q'];
960f3f0262cSandi}
961f3f0262cSandi
962f3f0262cSandi/**
96315fae107Sandi * Try to set correct locale
96415fae107Sandi *
965095bfd5cSandi * @deprecated No longer used
96615fae107Sandi * @author     Andreas Gohr <andi@splitbrain.org>
967f3f0262cSandi */
968f3f0262cSandifunction setCorrectLocale(){
969f3f0262cSandi  global $conf;
970f3f0262cSandi  global $lang;
971f3f0262cSandi
972f3f0262cSandi  $enc = strtoupper($lang['encoding']);
973f3f0262cSandi  foreach ($lang['locales'] as $loc){
974f3f0262cSandi    //try locale
975f3f0262cSandi    if(@setlocale(LC_ALL,$loc)) return;
976f3f0262cSandi    //try loceale with encoding
977f3f0262cSandi    if(@setlocale(LC_ALL,"$loc.$enc")) return;
978f3f0262cSandi  }
979f3f0262cSandi  //still here? try to set from environment
980f3f0262cSandi  @setlocale(LC_ALL,"");
981f3f0262cSandi}
982f3f0262cSandi
983f3f0262cSandi/**
984f3f0262cSandi * Return the human readable size of a file
985f3f0262cSandi *
986f3f0262cSandi * @param       int    $size   A file size
987f3f0262cSandi * @param       int    $dec    A number of decimal places
988f3f0262cSandi * @author      Martin Benjamin <b.martin@cybernet.ch>
989f3f0262cSandi * @author      Aidan Lister <aidan@php.net>
990f3f0262cSandi * @version     1.0.0
991f3f0262cSandi */
992f31d5b73Sandifunction filesize_h($size, $dec = 1){
993f3f0262cSandi  $sizes = array('B', 'KB', 'MB', 'GB');
994f3f0262cSandi  $count = count($sizes);
995f3f0262cSandi  $i = 0;
996f3f0262cSandi
997f3f0262cSandi  while ($size >= 1024 && ($i < $count - 1)) {
998f3f0262cSandi    $size /= 1024;
999f3f0262cSandi    $i++;
1000f3f0262cSandi  }
1001f3f0262cSandi
1002f3f0262cSandi  return round($size, $dec) . ' ' . $sizes[$i];
1003f3f0262cSandi}
1004f3f0262cSandi
100515fae107Sandi/**
100600a7b5adSEsther Brunner * return an obfuscated email address in line with $conf['mailguard'] setting
100700a7b5adSEsther Brunner *
100800a7b5adSEsther Brunner * @author Harry Fuecks <hfuecks@gmail.com>
100900a7b5adSEsther Brunner * @author Christopher Smith <chris@jalakai.co.uk>
101000a7b5adSEsther Brunner */
101100a7b5adSEsther Brunnerfunction obfuscate($email) {
101200a7b5adSEsther Brunner  global $conf;
101300a7b5adSEsther Brunner
101400a7b5adSEsther Brunner  switch ($conf['mailguard']) {
101500a7b5adSEsther Brunner    case 'visible' :
101600a7b5adSEsther Brunner      $obfuscate = array('@' => ' [at] ', '.' => ' [dot] ', '-' => ' [dash] ');
101700a7b5adSEsther Brunner      return strtr($email, $obfuscate);
101800a7b5adSEsther Brunner
101900a7b5adSEsther Brunner    case 'hex' :
102000a7b5adSEsther Brunner      $encode = '';
102100a7b5adSEsther Brunner      for ($x=0; $x < strlen($email); $x++) $encode .= '&#x' . bin2hex($email{$x}).';';
102200a7b5adSEsther Brunner      return $encode;
102300a7b5adSEsther Brunner
102400a7b5adSEsther Brunner    case 'none' :
102500a7b5adSEsther Brunner    default :
102600a7b5adSEsther Brunner      return $email;
102700a7b5adSEsther Brunner  }
102800a7b5adSEsther Brunner}
102900a7b5adSEsther Brunner
103000a7b5adSEsther Brunner/**
1031dc57ef04Sandi * Return DokuWikis version
103215fae107Sandi *
103315fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
103415fae107Sandi */
1035f31d5b73Sandifunction getVersion(){
1036f31d5b73Sandi  //import version string
1037f31d5b73Sandi  if(@file_exists('VERSION')){
1038f31d5b73Sandi    //official release
10390647ce3bSAndreas Gohr    return 'Release '.trim(io_readfile(DOKU_INC.'/VERSION'));
1040f31d5b73Sandi  }elseif(is_dir('_darcs')){
1041f31d5b73Sandi    //darcs checkout
1042f31d5b73Sandi    $inv = file('_darcs/inventory');
1043f31d5b73Sandi    $inv = preg_grep('#andi@splitbrain\.org\*\*\d{14}#',$inv);
1044f31d5b73Sandi    $cur = array_pop($inv);
1045f31d5b73Sandi    preg_match('#\*\*(\d{4})(\d{2})(\d{2})#',$cur,$matches);
1046f31d5b73Sandi    return 'Darcs '.$matches[1].'-'.$matches[2].'-'.$matches[3];
1047f31d5b73Sandi  }else{
1048f31d5b73Sandi    return 'snapshot?';
1049f31d5b73Sandi  }
1050f31d5b73Sandi}
1051f31d5b73Sandi
1052f31d5b73Sandi/**
1053f31d5b73Sandi * Run a few sanity checks
1054f31d5b73Sandi *
1055f31d5b73Sandi * @author Andreas Gohr <andi@splitbrain.org>
1056f31d5b73Sandi */
1057f3f0262cSandifunction check(){
1058f3f0262cSandi  global $conf;
1059f3f0262cSandi  global $INFO;
1060f3f0262cSandi
1061f31d5b73Sandi  msg('DokuWiki version: '.getVersion(),1);
1062f31d5b73Sandi
106349022a38Sandi  if(version_compare(phpversion(),'4.3.0','<')){
106449022a38Sandi    msg('Your PHP version is too old ('.phpversion().' vs. 4.3.+ recommended)',-1);
106549022a38Sandi  }elseif(version_compare(phpversion(),'4.3.10','<')){
106649022a38Sandi    msg('Consider upgrading PHP to 4.3.10 or higher for security reasons (your version: '.phpversion().')',0);
106749022a38Sandi  }else{
106849022a38Sandi    msg('PHP version '.phpversion(),1);
106949022a38Sandi  }
107049022a38Sandi
1071f3f0262cSandi  if(is_writable($conf['changelog'])){
1072f3f0262cSandi    msg('Changelog is writable',1);
1073f3f0262cSandi  }else{
1074f3f0262cSandi    msg('Changelog is not writable',-1);
1075f3f0262cSandi  }
1076f3f0262cSandi
1077f3f0262cSandi  if(is_writable($conf['datadir'])){
1078f3f0262cSandi    msg('Datadir is writable',1);
1079f3f0262cSandi  }else{
1080f3f0262cSandi    msg('Datadir is not writable',-1);
1081f3f0262cSandi  }
1082f3f0262cSandi
1083f3f0262cSandi  if(is_writable($conf['olddir'])){
1084f3f0262cSandi    msg('Attic is writable',1);
1085f3f0262cSandi  }else{
1086f3f0262cSandi    msg('Attic is not writable',-1);
1087f3f0262cSandi  }
1088f3f0262cSandi
1089f3f0262cSandi  if(is_writable($conf['mediadir'])){
1090f3f0262cSandi    msg('Mediadir is writable',1);
1091f3f0262cSandi  }else{
1092f3f0262cSandi    msg('Mediadir is not writable',-1);
1093f3f0262cSandi  }
1094f3f0262cSandi
109598407a7aSandi  if(is_writable($conf['cachedir'])){
109698407a7aSandi    msg('Cachedir is writable',1);
109798407a7aSandi  }else{
109898407a7aSandi    msg('Cachedir is not writable',-1);
109998407a7aSandi  }
110098407a7aSandi
1101e7cb32dcSAndreas Gohr  if(is_writable(DOKU_CONF.'users.auth.php')){
11028c4f28e8Sjan    msg('conf/users.auth.php is writable',1);
1103f3f0262cSandi  }else{
11048c4f28e8Sjan    msg('conf/users.auth.php is not writable',0);
1105f3f0262cSandi  }
110693a9e835Sandi
110793a9e835Sandi  if(function_exists('mb_strpos')){
110893a9e835Sandi    if(defined('UTF8_NOMBSTRING')){
110993a9e835Sandi      msg('mb_string extension is available but will not be used',0);
111093a9e835Sandi    }else{
111193a9e835Sandi      msg('mb_string extension is available and will be used',1);
111293a9e835Sandi    }
111393a9e835Sandi  }else{
111493a9e835Sandi    msg('mb_string extension not available - PHP only replacements will be used',0);
111593a9e835Sandi  }
1116f3f0262cSandi
1117f3f0262cSandi  msg('Your current permission for this page is '.$INFO['perm'],0);
1118f3f0262cSandi
1119f3f0262cSandi  if(is_writable($INFO['filepath'])){
1120f3f0262cSandi    msg('The current page is writable by the webserver',0);
1121f3f0262cSandi  }else{
1122f3f0262cSandi    msg('The current page is not writable by the webserver',0);
1123f3f0262cSandi  }
1124f3f0262cSandi
1125f3f0262cSandi  if($INFO['writable']){
1126f3f0262cSandi    msg('The current page is writable by you',0);
1127f3f0262cSandi  }else{
1128f3f0262cSandi    msg('The current page is not writable you',0);
1129f3f0262cSandi  }
1130f3f0262cSandi}
1131340756e4Sandi
1132b158d625SSteven Danz/**
1133b158d625SSteven Danz * Let us know if a user is tracking a page
1134b158d625SSteven Danz *
11351380fc45SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
1136b158d625SSteven Danz */
11371380fc45SAndreas Gohrfunction is_subscribed($id,$uid){
11381380fc45SAndreas Gohr  $file=metaFN($id,'.mlist');
11391380fc45SAndreas Gohr  if (@file_exists($file)) {
1140b158d625SSteven Danz    $mlist = file($file);
11411380fc45SAndreas Gohr    $pos = array_search($uid."\n",$mlist);
11421380fc45SAndreas Gohr    return is_int($pos);
1143b158d625SSteven Danz  }
11441380fc45SAndreas Gohr
1145b158d625SSteven Danz  return false;
1146b158d625SSteven Danz}
1147340756e4Sandi
1148f9eb5648Ssteven-danz/**
1149f9eb5648Ssteven-danz * Return a string with the email addresses of all the
1150f9eb5648Ssteven-danz * users subscribed to a page
1151f9eb5648Ssteven-danz *
115226a0801fSAndreas Gohr * @author Steven Danz <steven-danz@kc.rr.com>
1153f9eb5648Ssteven-danz */
1154f9eb5648Ssteven-danzfunction subscriber_addresslist($id){
1155f9eb5648Ssteven-danz  global $conf;
1156f9eb5648Ssteven-danz
1157f9eb5648Ssteven-danz  $emails = '';
1158f9eb5648Ssteven-danz
115926a0801fSAndreas Gohr  if (!$conf['subscribers']) return;
116026a0801fSAndreas Gohr
1161f9eb5648Ssteven-danz  $mlist = array();
1162f9eb5648Ssteven-danz  $file=metaFN($id,'.mlist');
1163f9eb5648Ssteven-danz  if (file_exists($file)) {
1164f9eb5648Ssteven-danz    $mlist = file($file);
1165f9eb5648Ssteven-danz  }
1166f9eb5648Ssteven-danz  if(count($mlist) > 0) {
1167f9eb5648Ssteven-danz    foreach ($mlist as $who) {
1168f9eb5648Ssteven-danz      $who = rtrim($who);
1169f9eb5648Ssteven-danz      $info = auth_getUserData($who);
1170f9eb5648Ssteven-danz      $level = auth_aclcheck($id,$who,$info['grps']);
1171f9eb5648Ssteven-danz      if ($level >= AUTH_READ) {
1172f9eb5648Ssteven-danz        if (strcasecmp($info['mail'],$conf['notify']) != 0) {
1173f9eb5648Ssteven-danz          if (empty($emails)) {
1174f9eb5648Ssteven-danz            $emails = $info['mail'];
1175f9eb5648Ssteven-danz          } else {
1176f9eb5648Ssteven-danz            $emails = "$emails,".$info['mail'];
1177f9eb5648Ssteven-danz          }
1178f9eb5648Ssteven-danz        }
1179f9eb5648Ssteven-danz      }
1180f9eb5648Ssteven-danz    }
1181f9eb5648Ssteven-danz  }
1182f9eb5648Ssteven-danz
1183f9eb5648Ssteven-danz  return $emails;
1184f9eb5648Ssteven-danz}
1185f9eb5648Ssteven-danz
118689541d4bSAndreas Gohr/**
118789541d4bSAndreas Gohr * Removes quoting backslashes
118889541d4bSAndreas Gohr *
118989541d4bSAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
119089541d4bSAndreas Gohr */
119189541d4bSAndreas Gohrfunction unslash($string,$char="'"){
119289541d4bSAndreas Gohr  return str_replace('\\'.$char,$char,$string);
119389541d4bSAndreas Gohr}
119489541d4bSAndreas Gohr
1195340756e4Sandi//Setup VIM: ex: et ts=2 enc=utf-8 :
1196