xref: /dokuwiki/inc/common.php (revision d519720692b97f1c1705a3cce804749068fea697)
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/**
24*d5197206Schris * Wrapper around htmlspecialchars()
25*d5197206Schris *
26*d5197206Schris * @author Andreas Gohr <andi@splitbrain.org>
27*d5197206Schris * @see    htmlspecialchars()
28*d5197206Schris */
29*d5197206Schrisfunction hsc($string){
30*d5197206Schris  return htmlspecialchars($string, ENT_QUOTES, 'UTF-8');
31*d5197206Schris}
32*d5197206Schris
33*d5197206Schris/**
34*d5197206Schris * print a newline terminated string
35*d5197206Schris *
36*d5197206Schris * You can give an indention as optional parameter
37*d5197206Schris *
38*d5197206Schris * @author Andreas Gohr <andi@splitbrain.org>
39*d5197206Schris */
40*d5197206Schrisfunction ptln($string,$intend=0){
41*d5197206Schris  for($i=0; $i<$intend; $i++) print ' ';
42*d5197206Schris  print"$string\n";
43*d5197206Schris}
44*d5197206Schris
45*d5197206Schris/**
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
97652610a2Sandi  //who's the editor
98652610a2Sandi  if($REV){
99652610a2Sandi    $revinfo = getRevisionInfo($ID,$REV);
100652610a2Sandi  }else{
101652610a2Sandi    $revinfo = getRevisionInfo($ID,$info['lastmod']);
102652610a2Sandi  }
103652610a2Sandi  $info['ip']     = $revinfo['ip'];
104652610a2Sandi  $info['user']   = $revinfo['user'];
105652610a2Sandi  $info['sum']    = $revinfo['sum'];
106b6912aeaSAndreas Gohr  $info['minor']  = $revinfo['minor'];
10759f257aeSchris
10888f522e9Sandi  if($revinfo['user']){
10988f522e9Sandi    $info['editor'] = $revinfo['user'];
11088f522e9Sandi  }else{
11188f522e9Sandi    $info['editor'] = $revinfo['ip'];
11288f522e9Sandi  }
113652610a2Sandi
114ee4c4a1bSAndreas Gohr  // draft
115ee4c4a1bSAndreas Gohr  $draft = getCacheName($info['client'].$ID,'.draft');
116ee4c4a1bSAndreas Gohr  if(@file_exists($draft)){
117ee4c4a1bSAndreas Gohr    if(@filemtime($draft) < @filemtime(wikiFN($ID))){
118ee4c4a1bSAndreas Gohr      // remove stale draft
119ee4c4a1bSAndreas Gohr      @unlink($draft);
120ee4c4a1bSAndreas Gohr    }else{
121ee4c4a1bSAndreas Gohr      $info['draft'] = $draft;
122ee4c4a1bSAndreas Gohr    }
123ee4c4a1bSAndreas Gohr  }
124ee4c4a1bSAndreas Gohr
125f3f0262cSandi  return $info;
126f3f0262cSandi}
127f3f0262cSandi
128f3f0262cSandi/**
1292684e50aSAndreas Gohr * Build an string of URL parameters
1302684e50aSAndreas Gohr *
1312684e50aSAndreas Gohr * @author Andreas Gohr
1322684e50aSAndreas Gohr */
133b174aeaeSchrisfunction buildURLparams($params, $sep='&amp;'){
1342684e50aSAndreas Gohr  $url = '';
1352684e50aSAndreas Gohr  $amp = false;
1362684e50aSAndreas Gohr  foreach($params as $key => $val){
137b174aeaeSchris    if($amp) $url .= $sep;
1382684e50aSAndreas Gohr
1392684e50aSAndreas Gohr    $url .= $key.'=';
140b6c6979fSAndreas Gohr    $url .= rawurlencode($val);
1412684e50aSAndreas Gohr    $amp = true;
1422684e50aSAndreas Gohr  }
1432684e50aSAndreas Gohr  return $url;
1442684e50aSAndreas Gohr}
1452684e50aSAndreas Gohr
1462684e50aSAndreas Gohr/**
1472684e50aSAndreas Gohr * Build an string of html tag attributes
1482684e50aSAndreas Gohr *
1492684e50aSAndreas Gohr * @author Andreas Gohr
1502684e50aSAndreas Gohr */
1512684e50aSAndreas Gohrfunction buildAttributes($params){
1522684e50aSAndreas Gohr  $url = '';
1532684e50aSAndreas Gohr  foreach($params as $key => $val){
1542684e50aSAndreas Gohr    $url .= $key.'="';
1552684e50aSAndreas Gohr    $url .= htmlspecialchars ($val);
1562684e50aSAndreas Gohr    $url .= '" ';
1572684e50aSAndreas Gohr  }
1582684e50aSAndreas Gohr  return $url;
1592684e50aSAndreas Gohr}
1602684e50aSAndreas Gohr
1612684e50aSAndreas Gohr
1622684e50aSAndreas Gohr/**
1630396becbSandi * print a message
1640396becbSandi *
1650396becbSandi * If HTTP headers were not sent yet the message is added
1660396becbSandi * to the global message array else it's printed directly
1670396becbSandi * using html_msgarea()
1680396becbSandi *
169f3f0262cSandi *
170f3f0262cSandi * Levels can be:
171f3f0262cSandi *
172f3f0262cSandi * -1 error
173f3f0262cSandi *  0 info
174f3f0262cSandi *  1 success
17515fae107Sandi *
17615fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
1770396becbSandi * @see    html_msgarea
178f3f0262cSandi */
1790d58d74eSAndreas Gohrfunction msg($message,$lvl=0,$line='',$file=''){
180f3f0262cSandi  global $MSG;
181f3f0262cSandi  $errors[-1] = 'error';
182f3f0262cSandi  $errors[0]  = 'info';
183f3f0262cSandi  $errors[1]  = 'success';
184f3f0262cSandi
1850d58d74eSAndreas Gohr  if($line || $file) $message.=' ['.basename($file).':'.$line.']';
1860d58d74eSAndreas Gohr
187cc20ad51Sandi  if(!headers_sent()){
188f3f0262cSandi    if(!isset($MSG)) $MSG = array();
189f3f0262cSandi    $MSG[]=array('lvl' => $errors[$lvl], 'msg' => $message);
1900396becbSandi  }else{
1910396becbSandi    $MSG = array();
1920396becbSandi    $MSG[]=array('lvl' => $errors[$lvl], 'msg' => $message);
193f62ea8a1Sandi    if(function_exists('html_msgarea')){
1940396becbSandi      html_msgarea();
195f62ea8a1Sandi    }else{
196f62ea8a1Sandi      print "ERROR($lvl) $message";
197f62ea8a1Sandi    }
1980396becbSandi  }
199f3f0262cSandi}
200f3f0262cSandi
201f3f0262cSandi/**
20215fae107Sandi * This builds the breadcrumb trail and returns it as array
20315fae107Sandi *
20415fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
205f3f0262cSandi */
206f3f0262cSandifunction breadcrumbs(){
2078746e727Sandi  // we prepare the breadcrumbs early for quick session closing
2088746e727Sandi  static $crumbs = null;
2098746e727Sandi  if($crumbs != null) return $crumbs;
2108746e727Sandi
211f3f0262cSandi  global $ID;
212f3f0262cSandi  global $ACT;
213f3f0262cSandi  global $conf;
214f3f0262cSandi  $crumbs = $_SESSION[$conf['title']]['bc'];
215f3f0262cSandi
216f3f0262cSandi  //first visit?
217f3f0262cSandi  if (!is_array($crumbs)){
218f3f0262cSandi    $crumbs = array();
219f3f0262cSandi  }
220f3f0262cSandi  //we only save on show and existing wiki documents
221a77f5846Sjan  $file = wikiFN($ID);
222a77f5846Sjan  if($ACT != 'show' || !@file_exists($file)){
223f3f0262cSandi    $_SESSION[$conf['title']]['bc'] = $crumbs;
224f3f0262cSandi    return $crumbs;
225f3f0262cSandi  }
226a77f5846Sjan
227a77f5846Sjan  // page names
228a77f5846Sjan  $name = noNS($ID);
229a77f5846Sjan  if ($conf['useheading']) {
230a77f5846Sjan    // get page title
231bb0a59d4Sjan    $title = p_get_first_heading($ID);
232a77f5846Sjan    if ($title) {
233a77f5846Sjan      $name = $title;
234a77f5846Sjan    }
235a77f5846Sjan  }
236a77f5846Sjan
237f3f0262cSandi  //remove ID from array
238a77f5846Sjan  if (isset($crumbs[$ID])) {
239a77f5846Sjan    unset($crumbs[$ID]);
240f3f0262cSandi  }
241f3f0262cSandi
242f3f0262cSandi  //add to array
243a77f5846Sjan  $crumbs[$ID] = $name;
244f3f0262cSandi  //reduce size
245f3f0262cSandi  while(count($crumbs) > $conf['breadcrumbs']){
246f3f0262cSandi    array_shift($crumbs);
247f3f0262cSandi  }
248f3f0262cSandi  //save to session
249f3f0262cSandi  $_SESSION[$conf['title']]['bc'] = $crumbs;
250f3f0262cSandi  return $crumbs;
251f3f0262cSandi}
252f3f0262cSandi
253f3f0262cSandi/**
25415fae107Sandi * Filter for page IDs
25515fae107Sandi *
256f3f0262cSandi * This is run on a ID before it is outputted somewhere
257f3f0262cSandi * currently used to replace the colon with something else
258f3f0262cSandi * on Windows systems and to have proper URL encoding
25915fae107Sandi *
26049c713a3Sandi * Urlencoding is ommitted when the second parameter is false
26149c713a3Sandi *
26215fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
263f3f0262cSandi */
26449c713a3Sandifunction idfilter($id,$ue=true){
265f3f0262cSandi  global $conf;
266f3f0262cSandi  if ($conf['useslash'] && $conf['userewrite']){
267f3f0262cSandi    $id = strtr($id,':','/');
268f3f0262cSandi  }elseif (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' &&
269f3f0262cSandi      $conf['userewrite']) {
270f3f0262cSandi    $id = strtr($id,':',';');
271f3f0262cSandi  }
27249c713a3Sandi  if($ue){
273b6c6979fSAndreas Gohr    $id = rawurlencode($id);
274f3f0262cSandi    $id = str_replace('%3A',':',$id); //keep as colon
275f3f0262cSandi    $id = str_replace('%2F','/',$id); //keep as slash
27649c713a3Sandi  }
277f3f0262cSandi  return $id;
278f3f0262cSandi}
279f3f0262cSandi
280f3f0262cSandi/**
281ed7b5f09Sandi * This builds a link to a wikipage
28215fae107Sandi *
2836c7843b5Sandi * It handles URL rewriting and adds additional parameter if
2846c7843b5Sandi * given in $more
2856c7843b5Sandi *
28615fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
287f3f0262cSandi */
288b174aeaeSchrisfunction wl($id='',$more='',$abs=false,$sep='&amp;'){
289f3f0262cSandi  global $conf;
2906de3759aSAndreas Gohr  if(is_array($more)){
291b174aeaeSchris    $more = buildURLparams($more,$sep);
2926de3759aSAndreas Gohr  }else{
293b174aeaeSchris    $more = str_replace(',',$sep,$more);
2946de3759aSAndreas Gohr  }
295f3f0262cSandi
296f3f0262cSandi  $id    = idfilter($id);
297ed7b5f09Sandi  if($abs){
298ed7b5f09Sandi    $xlink = DOKU_URL;
299ed7b5f09Sandi  }else{
300ed7b5f09Sandi    $xlink = DOKU_BASE;
301ed7b5f09Sandi  }
302f3f0262cSandi
3036c7843b5Sandi  if($conf['userewrite'] == 2){
3046c7843b5Sandi    $xlink .= DOKU_SCRIPT.'/'.$id;
3056c7843b5Sandi    if($more) $xlink .= '?'.$more;
3066c7843b5Sandi  }elseif($conf['userewrite']){
307f3f0262cSandi    $xlink .= $id;
308f3f0262cSandi    if($more) $xlink .= '?'.$more;
3096c7843b5Sandi  }else{
3106c7843b5Sandi    $xlink .= DOKU_SCRIPT.'?id='.$id;
311b174aeaeSchris    if($more) $xlink .= $sep.$more;
312f3f0262cSandi  }
313f3f0262cSandi
314f3f0262cSandi  return $xlink;
315f3f0262cSandi}
316f3f0262cSandi
317f3f0262cSandi/**
3186de3759aSAndreas Gohr * Build a link to a media file
3196de3759aSAndreas Gohr *
3206de3759aSAndreas Gohr * Will return a link to the detail page if $direct is false
3216de3759aSAndreas Gohr */
322b174aeaeSchrisfunction ml($id='',$more='',$direct=true,$sep='&amp;'){
3236de3759aSAndreas Gohr  global $conf;
3246de3759aSAndreas Gohr  if(is_array($more)){
325b174aeaeSchris    $more = buildURLparams($more,$sep);
3266de3759aSAndreas Gohr  }else{
327b174aeaeSchris    $more = str_replace(',',$sep,$more);
3286de3759aSAndreas Gohr  }
3296de3759aSAndreas Gohr
3306de3759aSAndreas Gohr  $xlink = DOKU_BASE;
3316de3759aSAndreas Gohr
3326de3759aSAndreas Gohr  // external URLs are always direct without rewriting
3336de3759aSAndreas Gohr  if(preg_match('#^(https?|ftp)://#i',$id)){
3346de3759aSAndreas Gohr    $xlink .= 'lib/exe/fetch.php';
3356de3759aSAndreas Gohr    if($more){
3366de3759aSAndreas Gohr      $xlink .= '?'.$more;
337b174aeaeSchris      $xlink .= $sep.'media='.rawurlencode($id);
3386de3759aSAndreas Gohr    }else{
339b6c6979fSAndreas Gohr      $xlink .= '?media='.rawurlencode($id);
3406de3759aSAndreas Gohr    }
3416de3759aSAndreas Gohr    return $xlink;
3426de3759aSAndreas Gohr  }
3436de3759aSAndreas Gohr
3446de3759aSAndreas Gohr  $id = idfilter($id);
3456de3759aSAndreas Gohr
3466de3759aSAndreas Gohr  // decide on scriptname
3476de3759aSAndreas Gohr  if($direct){
3486de3759aSAndreas Gohr    if($conf['userewrite'] == 1){
3496de3759aSAndreas Gohr      $script = '_media';
3506de3759aSAndreas Gohr    }else{
3516de3759aSAndreas Gohr      $script = 'lib/exe/fetch.php';
3526de3759aSAndreas Gohr    }
3536de3759aSAndreas Gohr  }else{
3546de3759aSAndreas Gohr    if($conf['userewrite'] == 1){
3556de3759aSAndreas Gohr      $script = '_detail';
3566de3759aSAndreas Gohr    }else{
3576de3759aSAndreas Gohr      $script = 'lib/exe/detail.php';
3586de3759aSAndreas Gohr    }
3596de3759aSAndreas Gohr  }
3606de3759aSAndreas Gohr
3616de3759aSAndreas Gohr  // build URL based on rewrite mode
3626de3759aSAndreas Gohr   if($conf['userewrite']){
3636de3759aSAndreas Gohr     $xlink .= $script.'/'.$id;
3646de3759aSAndreas Gohr     if($more) $xlink .= '?'.$more;
3656de3759aSAndreas Gohr   }else{
3666de3759aSAndreas Gohr     if($more){
367a99d3236SEsther Brunner       $xlink .= $script.'?'.$more;
368b174aeaeSchris       $xlink .= $sep.'media='.$id;
3696de3759aSAndreas Gohr     }else{
370a99d3236SEsther Brunner       $xlink .= $script.'?media='.$id;
3716de3759aSAndreas Gohr     }
3726de3759aSAndreas Gohr   }
3736de3759aSAndreas Gohr
3746de3759aSAndreas Gohr  return $xlink;
3756de3759aSAndreas Gohr}
3766de3759aSAndreas Gohr
3776de3759aSAndreas Gohr
3786de3759aSAndreas Gohr
3796de3759aSAndreas Gohr/**
380f3f0262cSandi * Just builds a link to a script
38115fae107Sandi *
382ed7b5f09Sandi * @todo   maybe obsolete
38315fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
384f3f0262cSandi */
385f3f0262cSandifunction script($script='doku.php'){
386ed7b5f09Sandi#  $link = getBaseURL();
387ed7b5f09Sandi#  $link .= $script;
388ed7b5f09Sandi#  return $link;
389ed7b5f09Sandi  return DOKU_BASE.DOKU_SCRIPT;
390f3f0262cSandi}
391f3f0262cSandi
392f3f0262cSandi/**
39315fae107Sandi * Spamcheck against wordlist
39415fae107Sandi *
395f3f0262cSandi * Checks the wikitext against a list of blocked expressions
396f3f0262cSandi * returns true if the text contains any bad words
39715fae107Sandi *
39815fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
399f3f0262cSandi */
400f3f0262cSandifunction checkwordblock(){
401f3f0262cSandi  global $TEXT;
402f3f0262cSandi  global $conf;
403f3f0262cSandi
404f3f0262cSandi  if(!$conf['usewordblock']) return false;
405f3f0262cSandi
406b9ac8716Schris  $wordblocks = getWordblocks();
4073e2965d7Sandi  //how many lines to read at once (to work around some PCRE limits)
4083e2965d7Sandi  if(version_compare(phpversion(),'4.3.0','<')){
4093e2965d7Sandi    //old versions of PCRE define a maximum of parenthesises even if no
4103e2965d7Sandi    //backreferences are used - the maximum is 99
4113e2965d7Sandi    //this is very bad performancewise and may even be too high still
4123e2965d7Sandi    $chunksize = 40;
4133e2965d7Sandi  }else{
414703f6fdeSandi    //read file in chunks of 600 - this should work around the
4153e2965d7Sandi    //MAX_PATTERN_SIZE in modern PCRE
416444b87a5SAndreas Gohr    $chunksize = 400;
4173e2965d7Sandi  }
418b9ac8716Schris  while($blocks = array_splice($wordblocks,0,$chunksize)){
419f3f0262cSandi    $re = array();
420f3f0262cSandi    #build regexp from blocks
421f3f0262cSandi    foreach($blocks as $block){
422f3f0262cSandi      $block = preg_replace('/#.*$/','',$block);
423f3f0262cSandi      $block = trim($block);
424f3f0262cSandi      if(empty($block)) continue;
425f3f0262cSandi      $re[]  = $block;
426f3f0262cSandi    }
427b9ac8716Schris    if(preg_match('#('.join('|',$re).')#si',$TEXT, $match=array())) {
428b9ac8716Schris      return true;
429b9ac8716Schris    }
430703f6fdeSandi  }
431f3f0262cSandi  return false;
432f3f0262cSandi}
433f3f0262cSandi
434f3f0262cSandi/**
43515fae107Sandi * Return the IP of the client
43615fae107Sandi *
4376d8affe6SAndreas Gohr * Honours X-Forwarded-For and X-Real-IP Proxy Headers
43815fae107Sandi *
4396d8affe6SAndreas Gohr * It returns a comma separated list of IPs if the above mentioned
4406d8affe6SAndreas Gohr * headers are set. If the single parameter is set, it tries to return
4416d8affe6SAndreas Gohr * a routable public address, prefering the ones suplied in the X
4426d8affe6SAndreas Gohr * headers
4436d8affe6SAndreas Gohr *
4446d8affe6SAndreas Gohr * @param  boolean $single If set only a single IP is returned
44515fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
446f3f0262cSandi */
4476d8affe6SAndreas Gohrfunction clientIP($single=false){
4486d8affe6SAndreas Gohr  $ip = array();
4496d8affe6SAndreas Gohr  $ip[] = $_SERVER['REMOTE_ADDR'];
4506d8affe6SAndreas Gohr  if($_SERVER['HTTP_X_FORWARDED_FOR'])
4516d8affe6SAndreas Gohr    $ip = array_merge($ip,explode(',',$_SERVER['HTTP_X_FORWARDED_FOR']));
4526d8affe6SAndreas Gohr  if($_SERVER['HTTP_X_REAL_IP'])
4536d8affe6SAndreas Gohr    $ip = array_merge($ip,explode(',',$_SERVER['HTTP_X_REAL_IP']));
4546d8affe6SAndreas Gohr
4556d8affe6SAndreas Gohr  // remove any non-IP stuff
4566d8affe6SAndreas Gohr  $cnt = count($ip);
4576d8affe6SAndreas Gohr  for($i=0; $i<$cnt; $i++){
4586d8affe6SAndreas Gohr    $ip[$i] = preg_replace('/[^0-9\.]+/','',$ip[$i]);
4596d8affe6SAndreas Gohr    if(!preg_match('/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/',$ip[$i])) $ip[$i] = '';
4606d8affe6SAndreas Gohr    if(empty($ip[$i])) unset($ip[$i]);
461f3f0262cSandi  }
4626d8affe6SAndreas Gohr  $ip = array_values(array_unique($ip));
4636d8affe6SAndreas Gohr  if(!$ip[0]) $ip[0] = '0.0.0.0'; // for some strange reason we don't have a IP
4646d8affe6SAndreas Gohr
4656d8affe6SAndreas Gohr  if(!$single) return join(',',$ip);
4666d8affe6SAndreas Gohr
4676d8affe6SAndreas Gohr  // decide which IP to use, trying to avoid local addresses
4686d8affe6SAndreas Gohr  $ip = array_reverse($ip);
4696d8affe6SAndreas Gohr  foreach($ip as $i){
4706d8affe6SAndreas Gohr    if(preg_match('/^(127\.|10\.|192\.168\.|172\.((1[6-9])|(2[0-9])|(3[0-1]))\.)/',$i)){
4716d8affe6SAndreas Gohr      continue;
4726d8affe6SAndreas Gohr    }else{
4736d8affe6SAndreas Gohr      return $i;
4746d8affe6SAndreas Gohr    }
4756d8affe6SAndreas Gohr  }
4766d8affe6SAndreas Gohr  // still here? just use the first (last) address
4776d8affe6SAndreas Gohr  return $ip[0];
478f3f0262cSandi}
479f3f0262cSandi
480f3f0262cSandi/**
48115fae107Sandi * Checks if a given page is currently locked.
48215fae107Sandi *
483f3f0262cSandi * removes stale lockfiles
48415fae107Sandi *
48515fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
486f3f0262cSandi */
487f3f0262cSandifunction checklock($id){
488f3f0262cSandi  global $conf;
489f3f0262cSandi  $lock = wikiFN($id).'.lock';
490f3f0262cSandi
491f3f0262cSandi  //no lockfile
492f3f0262cSandi  if(!@file_exists($lock)) return false;
493f3f0262cSandi
494f3f0262cSandi  //lockfile expired
495f3f0262cSandi  if((time() - filemtime($lock)) > $conf['locktime']){
496f3f0262cSandi    unlink($lock);
497f3f0262cSandi    return false;
498f3f0262cSandi  }
499f3f0262cSandi
500f3f0262cSandi  //my own lock
501f3f0262cSandi  $ip = io_readFile($lock);
502f3f0262cSandi  if( ($ip == clientIP()) || ($ip == $_SERVER['REMOTE_USER']) ){
503f3f0262cSandi    return false;
504f3f0262cSandi  }
505f3f0262cSandi
506f3f0262cSandi  return $ip;
507f3f0262cSandi}
508f3f0262cSandi
509f3f0262cSandi/**
51015fae107Sandi * Lock a page for editing
51115fae107Sandi *
51215fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
513f3f0262cSandi */
514f3f0262cSandifunction lock($id){
515f3f0262cSandi  $lock = wikiFN($id).'.lock';
516f3f0262cSandi  if($_SERVER['REMOTE_USER']){
517f3f0262cSandi    io_saveFile($lock,$_SERVER['REMOTE_USER']);
518f3f0262cSandi  }else{
519f3f0262cSandi    io_saveFile($lock,clientIP());
520f3f0262cSandi  }
521f3f0262cSandi}
522f3f0262cSandi
523f3f0262cSandi/**
52415fae107Sandi * Unlock a page if it was locked by the user
525f3f0262cSandi *
52615fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
52715fae107Sandi * @return bool true if a lock was removed
528f3f0262cSandi */
529f3f0262cSandifunction unlock($id){
530f3f0262cSandi  $lock = wikiFN($id).'.lock';
531f3f0262cSandi  if(@file_exists($lock)){
532f3f0262cSandi    $ip = io_readFile($lock);
533f3f0262cSandi    if( ($ip == clientIP()) || ($ip == $_SERVER['REMOTE_USER']) ){
534f3f0262cSandi      @unlink($lock);
535f3f0262cSandi      return true;
536f3f0262cSandi    }
537f3f0262cSandi  }
538f3f0262cSandi  return false;
539f3f0262cSandi}
540f3f0262cSandi
541f3f0262cSandi/**
542f3f0262cSandi * convert line ending to unix format
543f3f0262cSandi *
54415fae107Sandi * @see    formText() for 2crlf conversion
54515fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
546f3f0262cSandi */
547f3f0262cSandifunction cleanText($text){
548f3f0262cSandi  $text = preg_replace("/(\015\012)|(\015)/","\012",$text);
549f3f0262cSandi  return $text;
550f3f0262cSandi}
551f3f0262cSandi
552f3f0262cSandi/**
553f3f0262cSandi * Prepares text for print in Webforms by encoding special chars.
554f3f0262cSandi * It also converts line endings to Windows format which is
555f3f0262cSandi * pseudo standard for webforms.
556f3f0262cSandi *
55715fae107Sandi * @see    cleanText() for 2unix conversion
55815fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
559f3f0262cSandi */
560f3f0262cSandifunction formText($text){
561f3f0262cSandi  $text = preg_replace("/\012/","\015\012",$text);
562f3f0262cSandi  return htmlspecialchars($text);
563f3f0262cSandi}
564f3f0262cSandi
565f3f0262cSandi/**
56615fae107Sandi * Returns the specified local text in raw format
56715fae107Sandi *
56815fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
569f3f0262cSandi */
570f3f0262cSandifunction rawLocale($id){
571f3f0262cSandi  return io_readFile(localeFN($id));
572f3f0262cSandi}
573f3f0262cSandi
574f3f0262cSandi/**
575f3f0262cSandi * Returns the raw WikiText
57615fae107Sandi *
57715fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
578f3f0262cSandi */
579f3f0262cSandifunction rawWiki($id,$rev=''){
580f3f0262cSandi  return io_readFile(wikiFN($id,$rev));
581f3f0262cSandi}
582f3f0262cSandi
583f3f0262cSandi/**
5847146cee2SAndreas Gohr * Returns the pagetemplate contents for the ID's namespace
5857146cee2SAndreas Gohr *
5867146cee2SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
5877146cee2SAndreas Gohr */
5887146cee2SAndreas Gohrfunction pageTemplate($id){
589a15ce62dSEsther Brunner  global $conf;
590a15ce62dSEsther Brunner  global $INFO;
591a15ce62dSEsther Brunner  $tpl = io_readFile(dirname(wikiFN($id)).'/_template.txt');
592a15ce62dSEsther Brunner  $tpl = str_replace('@ID@',$id,$tpl);
593a15ce62dSEsther Brunner  $tpl = str_replace('@NS@',getNS($id),$tpl);
594a15ce62dSEsther Brunner  $tpl = str_replace('@PAGE@',strtr(noNS($id),'_',' '),$tpl);
595a15ce62dSEsther Brunner  $tpl = str_replace('@USER@',$_SERVER['REMOTE_USER'],$tpl);
596a15ce62dSEsther Brunner  $tpl = str_replace('@NAME@',$INFO['userinfo']['name'],$tpl);
597a15ce62dSEsther Brunner  $tpl = str_replace('@MAIL@',$INFO['userinfo']['mail'],$tpl);
598a15ce62dSEsther Brunner  $tpl = str_replace('@DATE@',date($conf['dformat']),$tpl);
599a15ce62dSEsther Brunner  return $tpl;
6007146cee2SAndreas Gohr}
6017146cee2SAndreas Gohr
6027146cee2SAndreas Gohr
6037146cee2SAndreas Gohr/**
60415fae107Sandi * Returns the raw Wiki Text in three slices.
60515fae107Sandi *
60615fae107Sandi * The range parameter needs to have the form "from-to"
60715cfe303Sandi * and gives the range of the section in bytes - no
60815cfe303Sandi * UTF-8 awareness is needed.
609f3f0262cSandi * The returned order is prefix, section and suffix.
61015fae107Sandi *
61115fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
612f3f0262cSandi */
613f3f0262cSandifunction rawWikiSlices($range,$id,$rev=''){
614f3f0262cSandi  list($from,$to) = split('-',$range,2);
615f3f0262cSandi  $text = io_readFile(wikiFN($id,$rev));
616f3f0262cSandi  if(!$from) $from = 0;
617c3d8e19bSandi  if(!$to)   $to   = strlen($text)+1;
618f3f0262cSandi
61915cfe303Sandi  $slices[0] = substr($text,0,$from-1);
62015cfe303Sandi  $slices[1] = substr($text,$from-1,$to-$from);
62115cfe303Sandi  $slices[2] = substr($text,$to);
622f3f0262cSandi
623f3f0262cSandi  return $slices;
624f3f0262cSandi}
625f3f0262cSandi
626f3f0262cSandi/**
62715fae107Sandi * Joins wiki text slices
62815fae107Sandi *
629f3f0262cSandi * function to join the text slices with correct lineendings again.
630f3f0262cSandi * When the pretty parameter is set to true it adds additional empty
631f3f0262cSandi * lines between sections if needed (used on saving).
63215fae107Sandi *
63315fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
634f3f0262cSandi */
635f3f0262cSandifunction con($pre,$text,$suf,$pretty=false){
636f3f0262cSandi
637f3f0262cSandi  if($pretty){
638f3f0262cSandi    if($pre && substr($pre,-1) != "\n") $pre .= "\n";
639f3f0262cSandi    if($suf && substr($text,-1) != "\n") $text .= "\n";
640f3f0262cSandi  }
641f3f0262cSandi
642f3f0262cSandi  if($pre) $pre .= "\n";
643f3f0262cSandi  if($suf) $text .= "\n";
644f3f0262cSandi  return $pre.$text.$suf;
645f3f0262cSandi}
646f3f0262cSandi
647f3f0262cSandi/**
64815fae107Sandi * print debug messages
64915fae107Sandi *
650f3f0262cSandi * little function to print the content of a var
65115fae107Sandi *
65215fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
653f3f0262cSandi */
654f3f0262cSandifunction dbg($msg,$hidden=false){
655f3f0262cSandi  (!$hidden) ? print '<pre class="dbg">' : print "<!--\n";
656f3f0262cSandi  print_r($msg);
657f3f0262cSandi  (!$hidden) ? print '</pre>' : print "\n-->";
658f3f0262cSandi}
659f3f0262cSandi
660f3f0262cSandi/**
661f3f0262cSandi * Add's an entry to the changelog
66215fae107Sandi *
66315fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
664f3f0262cSandi */
665b6912aeaSAndreas Gohrfunction addLogEntry($date,$id,$summary='',$minor=false){
666f3f0262cSandi  global $conf;
667c1049928Sandi
668c1049928Sandi  if(!@is_writable($conf['changelog'])){
669c1049928Sandi    msg($conf['changelog'].' is not writable!',-1);
670c1049928Sandi    return;
671c1049928Sandi  }
672c1049928Sandi
673652610a2Sandi  if(!$date) $date = time(); //use current time if none supplied
674f3f0262cSandi  $remote = $_SERVER['REMOTE_ADDR'];
675f3f0262cSandi  $user   = $_SERVER['REMOTE_USER'];
676f3f0262cSandi
677b6912aeaSAndreas Gohr  if($conf['useacl'] && $user && $minor){
678b6912aeaSAndreas Gohr    $summary = '*'.$summary;
679b6912aeaSAndreas Gohr  }else{
680b6912aeaSAndreas Gohr    $summary = ' '.$summary;
681b6912aeaSAndreas Gohr  }
682b6912aeaSAndreas Gohr
683f3f0262cSandi  $logline = join("\t",array($date,$remote,$id,$user,$summary))."\n";
684dbb00abcSEsther Brunner  io_saveFile($conf['changelog'],$logline,true);
685f3f0262cSandi}
686f3f0262cSandi
687f3f0262cSandi/**
688b6912aeaSAndreas Gohr * Checks an summary entry if it was a minor edit
689b6912aeaSAndreas Gohr *
690b6912aeaSAndreas Gohr * The summary is cleaned of the marker char
691b6912aeaSAndreas Gohr *
692b6912aeaSAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
693b6912aeaSAndreas Gohr */
694b6912aeaSAndreas Gohrfunction isMinor(&$summary){
695b6912aeaSAndreas Gohr  if(substr($summary,0,1) == '*'){
696b6912aeaSAndreas Gohr    $summary = substr($summary,1);
697b6912aeaSAndreas Gohr    return true;
698b6912aeaSAndreas Gohr  }
699b6912aeaSAndreas Gohr  $summary = trim($summary);
700b6912aeaSAndreas Gohr  return false;
701b6912aeaSAndreas Gohr}
702b6912aeaSAndreas Gohr
703b6912aeaSAndreas Gohr/**
704d437bcc4SAndreas Gohr * Internal function used by getRecents
705d437bcc4SAndreas Gohr *
706d437bcc4SAndreas Gohr * don't call directly
707d437bcc4SAndreas Gohr *
708d437bcc4SAndreas Gohr * @see getRecents()
709d437bcc4SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
710d437bcc4SAndreas Gohr */
711b6912aeaSAndreas Gohrfunction _handleRecent($line,$ns,$flags){
712d437bcc4SAndreas Gohr  static $seen  = array();         //caches seen pages and skip them
713d437bcc4SAndreas Gohr  if(empty($line)) return false;   //skip empty lines
714d437bcc4SAndreas Gohr
715d437bcc4SAndreas Gohr  // split the line into parts
716d437bcc4SAndreas Gohr  list($dt,$ip,$id,$usr,$sum) = explode("\t",$line);
717d437bcc4SAndreas Gohr
718d437bcc4SAndreas Gohr  // skip seen ones
719d437bcc4SAndreas Gohr  if($seen[$id]) return false;
720b6912aeaSAndreas Gohr  $recent = array();
721b6912aeaSAndreas Gohr
722b6912aeaSAndreas Gohr  // check minors
723b6912aeaSAndreas Gohr  if(isMinor($sum)){
724b6912aeaSAndreas Gohr    // skip minors
725b6912aeaSAndreas Gohr    if($flags & RECENTS_SKIP_MINORS) return false;
726b6912aeaSAndreas Gohr    $recent['minor'] = true;
727b6912aeaSAndreas Gohr  }else{
728b6912aeaSAndreas Gohr    $recent['minor'] = false;
729b6912aeaSAndreas Gohr  }
730d437bcc4SAndreas Gohr
731d437bcc4SAndreas Gohr  // remember in seen to skip additional sights
732d437bcc4SAndreas Gohr  $seen[$id] = 1;
733d437bcc4SAndreas Gohr
7340dc92c6fSAndreas Gohr  // check if it's a hidden page
7350dc92c6fSAndreas Gohr  if(isHiddenPage($id)) return false;
7360dc92c6fSAndreas Gohr
737d437bcc4SAndreas Gohr  // filter namespace
738d437bcc4SAndreas Gohr  if (($ns) && (strpos($id,$ns.':') !== 0)) return false;
739d437bcc4SAndreas Gohr
740d437bcc4SAndreas Gohr  // exclude subnamespaces
741b6912aeaSAndreas Gohr  if (($flags & RECENTS_SKIP_SUBSPACES) && (getNS($id) != $ns)) return false;
742d437bcc4SAndreas Gohr
743ae56bfb6SAndreas Gohr  // check ACL
744ae56bfb6SAndreas Gohr  if (auth_quickaclcheck($id) < AUTH_READ) return false;
745ae56bfb6SAndreas Gohr
746d437bcc4SAndreas Gohr  // check existance
747d437bcc4SAndreas Gohr  if(!@file_exists(wikiFN($id))){
748b6912aeaSAndreas Gohr    if($flags & RECENTS_SKIP_DELETED){
749d437bcc4SAndreas Gohr      return false;
750d437bcc4SAndreas Gohr    }else{
751d437bcc4SAndreas Gohr      $recent['del'] = true;
752d437bcc4SAndreas Gohr    }
753d437bcc4SAndreas Gohr  }else{
754d437bcc4SAndreas Gohr    $recent['del'] = false;
755d437bcc4SAndreas Gohr  }
756d437bcc4SAndreas Gohr
757d437bcc4SAndreas Gohr  $recent['id']   = $id;
758d437bcc4SAndreas Gohr  $recent['date'] = $dt;
759d437bcc4SAndreas Gohr  $recent['ip']   = $ip;
760d437bcc4SAndreas Gohr  $recent['user'] = $usr;
761d437bcc4SAndreas Gohr  $recent['sum']  = $sum;
762d437bcc4SAndreas Gohr
763d437bcc4SAndreas Gohr  return $recent;
764d437bcc4SAndreas Gohr}
765d437bcc4SAndreas Gohr
766b6912aeaSAndreas Gohr
767d437bcc4SAndreas Gohr/**
768f3f0262cSandi * returns an array of recently changed files using the
769f3f0262cSandi * changelog
770d437bcc4SAndreas Gohr *
771b6912aeaSAndreas Gohr * The following constants can be used to control which changes are
772b6912aeaSAndreas Gohr * included. Add them together as needed.
773b6912aeaSAndreas Gohr *
774b6912aeaSAndreas Gohr * RECENTS_SKIP_DELETED   - don't include deleted pages
775b6912aeaSAndreas Gohr * RECENTS_SKIP_MINORS    - don't include minor changes
776b6912aeaSAndreas Gohr * RECENTS_SKIP_SUBSPACES - don't include subspaces
777b6912aeaSAndreas Gohr *
778d437bcc4SAndreas Gohr * @param int    $first   number of first entry returned (for paginating
779d437bcc4SAndreas Gohr * @param int    $num     return $num entries
780d437bcc4SAndreas Gohr * @param string $ns      restrict to given namespace
781b6912aeaSAndreas Gohr * @param bool   $flags   see above
78215fae107Sandi *
78315fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
784f3f0262cSandi */
785b6912aeaSAndreas Gohrfunction getRecents($first,$num,$ns='',$flags=0){
786f3f0262cSandi  global $conf;
787f3f0262cSandi  $recent = array();
788d437bcc4SAndreas Gohr  $count  = 0;
7895749f1ceSmatthiasgrimm
7905749f1ceSmatthiasgrimm  if(!$num)
7915749f1ceSmatthiasgrimm    return $recent;
792f3f0262cSandi
793c1049928Sandi  if(!@is_readable($conf['changelog'])){
794c1049928Sandi    msg($conf['changelog'].' is not readable',-1);
795c1049928Sandi    return $recent;
796c1049928Sandi  }
797c1049928Sandi
798d437bcc4SAndreas Gohr  $fh  = fopen($conf['changelog'],'r');
799d437bcc4SAndreas Gohr  $buf = '';
800d437bcc4SAndreas Gohr  $csz = 4096;                              //chunksize
801d437bcc4SAndreas Gohr  fseek($fh,0,SEEK_END);                    // jump to the end
802d437bcc4SAndreas Gohr  $pos = ftell($fh);                        // position pointer
803f3f0262cSandi
804d437bcc4SAndreas Gohr  // now read backwards into buffer
805d437bcc4SAndreas Gohr  while($pos > 0){
806d437bcc4SAndreas Gohr    $pos -= $csz;                           // seek to previous chunk...
807f6c1156dSWolfgang Ocker    if($pos < 0) {                          // ...or rest of file
808f6c1156dSWolfgang Ocker      $csz += $pos;
809f6c1156dSWolfgang Ocker      $pos = 0;
810f6c1156dSWolfgang Ocker    }
811f6c1156dSWolfgang Ocker
812d437bcc4SAndreas Gohr    fseek($fh,$pos);
813dbb00abcSEsther Brunner
814d437bcc4SAndreas Gohr    $buf = fread($fh,$csz).$buf;            // prepend to buffer
8158f1d587cSEsther Brunner
816d437bcc4SAndreas Gohr    $lines = explode("\n",$buf);            // split buffer into lines
8175749f1ceSmatthiasgrimm
818d437bcc4SAndreas Gohr    if($pos > 0){
819d437bcc4SAndreas Gohr      $buf = array_shift($lines);           // first one may be still incomplete
820f3f0262cSandi    }
821d437bcc4SAndreas Gohr
822d437bcc4SAndreas Gohr    $cnt = count($lines);
823d437bcc4SAndreas Gohr    if(!$cnt) continue;                     // no lines yet
824d437bcc4SAndreas Gohr
825d437bcc4SAndreas Gohr    // handle lines
826d437bcc4SAndreas Gohr    for($i = $cnt-1; $i >= 0; $i--){
827b6912aeaSAndreas Gohr      $rec = _handleRecent($lines[$i],$ns,$flags);
828d437bcc4SAndreas Gohr      if($rec !== false){
829d437bcc4SAndreas Gohr        if(--$first >= 0) continue;         // skip first entries
830d437bcc4SAndreas Gohr        $recent[] = $rec;
831d437bcc4SAndreas Gohr        $count++;
832d437bcc4SAndreas Gohr
833d437bcc4SAndreas Gohr        // break while when we have enough entries
834d437bcc4SAndreas Gohr        if($count >= $num){
835d437bcc4SAndreas Gohr          $pos = 0; // will break the while loop
836d437bcc4SAndreas Gohr          break;    // will break the for loop
837f3f0262cSandi        }
838f3f0262cSandi      }
839d437bcc4SAndreas Gohr    }
840d437bcc4SAndreas Gohr  }// end of while
841d437bcc4SAndreas Gohr
842d437bcc4SAndreas Gohr  fclose($fh);
843f3f0262cSandi  return $recent;
844f3f0262cSandi}
845f3f0262cSandi
846f3f0262cSandi/**
84751815db0SYann * Compare the logline $a to the timestamp $b
84851815db0SYann * @author Yann Hamon <yann.hamon@mandragor.org>
84951815db0SYann * @return integer 0 if the logline has timestamp $b, <0 if the timestam
85051815db0SYann *         of $a is greater than $b, >0 else.
85151815db0SYann */
85251815db0SYannfunction hasTimestamp($a, $b)
85351815db0SYann{
85451815db0SYann  if (strpos($a, $b) === 0)
85551815db0SYann    return 0;
85651815db0SYann  else
85751815db0SYann    return strcmp ($a, $b);
85851815db0SYann}
85951815db0SYann
86051815db0SYann/**
86151815db0SYann * performs a dichotomic search on an array using
86251815db0SYann * a custom compare function
86351815db0SYann *
86451815db0SYann * @author Yann Hamon <yann.hamon@mandragor.org>
86551815db0SYann */
86651815db0SYannfunction array_dichotomic_search($ar, $value, $compareFunc) {
86751815db0SYann  $value = trim($value);
86851815db0SYann  if (!$ar || !$value || !$compareFunc) return (null);
86951815db0SYann  $len = count($ar);
87051815db0SYann
87151815db0SYann  $l = 0;
87251815db0SYann  $r = $len-1;
87351815db0SYann
87451815db0SYann  do {
87551815db0SYann    $i = floor(($l+$r)/2);
87651815db0SYann    if ($compareFunc($ar[$i], $value)<0)
87751815db0SYann      $l = $i+1;
87851815db0SYann    else
87951815db0SYann     $r = $i-1;
88051815db0SYann  } while ($compareFunc($ar[$i], $value)!=0 && $l<=$r);
88151815db0SYann
88251815db0SYann  if ($compareFunc($ar[$i], $value)==0)
88351815db0SYann    return $i;
88451815db0SYann  else
88551815db0SYann    return -1;
88651815db0SYann}
88751815db0SYann
88851815db0SYann/**
889652610a2Sandi * gets additonal informations for a certain pagerevison
890652610a2Sandi * from the changelog
891652610a2Sandi *
892652610a2Sandi * @author Andreas Gohr <andi@splitbrain.org>
89351815db0SYann * @author Yann Hamon <yann.hamon@mandragor.org>
894652610a2Sandi */
895652610a2Sandifunction getRevisionInfo($id,$rev){
896652610a2Sandi  global $conf;
897258641c6Sandi
898258641c6Sandi  if(!$rev) return(null);
899258641c6Sandi
900c1049928Sandi  $info = array();
901c1049928Sandi  if(!@is_readable($conf['changelog'])){
902c1049928Sandi    msg($conf['changelog'].' is not readable',-1);
903c1049928Sandi    return $recent;
904c1049928Sandi  }
905652610a2Sandi  $loglines = file($conf['changelog']);
90651815db0SYann
90751815db0SYann  // Search for a line with a matching timestamp
90851815db0SYann  $index = array_dichotomic_search ($loglines, $rev, hasTimestamp);
90951815db0SYann  if ($index == -1)
91051815db0SYann    return;
91151815db0SYann
91251815db0SYann  // The following code is necessary when there is more than
91351815db0SYann  // one line with one same timestamp
91451815db0SYann  $loglines_matching = array();
91551815db0SYann  $loglines_matching[] =  $loglines[$index];
91651815db0SYann  for ($i=$index-1;hasTimestamp($loglines[$i], $rev) == 0; $i--)
91751815db0SYann    $loglines_matching[] = $loglines[$i];
91851815db0SYann  for ($i=$index+1;hasTimestamp($loglines[$i], $rev) == 0; $i++)
91951815db0SYann    $loglines_matching[] = $loglines[$i];
92051815db0SYann
92151815db0SYann  // Match only lines concerning the document $id
92251815db0SYann  $loglines_matching = preg_grep("/$rev\t\d+\.\d+\.\d+\.\d+\t$id\t/",$loglines_matching);
92351815db0SYann
92451815db0SYann  $loglines_matching = array_reverse($loglines_matching); //reverse sort on timestamp
92551815db0SYann  $line = split("\t",$loglines_matching[0]);
92651815db0SYann
927652610a2Sandi  $info['date']  = $line[0];
928652610a2Sandi  $info['ip']    = $line[1];
929652610a2Sandi  $info['user']  = $line[3];
930652610a2Sandi  $info['sum']   = $line[4];
931b6912aeaSAndreas Gohr  $info['minor'] = isMinor($info['sum']);
932652610a2Sandi  return $info;
933652610a2Sandi}
934652610a2Sandi
93551815db0SYann
936652610a2Sandi/**
937f3f0262cSandi * Saves a wikitext by calling io_saveFile
93815fae107Sandi *
93915fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
940f3f0262cSandi */
941b6912aeaSAndreas Gohrfunction saveWikiText($id,$text,$summary,$minor=false){
942f3f0262cSandi  global $conf;
943f3f0262cSandi  global $lang;
944f3f0262cSandi  // ignore if no changes were made
945f3f0262cSandi  if($text == rawWiki($id,'')){
946f3f0262cSandi    return;
947f3f0262cSandi  }
948f3f0262cSandi
949f3f0262cSandi  $file = wikiFN($id);
950f3f0262cSandi  $old  = saveOldRevision($id);
951f3f0262cSandi
952f3f0262cSandi  if (empty($text)){
953e1f3d9e1SEsther Brunner    // remove empty file
954f3f0262cSandi    @unlink($file);
955e1f3d9e1SEsther Brunner    // remove any meta info
956e1f3d9e1SEsther Brunner    $mfiles = metaFiles($id);
957e1f3d9e1SEsther Brunner    foreach ($mfiles as $mfile) {
958e1f3d9e1SEsther Brunner      if (file_exists($mfile)) @unlink($mfile);
959b158d625SSteven Danz    }
960f3f0262cSandi    $del = true;
9613ce054b3Sandi    // autoset summary on deletion
9623ce054b3Sandi    if(empty($summary)) $summary = $lang['deleted'];
963f864871eSAndreas Gohr    // unlock early
964f864871eSAndreas Gohr    unlock($id);
96553d6ccfeSandi    // remove empty namespaces
96653d6ccfeSandi    io_sweepNS($id);
967f3f0262cSandi  }else{
968f3f0262cSandi    // save file (datadir is created in io_saveFile)
969f3f0262cSandi    io_saveFile($file,$text);
970f3f0262cSandi    $del = false;
971f3f0262cSandi  }
972f3f0262cSandi
973b6912aeaSAndreas Gohr  addLogEntry(@filemtime($file),$id,$summary,$minor);
97426a0801fSAndreas Gohr  // send notify mails
97590033e9dSAndreas Gohr  notify($id,'admin',$old,$summary,$minor);
97690033e9dSAndreas Gohr  notify($id,'subscribers',$old,$summary,$minor);
977f3f0262cSandi
978f3f0262cSandi  //purge cache on add by updating the purgefile
979f3f0262cSandi  if($conf['purgeonadd'] && (!$old || $del)){
98098407a7aSandi    io_saveFile($conf['cachedir'].'/purgefile',time());
981f3f0262cSandi  }
982f3f0262cSandi}
983f3f0262cSandi
984f3f0262cSandi/**
985f3f0262cSandi * moves the current version to the attic and returns its
986f3f0262cSandi * revision date
98715fae107Sandi *
98815fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
989f3f0262cSandi */
990f3f0262cSandifunction saveOldRevision($id){
991f3f0262cSandi  global $conf;
992f3f0262cSandi  $oldf = wikiFN($id);
993f3f0262cSandi  if(!@file_exists($oldf)) return '';
994f3f0262cSandi  $date = filemtime($oldf);
995f3f0262cSandi  $newf = wikiFN($id,$date);
996f3f0262cSandi  if(substr($newf,-3)=='.gz'){
997f3f0262cSandi    io_saveFile($newf,rawWiki($id));
998f3f0262cSandi  }else{
999f3f0262cSandi    io_makeFileDir($newf);
1000f3f0262cSandi    copy($oldf, $newf);
1001f3f0262cSandi  }
1002f3f0262cSandi  return $date;
1003f3f0262cSandi}
1004f3f0262cSandi
1005f3f0262cSandi/**
100626a0801fSAndreas Gohr * Sends a notify mail on page change
100726a0801fSAndreas Gohr *
100826a0801fSAndreas Gohr * @param  string  $id       The changed page
100926a0801fSAndreas Gohr * @param  string  $who      Who to notify (admin|subscribers)
101026a0801fSAndreas Gohr * @param  int     $rev      Old page revision
101126a0801fSAndreas Gohr * @param  string  $summary  What changed
101290033e9dSAndreas Gohr * @param  boolean $minor    Is this a minor edit?
101315fae107Sandi *
101415fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
1015f3f0262cSandi */
101690033e9dSAndreas Gohrfunction notify($id,$who,$rev='',$summary='',$minor=false){
1017f3f0262cSandi  global $lang;
1018f3f0262cSandi  global $conf;
1019b158d625SSteven Danz
102026a0801fSAndreas Gohr  // decide if there is something to do
102126a0801fSAndreas Gohr  if($who == 'admin'){
102226a0801fSAndreas Gohr    if(empty($conf['notify'])) return; //notify enabled?
1023f3f0262cSandi    $text = rawLocale('mailtext');
102426a0801fSAndreas Gohr    $to   = $conf['notify'];
102526a0801fSAndreas Gohr    $bcc  = '';
102626a0801fSAndreas Gohr  }elseif($who == 'subscribers'){
102726a0801fSAndreas Gohr    if(!$conf['subscribers']) return; //subscribers enabled?
102890033e9dSAndreas Gohr    if($conf['useacl'] && $_SERVER['REMOTE_USER'] && $minor) return; //skip minors
102926a0801fSAndreas Gohr    $bcc  = subscriber_addresslist($id);
103026a0801fSAndreas Gohr    if(empty($bcc)) return;
103126a0801fSAndreas Gohr    $to   = '';
103226a0801fSAndreas Gohr    $text = rawLocale('subscribermail');
103326a0801fSAndreas Gohr  }else{
103426a0801fSAndreas Gohr    return; //just to be safe
103526a0801fSAndreas Gohr  }
103626a0801fSAndreas Gohr
1037f3f0262cSandi  $text = str_replace('@DATE@',date($conf['dformat']),$text);
1038f3f0262cSandi  $text = str_replace('@BROWSER@',$_SERVER['HTTP_USER_AGENT'],$text);
1039f3f0262cSandi  $text = str_replace('@IPADDRESS@',$_SERVER['REMOTE_ADDR'],$text);
1040f3f0262cSandi  $text = str_replace('@HOSTNAME@',gethostbyaddr($_SERVER['REMOTE_ADDR']),$text);
1041ed7b5f09Sandi  $text = str_replace('@NEWPAGE@',wl($id,'',true),$text);
104226a0801fSAndreas Gohr  $text = str_replace('@PAGE@',$id,$text);
104326a0801fSAndreas Gohr  $text = str_replace('@TITLE@',$conf['title'],$text);
1044ed7b5f09Sandi  $text = str_replace('@DOKUWIKIURL@',DOKU_URL,$text);
1045f3f0262cSandi  $text = str_replace('@SUMMARY@',$summary,$text);
10467a82afdcSandi  $text = str_replace('@USER@',$_SERVER['REMOTE_USER'],$text);
1047f3f0262cSandi
1048f3f0262cSandi  if($rev){
1049f3f0262cSandi    $subject = $lang['mail_changed'].' '.$id;
1050ed7b5f09Sandi    $text = str_replace('@OLDPAGE@',wl($id,"rev=$rev",true),$text);
1051ccdfa6c0SAndreas Gohr    require_once(DOKU_INC.'inc/DifferenceEngine.php');
1052f3f0262cSandi    $df  = new Diff(split("\n",rawWiki($id,$rev)),
1053f3f0262cSandi                    split("\n",rawWiki($id)));
1054f3f0262cSandi    $dformat = new UnifiedDiffFormatter();
1055f3f0262cSandi    $diff    = $dformat->format($df);
1056f3f0262cSandi  }else{
1057f3f0262cSandi    $subject=$lang['mail_newpage'].' '.$id;
1058f3f0262cSandi    $text = str_replace('@OLDPAGE@','none',$text);
1059f3f0262cSandi    $diff = rawWiki($id);
1060f3f0262cSandi  }
1061f3f0262cSandi  $text = str_replace('@DIFF@',$diff,$text);
1062241f3a36Sandi  $subject = '['.$conf['title'].'] '.$subject;
1063f3f0262cSandi
106426a0801fSAndreas Gohr  mail_send($to,$subject,$text,$conf['mailfrom'],'',$bcc);
1065f3f0262cSandi}
1066f3f0262cSandi
106715fae107Sandi/**
106815fae107Sandi * Return a list of available page revisons
106915fae107Sandi *
107015fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
107115fae107Sandi */
1072f3f0262cSandifunction getRevisions($id){
1073f3f0262cSandi  $revd = dirname(wikiFN($id,'foo'));
1074f3f0262cSandi  $revs = array();
1075f3f0262cSandi  $clid = cleanID($id);
1076f3f0262cSandi  if(strrpos($clid,':')) $clid = substr($clid,strrpos($clid,':')+1); //remove path
1077493a6929SKobaYY  $clid = utf8_encodeFN($clid);
1078f3f0262cSandi
1079f3f0262cSandi  if (is_dir($revd) && $dh = opendir($revd)) {
1080f3f0262cSandi    while (($file = readdir($dh)) !== false) {
1081f3f0262cSandi      if (is_dir($revd.'/'.$file)) continue;
1082f3f0262cSandi      if (preg_match('/^'.$clid.'\.(\d+)\.txt(\.gz)?$/',$file,$match)){
1083f3f0262cSandi        $revs[]=$match[1];
1084f3f0262cSandi      }
1085f3f0262cSandi    }
1086f3f0262cSandi    closedir($dh);
1087f3f0262cSandi  }
1088f3f0262cSandi  rsort($revs);
1089f3f0262cSandi  return $revs;
1090f3f0262cSandi}
1091f3f0262cSandi
1092f3f0262cSandi/**
1093f3f0262cSandi * extracts the query from a google referer
109415fae107Sandi *
10956b13307fSandi * @todo   should be more generic and support yahoo et al
109615fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
1097f3f0262cSandi */
1098f3f0262cSandifunction getGoogleQuery(){
1099f3f0262cSandi  $url = parse_url($_SERVER['HTTP_REFERER']);
11005c3f206fSandi  if(!$url) return '';
1101f3f0262cSandi
1102f3f0262cSandi  if(!preg_match("#google\.#i",$url['host'])) return '';
1103f3f0262cSandi  $query = array();
1104f3f0262cSandi  parse_str($url['query'],$query);
1105f3f0262cSandi
1106f3f0262cSandi  return $query['q'];
1107f3f0262cSandi}
1108f3f0262cSandi
1109f3f0262cSandi/**
111015fae107Sandi * Try to set correct locale
111115fae107Sandi *
1112095bfd5cSandi * @deprecated No longer used
111315fae107Sandi * @author     Andreas Gohr <andi@splitbrain.org>
1114f3f0262cSandi */
1115f3f0262cSandifunction setCorrectLocale(){
1116f3f0262cSandi  global $conf;
1117f3f0262cSandi  global $lang;
1118f3f0262cSandi
1119f3f0262cSandi  $enc = strtoupper($lang['encoding']);
1120f3f0262cSandi  foreach ($lang['locales'] as $loc){
1121f3f0262cSandi    //try locale
1122f3f0262cSandi    if(@setlocale(LC_ALL,$loc)) return;
1123f3f0262cSandi    //try loceale with encoding
1124f3f0262cSandi    if(@setlocale(LC_ALL,"$loc.$enc")) return;
1125f3f0262cSandi  }
1126f3f0262cSandi  //still here? try to set from environment
1127f3f0262cSandi  @setlocale(LC_ALL,"");
1128f3f0262cSandi}
1129f3f0262cSandi
1130f3f0262cSandi/**
1131f3f0262cSandi * Return the human readable size of a file
1132f3f0262cSandi *
1133f3f0262cSandi * @param       int    $size   A file size
1134f3f0262cSandi * @param       int    $dec    A number of decimal places
1135f3f0262cSandi * @author      Martin Benjamin <b.martin@cybernet.ch>
1136f3f0262cSandi * @author      Aidan Lister <aidan@php.net>
1137f3f0262cSandi * @version     1.0.0
1138f3f0262cSandi */
1139f31d5b73Sandifunction filesize_h($size, $dec = 1){
1140f3f0262cSandi  $sizes = array('B', 'KB', 'MB', 'GB');
1141f3f0262cSandi  $count = count($sizes);
1142f3f0262cSandi  $i = 0;
1143f3f0262cSandi
1144f3f0262cSandi  while ($size >= 1024 && ($i < $count - 1)) {
1145f3f0262cSandi    $size /= 1024;
1146f3f0262cSandi    $i++;
1147f3f0262cSandi  }
1148f3f0262cSandi
1149f3f0262cSandi  return round($size, $dec) . ' ' . $sizes[$i];
1150f3f0262cSandi}
1151f3f0262cSandi
115215fae107Sandi/**
115300a7b5adSEsther Brunner * return an obfuscated email address in line with $conf['mailguard'] setting
115400a7b5adSEsther Brunner *
115500a7b5adSEsther Brunner * @author Harry Fuecks <hfuecks@gmail.com>
115600a7b5adSEsther Brunner * @author Christopher Smith <chris@jalakai.co.uk>
115700a7b5adSEsther Brunner */
115800a7b5adSEsther Brunnerfunction obfuscate($email) {
115900a7b5adSEsther Brunner  global $conf;
116000a7b5adSEsther Brunner
116100a7b5adSEsther Brunner  switch ($conf['mailguard']) {
116200a7b5adSEsther Brunner    case 'visible' :
116300a7b5adSEsther Brunner      $obfuscate = array('@' => ' [at] ', '.' => ' [dot] ', '-' => ' [dash] ');
116400a7b5adSEsther Brunner      return strtr($email, $obfuscate);
116500a7b5adSEsther Brunner
116600a7b5adSEsther Brunner    case 'hex' :
116700a7b5adSEsther Brunner      $encode = '';
116800a7b5adSEsther Brunner      for ($x=0; $x < strlen($email); $x++) $encode .= '&#x' . bin2hex($email{$x}).';';
116900a7b5adSEsther Brunner      return $encode;
117000a7b5adSEsther Brunner
117100a7b5adSEsther Brunner    case 'none' :
117200a7b5adSEsther Brunner    default :
117300a7b5adSEsther Brunner      return $email;
117400a7b5adSEsther Brunner  }
117500a7b5adSEsther Brunner}
117600a7b5adSEsther Brunner
117700a7b5adSEsther Brunner/**
1178dc57ef04Sandi * Return DokuWikis version
117915fae107Sandi *
118015fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
118115fae107Sandi */
1182f31d5b73Sandifunction getVersion(){
1183f31d5b73Sandi  //import version string
1184f31d5b73Sandi  if(@file_exists('VERSION')){
1185f31d5b73Sandi    //official release
11860647ce3bSAndreas Gohr    return 'Release '.trim(io_readfile(DOKU_INC.'/VERSION'));
1187f31d5b73Sandi  }elseif(is_dir('_darcs')){
1188f31d5b73Sandi    //darcs checkout
1189f31d5b73Sandi    $inv = file('_darcs/inventory');
1190ae41559bSAndreas Gohr    $inv = preg_grep('#\*\*\d{14}[\]$]#',$inv);
1191f31d5b73Sandi    $cur = array_pop($inv);
1192f31d5b73Sandi    preg_match('#\*\*(\d{4})(\d{2})(\d{2})#',$cur,$matches);
1193f31d5b73Sandi    return 'Darcs '.$matches[1].'-'.$matches[2].'-'.$matches[3];
1194f31d5b73Sandi  }else{
1195f31d5b73Sandi    return 'snapshot?';
1196f31d5b73Sandi  }
1197f31d5b73Sandi}
1198f31d5b73Sandi
1199f31d5b73Sandi/**
1200f31d5b73Sandi * Run a few sanity checks
1201f31d5b73Sandi *
1202f31d5b73Sandi * @author Andreas Gohr <andi@splitbrain.org>
1203f31d5b73Sandi */
1204f3f0262cSandifunction check(){
1205f3f0262cSandi  global $conf;
1206f3f0262cSandi  global $INFO;
1207f3f0262cSandi
1208f31d5b73Sandi  msg('DokuWiki version: '.getVersion(),1);
1209f31d5b73Sandi
121049022a38Sandi  if(version_compare(phpversion(),'4.3.0','<')){
121149022a38Sandi    msg('Your PHP version is too old ('.phpversion().' vs. 4.3.+ recommended)',-1);
121249022a38Sandi  }elseif(version_compare(phpversion(),'4.3.10','<')){
121349022a38Sandi    msg('Consider upgrading PHP to 4.3.10 or higher for security reasons (your version: '.phpversion().')',0);
121449022a38Sandi  }else{
121549022a38Sandi    msg('PHP version '.phpversion(),1);
121649022a38Sandi  }
121749022a38Sandi
1218f3f0262cSandi  if(is_writable($conf['changelog'])){
1219f3f0262cSandi    msg('Changelog is writable',1);
1220f3f0262cSandi  }else{
1221f3f0262cSandi    msg('Changelog is not writable',-1);
1222f3f0262cSandi  }
1223f3f0262cSandi
1224f3f0262cSandi  if(is_writable($conf['datadir'])){
1225f3f0262cSandi    msg('Datadir is writable',1);
1226f3f0262cSandi  }else{
1227f3f0262cSandi    msg('Datadir is not writable',-1);
1228f3f0262cSandi  }
1229f3f0262cSandi
1230f3f0262cSandi  if(is_writable($conf['olddir'])){
1231f3f0262cSandi    msg('Attic is writable',1);
1232f3f0262cSandi  }else{
1233f3f0262cSandi    msg('Attic is not writable',-1);
1234f3f0262cSandi  }
1235f3f0262cSandi
1236f3f0262cSandi  if(is_writable($conf['mediadir'])){
1237f3f0262cSandi    msg('Mediadir is writable',1);
1238f3f0262cSandi  }else{
1239f3f0262cSandi    msg('Mediadir is not writable',-1);
1240f3f0262cSandi  }
1241f3f0262cSandi
124298407a7aSandi  if(is_writable($conf['cachedir'])){
124398407a7aSandi    msg('Cachedir is writable',1);
124498407a7aSandi  }else{
124598407a7aSandi    msg('Cachedir is not writable',-1);
124698407a7aSandi  }
124798407a7aSandi
1248e7cb32dcSAndreas Gohr  if(is_writable(DOKU_CONF.'users.auth.php')){
12498c4f28e8Sjan    msg('conf/users.auth.php is writable',1);
1250f3f0262cSandi  }else{
12518c4f28e8Sjan    msg('conf/users.auth.php is not writable',0);
1252f3f0262cSandi  }
125393a9e835Sandi
125493a9e835Sandi  if(function_exists('mb_strpos')){
125593a9e835Sandi    if(defined('UTF8_NOMBSTRING')){
125693a9e835Sandi      msg('mb_string extension is available but will not be used',0);
125793a9e835Sandi    }else{
125893a9e835Sandi      msg('mb_string extension is available and will be used',1);
125993a9e835Sandi    }
126093a9e835Sandi  }else{
126193a9e835Sandi    msg('mb_string extension not available - PHP only replacements will be used',0);
126293a9e835Sandi  }
1263f42d1c75SAndreas Gohr
1264f42d1c75SAndreas Gohr  if($conf['allowdebug']){
1265f42d1c75SAndreas Gohr    msg('Debugging support is enabled. If you don\'t need it you should set $conf[\'allowdebug\'] = 0',-1);
1266f42d1c75SAndreas Gohr  }else{
1267f42d1c75SAndreas Gohr    msg('Debugging support is disabled',1);
1268f42d1c75SAndreas Gohr  }
1269f3f0262cSandi
1270f3f0262cSandi  msg('Your current permission for this page is '.$INFO['perm'],0);
1271f3f0262cSandi
1272f3f0262cSandi  if(is_writable($INFO['filepath'])){
1273f3f0262cSandi    msg('The current page is writable by the webserver',0);
1274f3f0262cSandi  }else{
1275f3f0262cSandi    msg('The current page is not writable by the webserver',0);
1276f3f0262cSandi  }
1277f3f0262cSandi
1278f3f0262cSandi  if($INFO['writable']){
1279f3f0262cSandi    msg('The current page is writable by you',0);
1280f3f0262cSandi  }else{
1281f3f0262cSandi    msg('The current page is not writable you',0);
1282f3f0262cSandi  }
1283f3f0262cSandi}
1284340756e4Sandi
1285b158d625SSteven Danz/**
1286b158d625SSteven Danz * Let us know if a user is tracking a page
1287b158d625SSteven Danz *
12881380fc45SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
1289b158d625SSteven Danz */
12901380fc45SAndreas Gohrfunction is_subscribed($id,$uid){
12911380fc45SAndreas Gohr  $file=metaFN($id,'.mlist');
12921380fc45SAndreas Gohr  if (@file_exists($file)) {
1293b158d625SSteven Danz    $mlist = file($file);
12941380fc45SAndreas Gohr    $pos = array_search($uid."\n",$mlist);
12951380fc45SAndreas Gohr    return is_int($pos);
1296b158d625SSteven Danz  }
12971380fc45SAndreas Gohr
1298b158d625SSteven Danz  return false;
1299b158d625SSteven Danz}
1300340756e4Sandi
1301f9eb5648Ssteven-danz/**
1302f9eb5648Ssteven-danz * Return a string with the email addresses of all the
1303f9eb5648Ssteven-danz * users subscribed to a page
1304f9eb5648Ssteven-danz *
130526a0801fSAndreas Gohr * @author Steven Danz <steven-danz@kc.rr.com>
1306f9eb5648Ssteven-danz */
1307f9eb5648Ssteven-danzfunction subscriber_addresslist($id){
1308f9eb5648Ssteven-danz  global $conf;
1309cd52f92dSchris  global $auth;
1310f9eb5648Ssteven-danz
1311f9eb5648Ssteven-danz  $emails = '';
1312f9eb5648Ssteven-danz
131326a0801fSAndreas Gohr  if (!$conf['subscribers']) return;
131426a0801fSAndreas Gohr
1315f9eb5648Ssteven-danz  $mlist = array();
1316f9eb5648Ssteven-danz  $file=metaFN($id,'.mlist');
1317f9eb5648Ssteven-danz  if (file_exists($file)) {
1318f9eb5648Ssteven-danz    $mlist = file($file);
1319f9eb5648Ssteven-danz  }
1320f9eb5648Ssteven-danz  if(count($mlist) > 0) {
1321f9eb5648Ssteven-danz    foreach ($mlist as $who) {
1322f9eb5648Ssteven-danz      $who = rtrim($who);
1323cd52f92dSchris      $info = $auth->getUserData($who);
1324f9eb5648Ssteven-danz      $level = auth_aclcheck($id,$who,$info['grps']);
1325f9eb5648Ssteven-danz      if ($level >= AUTH_READ) {
1326f9eb5648Ssteven-danz        if (strcasecmp($info['mail'],$conf['notify']) != 0) {
1327f9eb5648Ssteven-danz          if (empty($emails)) {
1328f9eb5648Ssteven-danz            $emails = $info['mail'];
1329f9eb5648Ssteven-danz          } else {
1330f9eb5648Ssteven-danz            $emails = "$emails,".$info['mail'];
1331f9eb5648Ssteven-danz          }
1332f9eb5648Ssteven-danz        }
1333f9eb5648Ssteven-danz      }
1334f9eb5648Ssteven-danz    }
1335f9eb5648Ssteven-danz  }
1336f9eb5648Ssteven-danz
1337f9eb5648Ssteven-danz  return $emails;
1338f9eb5648Ssteven-danz}
1339f9eb5648Ssteven-danz
134089541d4bSAndreas Gohr/**
134189541d4bSAndreas Gohr * Removes quoting backslashes
134289541d4bSAndreas Gohr *
134389541d4bSAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
134489541d4bSAndreas Gohr */
134589541d4bSAndreas Gohrfunction unslash($string,$char="'"){
134689541d4bSAndreas Gohr  return str_replace('\\'.$char,$char,$string);
134789541d4bSAndreas Gohr}
134889541d4bSAndreas Gohr
1349340756e4Sandi//Setup VIM: ex: et ts=2 enc=utf-8 :
1350