xref: /dokuwiki/inc/common.php (revision a701424f8d6706a4fbbd1317907bc0e92b382873)
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
9ed7b5f09Sandiif(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../').'/');
10e7cb32dcSAndreas Gohrrequire_once(DOKU_CONF.'dokuwiki.php');
11ed7b5f09Sandirequire_once(DOKU_INC.'inc/io.php');
127d559c7fSBen Coburnrequire_once(DOKU_INC.'inc/changelog.php');
13ed7b5f09Sandirequire_once(DOKU_INC.'inc/utf8.php');
14ed7b5f09Sandirequire_once(DOKU_INC.'inc/mail.php');
15c112d578Sandirequire_once(DOKU_INC.'inc/parserutils.php');
16c29dc6e4SAndreas Gohrrequire_once(DOKU_INC.'inc/infoutils.php');
17f3f0262cSandi
18f3f0262cSandi/**
19b6912aeaSAndreas Gohr * These constants are used with the recents function
20b6912aeaSAndreas Gohr */
21b6912aeaSAndreas Gohrdefine('RECENTS_SKIP_DELETED',2);
22b6912aeaSAndreas Gohrdefine('RECENTS_SKIP_MINORS',4);
23b6912aeaSAndreas Gohrdefine('RECENTS_SKIP_SUBSPACES',8);
24b6912aeaSAndreas Gohr
25b6912aeaSAndreas Gohr/**
26d5197206Schris * Wrapper around htmlspecialchars()
27d5197206Schris *
28d5197206Schris * @author Andreas Gohr <andi@splitbrain.org>
29d5197206Schris * @see    htmlspecialchars()
30d5197206Schris */
31d5197206Schrisfunction hsc($string){
32d5197206Schris  return htmlspecialchars($string, ENT_QUOTES, 'UTF-8');
33d5197206Schris}
34d5197206Schris
35d5197206Schris/**
36d5197206Schris * print a newline terminated string
37d5197206Schris *
38d5197206Schris * You can give an indention as optional parameter
39d5197206Schris *
40d5197206Schris * @author Andreas Gohr <andi@splitbrain.org>
41d5197206Schris */
42d5197206Schrisfunction ptln($string,$intend=0){
43d5197206Schris  for($i=0; $i<$intend; $i++) print ' ';
4402b0b681SAndreas Gohr  echo "$string\n";
4502b0b681SAndreas Gohr}
4602b0b681SAndreas Gohr
4702b0b681SAndreas Gohr/**
4802b0b681SAndreas Gohr * strips control characters (<32) from the given string
4902b0b681SAndreas Gohr *
5002b0b681SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
5102b0b681SAndreas Gohr */
5202b0b681SAndreas Gohrfunction stripctl($string){
5302b0b681SAndreas Gohr  return preg_replace('/[\x00-\x1F]+/s','',$string);
54d5197206Schris}
55d5197206Schris
56d5197206Schris/**
5715fae107Sandi * Return info about the current document as associative
58f3f0262cSandi * array.
5915fae107Sandi *
6015fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
61f3f0262cSandi */
62f3f0262cSandifunction pageinfo(){
63f3f0262cSandi  global $ID;
64f3f0262cSandi  global $REV;
65f3f0262cSandi  global $USERINFO;
66f3f0262cSandi  global $conf;
67f3f0262cSandi
686afe8dcaSchris  // include ID & REV not redundant, as some parts of DokuWiki may temporarily change $ID, e.g. p_wiki_xhtml
696afe8dcaSchris  // FIXME ... perhaps it would be better to ensure the temporary changes weren't necessary
706afe8dcaSchris  $info['id'] = $ID;
716afe8dcaSchris  $info['rev'] = $REV;
726afe8dcaSchris
73f3f0262cSandi  if($_SERVER['REMOTE_USER']){
74f3f0262cSandi    $info['userinfo']   = $USERINFO;
75f3f0262cSandi    $info['perm']       = auth_quickaclcheck($ID);
761380fc45SAndreas Gohr    $info['subscribed'] = is_subscribed($ID,$_SERVER['REMOTE_USER']);
77ee4c4a1bSAndreas Gohr    $info['client']     = $_SERVER['REMOTE_USER'];
7817ee7f66SAndreas Gohr
7917ee7f66SAndreas Gohr    // if some outside auth were used only REMOTE_USER is set
8017ee7f66SAndreas Gohr    if(!$info['userinfo']['name']){
8117ee7f66SAndreas Gohr      $info['userinfo']['name'] = $_SERVER['REMOTE_USER'];
8217ee7f66SAndreas Gohr    }
83ee4c4a1bSAndreas Gohr
84f3f0262cSandi  }else{
85f3f0262cSandi    $info['perm']       = auth_aclcheck($ID,'',null);
861380fc45SAndreas Gohr    $info['subscribed'] = false;
87ee4c4a1bSAndreas Gohr    $info['client']     = clientIP(true);
88f3f0262cSandi  }
89f3f0262cSandi
90f3f0262cSandi  $info['namespace'] = getNS($ID);
91f3f0262cSandi  $info['locked']    = checklock($ID);
92f3f0262cSandi  $info['filepath']  = realpath(wikiFN($ID,$REV));
93f3f0262cSandi  $info['exists']    = @file_exists($info['filepath']);
94f3f0262cSandi  if($REV && !$info['exists']){
95f3f0262cSandi    //check if current revision was meant
96f3f0262cSandi    $cur = wikiFN($ID);
97f3f0262cSandi    if(@file_exists($cur) && (@filemtime($cur) == $REV)){
98f3f0262cSandi      $info['filepath'] = realpath($cur);
99f3f0262cSandi      $info['exists']   = true;
100f3f0262cSandi      $REV = '';
101f3f0262cSandi    }
102f3f0262cSandi  }
103c112d578Sandi  $info['rev'] = $REV;
104f3f0262cSandi  if($info['exists']){
105f3f0262cSandi    $info['writable'] = (is_writable($info['filepath']) &&
106f3f0262cSandi                         ($info['perm'] >= AUTH_EDIT));
107f3f0262cSandi  }else{
108f3f0262cSandi    $info['writable'] = ($info['perm'] >= AUTH_CREATE);
109f3f0262cSandi  }
110f3f0262cSandi  $info['editable']  = ($info['writable'] && empty($info['lock']));
111f3f0262cSandi  $info['lastmod']   = @filemtime($info['filepath']);
112f3f0262cSandi
11371726d78SBen Coburn  //load page meta data
11471726d78SBen Coburn  $info['meta'] = p_get_metadata($ID);
11571726d78SBen Coburn
116652610a2Sandi  //who's the editor
117652610a2Sandi  if($REV){
11871726d78SBen Coburn    $revinfo = getRevisionInfo($ID, $REV, 1024);
119652610a2Sandi  }else{
120bb4866bdSchris    $revinfo = isset($info['meta']['last_change']) ? $info['meta']['last_change'] : getRevisionInfo($ID,$info['lastmod'],1024);
121652610a2Sandi  }
122bb4866bdSchris
123652610a2Sandi  $info['ip']     = $revinfo['ip'];
124652610a2Sandi  $info['user']   = $revinfo['user'];
125652610a2Sandi  $info['sum']    = $revinfo['sum'];
12671726d78SBen Coburn  // See also $INFO['meta']['last_change'] which is the most recent log line for page $ID.
12771726d78SBen Coburn  // Use $INFO['meta']['last_change']['type']==='e' in place of $info['minor'].
12859f257aeSchris
12988f522e9Sandi  if($revinfo['user']){
13088f522e9Sandi    $info['editor'] = $revinfo['user'];
13188f522e9Sandi  }else{
13288f522e9Sandi    $info['editor'] = $revinfo['ip'];
13388f522e9Sandi  }
134652610a2Sandi
135ee4c4a1bSAndreas Gohr  // draft
136ee4c4a1bSAndreas Gohr  $draft = getCacheName($info['client'].$ID,'.draft');
137ee4c4a1bSAndreas Gohr  if(@file_exists($draft)){
138ee4c4a1bSAndreas Gohr    if(@filemtime($draft) < @filemtime(wikiFN($ID))){
139ee4c4a1bSAndreas Gohr      // remove stale draft
140ee4c4a1bSAndreas Gohr      @unlink($draft);
141ee4c4a1bSAndreas Gohr    }else{
142ee4c4a1bSAndreas Gohr      $info['draft'] = $draft;
143ee4c4a1bSAndreas Gohr    }
144ee4c4a1bSAndreas Gohr  }
145ee4c4a1bSAndreas Gohr
146f3f0262cSandi  return $info;
147f3f0262cSandi}
148f3f0262cSandi
149f3f0262cSandi/**
1502684e50aSAndreas Gohr * Build an string of URL parameters
1512684e50aSAndreas Gohr *
1522684e50aSAndreas Gohr * @author Andreas Gohr
1532684e50aSAndreas Gohr */
154b174aeaeSchrisfunction buildURLparams($params, $sep='&amp;'){
1552684e50aSAndreas Gohr  $url = '';
1562684e50aSAndreas Gohr  $amp = false;
1572684e50aSAndreas Gohr  foreach($params as $key => $val){
158b174aeaeSchris    if($amp) $url .= $sep;
1592684e50aSAndreas Gohr
1602684e50aSAndreas Gohr    $url .= $key.'=';
161b6c6979fSAndreas Gohr    $url .= rawurlencode($val);
1622684e50aSAndreas Gohr    $amp = true;
1632684e50aSAndreas Gohr  }
1642684e50aSAndreas Gohr  return $url;
1652684e50aSAndreas Gohr}
1662684e50aSAndreas Gohr
1672684e50aSAndreas Gohr/**
1682684e50aSAndreas Gohr * Build an string of html tag attributes
1692684e50aSAndreas Gohr *
1707bff22c0SAndreas Gohr * Skips keys starting with '_', values get HTML encoded
1717bff22c0SAndreas Gohr *
1722684e50aSAndreas Gohr * @author Andreas Gohr
1732684e50aSAndreas Gohr */
1742684e50aSAndreas Gohrfunction buildAttributes($params){
1752684e50aSAndreas Gohr  $url = '';
1762684e50aSAndreas Gohr  foreach($params as $key => $val){
1777bff22c0SAndreas Gohr    if($key{0} == '_') continue;
1787bff22c0SAndreas Gohr
1792684e50aSAndreas Gohr    $url .= $key.'="';
1802684e50aSAndreas Gohr    $url .= htmlspecialchars ($val);
1812684e50aSAndreas Gohr    $url .= '" ';
1822684e50aSAndreas Gohr  }
1832684e50aSAndreas Gohr  return $url;
1842684e50aSAndreas Gohr}
1852684e50aSAndreas Gohr
1862684e50aSAndreas Gohr
1872684e50aSAndreas Gohr/**
18815fae107Sandi * This builds the breadcrumb trail and returns it as array
18915fae107Sandi *
19015fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
191f3f0262cSandi */
192f3f0262cSandifunction breadcrumbs(){
1938746e727Sandi  // we prepare the breadcrumbs early for quick session closing
1948746e727Sandi  static $crumbs = null;
1958746e727Sandi  if($crumbs != null) return $crumbs;
1968746e727Sandi
197f3f0262cSandi  global $ID;
198f3f0262cSandi  global $ACT;
199f3f0262cSandi  global $conf;
200e71ce681SAndreas Gohr  $crumbs = $_SESSION[DOKU_COOKIE]['bc'];
201f3f0262cSandi
202f3f0262cSandi  //first visit?
203f3f0262cSandi  if (!is_array($crumbs)){
204f3f0262cSandi    $crumbs = array();
205f3f0262cSandi  }
206f3f0262cSandi  //we only save on show and existing wiki documents
207a77f5846Sjan  $file = wikiFN($ID);
208a77f5846Sjan  if($ACT != 'show' || !@file_exists($file)){
209e71ce681SAndreas Gohr    $_SESSION[DOKU_COOKIE]['bc'] = $crumbs;
210f3f0262cSandi    return $crumbs;
211f3f0262cSandi  }
212a77f5846Sjan
213a77f5846Sjan  // page names
214a77f5846Sjan  $name = noNS($ID);
215a77f5846Sjan  if ($conf['useheading']) {
216a77f5846Sjan    // get page title
217bb0a59d4Sjan    $title = p_get_first_heading($ID);
218a77f5846Sjan    if ($title) {
219a77f5846Sjan      $name = $title;
220a77f5846Sjan    }
221a77f5846Sjan  }
222a77f5846Sjan
223f3f0262cSandi  //remove ID from array
224a77f5846Sjan  if (isset($crumbs[$ID])) {
225a77f5846Sjan    unset($crumbs[$ID]);
226f3f0262cSandi  }
227f3f0262cSandi
228f3f0262cSandi  //add to array
229a77f5846Sjan  $crumbs[$ID] = $name;
230f3f0262cSandi  //reduce size
231f3f0262cSandi  while(count($crumbs) > $conf['breadcrumbs']){
232f3f0262cSandi    array_shift($crumbs);
233f3f0262cSandi  }
234f3f0262cSandi  //save to session
235e71ce681SAndreas Gohr  $_SESSION[DOKU_COOKIE]['bc'] = $crumbs;
236f3f0262cSandi  return $crumbs;
237f3f0262cSandi}
238f3f0262cSandi
239f3f0262cSandi/**
24015fae107Sandi * Filter for page IDs
24115fae107Sandi *
242f3f0262cSandi * This is run on a ID before it is outputted somewhere
243f3f0262cSandi * currently used to replace the colon with something else
244f3f0262cSandi * on Windows systems and to have proper URL encoding
24515fae107Sandi *
24649c713a3Sandi * Urlencoding is ommitted when the second parameter is false
24749c713a3Sandi *
24815fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
249f3f0262cSandi */
25049c713a3Sandifunction idfilter($id,$ue=true){
251f3f0262cSandi  global $conf;
252f3f0262cSandi  if ($conf['useslash'] && $conf['userewrite']){
253f3f0262cSandi    $id = strtr($id,':','/');
254f3f0262cSandi  }elseif (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' &&
255f3f0262cSandi      $conf['userewrite']) {
256f3f0262cSandi    $id = strtr($id,':',';');
257f3f0262cSandi  }
25849c713a3Sandi  if($ue){
259b6c6979fSAndreas Gohr    $id = rawurlencode($id);
260f3f0262cSandi    $id = str_replace('%3A',':',$id); //keep as colon
261f3f0262cSandi    $id = str_replace('%2F','/',$id); //keep as slash
26249c713a3Sandi  }
263f3f0262cSandi  return $id;
264f3f0262cSandi}
265f3f0262cSandi
266f3f0262cSandi/**
267ed7b5f09Sandi * This builds a link to a wikipage
26815fae107Sandi *
2696c7843b5Sandi * It handles URL rewriting and adds additional parameter if
2706c7843b5Sandi * given in $more
2716c7843b5Sandi *
27215fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
273f3f0262cSandi */
274b174aeaeSchrisfunction wl($id='',$more='',$abs=false,$sep='&amp;'){
275f3f0262cSandi  global $conf;
2766de3759aSAndreas Gohr  if(is_array($more)){
277b174aeaeSchris    $more = buildURLparams($more,$sep);
2786de3759aSAndreas Gohr  }else{
279b174aeaeSchris    $more = str_replace(',',$sep,$more);
2806de3759aSAndreas Gohr  }
281f3f0262cSandi
282f3f0262cSandi  $id    = idfilter($id);
283ed7b5f09Sandi  if($abs){
284ed7b5f09Sandi    $xlink = DOKU_URL;
285ed7b5f09Sandi  }else{
286ed7b5f09Sandi    $xlink = DOKU_BASE;
287ed7b5f09Sandi  }
288f3f0262cSandi
2896c7843b5Sandi  if($conf['userewrite'] == 2){
2906c7843b5Sandi    $xlink .= DOKU_SCRIPT.'/'.$id;
2916c7843b5Sandi    if($more) $xlink .= '?'.$more;
2926c7843b5Sandi  }elseif($conf['userewrite']){
293f3f0262cSandi    $xlink .= $id;
294f3f0262cSandi    if($more) $xlink .= '?'.$more;
2956c7843b5Sandi  }else{
2966c7843b5Sandi    $xlink .= DOKU_SCRIPT.'?id='.$id;
297b174aeaeSchris    if($more) $xlink .= $sep.$more;
298f3f0262cSandi  }
299f3f0262cSandi
300f3f0262cSandi  return $xlink;
301f3f0262cSandi}
302f3f0262cSandi
303f3f0262cSandi/**
304f5c2808fSBen Coburn * This builds a link to an alternate page format
305f5c2808fSBen Coburn *
306f5c2808fSBen Coburn * Handles URL rewriting if enabled. Follows the style of wl().
307f5c2808fSBen Coburn *
308f5c2808fSBen Coburn * @author Ben Coburn <btcoburn@silicodon.net>
309f5c2808fSBen Coburn */
310f5c2808fSBen Coburnfunction exportlink($id='',$format='raw',$more='',$abs=false,$sep='&amp;'){
311f5c2808fSBen Coburn  global $conf;
312f5c2808fSBen Coburn  if(is_array($more)){
313f5c2808fSBen Coburn    $more = buildURLparams($more,$sep);
314f5c2808fSBen Coburn  }else{
315f5c2808fSBen Coburn    $more = str_replace(',',$sep,$more);
316f5c2808fSBen Coburn  }
317f5c2808fSBen Coburn
318f5c2808fSBen Coburn  $format = rawurlencode($format);
319f5c2808fSBen Coburn  $id = idfilter($id);
320f5c2808fSBen Coburn  if($abs){
321f5c2808fSBen Coburn    $xlink = DOKU_URL;
322f5c2808fSBen Coburn  }else{
323f5c2808fSBen Coburn    $xlink = DOKU_BASE;
324f5c2808fSBen Coburn  }
325f5c2808fSBen Coburn
326f5c2808fSBen Coburn  if($conf['userewrite'] == 2){
327f5c2808fSBen Coburn    $xlink .= DOKU_SCRIPT.'/'.$id.'?do=export_'.$format;
328f5c2808fSBen Coburn    if($more) $xlink .= $sep.$more;
329f5c2808fSBen Coburn  }elseif($conf['userewrite'] == 1){
330f5c2808fSBen Coburn    $xlink .= '_export/'.$format.'/'.$id;
331f5c2808fSBen Coburn    if($more) $xlink .= '?'.$more;
332f5c2808fSBen Coburn  }else{
333f5c2808fSBen Coburn    $xlink .= DOKU_SCRIPT.'?do=export_'.$format.$sep.'id='.$id;
334f5c2808fSBen Coburn    if($more) $xlink .= $sep.$more;
335f5c2808fSBen Coburn  }
336f5c2808fSBen Coburn
337f5c2808fSBen Coburn  return $xlink;
338f5c2808fSBen Coburn}
339f5c2808fSBen Coburn
340f5c2808fSBen Coburn/**
3416de3759aSAndreas Gohr * Build a link to a media file
3426de3759aSAndreas Gohr *
3436de3759aSAndreas Gohr * Will return a link to the detail page if $direct is false
3446de3759aSAndreas Gohr */
345b174aeaeSchrisfunction ml($id='',$more='',$direct=true,$sep='&amp;'){
3466de3759aSAndreas Gohr  global $conf;
3476de3759aSAndreas Gohr  if(is_array($more)){
348b174aeaeSchris    $more = buildURLparams($more,$sep);
3496de3759aSAndreas Gohr  }else{
350b174aeaeSchris    $more = str_replace(',',$sep,$more);
3516de3759aSAndreas Gohr  }
3526de3759aSAndreas Gohr
3536de3759aSAndreas Gohr  $xlink = DOKU_BASE;
3546de3759aSAndreas Gohr
3556de3759aSAndreas Gohr  // external URLs are always direct without rewriting
3566de3759aSAndreas Gohr  if(preg_match('#^(https?|ftp)://#i',$id)){
3576de3759aSAndreas Gohr    $xlink .= 'lib/exe/fetch.php';
3586de3759aSAndreas Gohr    if($more){
3596de3759aSAndreas Gohr      $xlink .= '?'.$more;
360b174aeaeSchris      $xlink .= $sep.'media='.rawurlencode($id);
3616de3759aSAndreas Gohr    }else{
362b6c6979fSAndreas Gohr      $xlink .= '?media='.rawurlencode($id);
3636de3759aSAndreas Gohr    }
3646de3759aSAndreas Gohr    return $xlink;
3656de3759aSAndreas Gohr  }
3666de3759aSAndreas Gohr
3676de3759aSAndreas Gohr  $id = idfilter($id);
3686de3759aSAndreas Gohr
3696de3759aSAndreas Gohr  // decide on scriptname
3706de3759aSAndreas Gohr  if($direct){
3716de3759aSAndreas Gohr    if($conf['userewrite'] == 1){
3726de3759aSAndreas Gohr      $script = '_media';
3736de3759aSAndreas Gohr    }else{
3746de3759aSAndreas Gohr      $script = 'lib/exe/fetch.php';
3756de3759aSAndreas Gohr    }
3766de3759aSAndreas Gohr  }else{
3776de3759aSAndreas Gohr    if($conf['userewrite'] == 1){
3786de3759aSAndreas Gohr      $script = '_detail';
3796de3759aSAndreas Gohr    }else{
3806de3759aSAndreas Gohr      $script = 'lib/exe/detail.php';
3816de3759aSAndreas Gohr    }
3826de3759aSAndreas Gohr  }
3836de3759aSAndreas Gohr
3846de3759aSAndreas Gohr  // build URL based on rewrite mode
3856de3759aSAndreas Gohr   if($conf['userewrite']){
3866de3759aSAndreas Gohr     $xlink .= $script.'/'.$id;
3876de3759aSAndreas Gohr     if($more) $xlink .= '?'.$more;
3886de3759aSAndreas Gohr   }else{
3896de3759aSAndreas Gohr     if($more){
390a99d3236SEsther Brunner       $xlink .= $script.'?'.$more;
391b174aeaeSchris       $xlink .= $sep.'media='.$id;
3926de3759aSAndreas Gohr     }else{
393a99d3236SEsther Brunner       $xlink .= $script.'?media='.$id;
3946de3759aSAndreas Gohr     }
3956de3759aSAndreas Gohr   }
3966de3759aSAndreas Gohr
3976de3759aSAndreas Gohr  return $xlink;
3986de3759aSAndreas Gohr}
3996de3759aSAndreas Gohr
4006de3759aSAndreas Gohr
4016de3759aSAndreas Gohr
4026de3759aSAndreas Gohr/**
403f3f0262cSandi * Just builds a link to a script
40415fae107Sandi *
405ed7b5f09Sandi * @todo   maybe obsolete
40615fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
407f3f0262cSandi */
408f3f0262cSandifunction script($script='doku.php'){
409ed7b5f09Sandi#  $link = getBaseURL();
410ed7b5f09Sandi#  $link .= $script;
411ed7b5f09Sandi#  return $link;
412ed7b5f09Sandi  return DOKU_BASE.DOKU_SCRIPT;
413f3f0262cSandi}
414f3f0262cSandi
415f3f0262cSandi/**
41615fae107Sandi * Spamcheck against wordlist
41715fae107Sandi *
418f3f0262cSandi * Checks the wikitext against a list of blocked expressions
419f3f0262cSandi * returns true if the text contains any bad words
42015fae107Sandi *
42115fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
422f3f0262cSandi */
423f3f0262cSandifunction checkwordblock(){
424f3f0262cSandi  global $TEXT;
425f3f0262cSandi  global $conf;
426f3f0262cSandi
427f3f0262cSandi  if(!$conf['usewordblock']) return false;
428f3f0262cSandi
429041d1964SAndreas Gohr  // we prepare the text a tiny bit to prevent spammers circumventing URL checks
430041d1964SAndreas Gohr  $text = preg_replace('!(\b)(www\.[\w.:?\-;,]+?\.[\w.:?\-;,]+?[\w/\#~:.?+=&%@\!\-.:?\-;,]+?)([.:?\-;,]*[^\w/\#~:.?+=&%@\!\-.:?\-;,])!i','\1http://\2 \2\3',$TEXT);
431041d1964SAndreas Gohr
432b9ac8716Schris  $wordblocks = getWordblocks();
4333e2965d7Sandi  //how many lines to read at once (to work around some PCRE limits)
4343e2965d7Sandi  if(version_compare(phpversion(),'4.3.0','<')){
4353e2965d7Sandi    //old versions of PCRE define a maximum of parenthesises even if no
4363e2965d7Sandi    //backreferences are used - the maximum is 99
4373e2965d7Sandi    //this is very bad performancewise and may even be too high still
4383e2965d7Sandi    $chunksize = 40;
4393e2965d7Sandi  }else{
440a51d08efSAndreas Gohr    //read file in chunks of 200 - this should work around the
4413e2965d7Sandi    //MAX_PATTERN_SIZE in modern PCRE
442a51d08efSAndreas Gohr    $chunksize = 200;
4433e2965d7Sandi  }
444b9ac8716Schris  while($blocks = array_splice($wordblocks,0,$chunksize)){
445f3f0262cSandi    $re = array();
446f3f0262cSandi    #build regexp from blocks
447f3f0262cSandi    foreach($blocks as $block){
448f3f0262cSandi      $block = preg_replace('/#.*$/','',$block);
449f3f0262cSandi      $block = trim($block);
450f3f0262cSandi      if(empty($block)) continue;
451f3f0262cSandi      $re[]  = $block;
452f3f0262cSandi    }
453041d1964SAndreas Gohr    if(preg_match('#('.join('|',$re).')#si',$text, $match=array())) {
454b9ac8716Schris      return true;
455b9ac8716Schris    }
456703f6fdeSandi  }
457f3f0262cSandi  return false;
458f3f0262cSandi}
459f3f0262cSandi
460f3f0262cSandi/**
46115fae107Sandi * Return the IP of the client
46215fae107Sandi *
4636d8affe6SAndreas Gohr * Honours X-Forwarded-For and X-Real-IP Proxy Headers
46415fae107Sandi *
4656d8affe6SAndreas Gohr * It returns a comma separated list of IPs if the above mentioned
4666d8affe6SAndreas Gohr * headers are set. If the single parameter is set, it tries to return
4676d8affe6SAndreas Gohr * a routable public address, prefering the ones suplied in the X
4686d8affe6SAndreas Gohr * headers
4696d8affe6SAndreas Gohr *
4706d8affe6SAndreas Gohr * @param  boolean $single If set only a single IP is returned
47115fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
472f3f0262cSandi */
4736d8affe6SAndreas Gohrfunction clientIP($single=false){
4746d8affe6SAndreas Gohr  $ip = array();
4756d8affe6SAndreas Gohr  $ip[] = $_SERVER['REMOTE_ADDR'];
476bb4866bdSchris  if(!empty($_SERVER['HTTP_X_FORWARDED_FOR']))
4776d8affe6SAndreas Gohr    $ip = array_merge($ip,explode(',',$_SERVER['HTTP_X_FORWARDED_FOR']));
478bb4866bdSchris  if(!empty($_SERVER['HTTP_X_REAL_IP']))
4796d8affe6SAndreas Gohr    $ip = array_merge($ip,explode(',',$_SERVER['HTTP_X_REAL_IP']));
4806d8affe6SAndreas Gohr
4816d8affe6SAndreas Gohr  // remove any non-IP stuff
4826d8affe6SAndreas Gohr  $cnt = count($ip);
4834ff28443Schris  $match = array();
4846d8affe6SAndreas Gohr  for($i=0; $i<$cnt; $i++){
4854ff28443Schris    if(preg_match('/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/',$ip[$i],$match)) {
4864ff28443Schris      $ip[$i] = $match[0];
4874ff28443Schris    } else {
4884ff28443Schris      $ip[$i] = '';
4894ff28443Schris    }
4906d8affe6SAndreas Gohr    if(empty($ip[$i])) unset($ip[$i]);
491f3f0262cSandi  }
4926d8affe6SAndreas Gohr  $ip = array_values(array_unique($ip));
4936d8affe6SAndreas Gohr  if(!$ip[0]) $ip[0] = '0.0.0.0'; // for some strange reason we don't have a IP
4946d8affe6SAndreas Gohr
4956d8affe6SAndreas Gohr  if(!$single) return join(',',$ip);
4966d8affe6SAndreas Gohr
4976d8affe6SAndreas Gohr  // decide which IP to use, trying to avoid local addresses
4986d8affe6SAndreas Gohr  $ip = array_reverse($ip);
4996d8affe6SAndreas Gohr  foreach($ip as $i){
5006d8affe6SAndreas Gohr    if(preg_match('/^(127\.|10\.|192\.168\.|172\.((1[6-9])|(2[0-9])|(3[0-1]))\.)/',$i)){
5016d8affe6SAndreas Gohr      continue;
5026d8affe6SAndreas Gohr    }else{
5036d8affe6SAndreas Gohr      return $i;
5046d8affe6SAndreas Gohr    }
5056d8affe6SAndreas Gohr  }
5066d8affe6SAndreas Gohr  // still here? just use the first (last) address
5076d8affe6SAndreas Gohr  return $ip[0];
508f3f0262cSandi}
509f3f0262cSandi
510f3f0262cSandi/**
51115fae107Sandi * Checks if a given page is currently locked.
51215fae107Sandi *
513f3f0262cSandi * removes stale lockfiles
51415fae107Sandi *
51515fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
516f3f0262cSandi */
517f3f0262cSandifunction checklock($id){
518f3f0262cSandi  global $conf;
519c9b4bd1eSBen Coburn  $lock = wikiLockFN($id);
520f3f0262cSandi
521f3f0262cSandi  //no lockfile
522f3f0262cSandi  if(!@file_exists($lock)) return false;
523f3f0262cSandi
524f3f0262cSandi  //lockfile expired
525f3f0262cSandi  if((time() - filemtime($lock)) > $conf['locktime']){
526d8186216SBen Coburn    @unlink($lock);
527f3f0262cSandi    return false;
528f3f0262cSandi  }
529f3f0262cSandi
530f3f0262cSandi  //my own lock
531f3f0262cSandi  $ip = io_readFile($lock);
532f3f0262cSandi  if( ($ip == clientIP()) || ($ip == $_SERVER['REMOTE_USER']) ){
533f3f0262cSandi    return false;
534f3f0262cSandi  }
535f3f0262cSandi
536f3f0262cSandi  return $ip;
537f3f0262cSandi}
538f3f0262cSandi
539f3f0262cSandi/**
54015fae107Sandi * Lock a page for editing
54115fae107Sandi *
54215fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
543f3f0262cSandi */
544f3f0262cSandifunction lock($id){
545c9b4bd1eSBen Coburn  $lock = wikiLockFN($id);
546f3f0262cSandi  if($_SERVER['REMOTE_USER']){
547f3f0262cSandi    io_saveFile($lock,$_SERVER['REMOTE_USER']);
548f3f0262cSandi  }else{
549f3f0262cSandi    io_saveFile($lock,clientIP());
550f3f0262cSandi  }
551f3f0262cSandi}
552f3f0262cSandi
553f3f0262cSandi/**
55415fae107Sandi * Unlock a page if it was locked by the user
555f3f0262cSandi *
55615fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
55715fae107Sandi * @return bool true if a lock was removed
558f3f0262cSandi */
559f3f0262cSandifunction unlock($id){
560c9b4bd1eSBen Coburn  $lock = wikiLockFN($id);
561f3f0262cSandi  if(@file_exists($lock)){
562f3f0262cSandi    $ip = io_readFile($lock);
563f3f0262cSandi    if( ($ip == clientIP()) || ($ip == $_SERVER['REMOTE_USER']) ){
564f3f0262cSandi      @unlink($lock);
565f3f0262cSandi      return true;
566f3f0262cSandi    }
567f3f0262cSandi  }
568f3f0262cSandi  return false;
569f3f0262cSandi}
570f3f0262cSandi
571f3f0262cSandi/**
572f3f0262cSandi * convert line ending to unix format
573f3f0262cSandi *
57415fae107Sandi * @see    formText() for 2crlf conversion
57515fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
576f3f0262cSandi */
577f3f0262cSandifunction cleanText($text){
578f3f0262cSandi  $text = preg_replace("/(\015\012)|(\015)/","\012",$text);
579f3f0262cSandi  return $text;
580f3f0262cSandi}
581f3f0262cSandi
582f3f0262cSandi/**
583f3f0262cSandi * Prepares text for print in Webforms by encoding special chars.
584f3f0262cSandi * It also converts line endings to Windows format which is
585f3f0262cSandi * pseudo standard for webforms.
586f3f0262cSandi *
58715fae107Sandi * @see    cleanText() for 2unix conversion
58815fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
589f3f0262cSandi */
590f3f0262cSandifunction formText($text){
591f3f0262cSandi  $text = preg_replace("/\012/","\015\012",$text);
592f3f0262cSandi  return htmlspecialchars($text);
593f3f0262cSandi}
594f3f0262cSandi
595f3f0262cSandi/**
59615fae107Sandi * Returns the specified local text in raw format
59715fae107Sandi *
59815fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
599f3f0262cSandi */
600f3f0262cSandifunction rawLocale($id){
601f3f0262cSandi  return io_readFile(localeFN($id));
602f3f0262cSandi}
603f3f0262cSandi
604f3f0262cSandi/**
605f3f0262cSandi * Returns the raw WikiText
60615fae107Sandi *
60715fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
608f3f0262cSandi */
609f3f0262cSandifunction rawWiki($id,$rev=''){
610cc7d0c94SBen Coburn  return io_readWikiPage(wikiFN($id, $rev), $id, $rev);
611f3f0262cSandi}
612f3f0262cSandi
613f3f0262cSandi/**
6147146cee2SAndreas Gohr * Returns the pagetemplate contents for the ID's namespace
6157146cee2SAndreas Gohr *
6167146cee2SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
6177146cee2SAndreas Gohr */
618b7d5a5f0SAndreas Gohrfunction pageTemplate($data){
619b7d5a5f0SAndreas Gohr  $id = $data[0];
620a15ce62dSEsther Brunner  global $conf;
621a15ce62dSEsther Brunner  global $INFO;
622a15ce62dSEsther Brunner  $tpl = io_readFile(dirname(wikiFN($id)).'/_template.txt');
623a15ce62dSEsther Brunner  $tpl = str_replace('@ID@',$id,$tpl);
624a15ce62dSEsther Brunner  $tpl = str_replace('@NS@',getNS($id),$tpl);
625a15ce62dSEsther Brunner  $tpl = str_replace('@PAGE@',strtr(noNS($id),'_',' '),$tpl);
626a15ce62dSEsther Brunner  $tpl = str_replace('@USER@',$_SERVER['REMOTE_USER'],$tpl);
627a15ce62dSEsther Brunner  $tpl = str_replace('@NAME@',$INFO['userinfo']['name'],$tpl);
628a15ce62dSEsther Brunner  $tpl = str_replace('@MAIL@',$INFO['userinfo']['mail'],$tpl);
629a15ce62dSEsther Brunner  $tpl = str_replace('@DATE@',date($conf['dformat']),$tpl);
630a15ce62dSEsther Brunner  return $tpl;
6317146cee2SAndreas Gohr}
6327146cee2SAndreas Gohr
6337146cee2SAndreas Gohr
6347146cee2SAndreas Gohr/**
63515fae107Sandi * Returns the raw Wiki Text in three slices.
63615fae107Sandi *
63715fae107Sandi * The range parameter needs to have the form "from-to"
63815cfe303Sandi * and gives the range of the section in bytes - no
63915cfe303Sandi * UTF-8 awareness is needed.
640f3f0262cSandi * The returned order is prefix, section and suffix.
64115fae107Sandi *
64215fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
643f3f0262cSandi */
644f3f0262cSandifunction rawWikiSlices($range,$id,$rev=''){
645f3f0262cSandi  list($from,$to) = split('-',$range,2);
646cc7d0c94SBen Coburn  $text = io_readWikiPage(wikiFN($id, $rev), $id, $rev);
647f3f0262cSandi  if(!$from) $from = 0;
648c3d8e19bSandi  if(!$to)   $to   = strlen($text)+1;
649f3f0262cSandi
65015cfe303Sandi  $slices[0] = substr($text,0,$from-1);
65115cfe303Sandi  $slices[1] = substr($text,$from-1,$to-$from);
65215cfe303Sandi  $slices[2] = substr($text,$to);
653f3f0262cSandi
654f3f0262cSandi  return $slices;
655f3f0262cSandi}
656f3f0262cSandi
657f3f0262cSandi/**
65815fae107Sandi * Joins wiki text slices
65915fae107Sandi *
660f3f0262cSandi * function to join the text slices with correct lineendings again.
661f3f0262cSandi * When the pretty parameter is set to true it adds additional empty
662f3f0262cSandi * lines between sections if needed (used on saving).
66315fae107Sandi *
66415fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
665f3f0262cSandi */
666f3f0262cSandifunction con($pre,$text,$suf,$pretty=false){
667f3f0262cSandi
668f3f0262cSandi  if($pretty){
669f3f0262cSandi    if($pre && substr($pre,-1) != "\n") $pre .= "\n";
670f3f0262cSandi    if($suf && substr($text,-1) != "\n") $text .= "\n";
671f3f0262cSandi  }
672f3f0262cSandi
673f3f0262cSandi  if($pre) $pre .= "\n";
674f3f0262cSandi  if($suf) $text .= "\n";
675f3f0262cSandi  return $pre.$text.$suf;
676f3f0262cSandi}
677f3f0262cSandi
678f3f0262cSandi/**
679*a701424fSBen Coburn * Saves a wikitext by calling io_writeWikiPage.
680*a701424fSBen Coburn * Also directs changelog and attic updates.
68115fae107Sandi *
68215fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
68371726d78SBen Coburn * @author Ben Coburn <btcoburn@silicodon.net>
684f3f0262cSandi */
685b6912aeaSAndreas Gohrfunction saveWikiText($id,$text,$summary,$minor=false){
686*a701424fSBen Coburn  /* Note to developers:
687*a701424fSBen Coburn     This code is subtle and delicate. Test the behavior of
688*a701424fSBen Coburn     the attic and changelog with dokuwiki and external edits
689*a701424fSBen Coburn     after any changes. External edits change the wiki page
690*a701424fSBen Coburn     directly without using php or dokuwiki.
691*a701424fSBen Coburn  */
692f3f0262cSandi  global $conf;
693f3f0262cSandi  global $lang;
69471726d78SBen Coburn  global $REV;
695f3f0262cSandi  // ignore if no changes were made
696f3f0262cSandi  if($text == rawWiki($id,'')){
697f3f0262cSandi    return;
698f3f0262cSandi  }
699f3f0262cSandi
700f3f0262cSandi  $file = wikiFN($id);
701*a701424fSBen Coburn  $old = @filemtime($file); // from page
70271726d78SBen Coburn  $wasRemoved = empty($text);
703d8186216SBen Coburn  $wasCreated = !@file_exists($file);
70471726d78SBen Coburn  $wasReverted = ($REV==true);
705e45b34cdSBen Coburn  $newRev = false;
706*a701424fSBen Coburn  $oldRev = getRevisions($id, -1, 1, 1024); // from changelog
707*a701424fSBen Coburn  $oldRev = (int)(empty($oldRev)?0:$oldRev[0]);
708*a701424fSBen Coburn  if(!@file_exists(wikiFN($id, $old)) && @file_exists($file) && $old>=$oldRev) {
70946844156SBen Coburn    // add old revision to the attic if missing
71046844156SBen Coburn    saveOldRevision($id);
71146844156SBen Coburn    // add a changelog entry if this edit came from outside dokuwiki
712*a701424fSBen Coburn    if ($old>$oldRev) {
71346844156SBen Coburn      addLogEntry($old, $id);
71446844156SBen Coburn      // send notify mails
71546844156SBen Coburn      notify($id,'admin',$oldRev,'',false);
71646844156SBen Coburn      notify($id,'subscribers',$oldRev,'',false);
71746844156SBen Coburn      // remove soon to be stale instructions
71846844156SBen Coburn      $cache = new cache_instructions($id, $file);
71946844156SBen Coburn      $cache->removeCache();
72046844156SBen Coburn    }
72146844156SBen Coburn  }
722f3f0262cSandi
72371726d78SBen Coburn  if ($wasRemoved){
724e45b34cdSBen Coburn    // pre-save deleted revision
725e45b34cdSBen Coburn    @touch($file);
72646844156SBen Coburn    clearstatcache();
727e45b34cdSBen Coburn    $newRev = saveOldRevision($id);
728e1f3d9e1SEsther Brunner    // remove empty file
729f3f0262cSandi    @unlink($file);
73071726d78SBen Coburn    // remove old meta info...
731e1f3d9e1SEsther Brunner    $mfiles = metaFiles($id);
73271726d78SBen Coburn    $changelog = metaFN($id, '.changes');
733e1f3d9e1SEsther Brunner    foreach ($mfiles as $mfile) {
73471726d78SBen Coburn      // but keep per-page changelog to preserve page history
735d8186216SBen Coburn      if (@file_exists($mfile) && $mfile!==$changelog) { @unlink($mfile); }
736b158d625SSteven Danz    }
737f3f0262cSandi    $del = true;
7383ce054b3Sandi    // autoset summary on deletion
7393ce054b3Sandi    if(empty($summary)) $summary = $lang['deleted'];
74053d6ccfeSandi    // remove empty namespaces
741cc7d0c94SBen Coburn    io_sweepNS($id, 'datadir');
742cc7d0c94SBen Coburn    io_sweepNS($id, 'mediadir');
743f3f0262cSandi  }else{
744cc7d0c94SBen Coburn    // save file (namespace dir is created in io_writeWikiPage)
745cc7d0c94SBen Coburn    io_writeWikiPage($file, $text, $id);
74646844156SBen Coburn    // pre-save the revision, to keep the attic in sync
74746844156SBen Coburn    $newRev = saveOldRevision($id);
748f3f0262cSandi    $del = false;
749f3f0262cSandi  }
750f3f0262cSandi
75171726d78SBen Coburn  // select changelog line type
75271726d78SBen Coburn  $extra = '';
75371726d78SBen Coburn  $type = 'E';
75471726d78SBen Coburn  if ($wasReverted) {
75571726d78SBen Coburn    $type = 'R';
75671726d78SBen Coburn    $extra = $REV;
75771726d78SBen Coburn  }
75871726d78SBen Coburn  else if ($wasCreated) { $type = 'C'; }
75971726d78SBen Coburn  else if ($wasRemoved) { $type = 'D'; }
76071726d78SBen Coburn  else if ($minor && $conf['useacl'] && $_SERVER['REMOTE_USER']) { $type = 'e'; } //minor edits only for logged in users
76171726d78SBen Coburn
762e45b34cdSBen Coburn  addLogEntry($newRev, $id, $type, $summary, $extra);
76326a0801fSAndreas Gohr  // send notify mails
76490033e9dSAndreas Gohr  notify($id,'admin',$old,$summary,$minor);
76590033e9dSAndreas Gohr  notify($id,'subscribers',$old,$summary,$minor);
766f3f0262cSandi
767ce6b63d9Schris  // update the purgefile (timestamp of the last time anything within the wiki was changed)
76898407a7aSandi  io_saveFile($conf['cachedir'].'/purgefile',time());
769f3f0262cSandi}
770f3f0262cSandi
771f3f0262cSandi/**
772f3f0262cSandi * moves the current version to the attic and returns its
773f3f0262cSandi * revision date
77415fae107Sandi *
77515fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
776f3f0262cSandi */
777f3f0262cSandifunction saveOldRevision($id){
778f3f0262cSandi  global $conf;
779f3f0262cSandi  $oldf = wikiFN($id);
780f3f0262cSandi  if(!@file_exists($oldf)) return '';
781f3f0262cSandi  $date = filemtime($oldf);
782f3f0262cSandi  $newf = wikiFN($id,$date);
783cc7d0c94SBen Coburn  io_writeWikiPage($newf, rawWiki($id), $id, $date);
784f3f0262cSandi  return $date;
785f3f0262cSandi}
786f3f0262cSandi
787f3f0262cSandi/**
78826a0801fSAndreas Gohr * Sends a notify mail on page change
78926a0801fSAndreas Gohr *
79026a0801fSAndreas Gohr * @param  string  $id       The changed page
79126a0801fSAndreas Gohr * @param  string  $who      Who to notify (admin|subscribers)
79226a0801fSAndreas Gohr * @param  int     $rev      Old page revision
79326a0801fSAndreas Gohr * @param  string  $summary  What changed
79490033e9dSAndreas Gohr * @param  boolean $minor    Is this a minor edit?
79502a498e7Schris * @param  array   $replace  Additional string substitutions, @KEY@ to be replaced by value
79615fae107Sandi *
79715fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
798f3f0262cSandi */
79902a498e7Schrisfunction notify($id,$who,$rev='',$summary='',$minor=false,$replace=array()){
800f3f0262cSandi  global $lang;
801f3f0262cSandi  global $conf;
802b158d625SSteven Danz
80326a0801fSAndreas Gohr  // decide if there is something to do
80426a0801fSAndreas Gohr  if($who == 'admin'){
80526a0801fSAndreas Gohr    if(empty($conf['notify'])) return; //notify enabled?
806f3f0262cSandi    $text = rawLocale('mailtext');
80726a0801fSAndreas Gohr    $to   = $conf['notify'];
80826a0801fSAndreas Gohr    $bcc  = '';
80926a0801fSAndreas Gohr  }elseif($who == 'subscribers'){
81026a0801fSAndreas Gohr    if(!$conf['subscribers']) return; //subscribers enabled?
81190033e9dSAndreas Gohr    if($conf['useacl'] && $_SERVER['REMOTE_USER'] && $minor) return; //skip minors
81226a0801fSAndreas Gohr    $bcc  = subscriber_addresslist($id);
81326a0801fSAndreas Gohr    if(empty($bcc)) return;
81426a0801fSAndreas Gohr    $to   = '';
81526a0801fSAndreas Gohr    $text = rawLocale('subscribermail');
816a06e4bdbSSebastian Harl  }elseif($who == 'register'){
817a06e4bdbSSebastian Harl    if(empty($conf['registernotify'])) return;
818a06e4bdbSSebastian Harl    $text = rawLocale('registermail');
819a06e4bdbSSebastian Harl    $to   = $conf['registernotify'];
820a06e4bdbSSebastian Harl    $bcc  = '';
82126a0801fSAndreas Gohr  }else{
82226a0801fSAndreas Gohr    return; //just to be safe
82326a0801fSAndreas Gohr  }
82426a0801fSAndreas Gohr
825f3f0262cSandi  $text = str_replace('@DATE@',date($conf['dformat']),$text);
826f3f0262cSandi  $text = str_replace('@BROWSER@',$_SERVER['HTTP_USER_AGENT'],$text);
827f3f0262cSandi  $text = str_replace('@IPADDRESS@',$_SERVER['REMOTE_ADDR'],$text);
828f3f0262cSandi  $text = str_replace('@HOSTNAME@',gethostbyaddr($_SERVER['REMOTE_ADDR']),$text);
829ed7b5f09Sandi  $text = str_replace('@NEWPAGE@',wl($id,'',true),$text);
83026a0801fSAndreas Gohr  $text = str_replace('@PAGE@',$id,$text);
83126a0801fSAndreas Gohr  $text = str_replace('@TITLE@',$conf['title'],$text);
832ed7b5f09Sandi  $text = str_replace('@DOKUWIKIURL@',DOKU_URL,$text);
833f3f0262cSandi  $text = str_replace('@SUMMARY@',$summary,$text);
8347a82afdcSandi  $text = str_replace('@USER@',$_SERVER['REMOTE_USER'],$text);
835f3f0262cSandi
83602a498e7Schris  foreach ($replace as $key => $substitution) {
83702a498e7Schris    $text = str_replace('@'.strtoupper($key).'@',$substitution, $text);
83802a498e7Schris  }
83902a498e7Schris
840a06e4bdbSSebastian Harl  if($who == 'register'){
841a06e4bdbSSebastian Harl    $subject = $lang['mail_new_user'].' '.$summary;
842a06e4bdbSSebastian Harl  }elseif($rev){
843f3f0262cSandi    $subject = $lang['mail_changed'].' '.$id;
844ed7b5f09Sandi    $text = str_replace('@OLDPAGE@',wl($id,"rev=$rev",true),$text);
845ccdfa6c0SAndreas Gohr    require_once(DOKU_INC.'inc/DifferenceEngine.php');
846f3f0262cSandi    $df  = new Diff(split("\n",rawWiki($id,$rev)),
847f3f0262cSandi                    split("\n",rawWiki($id)));
848f3f0262cSandi    $dformat = new UnifiedDiffFormatter();
849f3f0262cSandi    $diff    = $dformat->format($df);
850f3f0262cSandi  }else{
851f3f0262cSandi    $subject=$lang['mail_newpage'].' '.$id;
852f3f0262cSandi    $text = str_replace('@OLDPAGE@','none',$text);
853f3f0262cSandi    $diff = rawWiki($id);
854f3f0262cSandi  }
855f3f0262cSandi  $text = str_replace('@DIFF@',$diff,$text);
856241f3a36Sandi  $subject = '['.$conf['title'].'] '.$subject;
857f3f0262cSandi
85826a0801fSAndreas Gohr  mail_send($to,$subject,$text,$conf['mailfrom'],'',$bcc);
859f3f0262cSandi}
860f3f0262cSandi
86115fae107Sandi/**
862f3f0262cSandi * extracts the query from a google referer
86315fae107Sandi *
8646b13307fSandi * @todo   should be more generic and support yahoo et al
86515fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
866f3f0262cSandi */
867f3f0262cSandifunction getGoogleQuery(){
868f3f0262cSandi  $url = parse_url($_SERVER['HTTP_REFERER']);
8695c3f206fSandi  if(!$url) return '';
870f3f0262cSandi
871f3f0262cSandi  if(!preg_match("#google\.#i",$url['host'])) return '';
872f3f0262cSandi  $query = array();
873f3f0262cSandi  parse_str($url['query'],$query);
874f3f0262cSandi
875f3f0262cSandi  return $query['q'];
876f3f0262cSandi}
877f3f0262cSandi
878f3f0262cSandi/**
87915fae107Sandi * Try to set correct locale
88015fae107Sandi *
881095bfd5cSandi * @deprecated No longer used
88215fae107Sandi * @author     Andreas Gohr <andi@splitbrain.org>
883f3f0262cSandi */
884f3f0262cSandifunction setCorrectLocale(){
885f3f0262cSandi  global $conf;
886f3f0262cSandi  global $lang;
887f3f0262cSandi
888f3f0262cSandi  $enc = strtoupper($lang['encoding']);
889f3f0262cSandi  foreach ($lang['locales'] as $loc){
890f3f0262cSandi    //try locale
891f3f0262cSandi    if(@setlocale(LC_ALL,$loc)) return;
892f3f0262cSandi    //try loceale with encoding
893f3f0262cSandi    if(@setlocale(LC_ALL,"$loc.$enc")) return;
894f3f0262cSandi  }
895f3f0262cSandi  //still here? try to set from environment
896f3f0262cSandi  @setlocale(LC_ALL,"");
897f3f0262cSandi}
898f3f0262cSandi
899f3f0262cSandi/**
900f3f0262cSandi * Return the human readable size of a file
901f3f0262cSandi *
902f3f0262cSandi * @param       int    $size   A file size
903f3f0262cSandi * @param       int    $dec    A number of decimal places
904f3f0262cSandi * @author      Martin Benjamin <b.martin@cybernet.ch>
905f3f0262cSandi * @author      Aidan Lister <aidan@php.net>
906f3f0262cSandi * @version     1.0.0
907f3f0262cSandi */
908f31d5b73Sandifunction filesize_h($size, $dec = 1){
909f3f0262cSandi  $sizes = array('B', 'KB', 'MB', 'GB');
910f3f0262cSandi  $count = count($sizes);
911f3f0262cSandi  $i = 0;
912f3f0262cSandi
913f3f0262cSandi  while ($size >= 1024 && ($i < $count - 1)) {
914f3f0262cSandi    $size /= 1024;
915f3f0262cSandi    $i++;
916f3f0262cSandi  }
917f3f0262cSandi
918f3f0262cSandi  return round($size, $dec) . ' ' . $sizes[$i];
919f3f0262cSandi}
920f3f0262cSandi
92115fae107Sandi/**
92200a7b5adSEsther Brunner * return an obfuscated email address in line with $conf['mailguard'] setting
92300a7b5adSEsther Brunner *
92400a7b5adSEsther Brunner * @author Harry Fuecks <hfuecks@gmail.com>
92500a7b5adSEsther Brunner * @author Christopher Smith <chris@jalakai.co.uk>
92600a7b5adSEsther Brunner */
92700a7b5adSEsther Brunnerfunction obfuscate($email) {
92800a7b5adSEsther Brunner  global $conf;
92900a7b5adSEsther Brunner
93000a7b5adSEsther Brunner  switch ($conf['mailguard']) {
93100a7b5adSEsther Brunner    case 'visible' :
93200a7b5adSEsther Brunner      $obfuscate = array('@' => ' [at] ', '.' => ' [dot] ', '-' => ' [dash] ');
93300a7b5adSEsther Brunner      return strtr($email, $obfuscate);
93400a7b5adSEsther Brunner
93500a7b5adSEsther Brunner    case 'hex' :
93600a7b5adSEsther Brunner      $encode = '';
93700a7b5adSEsther Brunner      for ($x=0; $x < strlen($email); $x++) $encode .= '&#x' . bin2hex($email{$x}).';';
93800a7b5adSEsther Brunner      return $encode;
93900a7b5adSEsther Brunner
94000a7b5adSEsther Brunner    case 'none' :
94100a7b5adSEsther Brunner    default :
94200a7b5adSEsther Brunner      return $email;
94300a7b5adSEsther Brunner  }
94400a7b5adSEsther Brunner}
94500a7b5adSEsther Brunner
94600a7b5adSEsther Brunner/**
947b158d625SSteven Danz * Let us know if a user is tracking a page
948b158d625SSteven Danz *
9491380fc45SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
950b158d625SSteven Danz */
9511380fc45SAndreas Gohrfunction is_subscribed($id,$uid){
9521380fc45SAndreas Gohr  $file=metaFN($id,'.mlist');
9531380fc45SAndreas Gohr  if (@file_exists($file)) {
954b158d625SSteven Danz    $mlist = file($file);
9551380fc45SAndreas Gohr    $pos = array_search($uid."\n",$mlist);
9561380fc45SAndreas Gohr    return is_int($pos);
957b158d625SSteven Danz  }
9581380fc45SAndreas Gohr
959b158d625SSteven Danz  return false;
960b158d625SSteven Danz}
961340756e4Sandi
962f9eb5648Ssteven-danz/**
963f9eb5648Ssteven-danz * Return a string with the email addresses of all the
964f9eb5648Ssteven-danz * users subscribed to a page
965f9eb5648Ssteven-danz *
96626a0801fSAndreas Gohr * @author Steven Danz <steven-danz@kc.rr.com>
967f9eb5648Ssteven-danz */
968f9eb5648Ssteven-danzfunction subscriber_addresslist($id){
969f9eb5648Ssteven-danz  global $conf;
970cd52f92dSchris  global $auth;
971f9eb5648Ssteven-danz
972f9eb5648Ssteven-danz  $emails = '';
973f9eb5648Ssteven-danz
97426a0801fSAndreas Gohr  if (!$conf['subscribers']) return;
97526a0801fSAndreas Gohr
976f9eb5648Ssteven-danz  $mlist = array();
977f9eb5648Ssteven-danz  $file=metaFN($id,'.mlist');
978d8186216SBen Coburn  if (@file_exists($file)) {
979f9eb5648Ssteven-danz    $mlist = file($file);
980f9eb5648Ssteven-danz  }
981f9eb5648Ssteven-danz  if(count($mlist) > 0) {
982f9eb5648Ssteven-danz    foreach ($mlist as $who) {
983f9eb5648Ssteven-danz      $who = rtrim($who);
984cd52f92dSchris      $info = $auth->getUserData($who);
985f9eb5648Ssteven-danz      $level = auth_aclcheck($id,$who,$info['grps']);
986f9eb5648Ssteven-danz      if ($level >= AUTH_READ) {
987f9eb5648Ssteven-danz        if (strcasecmp($info['mail'],$conf['notify']) != 0) {
988f9eb5648Ssteven-danz          if (empty($emails)) {
989f9eb5648Ssteven-danz            $emails = $info['mail'];
990f9eb5648Ssteven-danz          } else {
991f9eb5648Ssteven-danz            $emails = "$emails,".$info['mail'];
992f9eb5648Ssteven-danz          }
993f9eb5648Ssteven-danz        }
994f9eb5648Ssteven-danz      }
995f9eb5648Ssteven-danz    }
996f9eb5648Ssteven-danz  }
997f9eb5648Ssteven-danz
998f9eb5648Ssteven-danz  return $emails;
999f9eb5648Ssteven-danz}
1000f9eb5648Ssteven-danz
100189541d4bSAndreas Gohr/**
100289541d4bSAndreas Gohr * Removes quoting backslashes
100389541d4bSAndreas Gohr *
100489541d4bSAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
100589541d4bSAndreas Gohr */
100689541d4bSAndreas Gohrfunction unslash($string,$char="'"){
100789541d4bSAndreas Gohr  return str_replace('\\'.$char,$char,$string);
100889541d4bSAndreas Gohr}
100989541d4bSAndreas Gohr
1010340756e4Sandi//Setup VIM: ex: et ts=2 enc=utf-8 :
1011