xref: /dokuwiki/inc/common.php (revision 4bc480e54901356eb84ce27ab4a76c5f88875a60)
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
9fa8adffeSAndreas Gohrif(!defined('DOKU_INC')) die('meh.');
10f3f0262cSandi
11f3f0262cSandi/**
12b6912aeaSAndreas Gohr * These constants are used with the recents function
13b6912aeaSAndreas Gohr */
14b6912aeaSAndreas Gohrdefine('RECENTS_SKIP_DELETED', 2);
15b6912aeaSAndreas Gohrdefine('RECENTS_SKIP_MINORS', 4);
16b6912aeaSAndreas Gohrdefine('RECENTS_SKIP_SUBSPACES', 8);
170b926329SKate Arzamastsevadefine('RECENTS_MEDIA_CHANGES', 16);
180b926329SKate Arzamastsevadefine('RECENTS_MEDIA_PAGES_MIXED', 32);
19b6912aeaSAndreas Gohr
20b6912aeaSAndreas Gohr/**
21d5197206Schris * Wrapper around htmlspecialchars()
22d5197206Schris *
23d5197206Schris * @author Andreas Gohr <andi@splitbrain.org>
24d5197206Schris * @see    htmlspecialchars()
25d5197206Schris */
26d5197206Schrisfunction hsc($string) {
27d5197206Schris    return htmlspecialchars($string, ENT_QUOTES, 'UTF-8');
28d5197206Schris}
29d5197206Schris
30d5197206Schris/**
31d5197206Schris * print a newline terminated string
32d5197206Schris *
33d5197206Schris * You can give an indention as optional parameter
34d5197206Schris *
35d5197206Schris * @author Andreas Gohr <andi@splitbrain.org>
36d5197206Schris */
3725ec097bSChris Smithfunction ptln($string, $indent = 0) {
3825ec097bSChris Smith    echo str_repeat(' ', $indent)."$string\n";
3902b0b681SAndreas Gohr}
4002b0b681SAndreas Gohr
4102b0b681SAndreas Gohr/**
4202b0b681SAndreas Gohr * strips control characters (<32) from the given string
4302b0b681SAndreas Gohr *
4402b0b681SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
4502b0b681SAndreas Gohr */
4602b0b681SAndreas Gohrfunction stripctl($string) {
4702b0b681SAndreas Gohr    return preg_replace('/[\x00-\x1F]+/s', '', $string);
48d5197206Schris}
49d5197206Schris
50d5197206Schris/**
51634d7150SAndreas Gohr * Return a secret token to be used for CSRF attack prevention
52634d7150SAndreas Gohr *
53634d7150SAndreas Gohr * @author  Andreas Gohr <andi@splitbrain.org>
54634d7150SAndreas Gohr * @link    http://en.wikipedia.org/wiki/Cross-site_request_forgery
55634d7150SAndreas Gohr * @link    http://christ1an.blogspot.com/2007/04/preventing-csrf-efficiently.html
56634d7150SAndreas Gohr * @return  string
57634d7150SAndreas Gohr */
58634d7150SAndreas Gohrfunction getSecurityToken() {
59585bf44eSChristopher Smith    /** @var Input $INPUT */
60585bf44eSChristopher Smith    global $INPUT;
61585bf44eSChristopher Smith    return PassHash::hmac('md5', session_id().$INPUT->server->str('REMOTE_USER'), auth_cookiesalt());
62634d7150SAndreas Gohr}
63634d7150SAndreas Gohr
64634d7150SAndreas Gohr/**
65634d7150SAndreas Gohr * Check the secret CSRF token
66634d7150SAndreas Gohr */
67634d7150SAndreas Gohrfunction checkSecurityToken($token = null) {
68585bf44eSChristopher Smith    /** @var Input $INPUT */
697d01a0eaSTom N Harris    global $INPUT;
70585bf44eSChristopher Smith    if(!$INPUT->server->str('REMOTE_USER')) return true; // no logged in user, no need for a check
71df97eaacSAndreas Gohr
727d01a0eaSTom N Harris    if(is_null($token)) $token = $INPUT->str('sectok');
73634d7150SAndreas Gohr    if(getSecurityToken() != $token) {
74634d7150SAndreas Gohr        msg('Security Token did not match. Possible CSRF attack.', -1);
75634d7150SAndreas Gohr        return false;
76634d7150SAndreas Gohr    }
77634d7150SAndreas Gohr    return true;
78634d7150SAndreas Gohr}
79634d7150SAndreas Gohr
80634d7150SAndreas Gohr/**
81634d7150SAndreas Gohr * Print a hidden form field with a secret CSRF token
82634d7150SAndreas Gohr *
83634d7150SAndreas Gohr * @author  Andreas Gohr <andi@splitbrain.org>
84634d7150SAndreas Gohr */
85634d7150SAndreas Gohrfunction formSecurityToken($print = true) {
862404d0edSAnika Henke    $ret = '<div class="no"><input type="hidden" name="sectok" value="'.getSecurityToken().'" /></div>'."\n";
873272d797SAndreas Gohr    if($print) echo $ret;
88634d7150SAndreas Gohr    return $ret;
89634d7150SAndreas Gohr}
90634d7150SAndreas Gohr
91634d7150SAndreas Gohr/**
921015a57dSChristopher Smith * Determine basic information for a request of $id
9315fae107Sandi *
9415fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
957e87a794SChristopher Smith * @author Chris Smith <chris@jalakai.co.uk>
96f3f0262cSandi */
971015a57dSChristopher Smithfunction basicinfo($id, $htmlClient=true){
98f3f0262cSandi    global $USERINFO;
99585bf44eSChristopher Smith    /* @var Input $INPUT */
100585bf44eSChristopher Smith    global $INPUT;
1016afe8dcaSchris
102c66972f2SAdrian Lang    // set info about manager/admin status.
103c66972f2SAdrian Lang    $info['isadmin']   = false;
104c66972f2SAdrian Lang    $info['ismanager'] = false;
105585bf44eSChristopher Smith    if($INPUT->server->has('REMOTE_USER')) {
106f3f0262cSandi        $info['userinfo']   = $USERINFO;
1071015a57dSChristopher Smith        $info['perm']       = auth_quickaclcheck($id);
108585bf44eSChristopher Smith        $info['client']     = $INPUT->server->str('REMOTE_USER');
10917ee7f66SAndreas Gohr
110f8cc712eSAndreas Gohr        if($info['perm'] == AUTH_ADMIN) {
111f8cc712eSAndreas Gohr            $info['isadmin']   = true;
112f8cc712eSAndreas Gohr            $info['ismanager'] = true;
113f8cc712eSAndreas Gohr        } elseif(auth_ismanager()) {
114f8cc712eSAndreas Gohr            $info['ismanager'] = true;
115f8cc712eSAndreas Gohr        }
116f8cc712eSAndreas Gohr
11717ee7f66SAndreas Gohr        // if some outside auth were used only REMOTE_USER is set
11817ee7f66SAndreas Gohr        if(!$info['userinfo']['name']) {
119585bf44eSChristopher Smith            $info['userinfo']['name'] = $INPUT->server->str('REMOTE_USER');
12017ee7f66SAndreas Gohr        }
121ee4c4a1bSAndreas Gohr
122f3f0262cSandi    } else {
1231015a57dSChristopher Smith        $info['perm']       = auth_aclcheck($id, '', null);
124ee4c4a1bSAndreas Gohr        $info['client']     = clientIP(true);
125f3f0262cSandi    }
126f3f0262cSandi
1271015a57dSChristopher Smith    $info['namespace'] = getNS($id);
1281015a57dSChristopher Smith
1291015a57dSChristopher Smith    // mobile detection
1301015a57dSChristopher Smith    if ($htmlClient) {
1311015a57dSChristopher Smith        $info['ismobile'] = clientismobile();
1321015a57dSChristopher Smith    }
1331015a57dSChristopher Smith
1341015a57dSChristopher Smith    return $info;
1351015a57dSChristopher Smith }
1361015a57dSChristopher Smith
1371015a57dSChristopher Smith/**
1381015a57dSChristopher Smith * Return info about the current document as associative
1391015a57dSChristopher Smith * array.
1401015a57dSChristopher Smith *
1411015a57dSChristopher Smith * @author Andreas Gohr <andi@splitbrain.org>
1421015a57dSChristopher Smith */
1431015a57dSChristopher Smithfunction pageinfo() {
1441015a57dSChristopher Smith    global $ID;
1451015a57dSChristopher Smith    global $REV;
1461015a57dSChristopher Smith    global $RANGE;
1471015a57dSChristopher Smith    global $lang;
148585bf44eSChristopher Smith    /* @var Input $INPUT */
149585bf44eSChristopher Smith    global $INPUT;
1501015a57dSChristopher Smith
1511015a57dSChristopher Smith    $info = basicinfo($ID);
1521015a57dSChristopher Smith
1531015a57dSChristopher Smith    // include ID & REV not redundant, as some parts of DokuWiki may temporarily change $ID, e.g. p_wiki_xhtml
1541015a57dSChristopher Smith    // FIXME ... perhaps it would be better to ensure the temporary changes weren't necessary
1551015a57dSChristopher Smith    $info['id']  = $ID;
1561015a57dSChristopher Smith    $info['rev'] = $REV;
1571015a57dSChristopher Smith
158585bf44eSChristopher Smith    if($INPUT->server->has('REMOTE_USER')) {
1597e87a794SChristopher Smith        $sub = new Subscription();
1607e87a794SChristopher Smith        $info['subscribed'] = $sub->user_subscription();
1617e87a794SChristopher Smith    } else {
1627e87a794SChristopher Smith        $info['subscribed'] = false;
1637e87a794SChristopher Smith    }
1647e87a794SChristopher Smith
165f3f0262cSandi    $info['locked']     = checklock($ID);
16600976812SAndreas Gohr    $info['filepath']   = fullpath(wikiFN($ID));
1672ca9d91cSBen Coburn    $info['exists']     = @file_exists($info['filepath']);
16801c9a118SAndreas Gohr    $info['currentrev'] = @filemtime($info['filepath']);
1692ca9d91cSBen Coburn    if($REV) {
1702ca9d91cSBen Coburn        //check if current revision was meant
17101c9a118SAndreas Gohr        if($info['exists'] && ($info['currentrev'] == $REV)) {
1722ca9d91cSBen Coburn            $REV = '';
1737b3a6803SAndreas Gohr        } elseif($RANGE) {
1747b3a6803SAndreas Gohr            //section editing does not work with old revisions!
1757b3a6803SAndreas Gohr            $REV   = '';
1767b3a6803SAndreas Gohr            $RANGE = '';
1777b3a6803SAndreas Gohr            msg($lang['nosecedit'], 0);
1782ca9d91cSBen Coburn        } else {
1792ca9d91cSBen Coburn            //really use old revision
18000976812SAndreas Gohr            $info['filepath'] = fullpath(wikiFN($ID, $REV));
181f3f0262cSandi            $info['exists']   = @file_exists($info['filepath']);
182f3f0262cSandi        }
183f3f0262cSandi    }
184c112d578Sandi    $info['rev'] = $REV;
185f3f0262cSandi    if($info['exists']) {
186f3f0262cSandi        $info['writable'] = (is_writable($info['filepath']) &&
187f3f0262cSandi            ($info['perm'] >= AUTH_EDIT));
188f3f0262cSandi    } else {
189f3f0262cSandi        $info['writable'] = ($info['perm'] >= AUTH_CREATE);
190f3f0262cSandi    }
19150e988b1SAndreas Gohr    $info['editable'] = ($info['writable'] && empty($info['locked']));
192f3f0262cSandi    $info['lastmod']  = @filemtime($info['filepath']);
193f3f0262cSandi
19471726d78SBen Coburn    //load page meta data
19571726d78SBen Coburn    $info['meta'] = p_get_metadata($ID);
19671726d78SBen Coburn
197652610a2Sandi    //who's the editor
198047bad06SGerrit Uitslag    $pagelog = new PageChangeLog($ID, 1024);
199652610a2Sandi    if($REV) {
200f523c971SGerrit Uitslag        $revinfo = $pagelog->getRevisionInfo($REV);
201652610a2Sandi    } else {
2020e80bb5eSChristopher Smith        if(!empty($info['meta']['last_change']) && is_array($info['meta']['last_change'])) {
203aa27cf05SAndreas Gohr            $revinfo = $info['meta']['last_change'];
204aa27cf05SAndreas Gohr        } else {
205f523c971SGerrit Uitslag            $revinfo = $pagelog->getRevisionInfo($info['lastmod']);
206cd00a034SBen Coburn            // cache most recent changelog line in metadata if missing and still valid
207cd00a034SBen Coburn            if($revinfo !== false) {
208cd00a034SBen Coburn                $info['meta']['last_change'] = $revinfo;
209cd00a034SBen Coburn                p_set_metadata($ID, array('last_change' => $revinfo));
210cd00a034SBen Coburn            }
211cd00a034SBen Coburn        }
212cd00a034SBen Coburn    }
213cd00a034SBen Coburn    //and check for an external edit
214cd00a034SBen Coburn    if($revinfo !== false && $revinfo['date'] != $info['lastmod']) {
215cd00a034SBen Coburn        // cached changelog line no longer valid
216cd00a034SBen Coburn        $revinfo                     = false;
217cd00a034SBen Coburn        $info['meta']['last_change'] = $revinfo;
218cd00a034SBen Coburn        p_set_metadata($ID, array('last_change' => $revinfo));
219652610a2Sandi    }
220bb4866bdSchris
221652610a2Sandi    $info['ip']   = $revinfo['ip'];
222652610a2Sandi    $info['user'] = $revinfo['user'];
223652610a2Sandi    $info['sum']  = $revinfo['sum'];
22471726d78SBen Coburn    // See also $INFO['meta']['last_change'] which is the most recent log line for page $ID.
225ebf1501fSBen Coburn    // Use $INFO['meta']['last_change']['type']===DOKU_CHANGE_TYPE_MINOR_EDIT in place of $info['minor'].
22659f257aeSchris
22788f522e9Sandi    if($revinfo['user']) {
22888f522e9Sandi        $info['editor'] = $revinfo['user'];
22988f522e9Sandi    } else {
23088f522e9Sandi        $info['editor'] = $revinfo['ip'];
23188f522e9Sandi    }
232652610a2Sandi
233ee4c4a1bSAndreas Gohr    // draft
234ee4c4a1bSAndreas Gohr    $draft = getCacheName($info['client'].$ID, '.draft');
235ee4c4a1bSAndreas Gohr    if(@file_exists($draft)) {
236ee4c4a1bSAndreas Gohr        if(@filemtime($draft) < @filemtime(wikiFN($ID))) {
237ee4c4a1bSAndreas Gohr            // remove stale draft
238ee4c4a1bSAndreas Gohr            @unlink($draft);
239ee4c4a1bSAndreas Gohr        } else {
240ee4c4a1bSAndreas Gohr            $info['draft'] = $draft;
241ee4c4a1bSAndreas Gohr        }
242ee4c4a1bSAndreas Gohr    }
243ee4c4a1bSAndreas Gohr
2441015a57dSChristopher Smith    return $info;
2451015a57dSChristopher Smith}
2461015a57dSChristopher Smith
2471015a57dSChristopher Smith/**
2481015a57dSChristopher Smith * Return information about the current media item as an associative array.
2491015a57dSChristopher Smith */
2501015a57dSChristopher Smithfunction mediainfo(){
2511015a57dSChristopher Smith    global $NS;
2521015a57dSChristopher Smith    global $IMG;
2531015a57dSChristopher Smith
2541015a57dSChristopher Smith    $info = basicinfo("$NS:*");
2551015a57dSChristopher Smith    $info['image'] = $IMG;
2561c548ebeSAndreas Gohr
257f3f0262cSandi    return $info;
258f3f0262cSandi}
259f3f0262cSandi
260f3f0262cSandi/**
2612684e50aSAndreas Gohr * Build an string of URL parameters
2622684e50aSAndreas Gohr *
2632684e50aSAndreas Gohr * @author Andreas Gohr
2642684e50aSAndreas Gohr */
265b174aeaeSchrisfunction buildURLparams($params, $sep = '&amp;') {
2662684e50aSAndreas Gohr    $url = '';
2672684e50aSAndreas Gohr    $amp = false;
2682684e50aSAndreas Gohr    foreach($params as $key => $val) {
269b174aeaeSchris        if($amp) $url .= $sep;
2702684e50aSAndreas Gohr
27185e6871fSAdrian Lang        $url .= rawurlencode($key).'=';
2723a50618cSgweissbach        $url .= rawurlencode((string) $val);
2732684e50aSAndreas Gohr        $amp = true;
2742684e50aSAndreas Gohr    }
2752684e50aSAndreas Gohr    return $url;
2762684e50aSAndreas Gohr}
2772684e50aSAndreas Gohr
2782684e50aSAndreas Gohr/**
2792684e50aSAndreas Gohr * Build an string of html tag attributes
2802684e50aSAndreas Gohr *
2817bff22c0SAndreas Gohr * Skips keys starting with '_', values get HTML encoded
2827bff22c0SAndreas Gohr *
2832684e50aSAndreas Gohr * @author Andreas Gohr
2842684e50aSAndreas Gohr */
2854b030ce7SAndreas Gohrfunction buildAttributes($params, $skipempty = false) {
2862684e50aSAndreas Gohr    $url   = '';
2879063ec14SAdrian Lang    $white = false;
2882684e50aSAndreas Gohr    foreach($params as $key => $val) {
2897bff22c0SAndreas Gohr        if($key{0} == '_') continue;
290b1c94f1dSAndreas Gohr        if($val === '' && $skipempty) continue;
2919063ec14SAdrian Lang        if($white) $url .= ' ';
2927bff22c0SAndreas Gohr
2932684e50aSAndreas Gohr        $url .= $key.'="';
2942684e50aSAndreas Gohr        $url .= htmlspecialchars($val);
2952684e50aSAndreas Gohr        $url .= '"';
2969063ec14SAdrian Lang        $white = true;
2972684e50aSAndreas Gohr    }
2982684e50aSAndreas Gohr    return $url;
2992684e50aSAndreas Gohr}
3002684e50aSAndreas Gohr
3012684e50aSAndreas Gohr/**
30215fae107Sandi * This builds the breadcrumb trail and returns it as array
30315fae107Sandi *
30415fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
305f3f0262cSandi */
306f3f0262cSandifunction breadcrumbs() {
3078746e727Sandi    // we prepare the breadcrumbs early for quick session closing
3088746e727Sandi    static $crumbs = null;
3098746e727Sandi    if($crumbs != null) return $crumbs;
3108746e727Sandi
311f3f0262cSandi    global $ID;
312f3f0262cSandi    global $ACT;
313f3f0262cSandi    global $conf;
314f3f0262cSandi
315f3f0262cSandi    //first visit?
316c66972f2SAdrian Lang    $crumbs = isset($_SESSION[DOKU_COOKIE]['bc']) ? $_SESSION[DOKU_COOKIE]['bc'] : array();
317f3f0262cSandi    //we only save on show and existing wiki documents
318a77f5846Sjan    $file = wikiFN($ID);
319a77f5846Sjan    if($ACT != 'show' || !@file_exists($file)) {
320e71ce681SAndreas Gohr        $_SESSION[DOKU_COOKIE]['bc'] = $crumbs;
321f3f0262cSandi        return $crumbs;
322f3f0262cSandi    }
323a77f5846Sjan
324a77f5846Sjan    // page names
3251a84a0f3SAnika Henke    $name = noNSorNS($ID);
326fe9ec250SChris Smith    if(useHeading('navigation')) {
327a77f5846Sjan        // get page title
32867c15eceSMichael Hamann        $title = p_get_first_heading($ID, METADATA_RENDER_USING_SIMPLE_CACHE);
329a77f5846Sjan        if($title) {
330a77f5846Sjan            $name = $title;
331a77f5846Sjan        }
332a77f5846Sjan    }
333a77f5846Sjan
334f3f0262cSandi    //remove ID from array
335a77f5846Sjan    if(isset($crumbs[$ID])) {
336a77f5846Sjan        unset($crumbs[$ID]);
337f3f0262cSandi    }
338f3f0262cSandi
339f3f0262cSandi    //add to array
340a77f5846Sjan    $crumbs[$ID] = $name;
341f3f0262cSandi    //reduce size
342f3f0262cSandi    while(count($crumbs) > $conf['breadcrumbs']) {
343f3f0262cSandi        array_shift($crumbs);
344f3f0262cSandi    }
345f3f0262cSandi    //save to session
346e71ce681SAndreas Gohr    $_SESSION[DOKU_COOKIE]['bc'] = $crumbs;
347f3f0262cSandi    return $crumbs;
348f3f0262cSandi}
349f3f0262cSandi
350f3f0262cSandi/**
35115fae107Sandi * Filter for page IDs
35215fae107Sandi *
353f3f0262cSandi * This is run on a ID before it is outputted somewhere
354f3f0262cSandi * currently used to replace the colon with something else
355907f24f7SAndreas Gohr * on Windows (non-IIS) systems and to have proper URL encoding
356907f24f7SAndreas Gohr *
357907f24f7SAndreas Gohr * See discussions at https://github.com/splitbrain/dokuwiki/pull/84 and
358907f24f7SAndreas Gohr * https://github.com/splitbrain/dokuwiki/pull/173 why we use a whitelist of
359907f24f7SAndreas Gohr * unaffected servers instead of blacklisting affected servers here.
36015fae107Sandi *
36149c713a3Sandi * Urlencoding is ommitted when the second parameter is false
36249c713a3Sandi *
36315fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
364f3f0262cSandi */
36549c713a3Sandifunction idfilter($id, $ue = true) {
366f3f0262cSandi    global $conf;
367585bf44eSChristopher Smith    /* @var Input $INPUT */
368585bf44eSChristopher Smith    global $INPUT;
369585bf44eSChristopher Smith
370f3f0262cSandi    if($conf['useslash'] && $conf['userewrite']) {
371f3f0262cSandi        $id = strtr($id, ':', '/');
372f3f0262cSandi    } elseif(strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' &&
37358bedc8aSborekb        $conf['userewrite'] &&
374585bf44eSChristopher Smith        strpos($INPUT->server->str('SERVER_SOFTWARE'), 'Microsoft-IIS') === false
3753272d797SAndreas Gohr    ) {
376f3f0262cSandi        $id = strtr($id, ':', ';');
377f3f0262cSandi    }
37849c713a3Sandi    if($ue) {
379b6c6979fSAndreas Gohr        $id = rawurlencode($id);
380f3f0262cSandi        $id = str_replace('%3A', ':', $id); //keep as colon
381f3f0262cSandi        $id = str_replace('%2F', '/', $id); //keep as slash
38249c713a3Sandi    }
383f3f0262cSandi    return $id;
384f3f0262cSandi}
385f3f0262cSandi
386f3f0262cSandi/**
387ed7b5f09Sandi * This builds a link to a wikipage
38815fae107Sandi *
389*4bc480e5SAndreas Gohr * It handles URL rewriting and adds additional parameters
3906c7843b5Sandi *
39115fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
392*4bc480e5SAndreas Gohr *
393*4bc480e5SAndreas Gohr * @param string       $id             page id, defaults to start page
394*4bc480e5SAndreas Gohr * @param string|array $urlParameters  URL parameters, associative array recommended
395*4bc480e5SAndreas Gohr * @param bool         $absolute       request an absolute URL instead of relative
396*4bc480e5SAndreas Gohr * @param string       $separator      parameter separator
397*4bc480e5SAndreas Gohr * @return string
398f3f0262cSandi */
39916f15a81SDominik Eckelmannfunction wl($id = '', $urlParameters = '', $absolute = false, $separator = '&amp;') {
400f3f0262cSandi    global $conf;
40116f15a81SDominik Eckelmann    if(is_array($urlParameters)) {
40216f15a81SDominik Eckelmann        $urlParameters = buildURLparams($urlParameters, $separator);
4036de3759aSAndreas Gohr    } else {
40416f15a81SDominik Eckelmann        $urlParameters = str_replace(',', $separator, $urlParameters);
4056de3759aSAndreas Gohr    }
40616f15a81SDominik Eckelmann    if($id === '') {
40716f15a81SDominik Eckelmann        $id = $conf['start'];
40816f15a81SDominik Eckelmann    }
409f3f0262cSandi    $id = idfilter($id);
41016f15a81SDominik Eckelmann    if($absolute) {
411ed7b5f09Sandi        $xlink = DOKU_URL;
412ed7b5f09Sandi    } else {
413ed7b5f09Sandi        $xlink = DOKU_BASE;
414ed7b5f09Sandi    }
415f3f0262cSandi
4166c7843b5Sandi    if($conf['userewrite'] == 2) {
4176c7843b5Sandi        $xlink .= DOKU_SCRIPT.'/'.$id;
41816f15a81SDominik Eckelmann        if($urlParameters) $xlink .= '?'.$urlParameters;
4196c7843b5Sandi    } elseif($conf['userewrite']) {
420f3f0262cSandi        $xlink .= $id;
42116f15a81SDominik Eckelmann        if($urlParameters) $xlink .= '?'.$urlParameters;
422bce3726dSAndreas Gohr    } elseif($id) {
4236c7843b5Sandi        $xlink .= DOKU_SCRIPT.'?id='.$id;
42416f15a81SDominik Eckelmann        if($urlParameters) $xlink .= $separator.$urlParameters;
425bce3726dSAndreas Gohr    } else {
426bce3726dSAndreas Gohr        $xlink .= DOKU_SCRIPT;
42716f15a81SDominik Eckelmann        if($urlParameters) $xlink .= '?'.$urlParameters;
428f3f0262cSandi    }
429f3f0262cSandi
430f3f0262cSandi    return $xlink;
431f3f0262cSandi}
432f3f0262cSandi
433f3f0262cSandi/**
434f5c2808fSBen Coburn * This builds a link to an alternate page format
435f5c2808fSBen Coburn *
436f5c2808fSBen Coburn * Handles URL rewriting if enabled. Follows the style of wl().
437f5c2808fSBen Coburn *
438f5c2808fSBen Coburn * @author Ben Coburn <btcoburn@silicodon.net>
439*4bc480e5SAndreas Gohr * @param string       $id             page id, defaults to start page
440*4bc480e5SAndreas Gohr * @param string       $format         the export renderer to use
441*4bc480e5SAndreas Gohr * @param string|array $urlParameters  URL parameters, associative array recommended
442*4bc480e5SAndreas Gohr * @param bool         $abs            request an absolute URL instead of relative
443*4bc480e5SAndreas Gohr * @param string       $sep            parameter separator
444*4bc480e5SAndreas Gohr * @return string
445f5c2808fSBen Coburn */
446*4bc480e5SAndreas Gohrfunction exportlink($id = '', $format = 'raw', $urlParameters = '', $abs = false, $sep = '&amp;') {
447f5c2808fSBen Coburn    global $conf;
448*4bc480e5SAndreas Gohr    if(is_array($urlParameters)) {
449*4bc480e5SAndreas Gohr        $urlParameters = buildURLparams($urlParameters, $sep);
450f5c2808fSBen Coburn    } else {
451*4bc480e5SAndreas Gohr        $urlParameters = str_replace(',', $sep, $urlParameters);
452f5c2808fSBen Coburn    }
453f5c2808fSBen Coburn
454f5c2808fSBen Coburn    $format = rawurlencode($format);
455f5c2808fSBen Coburn    $id     = idfilter($id);
456f5c2808fSBen Coburn    if($abs) {
457f5c2808fSBen Coburn        $xlink = DOKU_URL;
458f5c2808fSBen Coburn    } else {
459f5c2808fSBen Coburn        $xlink = DOKU_BASE;
460f5c2808fSBen Coburn    }
461f5c2808fSBen Coburn
462f5c2808fSBen Coburn    if($conf['userewrite'] == 2) {
463f5c2808fSBen Coburn        $xlink .= DOKU_SCRIPT.'/'.$id.'?do=export_'.$format;
464*4bc480e5SAndreas Gohr        if($urlParameters) $xlink .= $sep.$urlParameters;
465f5c2808fSBen Coburn    } elseif($conf['userewrite'] == 1) {
466f5c2808fSBen Coburn        $xlink .= '_export/'.$format.'/'.$id;
467*4bc480e5SAndreas Gohr        if($urlParameters) $xlink .= '?'.$urlParameters;
468f5c2808fSBen Coburn    } else {
469f5c2808fSBen Coburn        $xlink .= DOKU_SCRIPT.'?do=export_'.$format.$sep.'id='.$id;
470*4bc480e5SAndreas Gohr        if($urlParameters) $xlink .= $sep.$urlParameters;
471f5c2808fSBen Coburn    }
472f5c2808fSBen Coburn
473f5c2808fSBen Coburn    return $xlink;
474f5c2808fSBen Coburn}
475f5c2808fSBen Coburn
476f5c2808fSBen Coburn/**
4776de3759aSAndreas Gohr * Build a link to a media file
4786de3759aSAndreas Gohr *
4796de3759aSAndreas Gohr * Will return a link to the detail page if $direct is false
4808c08db0aSAndreas Gohr *
4818c08db0aSAndreas Gohr * The $more parameter should always be given as array, the function then
4828c08db0aSAndreas Gohr * will strip default parameters to produce even cleaner URLs
4838c08db0aSAndreas Gohr *
4843272d797SAndreas Gohr * @param string  $id     the media file id or URL
4853272d797SAndreas Gohr * @param mixed   $more   string or array with additional parameters
4863272d797SAndreas Gohr * @param bool    $direct link to detail page if false
4873272d797SAndreas Gohr * @param string  $sep    URL parameter separator
4883272d797SAndreas Gohr * @param bool    $abs    Create an absolute URL
4893272d797SAndreas Gohr * @return string
4906de3759aSAndreas Gohr */
49155b2b31bSAndreas Gohrfunction ml($id = '', $more = '', $direct = true, $sep = '&amp;', $abs = false) {
4926de3759aSAndreas Gohr    global $conf;
493b9ee6a44SKlap-in    $isexternalimage = media_isexternal($id);
494826d2766SKlap-in    if(!$isexternalimage) {
495826d2766SKlap-in        $id = cleanID($id);
496826d2766SKlap-in    }
497826d2766SKlap-in
4986de3759aSAndreas Gohr    if(is_array($more)) {
4990f4e0092SChristopher Smith        // add token for resized images
500443e135dSChristopher Smith        if(!empty($more['w']) || !empty($more['h']) || $isexternalimage){
5010f4e0092SChristopher Smith            $more['tok'] = media_get_token($id,$more['w'],$more['h']);
5020f4e0092SChristopher Smith        }
5038c08db0aSAndreas Gohr        // strip defaults for shorter URLs
5048c08db0aSAndreas Gohr        if(isset($more['cache']) && $more['cache'] == 'cache') unset($more['cache']);
505443e135dSChristopher Smith        if(empty($more['w'])) unset($more['w']);
506443e135dSChristopher Smith        if(empty($more['h'])) unset($more['h']);
5078c08db0aSAndreas Gohr        if(isset($more['id']) && $direct) unset($more['id']);
508b174aeaeSchris        $more = buildURLparams($more, $sep);
5096de3759aSAndreas Gohr    } else {
5105e7db1e2SChristopher Smith        $matches = array();
511cc036f74SKlap-in        if (preg_match_all('/\b(w|h)=(\d*)\b/',$more,$matches,PREG_SET_ORDER) || $isexternalimage){
5125e7db1e2SChristopher Smith            $resize = array('w'=>0, 'h'=>0);
5135e7db1e2SChristopher Smith            foreach ($matches as $match){
5145e7db1e2SChristopher Smith                $resize[$match[1]] = $match[2];
5155e7db1e2SChristopher Smith            }
516cc036f74SKlap-in            $more .= $more === '' ? '' : $sep;
517cc036f74SKlap-in            $more .= 'tok='.media_get_token($id,$resize['w'],$resize['h']);
5185e7db1e2SChristopher Smith        }
5198c08db0aSAndreas Gohr        $more = str_replace('cache=cache', '', $more); //skip default
5208c08db0aSAndreas Gohr        $more = str_replace(',,', ',', $more);
521b174aeaeSchris        $more = str_replace(',', $sep, $more);
5226de3759aSAndreas Gohr    }
5236de3759aSAndreas Gohr
52455b2b31bSAndreas Gohr    if($abs) {
52555b2b31bSAndreas Gohr        $xlink = DOKU_URL;
52655b2b31bSAndreas Gohr    } else {
5276de3759aSAndreas Gohr        $xlink = DOKU_BASE;
52855b2b31bSAndreas Gohr    }
5296de3759aSAndreas Gohr
5306de3759aSAndreas Gohr    // external URLs are always direct without rewriting
531826d2766SKlap-in    if($isexternalimage) {
5326de3759aSAndreas Gohr        $xlink .= 'lib/exe/fetch.php';
533cc036f74SKlap-in        $xlink .= '?'.$more;
534b174aeaeSchris        $xlink .= $sep.'media='.rawurlencode($id);
5356de3759aSAndreas Gohr        return $xlink;
5366de3759aSAndreas Gohr    }
5376de3759aSAndreas Gohr
5386de3759aSAndreas Gohr    $id = idfilter($id);
5396de3759aSAndreas Gohr
5406de3759aSAndreas Gohr    // decide on scriptname
5416de3759aSAndreas Gohr    if($direct) {
5426de3759aSAndreas Gohr        if($conf['userewrite'] == 1) {
5436de3759aSAndreas Gohr            $script = '_media';
5446de3759aSAndreas Gohr        } else {
5456de3759aSAndreas Gohr            $script = 'lib/exe/fetch.php';
5466de3759aSAndreas Gohr        }
5476de3759aSAndreas Gohr    } else {
5486de3759aSAndreas Gohr        if($conf['userewrite'] == 1) {
5496de3759aSAndreas Gohr            $script = '_detail';
5506de3759aSAndreas Gohr        } else {
5516de3759aSAndreas Gohr            $script = 'lib/exe/detail.php';
5526de3759aSAndreas Gohr        }
5536de3759aSAndreas Gohr    }
5546de3759aSAndreas Gohr
5556de3759aSAndreas Gohr    // build URL based on rewrite mode
5566de3759aSAndreas Gohr    if($conf['userewrite']) {
5576de3759aSAndreas Gohr        $xlink .= $script.'/'.$id;
5586de3759aSAndreas Gohr        if($more) $xlink .= '?'.$more;
5596de3759aSAndreas Gohr    } else {
5606de3759aSAndreas Gohr        if($more) {
561a99d3236SEsther Brunner            $xlink .= $script.'?'.$more;
562b174aeaeSchris            $xlink .= $sep.'media='.$id;
5636de3759aSAndreas Gohr        } else {
564a99d3236SEsther Brunner            $xlink .= $script.'?media='.$id;
5656de3759aSAndreas Gohr        }
5666de3759aSAndreas Gohr    }
5676de3759aSAndreas Gohr
5686de3759aSAndreas Gohr    return $xlink;
5696de3759aSAndreas Gohr}
5706de3759aSAndreas Gohr
5716de3759aSAndreas Gohr/**
57225ca5b17SAndreas Gohr * Returns the URL to the DokuWiki base script
57315fae107Sandi *
57425ca5b17SAndreas Gohr * Consider using wl() instead, unless you absoutely need the doku.php endpoint
57525ca5b17SAndreas Gohr *
57615fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
577f3f0262cSandi */
57825ca5b17SAndreas Gohrfunction script() {
579ed7b5f09Sandi    return DOKU_BASE.DOKU_SCRIPT;
580f3f0262cSandi}
581f3f0262cSandi
582f3f0262cSandi/**
58315fae107Sandi * Spamcheck against wordlist
58415fae107Sandi *
585f3f0262cSandi * Checks the wikitext against a list of blocked expressions
586f3f0262cSandi * returns true if the text contains any bad words
58715fae107Sandi *
588e403cc58SMichael Klier * Triggers COMMON_WORDBLOCK_BLOCKED
589e403cc58SMichael Klier *
590e403cc58SMichael Klier *  Action Plugins can use this event to inspect the blocked data
591e403cc58SMichael Klier *  and gain information about the user who was blocked.
592e403cc58SMichael Klier *
593e403cc58SMichael Klier *  Event data:
594e403cc58SMichael Klier *    data['matches']  - array of matches
595e403cc58SMichael Klier *    data['userinfo'] - information about the blocked user
596e403cc58SMichael Klier *      [ip]           - ip address
597e403cc58SMichael Klier *      [user]         - username (if logged in)
598e403cc58SMichael Klier *      [mail]         - mail address (if logged in)
599e403cc58SMichael Klier *      [name]         - real name (if logged in)
600e403cc58SMichael Klier *
60115fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
6026dffa0e0SAndreas Gohr * @author Michael Klier <chi@chimeric.de>
6036dffa0e0SAndreas Gohr * @param  string $text - optional text to check, if not given the globals are used
6046dffa0e0SAndreas Gohr * @return bool         - true if a spam word was found
605f3f0262cSandi */
6066dffa0e0SAndreas Gohrfunction checkwordblock($text = '') {
607f3f0262cSandi    global $TEXT;
6086dffa0e0SAndreas Gohr    global $PRE;
6096dffa0e0SAndreas Gohr    global $SUF;
610e0086ca2SAndreas Gohr    global $SUM;
611f3f0262cSandi    global $conf;
612e403cc58SMichael Klier    global $INFO;
613585bf44eSChristopher Smith    /* @var Input $INPUT */
614585bf44eSChristopher Smith    global $INPUT;
615f3f0262cSandi
616f3f0262cSandi    if(!$conf['usewordblock']) return false;
617f3f0262cSandi
618e0086ca2SAndreas Gohr    if(!$text) $text = "$PRE $TEXT $SUF $SUM";
6196dffa0e0SAndreas Gohr
620041d1964SAndreas Gohr    // we prepare the text a tiny bit to prevent spammers circumventing URL checks
6216dffa0e0SAndreas Gohr    $text = preg_replace('!(\b)(www\.[\w.:?\-;,]+?\.[\w.:?\-;,]+?[\w/\#~:.?+=&%@\!\-.:?\-;,]+?)([.:?\-;,]*[^\w/\#~:.?+=&%@\!\-.:?\-;,])!i', '\1http://\2 \2\3', $text);
622041d1964SAndreas Gohr
623b9ac8716Schris    $wordblocks = getWordblocks();
6243e2965d7Sandi    // how many lines to read at once (to work around some PCRE limits)
6253e2965d7Sandi    if(version_compare(phpversion(), '4.3.0', '<')) {
6263e2965d7Sandi        // old versions of PCRE define a maximum of parenthesises even if no
6273e2965d7Sandi        // backreferences are used - the maximum is 99
6283e2965d7Sandi        // this is very bad performancewise and may even be too high still
6293e2965d7Sandi        $chunksize = 40;
6303e2965d7Sandi    } else {
631a51d08efSAndreas Gohr        // read file in chunks of 200 - this should work around the
6323e2965d7Sandi        // MAX_PATTERN_SIZE in modern PCRE
633a51d08efSAndreas Gohr        $chunksize = 200;
6343e2965d7Sandi    }
635b9ac8716Schris    while($blocks = array_splice($wordblocks, 0, $chunksize)) {
636f3f0262cSandi        $re = array();
63749eb6e38SAndreas Gohr        // build regexp from blocks
638f3f0262cSandi        foreach($blocks as $block) {
639f3f0262cSandi            $block = preg_replace('/#.*$/', '', $block);
640f3f0262cSandi            $block = trim($block);
641f3f0262cSandi            if(empty($block)) continue;
642f3f0262cSandi            $re[] = $block;
643f3f0262cSandi        }
644e403cc58SMichael Klier        if(count($re) && preg_match('#('.join('|', $re).')#si', $text, $matches)) {
645e403cc58SMichael Klier            // prepare event data
646e403cc58SMichael Klier            $data['matches']        = $matches;
647585bf44eSChristopher Smith            $data['userinfo']['ip'] = $INPUT->server->str('REMOTE_ADDR');
648585bf44eSChristopher Smith            if($INPUT->server->str('REMOTE_USER')) {
649585bf44eSChristopher Smith                $data['userinfo']['user'] = $INPUT->server->str('REMOTE_USER');
650e403cc58SMichael Klier                $data['userinfo']['name'] = $INFO['userinfo']['name'];
651e403cc58SMichael Klier                $data['userinfo']['mail'] = $INFO['userinfo']['mail'];
652e403cc58SMichael Klier            }
653e403cc58SMichael Klier            $callback = create_function('', 'return true;');
654e403cc58SMichael Klier            return trigger_event('COMMON_WORDBLOCK_BLOCKED', $data, $callback, true);
655b9ac8716Schris        }
656703f6fdeSandi    }
657f3f0262cSandi    return false;
658f3f0262cSandi}
659f3f0262cSandi
660f3f0262cSandi/**
66115fae107Sandi * Return the IP of the client
66215fae107Sandi *
6636d8affe6SAndreas Gohr * Honours X-Forwarded-For and X-Real-IP Proxy Headers
66415fae107Sandi *
6656d8affe6SAndreas Gohr * It returns a comma separated list of IPs if the above mentioned
6666d8affe6SAndreas Gohr * headers are set. If the single parameter is set, it tries to return
6676d8affe6SAndreas Gohr * a routable public address, prefering the ones suplied in the X
6686d8affe6SAndreas Gohr * headers
6696d8affe6SAndreas Gohr *
67015fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
6713272d797SAndreas Gohr * @param  boolean $single If set only a single IP is returned
6723272d797SAndreas Gohr * @return string
673f3f0262cSandi */
6746d8affe6SAndreas Gohrfunction clientIP($single = false) {
675585bf44eSChristopher Smith    /* @var Input $INPUT */
676585bf44eSChristopher Smith    global $INPUT;
677585bf44eSChristopher Smith
6786d8affe6SAndreas Gohr    $ip   = array();
679585bf44eSChristopher Smith    $ip[] = $INPUT->server->str('REMOTE_ADDR');
680585bf44eSChristopher Smith    if($INPUT->server->str('HTTP_X_FORWARDED_FOR')) {
681585bf44eSChristopher Smith        $ip = array_merge($ip, explode(',', str_replace(' ', '', $INPUT->server->str('HTTP_X_FORWARDED_FOR'))));
682585bf44eSChristopher Smith    }
683585bf44eSChristopher Smith    if($INPUT->server->str('HTTP_X_REAL_IP')) {
684585bf44eSChristopher Smith        $ip = array_merge($ip, explode(',', str_replace(' ', '', $INPUT->server->str('HTTP_X_REAL_IP'))));
685585bf44eSChristopher Smith    }
6866d8affe6SAndreas Gohr
687dc14c6d1SGuy Brand    // some IPv4/v6 regexps borrowed from Feyd
688dc14c6d1SGuy Brand    // see: http://forums.devnetwork.net/viewtopic.php?f=38&t=53479
689dc14c6d1SGuy Brand    $dec_octet   = '(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|[0-9])';
690dc14c6d1SGuy Brand    $hex_digit   = '[A-Fa-f0-9]';
691dc14c6d1SGuy Brand    $h16         = "{$hex_digit}{1,4}";
692dc14c6d1SGuy Brand    $IPv4Address = "$dec_octet\\.$dec_octet\\.$dec_octet\\.$dec_octet";
693dc14c6d1SGuy Brand    $ls32        = "(?:$h16:$h16|$IPv4Address)";
694dc14c6d1SGuy Brand    $IPv6Address =
695dc14c6d1SGuy Brand        "(?:(?:{$IPv4Address})|(?:".
696dc14c6d1SGuy Brand            "(?:$h16:){6}$ls32".
697dc14c6d1SGuy Brand            "|::(?:$h16:){5}$ls32".
698dc14c6d1SGuy Brand            "|(?:$h16)?::(?:$h16:){4}$ls32".
699dc14c6d1SGuy Brand            "|(?:(?:$h16:){0,1}$h16)?::(?:$h16:){3}$ls32".
700dc14c6d1SGuy Brand            "|(?:(?:$h16:){0,2}$h16)?::(?:$h16:){2}$ls32".
701dc14c6d1SGuy Brand            "|(?:(?:$h16:){0,3}$h16)?::(?:$h16:){1}$ls32".
702dc14c6d1SGuy Brand            "|(?:(?:$h16:){0,4}$h16)?::$ls32".
703dc14c6d1SGuy Brand            "|(?:(?:$h16:){0,5}$h16)?::$h16".
704dc14c6d1SGuy Brand            "|(?:(?:$h16:){0,6}$h16)?::".
705dc14c6d1SGuy Brand            ")(?:\\/(?:12[0-8]|1[0-1][0-9]|[1-9][0-9]|[0-9]))?)";
706dc14c6d1SGuy Brand
7076d8affe6SAndreas Gohr    // remove any non-IP stuff
7086d8affe6SAndreas Gohr    $cnt   = count($ip);
7094ff28443Schris    $match = array();
7106d8affe6SAndreas Gohr    for($i = 0; $i < $cnt; $i++) {
711dc14c6d1SGuy Brand        if(preg_match("/^$IPv4Address$/", $ip[$i], $match) || preg_match("/^$IPv6Address$/", $ip[$i], $match)) {
7124ff28443Schris            $ip[$i] = $match[0];
7134ff28443Schris        } else {
7144ff28443Schris            $ip[$i] = '';
7154ff28443Schris        }
7166d8affe6SAndreas Gohr        if(empty($ip[$i])) unset($ip[$i]);
717f3f0262cSandi    }
7186d8affe6SAndreas Gohr    $ip = array_values(array_unique($ip));
7196d8affe6SAndreas Gohr    if(!$ip[0]) $ip[0] = '0.0.0.0'; // for some strange reason we don't have a IP
7206d8affe6SAndreas Gohr
7216d8affe6SAndreas Gohr    if(!$single) return join(',', $ip);
7226d8affe6SAndreas Gohr
7236d8affe6SAndreas Gohr    // decide which IP to use, trying to avoid local addresses
7246d8affe6SAndreas Gohr    $ip = array_reverse($ip);
7256d8affe6SAndreas Gohr    foreach($ip as $i) {
7262343a762SAndreas Gohr        if(preg_match('/^(::1|[fF][eE]80:|127\.|10\.|192\.168\.|172\.((1[6-9])|(2[0-9])|(3[0-1]))\.)/', $i)) {
7276d8affe6SAndreas Gohr            continue;
7286d8affe6SAndreas Gohr        } else {
7296d8affe6SAndreas Gohr            return $i;
7306d8affe6SAndreas Gohr        }
7316d8affe6SAndreas Gohr    }
7326d8affe6SAndreas Gohr    // still here? just use the first (last) address
7336d8affe6SAndreas Gohr    return $ip[0];
734f3f0262cSandi}
735f3f0262cSandi
736f3f0262cSandi/**
7371c548ebeSAndreas Gohr * Check if the browser is on a mobile device
7381c548ebeSAndreas Gohr *
7391c548ebeSAndreas Gohr * Adapted from the example code at url below
7401c548ebeSAndreas Gohr *
7411c548ebeSAndreas Gohr * @link http://www.brainhandles.com/2007/10/15/detecting-mobile-browsers/#code
7421c548ebeSAndreas Gohr */
7431c548ebeSAndreas Gohrfunction clientismobile() {
744585bf44eSChristopher Smith    /* @var Input $INPUT */
745585bf44eSChristopher Smith    global $INPUT;
7461c548ebeSAndreas Gohr
747585bf44eSChristopher Smith    if($INPUT->server->has('HTTP_X_WAP_PROFILE')) return true;
7481c548ebeSAndreas Gohr
749585bf44eSChristopher Smith    if(preg_match('/wap\.|\.wap/i', $INPUT->server->str('HTTP_ACCEPT'))) return true;
7501c548ebeSAndreas Gohr
751585bf44eSChristopher Smith    if(!$INPUT->server->has('HTTP_USER_AGENT')) return false;
7521c548ebeSAndreas Gohr
7531c548ebeSAndreas Gohr    $uamatches = 'midp|j2me|avantg|docomo|novarra|palmos|palmsource|240x320|opwv|chtml|pda|windows ce|mmp\/|blackberry|mib\/|symbian|wireless|nokia|hand|mobi|phone|cdm|up\.b|audio|SIE\-|SEC\-|samsung|HTC|mot\-|mitsu|sagem|sony|alcatel|lg|erics|vx|NEC|philips|mmm|xx|panasonic|sharp|wap|sch|rover|pocket|benq|java|pt|pg|vox|amoi|bird|compal|kg|voda|sany|kdd|dbt|sendo|sgh|gradi|jb|\d\d\di|moto';
7541c548ebeSAndreas Gohr
755585bf44eSChristopher Smith    if(preg_match("/$uamatches/i", $INPUT->server->str('HTTP_USER_AGENT'))) return true;
7561c548ebeSAndreas Gohr
7571c548ebeSAndreas Gohr    return false;
7581c548ebeSAndreas Gohr}
7591c548ebeSAndreas Gohr
7601c548ebeSAndreas Gohr/**
76163211f61SGlen Harris * Convert one or more comma separated IPs to hostnames
76263211f61SGlen Harris *
76322ef1e32SAndreas Gohr * If $conf['dnslookups'] is disabled it simply returns the input string
76422ef1e32SAndreas Gohr *
76563211f61SGlen Harris * @author Glen Harris <astfgl@iamnota.org>
7663272d797SAndreas Gohr * @param  string $ips comma separated list of IP addresses
7673272d797SAndreas Gohr * @return string a comma separated list of hostnames
76863211f61SGlen Harris */
76963211f61SGlen Harrisfunction gethostsbyaddrs($ips) {
77022ef1e32SAndreas Gohr    global $conf;
77122ef1e32SAndreas Gohr    if(!$conf['dnslookups']) return $ips;
77222ef1e32SAndreas Gohr
77363211f61SGlen Harris    $hosts = array();
77463211f61SGlen Harris    $ips   = explode(',', $ips);
775551a720fSMichael Klier
776551a720fSMichael Klier    if(is_array($ips)) {
7773886270dSAndreas Gohr        foreach($ips as $ip) {
778551a720fSMichael Klier            $hosts[] = gethostbyaddr(trim($ip));
77963211f61SGlen Harris        }
780551a720fSMichael Klier        return join(',', $hosts);
781551a720fSMichael Klier    } else {
782551a720fSMichael Klier        return gethostbyaddr(trim($ips));
783551a720fSMichael Klier    }
78463211f61SGlen Harris}
78563211f61SGlen Harris
78663211f61SGlen Harris/**
78715fae107Sandi * Checks if a given page is currently locked.
78815fae107Sandi *
789f3f0262cSandi * removes stale lockfiles
79015fae107Sandi *
79115fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
792f3f0262cSandi */
793f3f0262cSandifunction checklock($id) {
794f3f0262cSandi    global $conf;
795585bf44eSChristopher Smith    /* @var Input $INPUT */
796585bf44eSChristopher Smith    global $INPUT;
797585bf44eSChristopher Smith
798c9b4bd1eSBen Coburn    $lock = wikiLockFN($id);
799f3f0262cSandi
800f3f0262cSandi    //no lockfile
801f3f0262cSandi    if(!@file_exists($lock)) return false;
802f3f0262cSandi
803f3f0262cSandi    //lockfile expired
804f3f0262cSandi    if((time() - filemtime($lock)) > $conf['locktime']) {
805d8186216SBen Coburn        @unlink($lock);
806f3f0262cSandi        return false;
807f3f0262cSandi    }
808f3f0262cSandi
809f3f0262cSandi    //my own lock
8106d2af55dSChristopher Smith    @list($ip, $session) = explode("\n", io_readFile($lock));
8110712fefaSAndreas Gohr    if($ip == $INPUT->server->str('REMOTE_USER') || $ip == clientIP() || (session_id() && $session == session_id())) {
812f3f0262cSandi        return false;
813f3f0262cSandi    }
814f3f0262cSandi
815f3f0262cSandi    return $ip;
816f3f0262cSandi}
817f3f0262cSandi
818f3f0262cSandi/**
81915fae107Sandi * Lock a page for editing
82015fae107Sandi *
82115fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
822f3f0262cSandi */
823f3f0262cSandifunction lock($id) {
824544ed901SDaniel Calviño Sánchez    global $conf;
825585bf44eSChristopher Smith    /* @var Input $INPUT */
826585bf44eSChristopher Smith    global $INPUT;
827544ed901SDaniel Calviño Sánchez
828544ed901SDaniel Calviño Sánchez    if($conf['locktime'] == 0) {
829544ed901SDaniel Calviño Sánchez        return;
830544ed901SDaniel Calviño Sánchez    }
831544ed901SDaniel Calviño Sánchez
832c9b4bd1eSBen Coburn    $lock = wikiLockFN($id);
833585bf44eSChristopher Smith    if($INPUT->server->str('REMOTE_USER')) {
834585bf44eSChristopher Smith        io_saveFile($lock, $INPUT->server->str('REMOTE_USER'));
835f3f0262cSandi    } else {
83685fef7e2SAndreas Gohr        io_saveFile($lock, clientIP()."\n".session_id());
837f3f0262cSandi    }
838f3f0262cSandi}
839f3f0262cSandi
840f3f0262cSandi/**
84115fae107Sandi * Unlock a page if it was locked by the user
842f3f0262cSandi *
84315fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
8443272d797SAndreas Gohr * @param string $id page id to unlock
84515fae107Sandi * @return bool true if a lock was removed
846f3f0262cSandi */
847f3f0262cSandifunction unlock($id) {
848585bf44eSChristopher Smith    /* @var Input $INPUT */
849585bf44eSChristopher Smith    global $INPUT;
850585bf44eSChristopher Smith
851c9b4bd1eSBen Coburn    $lock = wikiLockFN($id);
852f3f0262cSandi    if(@file_exists($lock)) {
8536d2af55dSChristopher Smith        @list($ip, $session) = explode("\n", io_readFile($lock));
854585bf44eSChristopher Smith        if($ip == $INPUT->server->str('REMOTE_USER') || $ip == clientIP() || $session == session_id()) {
855f3f0262cSandi            @unlink($lock);
856f3f0262cSandi            return true;
857f3f0262cSandi        }
858f3f0262cSandi    }
859f3f0262cSandi    return false;
860f3f0262cSandi}
861f3f0262cSandi
862f3f0262cSandi/**
863f3f0262cSandi * convert line ending to unix format
864f3f0262cSandi *
8656db7468bSAndreas Gohr * also makes sure the given text is valid UTF-8
8666db7468bSAndreas Gohr *
86715fae107Sandi * @see    formText() for 2crlf conversion
86815fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
869f3f0262cSandi */
870f3f0262cSandifunction cleanText($text) {
871f3f0262cSandi    $text = preg_replace("/(\015\012)|(\015)/", "\012", $text);
8726db7468bSAndreas Gohr
8736db7468bSAndreas Gohr    // if the text is not valid UTF-8 we simply assume latin1
8746db7468bSAndreas Gohr    // this won't break any worse than it breaks with the wrong encoding
8756db7468bSAndreas Gohr    // but might actually fix the problem in many cases
8766db7468bSAndreas Gohr    if(!utf8_check($text)) $text = utf8_encode($text);
8776db7468bSAndreas Gohr
878f3f0262cSandi    return $text;
879f3f0262cSandi}
880f3f0262cSandi
881f3f0262cSandi/**
882f3f0262cSandi * Prepares text for print in Webforms by encoding special chars.
883f3f0262cSandi * It also converts line endings to Windows format which is
884f3f0262cSandi * pseudo standard for webforms.
885f3f0262cSandi *
88615fae107Sandi * @see    cleanText() for 2unix conversion
88715fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
888f3f0262cSandi */
889f3f0262cSandifunction formText($text) {
8905b7d45a5SAndreas Gohr    $text = str_replace("\012", "\015\012", $text);
891f3f0262cSandi    return htmlspecialchars($text);
892f3f0262cSandi}
893f3f0262cSandi
894f3f0262cSandi/**
89515fae107Sandi * Returns the specified local text in raw format
89615fae107Sandi *
89715fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
898f3f0262cSandi */
8992adaf2b8SAndreas Gohrfunction rawLocale($id, $ext = 'txt') {
9002adaf2b8SAndreas Gohr    return io_readFile(localeFN($id, $ext));
901f3f0262cSandi}
902f3f0262cSandi
903f3f0262cSandi/**
904f3f0262cSandi * Returns the raw WikiText
90515fae107Sandi *
90615fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
907f3f0262cSandi */
908f3f0262cSandifunction rawWiki($id, $rev = '') {
909cc7d0c94SBen Coburn    return io_readWikiPage(wikiFN($id, $rev), $id, $rev);
910f3f0262cSandi}
911f3f0262cSandi
912f3f0262cSandi/**
9137146cee2SAndreas Gohr * Returns the pagetemplate contents for the ID's namespace
9147146cee2SAndreas Gohr *
9157b84afa2SAndreas Gohr * @triggers COMMON_PAGETPL_LOAD
9167146cee2SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
9177146cee2SAndreas Gohr */
918fe17917eSAdrian Langfunction pageTemplate($id) {
919a15ce62dSEsther Brunner    global $conf;
920e29549feSAndreas Gohr
921fe17917eSAdrian Lang    if(is_array($id)) $id = $id[0];
922e29549feSAndreas Gohr
9237b84afa2SAndreas Gohr    // prepare initial event data
9247b84afa2SAndreas Gohr    $data = array(
9257b84afa2SAndreas Gohr        'id'        => $id, // the id of the page to be created
9267b84afa2SAndreas Gohr        'tpl'       => '', // the text used as template
9277b84afa2SAndreas Gohr        'tplfile'   => '', // the file above text was/should be loaded from
9287b84afa2SAndreas Gohr        'doreplace' => true // should wildcard replacements be done on the text?
9297b84afa2SAndreas Gohr    );
9307b84afa2SAndreas Gohr
9317b84afa2SAndreas Gohr    $evt = new Doku_Event('COMMON_PAGETPL_LOAD', $data);
9327b84afa2SAndreas Gohr    if($evt->advise_before(true)) {
9337b84afa2SAndreas Gohr        // the before event might have loaded the content already
9347b84afa2SAndreas Gohr        if(empty($data['tpl'])) {
9357b84afa2SAndreas Gohr            // if the before event did not set a template file, try to find one
9367b84afa2SAndreas Gohr            if(empty($data['tplfile'])) {
937fe17917eSAdrian Lang                $path = dirname(wikiFN($id));
938e29549feSAndreas Gohr                if(@file_exists($path.'/_template.txt')) {
9397b84afa2SAndreas Gohr                    $data['tplfile'] = $path.'/_template.txt';
940e29549feSAndreas Gohr                } else {
941e29549feSAndreas Gohr                    // search upper namespaces for templates
942e29549feSAndreas Gohr                    $len = strlen(rtrim($conf['datadir'], '/'));
943e29549feSAndreas Gohr                    while(strlen($path) >= $len) {
944e29549feSAndreas Gohr                        if(@file_exists($path.'/__template.txt')) {
9457b84afa2SAndreas Gohr                            $data['tplfile'] = $path.'/__template.txt';
946e29549feSAndreas Gohr                            break;
947e29549feSAndreas Gohr                        }
948e29549feSAndreas Gohr                        $path = substr($path, 0, strrpos($path, '/'));
949e29549feSAndreas Gohr                    }
950e29549feSAndreas Gohr                }
9517b84afa2SAndreas Gohr            }
9527b84afa2SAndreas Gohr            // load the content
9533d7ac595SMichael Hamann            $data['tpl'] = io_readFile($data['tplfile']);
9547b84afa2SAndreas Gohr        }
955a1bbd05bSMichael Hamann        if($data['doreplace']) parsePageTemplate($data);
9567b84afa2SAndreas Gohr    }
9577b84afa2SAndreas Gohr    $evt->advise_after();
9587b84afa2SAndreas Gohr    unset($evt);
9597b84afa2SAndreas Gohr
960fe17917eSAdrian Lang    return $data['tpl'];
9612b1223ecSAdrian Lang}
9622b1223ecSAdrian Lang
9632b1223ecSAdrian Lang/**
9642b1223ecSAdrian Lang * Performs common page template replacements
9657b84afa2SAndreas Gohr * This works on data from COMMON_PAGETPL_LOAD
9662b1223ecSAdrian Lang *
9672b1223ecSAdrian Lang * @author Andreas Gohr <andi@splitbrain.org>
9682b1223ecSAdrian Lang */
969d535a2e9Sstretchyboyfunction parsePageTemplate(&$data) {
9703272d797SAndreas Gohr    /**
9713272d797SAndreas Gohr     * @var string $id        the id of the page to be created
9723272d797SAndreas Gohr     * @var string $tpl       the text used as template
9733272d797SAndreas Gohr     * @var string $tplfile   the file above text was/should be loaded from
9743272d797SAndreas Gohr     * @var bool   $doreplace should wildcard replacements be done on the text?
9753272d797SAndreas Gohr     */
976fe17917eSAdrian Lang    extract($data);
977fe17917eSAdrian Lang
978b856f7dfSAdrian Lang    global $USERINFO;
979bce53b1fSAdrian Lang    global $conf;
980585bf44eSChristopher Smith    /* @var Input $INPUT */
981585bf44eSChristopher Smith    global $INPUT;
982e29549feSAndreas Gohr
983e29549feSAndreas Gohr    // replace placeholders
98426ece5a7SAndreas Gohr    $file = noNS($id);
98537c1acbdSAdrian Lang    $page = strtr($file, $conf['sepchar'], ' ');
98626ece5a7SAndreas Gohr
9873272d797SAndreas Gohr    $tpl = str_replace(
9883272d797SAndreas Gohr        array(
98926ece5a7SAndreas Gohr             '@ID@',
99026ece5a7SAndreas Gohr             '@NS@',
99126ece5a7SAndreas Gohr             '@FILE@',
99226ece5a7SAndreas Gohr             '@!FILE@',
99326ece5a7SAndreas Gohr             '@!FILE!@',
99426ece5a7SAndreas Gohr             '@PAGE@',
99526ece5a7SAndreas Gohr             '@!PAGE@',
99626ece5a7SAndreas Gohr             '@!!PAGE@',
99726ece5a7SAndreas Gohr             '@!PAGE!@',
99826ece5a7SAndreas Gohr             '@USER@',
99926ece5a7SAndreas Gohr             '@NAME@',
100026ece5a7SAndreas Gohr             '@MAIL@',
100126ece5a7SAndreas Gohr             '@DATE@',
100226ece5a7SAndreas Gohr        ),
100326ece5a7SAndreas Gohr        array(
100426ece5a7SAndreas Gohr             $id,
100526ece5a7SAndreas Gohr             getNS($id),
100626ece5a7SAndreas Gohr             $file,
100726ece5a7SAndreas Gohr             utf8_ucfirst($file),
100826ece5a7SAndreas Gohr             utf8_strtoupper($file),
100926ece5a7SAndreas Gohr             $page,
101026ece5a7SAndreas Gohr             utf8_ucfirst($page),
101126ece5a7SAndreas Gohr             utf8_ucwords($page),
101226ece5a7SAndreas Gohr             utf8_strtoupper($page),
1013585bf44eSChristopher Smith             $INPUT->server->str('REMOTE_USER'),
1014b856f7dfSAdrian Lang             $USERINFO['name'],
1015b856f7dfSAdrian Lang             $USERINFO['mail'],
101626ece5a7SAndreas Gohr             $conf['dformat'],
10173272d797SAndreas Gohr        ), $tpl
10183272d797SAndreas Gohr    );
101926ece5a7SAndreas Gohr
10207d644fc8SAndreas Gohr    // we need the callback to work around strftime's char limit
10217d644fc8SAndreas Gohr    $tpl         = preg_replace_callback('/%./', create_function('$m', 'return strftime($m[0]);'), $tpl);
1022d535a2e9Sstretchyboy    $data['tpl'] = $tpl;
1023a15ce62dSEsther Brunner    return $tpl;
10247146cee2SAndreas Gohr}
10257146cee2SAndreas Gohr
10267146cee2SAndreas Gohr/**
102715fae107Sandi * Returns the raw Wiki Text in three slices.
102815fae107Sandi *
102915fae107Sandi * The range parameter needs to have the form "from-to"
103015cfe303Sandi * and gives the range of the section in bytes - no
103115cfe303Sandi * UTF-8 awareness is needed.
1032f3f0262cSandi * The returned order is prefix, section and suffix.
103315fae107Sandi *
103415fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
1035f3f0262cSandi */
1036f3f0262cSandifunction rawWikiSlices($range, $id, $rev = '') {
1037cc7d0c94SBen Coburn    $text = io_readWikiPage(wikiFN($id, $rev), $id, $rev);
1038f3f0262cSandi
103980fcb268SAdrian Lang    // Parse range
104080fcb268SAdrian Lang    list($from, $to) = explode('-', $range, 2);
104180fcb268SAdrian Lang    // Make range zero-based, use defaults if marker is missing
104280fcb268SAdrian Lang    $from = !$from ? 0 : ($from - 1);
104380fcb268SAdrian Lang    $to   = !$to ? strlen($text) : ($to - 1);
104480fcb268SAdrian Lang
104580fcb268SAdrian Lang    $slices[0] = substr($text, 0, $from);
104680fcb268SAdrian Lang    $slices[1] = substr($text, $from, $to - $from);
104715cfe303Sandi    $slices[2] = substr($text, $to);
1048f3f0262cSandi    return $slices;
1049f3f0262cSandi}
1050f3f0262cSandi
1051f3f0262cSandi/**
105215fae107Sandi * Joins wiki text slices
105315fae107Sandi *
105480fcb268SAdrian Lang * function to join the text slices.
1055f3f0262cSandi * When the pretty parameter is set to true it adds additional empty
1056f3f0262cSandi * lines between sections if needed (used on saving).
105715fae107Sandi *
105815fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
1059f3f0262cSandi */
1060f3f0262cSandifunction con($pre, $text, $suf, $pretty = false) {
1061f3f0262cSandi    if($pretty) {
106280fcb268SAdrian Lang        if($pre !== '' && substr($pre, -1) !== "\n" &&
10633272d797SAndreas Gohr            substr($text, 0, 1) !== "\n"
10643272d797SAndreas Gohr        ) {
106580fcb268SAdrian Lang            $pre .= "\n";
106680fcb268SAdrian Lang        }
106780fcb268SAdrian Lang        if($suf !== '' && substr($text, -1) !== "\n" &&
10683272d797SAndreas Gohr            substr($suf, 0, 1) !== "\n"
10693272d797SAndreas Gohr        ) {
107080fcb268SAdrian Lang            $text .= "\n";
107180fcb268SAdrian Lang        }
1072f3f0262cSandi    }
1073f3f0262cSandi
1074f3f0262cSandi    return $pre.$text.$suf;
1075f3f0262cSandi}
1076f3f0262cSandi
1077f3f0262cSandi/**
1078a701424fSBen Coburn * Saves a wikitext by calling io_writeWikiPage.
1079a701424fSBen Coburn * Also directs changelog and attic updates.
108015fae107Sandi *
108115fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
108271726d78SBen Coburn * @author Ben Coburn <btcoburn@silicodon.net>
1083f3f0262cSandi */
1084b6912aeaSAndreas Gohrfunction saveWikiText($id, $text, $summary, $minor = false) {
1085a701424fSBen Coburn    /* Note to developers:
1086a701424fSBen Coburn       This code is subtle and delicate. Test the behavior of
1087a701424fSBen Coburn       the attic and changelog with dokuwiki and external edits
1088a701424fSBen Coburn       after any changes. External edits change the wiki page
1089a701424fSBen Coburn       directly without using php or dokuwiki.
1090a701424fSBen Coburn     */
1091f3f0262cSandi    global $conf;
1092f3f0262cSandi    global $lang;
109371726d78SBen Coburn    global $REV;
1094585bf44eSChristopher Smith    /* @var Input $INPUT */
1095585bf44eSChristopher Smith    global $INPUT;
1096585bf44eSChristopher Smith
1097f3f0262cSandi    // ignore if no changes were made
1098f3f0262cSandi    if($text == rawWiki($id, '')) {
1099f3f0262cSandi        return;
1100f3f0262cSandi    }
1101f3f0262cSandi
1102f3f0262cSandi    $file        = wikiFN($id);
1103a701424fSBen Coburn    $old         = @filemtime($file); // from page
1104407e65b9SAndreas Gohr    $wasRemoved  = (trim($text) == ''); // check for empty or whitespace only
1105d8186216SBen Coburn    $wasCreated  = !@file_exists($file);
110671726d78SBen Coburn    $wasReverted = ($REV == true);
1107047bad06SGerrit Uitslag    $pagelog     = new PageChangeLog($id, 1024);
1108e45b34cdSBen Coburn    $newRev      = false;
1109f523c971SGerrit Uitslag    $oldRev      = $pagelog->getRevisions(-1, 1); // from changelog
1110a701424fSBen Coburn    $oldRev      = (int) (empty($oldRev) ? 0 : $oldRev[0]);
1111a701424fSBen Coburn    if(!@file_exists(wikiFN($id, $old)) && @file_exists($file) && $old >= $oldRev) {
111246844156SBen Coburn        // add old revision to the attic if missing
111346844156SBen Coburn        saveOldRevision($id);
111446844156SBen Coburn        // add a changelog entry if this edit came from outside dokuwiki
1115a701424fSBen Coburn        if($old > $oldRev) {
1116ebf1501fSBen Coburn            addLogEntry($old, $id, DOKU_CHANGE_TYPE_EDIT, $lang['external_edit'], '', array('ExternalEdit'=> true));
111746844156SBen Coburn            // remove soon to be stale instructions
111846844156SBen Coburn            $cache = new cache_instructions($id, $file);
111946844156SBen Coburn            $cache->removeCache();
112046844156SBen Coburn        }
112146844156SBen Coburn    }
1122f3f0262cSandi
112371726d78SBen Coburn    if($wasRemoved) {
112430725328SGabriel Birke        // Send "update" event with empty data, so plugins can react to page deletion
112530725328SGabriel Birke        $data = array(array($file, '', false), getNS($id), noNS($id), false);
112630725328SGabriel Birke        trigger_event('IO_WIKIPAGE_WRITE', $data);
1127e45b34cdSBen Coburn        // pre-save deleted revision
1128e45b34cdSBen Coburn        @touch($file);
112946844156SBen Coburn        clearstatcache();
1130e45b34cdSBen Coburn        $newRev = saveOldRevision($id);
1131e1f3d9e1SEsther Brunner        // remove empty file
1132f3f0262cSandi        @unlink($file);
1133c5f92742SMichael Hamann        // don't remove old meta info as it should be saved, plugins can use IO_WIKIPAGE_WRITE for removing their metadata...
1134c5f92742SMichael Hamann        // purge non-persistant meta data
11353d1f9ec3SMichael Klier        p_purge_metadata($id);
1136f3f0262cSandi        $del = true;
11373ce054b3Sandi        // autoset summary on deletion
11383ce054b3Sandi        if(empty($summary)) $summary = $lang['deleted'];
113953d6ccfeSandi        // remove empty namespaces
1140cc7d0c94SBen Coburn        io_sweepNS($id, 'datadir');
1141cc7d0c94SBen Coburn        io_sweepNS($id, 'mediadir');
1142f3f0262cSandi    } else {
1143cc7d0c94SBen Coburn        // save file (namespace dir is created in io_writeWikiPage)
1144cc7d0c94SBen Coburn        io_writeWikiPage($file, $text, $id);
114546844156SBen Coburn        // pre-save the revision, to keep the attic in sync
114646844156SBen Coburn        $newRev = saveOldRevision($id);
1147f3f0262cSandi        $del    = false;
1148f3f0262cSandi    }
1149f3f0262cSandi
115071726d78SBen Coburn    // select changelog line type
115171726d78SBen Coburn    $extra = '';
1152ebf1501fSBen Coburn    $type  = DOKU_CHANGE_TYPE_EDIT;
115371726d78SBen Coburn    if($wasReverted) {
1154ebf1501fSBen Coburn        $type  = DOKU_CHANGE_TYPE_REVERT;
115571726d78SBen Coburn        $extra = $REV;
11563272d797SAndreas Gohr    } else if($wasCreated) {
11573272d797SAndreas Gohr        $type = DOKU_CHANGE_TYPE_CREATE;
11583272d797SAndreas Gohr    } else if($wasRemoved) {
11593272d797SAndreas Gohr        $type = DOKU_CHANGE_TYPE_DELETE;
1160585bf44eSChristopher Smith    } else if($minor && $conf['useacl'] && $INPUT->server->str('REMOTE_USER')) {
11613272d797SAndreas Gohr        $type = DOKU_CHANGE_TYPE_MINOR_EDIT;
11623272d797SAndreas Gohr    } //minor edits only for logged in users
116371726d78SBen Coburn
1164e45b34cdSBen Coburn    addLogEntry($newRev, $id, $type, $summary, $extra);
116526a0801fSAndreas Gohr    // send notify mails
116690033e9dSAndreas Gohr    notify($id, 'admin', $old, $summary, $minor);
116790033e9dSAndreas Gohr    notify($id, 'subscribers', $old, $summary, $minor);
1168f3f0262cSandi
1169ce6b63d9Schris    // update the purgefile (timestamp of the last time anything within the wiki was changed)
117098407a7aSandi    io_saveFile($conf['cachedir'].'/purgefile', time());
11712eccbdaaSGina Haeussge
11722eccbdaaSGina Haeussge    // if useheading is enabled, purge the cache of all linking pages
1173fe9ec250SChris Smith    if(useHeading('content')) {
117407ff0babSMichael Hamann        $pages = ft_backlinks($id, true);
11752eccbdaaSGina Haeussge        foreach($pages as $page) {
11762eccbdaaSGina Haeussge            $cache = new cache_renderer($page, wikiFN($page), 'xhtml');
11772eccbdaaSGina Haeussge            $cache->removeCache();
11782eccbdaaSGina Haeussge        }
11792eccbdaaSGina Haeussge    }
1180f3f0262cSandi}
1181f3f0262cSandi
1182f3f0262cSandi/**
1183f3f0262cSandi * moves the current version to the attic and returns its
1184f3f0262cSandi * revision date
118515fae107Sandi *
118615fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
1187f3f0262cSandi */
1188f3f0262cSandifunction saveOldRevision($id) {
1189f3f0262cSandi    $oldf = wikiFN($id);
1190f3f0262cSandi    if(!@file_exists($oldf)) return '';
1191f3f0262cSandi    $date = filemtime($oldf);
1192f3f0262cSandi    $newf = wikiFN($id, $date);
1193cc7d0c94SBen Coburn    io_writeWikiPage($newf, rawWiki($id), $id, $date);
1194f3f0262cSandi    return $date;
1195f3f0262cSandi}
1196f3f0262cSandi
1197f3f0262cSandi/**
1198fde10de4SAdrian Lang * Sends a notify mail on page change or registration
119926a0801fSAndreas Gohr *
120026a0801fSAndreas Gohr * @param string     $id       The changed page
1201fde10de4SAdrian Lang * @param string     $who      Who to notify (admin|subscribers|register)
12023272d797SAndreas Gohr * @param int|string $rev Old page revision
120326a0801fSAndreas Gohr * @param string     $summary  What changed
120490033e9dSAndreas Gohr * @param boolean    $minor    Is this a minor edit?
120502a498e7Schris * @param array      $replace  Additional string substitutions, @KEY@ to be replaced by value
120615fae107Sandi *
12073272d797SAndreas Gohr * @return bool
120815fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
1209f3f0262cSandi */
121002a498e7Schrisfunction notify($id, $who, $rev = '', $summary = '', $minor = false, $replace = array()) {
1211f3f0262cSandi    global $conf;
1212585bf44eSChristopher Smith    /* @var Input $INPUT */
1213585bf44eSChristopher Smith    global $INPUT;
1214b158d625SSteven Danz
12156df843eeSAndreas Gohr    // decide if there is something to do, eg. whom to mail
121626a0801fSAndreas Gohr    if($who == 'admin') {
12173272d797SAndreas Gohr        if(empty($conf['notify'])) return false; //notify enabled?
12182ed38036SAndreas Gohr        $tpl = 'mailtext';
121926a0801fSAndreas Gohr        $to  = $conf['notify'];
122026a0801fSAndreas Gohr    } elseif($who == 'subscribers') {
122184c1127cSAndreas Gohr        if(!actionOK('subscribe')) return false; //subscribers enabled?
1222585bf44eSChristopher Smith        if($conf['useacl'] && $INPUT->server->str('REMOTE_USER') && $minor) return false; //skip minors
12238881fcc9SAdrian Lang        $data = array('id' => $id, 'addresslist' => '', 'self' => false);
12243272d797SAndreas Gohr        trigger_event(
12253272d797SAndreas Gohr            'COMMON_NOTIFY_ADDRESSLIST', $data,
1226835242b0SAndreas Gohr            array(new Subscription(), 'notifyaddresses')
12273272d797SAndreas Gohr        );
12282ed38036SAndreas Gohr        $to = $data['addresslist'];
12292ed38036SAndreas Gohr        if(empty($to)) return false;
12302ed38036SAndreas Gohr        $tpl = 'subscr_single';
123126a0801fSAndreas Gohr    } else {
12323272d797SAndreas Gohr        return false; //just to be safe
123326a0801fSAndreas Gohr    }
123426a0801fSAndreas Gohr
12356df843eeSAndreas Gohr    // prepare content
12362ed38036SAndreas Gohr    $subscription = new Subscription();
12372ed38036SAndreas Gohr    return $subscription->send_diff($to, $tpl, $id, $rev, $summary);
1238f3f0262cSandi}
12392ed38036SAndreas Gohr
124015fae107Sandi/**
124171f7bde7SAndreas Gohr * extracts the query from a search engine referrer
124215fae107Sandi *
124315fae107Sandi * @author Andreas Gohr <andi@splitbrain.org>
124471f7bde7SAndreas Gohr * @author Todd Augsburger <todd@rollerorgans.com>
1245f3f0262cSandi */
1246f3f0262cSandifunction getGoogleQuery() {
1247585bf44eSChristopher Smith    /* @var Input $INPUT */
1248585bf44eSChristopher Smith    global $INPUT;
1249585bf44eSChristopher Smith
1250585bf44eSChristopher Smith    if(!$INPUT->server->has('HTTP_REFERER')) {
1251c66972f2SAdrian Lang        return '';
1252c66972f2SAdrian Lang    }
1253585bf44eSChristopher Smith    $url = parse_url($INPUT->server->str('HTTP_REFERER'));
1254f3f0262cSandi
1255079b3ac1SAndreas Gohr    // only handle common SEs
1256079b3ac1SAndreas Gohr    if(!preg_match('/(google|bing|yahoo|ask|duckduckgo|babylon|aol|yandex)/',$url['host'])) return '';
1257e4d8a516SKazutaka Miyasaka
1258079b3ac1SAndreas Gohr    $query = array();
1259e4d8a516SKazutaka Miyasaka    // temporary workaround against PHP bug #49733
1260e4d8a516SKazutaka Miyasaka    // see http://bugs.php.net/bug.php?id=49733
1261e4d8a516SKazutaka Miyasaka    if(UTF8_MBSTRING) $enc = mb_internal_encoding();
1262f3f0262cSandi    parse_str($url['query'], $query);
1263e4d8a516SKazutaka Miyasaka    if(UTF8_MBSTRING) mb_internal_encoding($enc);
1264e4d8a516SKazutaka Miyasaka
1265c66972f2SAdrian Lang    $q = '';
1266079b3ac1SAndreas Gohr    if(isset($query['q'])){
1267079b3ac1SAndreas Gohr        $q = $query['q'];
1268079b3ac1SAndreas Gohr    }elseif(isset($query['p'])){
1269079b3ac1SAndreas Gohr        $q = $query['p'];
1270079b3ac1SAndreas Gohr    }elseif(isset($query['query'])){
1271079b3ac1SAndreas Gohr        $q = $query['query'];
1272079b3ac1SAndreas Gohr    }
1273079b3ac1SAndreas Gohr    $q = trim($q);
1274f3f0262cSandi
1275079b3ac1SAndreas Gohr    if(!$q) return '';
12766531ab03SAndreas Gohr    $q = preg_split('/[\s\'"\\\\`()\]\[?:!\.{};,#+*<>\\/]+/', $q, -1, PREG_SPLIT_NO_EMPTY);
1277f93b3b50SAndreas Gohr    return $q;
1278f3f0262cSandi}
1279f3f0262cSandi
1280f3f0262cSandi/**
1281f3f0262cSandi * Return the human readable size of a file
1282f3f0262cSandi *
1283f3f0262cSandi * @param       int $size A file size
1284f3f0262cSandi * @param       int $dec A number of decimal places
128574160ca1SGerrit Uitslag * @return string human readable size
1286f3f0262cSandi * @author      Martin Benjamin <b.martin@cybernet.ch>
1287f3f0262cSandi * @author      Aidan Lister <aidan@php.net>
1288f3f0262cSandi * @version     1.0.0
1289f3f0262cSandi */
1290f31d5b73Sandifunction filesize_h($size, $dec = 1) {
1291f3f0262cSandi    $sizes = array('B', 'KB', 'MB', 'GB');
1292f3f0262cSandi    $count = count($sizes);
1293f3f0262cSandi    $i     = 0;
1294f3f0262cSandi
1295f3f0262cSandi    while($size >= 1024 && ($i < $count - 1)) {
1296f3f0262cSandi        $size /= 1024;
1297f3f0262cSandi        $i++;
1298f3f0262cSandi    }
1299f3f0262cSandi
1300f3f0262cSandi    return round($size, $dec).' '.$sizes[$i];
1301f3f0262cSandi}
1302f3f0262cSandi
130315fae107Sandi/**
1304c57e365eSAndreas Gohr * Return the given timestamp as human readable, fuzzy age
1305c57e365eSAndreas Gohr *
1306c57e365eSAndreas Gohr * @author Andreas Gohr <gohr@cosmocode.de>
1307c57e365eSAndreas Gohr */
1308c57e365eSAndreas Gohrfunction datetime_h($dt) {
1309c57e365eSAndreas Gohr    global $lang;
1310c57e365eSAndreas Gohr
1311c57e365eSAndreas Gohr    $ago = time() - $dt;
1312c57e365eSAndreas Gohr    if($ago > 24 * 60 * 60 * 30 * 12 * 2) {
1313c57e365eSAndreas Gohr        return sprintf($lang['years'], round($ago / (24 * 60 * 60 * 30 * 12)));
1314c57e365eSAndreas Gohr    }
1315c57e365eSAndreas Gohr    if($ago > 24 * 60 * 60 * 30 * 2) {
1316c57e365eSAndreas Gohr        return sprintf($lang['months'], round($ago / (24 * 60 * 60 * 30)));
1317c57e365eSAndreas Gohr    }
1318c57e365eSAndreas Gohr    if($ago > 24 * 60 * 60 * 7 * 2) {
1319c57e365eSAndreas Gohr        return sprintf($lang['weeks'], round($ago / (24 * 60 * 60 * 7)));
1320c57e365eSAndreas Gohr    }
1321c57e365eSAndreas Gohr    if($ago > 24 * 60 * 60 * 2) {
1322c57e365eSAndreas Gohr        return sprintf($lang['days'], round($ago / (24 * 60 * 60)));
1323c57e365eSAndreas Gohr    }
1324c57e365eSAndreas Gohr    if($ago > 60 * 60 * 2) {
1325c57e365eSAndreas Gohr        return sprintf($lang['hours'], round($ago / (60 * 60)));
1326c57e365eSAndreas Gohr    }
1327c57e365eSAndreas Gohr    if($ago > 60 * 2) {
1328c57e365eSAndreas Gohr        return sprintf($lang['minutes'], round($ago / (60)));
1329c57e365eSAndreas Gohr    }
1330c57e365eSAndreas Gohr    return sprintf($lang['seconds'], $ago);
1331c57e365eSAndreas Gohr}
1332c57e365eSAndreas Gohr
1333c57e365eSAndreas Gohr/**
1334f2263577SAndreas Gohr * Wraps around strftime but provides support for fuzzy dates
1335f2263577SAndreas Gohr *
1336f2263577SAndreas Gohr * The format default to $conf['dformat']. It is passed to
1337f2263577SAndreas Gohr * strftime - %f can be used to get the value from datetime_h()
1338f2263577SAndreas Gohr *
1339f2263577SAndreas Gohr * @see datetime_h
1340f2263577SAndreas Gohr * @author Andreas Gohr <gohr@cosmocode.de>
1341f2263577SAndreas Gohr */
1342f2263577SAndreas Gohrfunction dformat($dt = null, $format = '') {
1343f2263577SAndreas Gohr    global $conf;
1344f2263577SAndreas Gohr
1345f2263577SAndreas Gohr    if(is_null($dt)) $dt = time();
1346f2263577SAndreas Gohr    $dt = (int) $dt;
1347f2263577SAndreas Gohr    if(!$format) $format = $conf['dformat'];
1348f2263577SAndreas Gohr
1349f2263577SAndreas Gohr    $format = str_replace('%f', datetime_h($dt), $format);
1350f2263577SAndreas Gohr    return strftime($format, $dt);
1351f2263577SAndreas Gohr}
1352f2263577SAndreas Gohr
1353f2263577SAndreas Gohr/**
1354c4f79b71SMichael Hamann * Formats a timestamp as ISO 8601 date
1355c4f79b71SMichael Hamann *
1356c4f79b71SMichael Hamann * @author <ungu at terong dot com>
1357c4f79b71SMichael Hamann * @link http://www.php.net/manual/en/function.date.php#54072
135863703ba5SAndreas Gohr * @param int $int_date: current date in UNIX timestamp
13593272d797SAndreas Gohr * @return string
1360c4f79b71SMichael Hamann */
1361c4f79b71SMichael Hamannfunction date_iso8601($int_date) {
1362c4f79b71SMichael Hamann    $date_mod     = date('Y-m-d\TH:i:s', $int_date);
1363c4f79b71SMichael Hamann    $pre_timezone = date('O', $int_date);
1364c4f79b71SMichael Hamann    $time_zone    = substr($pre_timezone, 0, 3).":".substr($pre_timezone, 3, 2);
1365c4f79b71SMichael Hamann    $date_mod .= $time_zone;
1366c4f79b71SMichael Hamann    return $date_mod;
1367c4f79b71SMichael Hamann}
1368c4f79b71SMichael Hamann
1369c4f79b71SMichael Hamann/**
137000a7b5adSEsther Brunner * return an obfuscated email address in line with $conf['mailguard'] setting
137100a7b5adSEsther Brunner *
137200a7b5adSEsther Brunner * @author Harry Fuecks <hfuecks@gmail.com>
137300a7b5adSEsther Brunner * @author Christopher Smith <chris@jalakai.co.uk>
137400a7b5adSEsther Brunner */
137500a7b5adSEsther Brunnerfunction obfuscate($email) {
137600a7b5adSEsther Brunner    global $conf;
137700a7b5adSEsther Brunner
137800a7b5adSEsther Brunner    switch($conf['mailguard']) {
137900a7b5adSEsther Brunner        case 'visible' :
138000a7b5adSEsther Brunner            $obfuscate = array('@' => ' [at] ', '.' => ' [dot] ', '-' => ' [dash] ');
138100a7b5adSEsther Brunner            return strtr($email, $obfuscate);
138200a7b5adSEsther Brunner
138300a7b5adSEsther Brunner        case 'hex' :
138400a7b5adSEsther Brunner            $encode = '';
138549eb6e38SAndreas Gohr            $len    = strlen($email);
138649eb6e38SAndreas Gohr            for($x = 0; $x < $len; $x++) {
138749eb6e38SAndreas Gohr                $encode .= '&#x'.bin2hex($email{$x}).';';
138849eb6e38SAndreas Gohr            }
138900a7b5adSEsther Brunner            return $encode;
139000a7b5adSEsther Brunner
139100a7b5adSEsther Brunner        case 'none' :
139200a7b5adSEsther Brunner        default :
139300a7b5adSEsther Brunner            return $email;
139400a7b5adSEsther Brunner    }
139500a7b5adSEsther Brunner}
139600a7b5adSEsther Brunner
139700a7b5adSEsther Brunner/**
139889541d4bSAndreas Gohr * Removes quoting backslashes
139989541d4bSAndreas Gohr *
140089541d4bSAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
140189541d4bSAndreas Gohr */
140289541d4bSAndreas Gohrfunction unslash($string, $char = "'") {
140389541d4bSAndreas Gohr    return str_replace('\\'.$char, $char, $string);
140489541d4bSAndreas Gohr}
140589541d4bSAndreas Gohr
140673038c47SAndreas Gohr/**
140773038c47SAndreas Gohr * Convert php.ini shorthands to byte
140873038c47SAndreas Gohr *
140973038c47SAndreas Gohr * @author <gilthans dot NO dot SPAM at gmail dot com>
141073038c47SAndreas Gohr * @link   http://de3.php.net/manual/en/ini.core.php#79564
141173038c47SAndreas Gohr */
141273038c47SAndreas Gohrfunction php_to_byte($v) {
141373038c47SAndreas Gohr    $l   = substr($v, -1);
141473038c47SAndreas Gohr    $ret = substr($v, 0, -1);
141573038c47SAndreas Gohr    switch(strtoupper($l)) {
141674160ca1SGerrit Uitslag        /** @noinspection PhpMissingBreakStatementInspection */
141773038c47SAndreas Gohr        case 'P':
141873038c47SAndreas Gohr            $ret *= 1024;
141974160ca1SGerrit Uitslag        /** @noinspection PhpMissingBreakStatementInspection */
142073038c47SAndreas Gohr        case 'T':
142173038c47SAndreas Gohr            $ret *= 1024;
142274160ca1SGerrit Uitslag        /** @noinspection PhpMissingBreakStatementInspection */
142373038c47SAndreas Gohr        case 'G':
142473038c47SAndreas Gohr            $ret *= 1024;
142574160ca1SGerrit Uitslag        /** @noinspection PhpMissingBreakStatementInspection */
142673038c47SAndreas Gohr        case 'M':
142773038c47SAndreas Gohr            $ret *= 1024;
142873038c47SAndreas Gohr        case 'K':
142973038c47SAndreas Gohr            $ret *= 1024;
143073038c47SAndreas Gohr            break;
143149cbd23eSOtto Vainio        default;
143249cbd23eSOtto Vainio            $ret *= 10;
143349cbd23eSOtto Vainio            break;
143473038c47SAndreas Gohr    }
143573038c47SAndreas Gohr    return $ret;
143673038c47SAndreas Gohr}
143773038c47SAndreas Gohr
1438546d3a99SAndreas Gohr/**
1439546d3a99SAndreas Gohr * Wrapper around preg_quote adding the default delimiter
1440546d3a99SAndreas Gohr */
1441546d3a99SAndreas Gohrfunction preg_quote_cb($string) {
1442546d3a99SAndreas Gohr    return preg_quote($string, '/');
1443546d3a99SAndreas Gohr}
144473038c47SAndreas Gohr
1445bd2f6c2fSAndreas Gohr/**
1446bd2f6c2fSAndreas Gohr * Shorten a given string by removing data from the middle
1447bd2f6c2fSAndreas Gohr *
1448c66972f2SAdrian Lang * You can give the string in two parts, the first part $keep
1449bd2f6c2fSAndreas Gohr * will never be shortened. The second part $short will be cut
1450bd2f6c2fSAndreas Gohr * in the middle to shorten but only if at least $min chars are
1451bd2f6c2fSAndreas Gohr * left to display it. Otherwise it will be left off.
1452bd2f6c2fSAndreas Gohr *
1453bd2f6c2fSAndreas Gohr * @param string $keep   the part to keep
1454bd2f6c2fSAndreas Gohr * @param string $short  the part to shorten
1455bd2f6c2fSAndreas Gohr * @param int    $max    maximum chars you want for the whole string
1456bd2f6c2fSAndreas Gohr * @param int    $min    minimum number of chars to have left for middle shortening
1457bd2f6c2fSAndreas Gohr * @param string $char   the shortening character to use
14583272d797SAndreas Gohr * @return string
1459bd2f6c2fSAndreas Gohr */
1460a5d27328SAndreas Gohrfunction shorten($keep, $short, $max, $min = 9, $char = '…') {
1461bd2f6c2fSAndreas Gohr    $max = $max - utf8_strlen($keep);
1462bd2f6c2fSAndreas Gohr    if($max < $min) return $keep;
1463bd2f6c2fSAndreas Gohr    $len = utf8_strlen($short);
1464bd2f6c2fSAndreas Gohr    if($len <= $max) return $keep.$short;
1465bd2f6c2fSAndreas Gohr    $half = floor($max / 2);
1466bd2f6c2fSAndreas Gohr    return $keep.utf8_substr($short, 0, $half - 1).$char.utf8_substr($short, $len - $half);
1467bd2f6c2fSAndreas Gohr}
1468bd2f6c2fSAndreas Gohr
1469dc58b6f4SAndy Webber/**
1470dc58b6f4SAndy Webber * Return the users realname or e-mail address for use
1471dc58b6f4SAndy Webber * in page footer and recent changes pages
1472dc58b6f4SAndy Webber *
147315f3bc49SGerrit Uitslag * @param string|bool $username or false when currently logged-in user should be used
147415f3bc49SGerrit Uitslag * @param bool $textonly true returns only plain text, true allows returning html
1475c0953023SGerrit Uitslag * @return string html or plain text(not escaped) of formatted user name
147615f3bc49SGerrit Uitslag *
1477dc58b6f4SAndy Webber * @author Andy Webber <dokuwiki AT andywebber DOT com>
1478dc58b6f4SAndy Webber */
147915f3bc49SGerrit Uitslagfunction editorinfo($username, $textonly = false) {
1480cd4635eeSGerrit Uitslag    return userlink($username, $textonly);
1481dc58b6f4SAndy Webber}
1482dc58b6f4SAndy Webber
148360a396c8SGerrit Uitslag/**
148460a396c8SGerrit Uitslag * Returns users realname w/o link
148560a396c8SGerrit Uitslag *
148660a396c8SGerrit Uitslag * @param string|bool $username or false when currently logged-in user should be used
148715f3bc49SGerrit Uitslag * @param bool $textonly true returns only plain text, true allows returning html
1488c0953023SGerrit Uitslag * @return string html or plain text(not escaped) of formatted user name
148960a396c8SGerrit Uitslag *
149060a396c8SGerrit Uitslag * @triggers COMMON_USER_LINK
149160a396c8SGerrit Uitslag */
1492cd4635eeSGerrit Uitslagfunction userlink($username = null, $textonly = false) {
149360a396c8SGerrit Uitslag    global $conf, $INFO;
149460a396c8SGerrit Uitslag    /** @var DokuWiki_Auth_Plugin $auth */
149560a396c8SGerrit Uitslag    global $auth;
149630f6ec4bSGerrit Uitslag    /** @var Input $INPUT */
149730f6ec4bSGerrit Uitslag    global $INPUT;
149860a396c8SGerrit Uitslag
149960a396c8SGerrit Uitslag    // prepare initial event data
150060a396c8SGerrit Uitslag    $data = array(
150160a396c8SGerrit Uitslag        'username' => $username, // the unique user name
150260a396c8SGerrit Uitslag        'name' => '',
150360a396c8SGerrit Uitslag        'link' => array( //setting 'link' to false disables linking
150460a396c8SGerrit Uitslag                         'target' => '',
150560a396c8SGerrit Uitslag                         'pre' => '',
150660a396c8SGerrit Uitslag                         'suf' => '',
150760a396c8SGerrit Uitslag                         'style' => '',
150860a396c8SGerrit Uitslag                         'more' => '',
150960a396c8SGerrit Uitslag                         'url' => '',
151060a396c8SGerrit Uitslag                         'title' => '',
151160a396c8SGerrit Uitslag                         'class' => ''
151260a396c8SGerrit Uitslag        ),
15134d5fc927SGerrit Uitslag        'userlink' => '', // formatted user name as will be returned
151415f3bc49SGerrit Uitslag        'textonly' => $textonly
151560a396c8SGerrit Uitslag    );
151662c8004eSGerrit Uitslag    if($username === null) {
151730f6ec4bSGerrit Uitslag        $data['username'] = $username = $INPUT->server->str('REMOTE_USER');
151815f3bc49SGerrit Uitslag        if($textonly){
151915f3bc49SGerrit Uitslag            $data['name'] = $INFO['userinfo']['name']. ' (' . $INPUT->server->str('REMOTE_USER') . ')';
152015f3bc49SGerrit Uitslag        }else {
152130f6ec4bSGerrit Uitslag            $data['name'] = '<bdi>' . hsc($INFO['userinfo']['name']) . '</bdi> (<bdi>' . hsc($INPUT->server->str('REMOTE_USER')) . '</bdi>)';
152260a396c8SGerrit Uitslag        }
152315f3bc49SGerrit Uitslag    }
152460a396c8SGerrit Uitslag
152560a396c8SGerrit Uitslag    $evt = new Doku_Event('COMMON_USER_LINK', $data);
152660a396c8SGerrit Uitslag    if($evt->advise_before(true)) {
152760a396c8SGerrit Uitslag        if(empty($data['name'])) {
152860a396c8SGerrit Uitslag            if($conf['showuseras'] == 'loginname') {
152915f3bc49SGerrit Uitslag                $data['name'] = $textonly ? $data['username'] : hsc($data['username']);
153060a396c8SGerrit Uitslag            } else {
153160a396c8SGerrit Uitslag                if($auth) $info = $auth->getUserData($username);
1532dc58b6f4SAndy Webber                if(isset($info) && $info) {
1533dc58b6f4SAndy Webber                    switch($conf['showuseras']) {
1534dc58b6f4SAndy Webber                        case 'username':
15357f081821SGerrit Uitslag                        case 'username_link':
153615f3bc49SGerrit Uitslag                            $data['name'] = $textonly ? $info['name'] : hsc($info['name']);
153760a396c8SGerrit Uitslag                            break;
1538dc58b6f4SAndy Webber                        case 'email':
1539dc58b6f4SAndy Webber                        case 'email_link':
154060a396c8SGerrit Uitslag                            $data['name'] = obfuscate($info['mail']);
154160a396c8SGerrit Uitslag                            break;
1542dc58b6f4SAndy Webber                    }
154360a396c8SGerrit Uitslag                }
154460a396c8SGerrit Uitslag            }
154560a396c8SGerrit Uitslag        }
15467f081821SGerrit Uitslag
15477f081821SGerrit Uitslag        /** @var Doku_Renderer_xhtml $xhtml_renderer */
15487f081821SGerrit Uitslag        static $xhtml_renderer = null;
15497f081821SGerrit Uitslag
155015f3bc49SGerrit Uitslag        if(!$data['textonly'] && empty($data['link']['url'])) {
15517f081821SGerrit Uitslag
15527f081821SGerrit Uitslag            if(in_array($conf['showuseras'], array('email_link', 'username_link'))) {
155360a396c8SGerrit Uitslag                if(!isset($info)) {
155460a396c8SGerrit Uitslag                    if($auth) $info = $auth->getUserData($username);
155560a396c8SGerrit Uitslag                }
155660a396c8SGerrit Uitslag                if(isset($info) && $info) {
15577f081821SGerrit Uitslag                    if($conf['showuseras'] == 'email_link') {
155860a396c8SGerrit Uitslag                        $data['link']['url'] = 'mailto:' . obfuscate($info['mail']);
1559dc58b6f4SAndy Webber                    } else {
15607f081821SGerrit Uitslag                        if(is_null($xhtml_renderer)) {
15617f081821SGerrit Uitslag                            $xhtml_renderer = p_get_renderer('xhtml');
15627f081821SGerrit Uitslag                        }
15637f081821SGerrit Uitslag                        if(empty($xhtml_renderer->interwiki)) {
15647f081821SGerrit Uitslag                            $xhtml_renderer->interwiki = getInterwiki();
15657f081821SGerrit Uitslag                        }
15667f081821SGerrit Uitslag                        $shortcut = 'user';
1567533772e1SGerrit Uitslag                        $exists = null;
15686496c33fSGerrit Uitslag                        $data['link']['url'] = $xhtml_renderer->_resolveInterWiki($shortcut, $username, $exists);
15692a2a43c4SGerrit Uitslag                        $data['link']['class'] .= ' interwiki iw_user';
15706496c33fSGerrit Uitslag                        if($exists !== null) {
15716496c33fSGerrit Uitslag                            if($exists) {
15726496c33fSGerrit Uitslag                                $data['link']['class'] .= ' wikilink1';
15736496c33fSGerrit Uitslag                            } else {
15746496c33fSGerrit Uitslag                                $data['link']['class'] .= ' wikilink2';
15756496c33fSGerrit Uitslag                                $data['link']['rel'] = 'nofollow';
15766496c33fSGerrit Uitslag                            }
15776496c33fSGerrit Uitslag                        }
1578dc58b6f4SAndy Webber                    }
1579dc58b6f4SAndy Webber                } else {
158015f3bc49SGerrit Uitslag                    $data['textonly'] = true;
1581dc58b6f4SAndy Webber                }
158260a396c8SGerrit Uitslag
158360a396c8SGerrit Uitslag            } else {
158415f3bc49SGerrit Uitslag                $data['textonly'] = true;
158560a396c8SGerrit Uitslag            }
158660a396c8SGerrit Uitslag        }
158760a396c8SGerrit Uitslag
158815f3bc49SGerrit Uitslag        if($data['textonly']) {
15894d5fc927SGerrit Uitslag            $data['userlink'] = $data['name'];
159060a396c8SGerrit Uitslag        } else {
159160a396c8SGerrit Uitslag            $data['link']['name'] = $data['name'];
159260a396c8SGerrit Uitslag            if(is_null($xhtml_renderer)) {
159360a396c8SGerrit Uitslag                $xhtml_renderer = p_get_renderer('xhtml');
159460a396c8SGerrit Uitslag            }
15954d5fc927SGerrit Uitslag            $data['userlink'] = $xhtml_renderer->_formatLink($data['link']);
159660a396c8SGerrit Uitslag        }
159760a396c8SGerrit Uitslag    }
159860a396c8SGerrit Uitslag    $evt->advise_after();
159960a396c8SGerrit Uitslag    unset($evt);
160060a396c8SGerrit Uitslag
16014d5fc927SGerrit Uitslag    return $data['userlink'];
1602066fee30SAndreas Gohr}
1603066fee30SAndreas Gohr
1604066fee30SAndreas Gohr/**
1605066fee30SAndreas Gohr * Returns the path to a image file for the currently chosen license.
1606066fee30SAndreas Gohr * When no image exists, returns an empty string
1607066fee30SAndreas Gohr *
1608066fee30SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
1609066fee30SAndreas Gohr * @param  string $type - type of image 'badge' or 'button'
16103272d797SAndreas Gohr * @return string
1611066fee30SAndreas Gohr */
1612066fee30SAndreas Gohrfunction license_img($type) {
1613066fee30SAndreas Gohr    global $license;
1614066fee30SAndreas Gohr    global $conf;
1615066fee30SAndreas Gohr    if(!$conf['license']) return '';
1616066fee30SAndreas Gohr    if(!is_array($license[$conf['license']])) return '';
1617066fee30SAndreas Gohr    $lic   = $license[$conf['license']];
1618066fee30SAndreas Gohr    $try   = array();
1619066fee30SAndreas Gohr    $try[] = 'lib/images/license/'.$type.'/'.$conf['license'].'.png';
1620066fee30SAndreas Gohr    $try[] = 'lib/images/license/'.$type.'/'.$conf['license'].'.gif';
1621066fee30SAndreas Gohr    if(substr($conf['license'], 0, 3) == 'cc-') {
1622066fee30SAndreas Gohr        $try[] = 'lib/images/license/'.$type.'/cc.png';
1623066fee30SAndreas Gohr    }
1624066fee30SAndreas Gohr    foreach($try as $src) {
1625066fee30SAndreas Gohr        if(@file_exists(DOKU_INC.$src)) return $src;
1626066fee30SAndreas Gohr    }
1627066fee30SAndreas Gohr    return '';
1628dc58b6f4SAndy Webber}
1629dc58b6f4SAndy Webber
163013c08e2fSMichael Klier/**
163113c08e2fSMichael Klier * Checks if the given amount of memory is available
163213c08e2fSMichael Klier *
163313c08e2fSMichael Klier * If the memory_get_usage() function is not available the
163413c08e2fSMichael Klier * function just assumes $bytes of already allocated memory
163513c08e2fSMichael Klier *
163613c08e2fSMichael Klier * @author Filip Oscadal <webmaster@illusionsoftworks.cz>
163713c08e2fSMichael Klier * @author Andreas Gohr <andi@splitbrain.org>
16383272d797SAndreas Gohr *
16393272d797SAndreas Gohr * @param  int $mem  Size of memory you want to allocate in bytes
16403272d797SAndreas Gohr * @param int  $bytes
16413272d797SAndreas Gohr * @internal param int $used already allocated memory (see above)
16423272d797SAndreas Gohr * @return bool
164313c08e2fSMichael Klier */
164413c08e2fSMichael Klierfunction is_mem_available($mem, $bytes = 1048576) {
164513c08e2fSMichael Klier    $limit = trim(ini_get('memory_limit'));
164613c08e2fSMichael Klier    if(empty($limit)) return true; // no limit set!
164713c08e2fSMichael Klier
164813c08e2fSMichael Klier    // parse limit to bytes
164913c08e2fSMichael Klier    $limit = php_to_byte($limit);
165013c08e2fSMichael Klier
165113c08e2fSMichael Klier    // get used memory if possible
165213c08e2fSMichael Klier    if(function_exists('memory_get_usage')) {
165313c08e2fSMichael Klier        $used = memory_get_usage();
165449eb6e38SAndreas Gohr    } else {
165549eb6e38SAndreas Gohr        $used = $bytes;
165613c08e2fSMichael Klier    }
165713c08e2fSMichael Klier
165813c08e2fSMichael Klier    if($used + $mem > $limit) {
165913c08e2fSMichael Klier        return false;
166013c08e2fSMichael Klier    }
166113c08e2fSMichael Klier
166213c08e2fSMichael Klier    return true;
166313c08e2fSMichael Klier}
166413c08e2fSMichael Klier
1665af2408d5SAndreas Gohr/**
1666af2408d5SAndreas Gohr * Send a HTTP redirect to the browser
1667af2408d5SAndreas Gohr *
1668af2408d5SAndreas Gohr * Works arround Microsoft IIS cookie sending bug. Exits the script.
1669af2408d5SAndreas Gohr *
1670af2408d5SAndreas Gohr * @link   http://support.microsoft.com/kb/q176113/
1671af2408d5SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
1672af2408d5SAndreas Gohr */
1673af2408d5SAndreas Gohrfunction send_redirect($url) {
1674585bf44eSChristopher Smith    /* @var Input $INPUT */
1675585bf44eSChristopher Smith    global $INPUT;
1676585bf44eSChristopher Smith
16770181f021SAndreas Gohr    //are there any undisplayed messages? keep them in session for display
16780181f021SAndreas Gohr    global $MSG;
16790181f021SAndreas Gohr    if(isset($MSG) && count($MSG) && !defined('NOSESSION')) {
16800181f021SAndreas Gohr        //reopen session, store data and close session again
16810181f021SAndreas Gohr        @session_start();
16820181f021SAndreas Gohr        $_SESSION[DOKU_COOKIE]['msg'] = $MSG;
16830181f021SAndreas Gohr    }
16840181f021SAndreas Gohr
1685d4869846SAndreas Gohr    // always close the session
1686d4869846SAndreas Gohr    session_write_close();
1687d4869846SAndreas Gohr
1688c10dcb7dSAndreas Gohr    // work around IE bug
1689c10dcb7dSAndreas Gohr    // http://www.ianhoar.com/2008/11/16/internet-explorer-6-and-redirected-anchor-links/
16906d2af55dSChristopher Smith    @list($url, $hash) = explode('#', $url);
1691c10dcb7dSAndreas Gohr    if($hash) {
1692c10dcb7dSAndreas Gohr        if(strpos($url, '?')) {
1693c10dcb7dSAndreas Gohr            $url = $url.'&#'.$hash;
1694c10dcb7dSAndreas Gohr        } else {
1695c10dcb7dSAndreas Gohr            $url = $url.'?&#'.$hash;
1696c10dcb7dSAndreas Gohr        }
1697c10dcb7dSAndreas Gohr    }
1698c10dcb7dSAndreas Gohr
1699af2408d5SAndreas Gohr    // check if running on IIS < 6 with CGI-PHP
1700585bf44eSChristopher Smith    if($INPUT->server->has('SERVER_SOFTWARE') && $INPUT->server->has('GATEWAY_INTERFACE') &&
1701585bf44eSChristopher Smith        (strpos($INPUT->server->str('GATEWAY_INTERFACE'), 'CGI') !== false) &&
1702585bf44eSChristopher Smith        (preg_match('|^Microsoft-IIS/(\d)\.\d$|', trim($INPUT->server->str('SERVER_SOFTWARE')), $matches)) &&
17033272d797SAndreas Gohr        $matches[1] < 6
17043272d797SAndreas Gohr    ) {
1705af2408d5SAndreas Gohr        header('Refresh: 0;url='.$url);
1706af2408d5SAndreas Gohr    } else {
1707af2408d5SAndreas Gohr        header('Location: '.$url);
1708af2408d5SAndreas Gohr    }
1709af2408d5SAndreas Gohr    exit;
1710af2408d5SAndreas Gohr}
1711af2408d5SAndreas Gohr
17125b75cd1fSAdrian Lang/**
17135b75cd1fSAdrian Lang * Validate a value using a set of valid values
17145b75cd1fSAdrian Lang *
17155b75cd1fSAdrian Lang * This function checks whether a specified value is set and in the array
17165b75cd1fSAdrian Lang * $valid_values. If not, the function returns a default value or, if no
17175b75cd1fSAdrian Lang * default is specified, throws an exception.
17185b75cd1fSAdrian Lang *
17195b75cd1fSAdrian Lang * @param string $param        The name of the parameter
17205b75cd1fSAdrian Lang * @param array  $valid_values A set of valid values; Optionally a default may
17215b75cd1fSAdrian Lang *                             be marked by the key “default”.
17225b75cd1fSAdrian Lang * @param array  $array        The array containing the value (typically $_POST
17235b75cd1fSAdrian Lang *                             or $_GET)
17245b75cd1fSAdrian Lang * @param string $exc          The text of the raised exception
17255b75cd1fSAdrian Lang *
17263272d797SAndreas Gohr * @throws Exception
17273272d797SAndreas Gohr * @return mixed
17285b75cd1fSAdrian Lang * @author Adrian Lang <lang@cosmocode.de>
17295b75cd1fSAdrian Lang */
17305b75cd1fSAdrian Langfunction valid_input_set($param, $valid_values, $array, $exc = '') {
17315b75cd1fSAdrian Lang    if(isset($array[$param]) && in_array($array[$param], $valid_values)) {
17325b75cd1fSAdrian Lang        return $array[$param];
17335b75cd1fSAdrian Lang    } elseif(isset($valid_values['default'])) {
17345b75cd1fSAdrian Lang        return $valid_values['default'];
17355b75cd1fSAdrian Lang    } else {
17365b75cd1fSAdrian Lang        throw new Exception($exc);
17375b75cd1fSAdrian Lang    }
17385b75cd1fSAdrian Lang}
17395b75cd1fSAdrian Lang
174063703ba5SAndreas Gohr/**
174163703ba5SAndreas Gohr * Read a preference from the DokuWiki cookie
1742646a531aSChristopher Smith * (remembering both keys & values are urlencoded)
174363703ba5SAndreas Gohr */
1744554a8c9fSAdrian Langfunction get_doku_pref($pref, $default) {
1745646a531aSChristopher Smith    $enc_pref = urlencode($pref);
1746646a531aSChristopher Smith    if(strpos($_COOKIE['DOKU_PREFS'], $enc_pref) !== false) {
1747554a8c9fSAdrian Lang        $parts = explode('#', $_COOKIE['DOKU_PREFS']);
174863703ba5SAndreas Gohr        $cnt   = count($parts);
174963703ba5SAndreas Gohr        for($i = 0; $i < $cnt; $i += 2) {
1750646a531aSChristopher Smith            if($parts[$i] == $enc_pref) {
1751646a531aSChristopher Smith                return urldecode($parts[$i + 1]);
1752554a8c9fSAdrian Lang            }
1753554a8c9fSAdrian Lang        }
1754554a8c9fSAdrian Lang    }
1755554a8c9fSAdrian Lang    return $default;
1756554a8c9fSAdrian Lang}
1757554a8c9fSAdrian Lang
17583c94d07bSAnika Henke/**
17593c94d07bSAnika Henke * Add a preference to the DokuWiki cookie
176036ec377eSChristopher Smith * (remembering $_COOKIE['DOKU_PREFS'] is urlencoded)
17613c94d07bSAnika Henke */
17623c94d07bSAnika Henkefunction set_doku_pref($pref, $val) {
17633c94d07bSAnika Henke    global $conf;
17643c94d07bSAnika Henke    $orig = get_doku_pref($pref, false);
17653c94d07bSAnika Henke    $cookieVal = '';
17663c94d07bSAnika Henke
17673c94d07bSAnika Henke    if($orig && ($orig != $val)) {
17683c94d07bSAnika Henke        $parts = explode('#', $_COOKIE['DOKU_PREFS']);
17693c94d07bSAnika Henke        $cnt   = count($parts);
177036ec377eSChristopher Smith        // urlencode $pref for the comparison
177136ec377eSChristopher Smith        $enc_pref = rawurlencode($pref);
17723c94d07bSAnika Henke        for($i = 0; $i < $cnt; $i += 2) {
177336ec377eSChristopher Smith            if($parts[$i] == $enc_pref) {
177436ec377eSChristopher Smith                $parts[$i + 1] = rawurlencode($val);
177550f261f7SMichael Hamann                break;
17763c94d07bSAnika Henke            }
17773c94d07bSAnika Henke        }
17783c94d07bSAnika Henke        $cookieVal = implode('#', $parts);
17793c94d07bSAnika Henke    } else if (!$orig) {
178036ec377eSChristopher Smith        $cookieVal = ($_COOKIE['DOKU_PREFS'] ? $_COOKIE['DOKU_PREFS'].'#' : '').rawurlencode($pref).'#'.rawurlencode($val);
17813c94d07bSAnika Henke    }
17823c94d07bSAnika Henke
17833c94d07bSAnika Henke    if (!empty($cookieVal)) {
178475e4dd8aSGerrit Uitslag        $cookieDir = empty($conf['cookiedir']) ? DOKU_REL : $conf['cookiedir'];
178575e4dd8aSGerrit Uitslag        setcookie('DOKU_PREFS', $cookieVal, time()+365*24*3600, $cookieDir, '', ($conf['securecookie'] && is_ssl()));
17863c94d07bSAnika Henke    }
17873c94d07bSAnika Henke}
17883c94d07bSAnika Henke
1789f8fb2d18SAndreas Gohr/**
1790f8fb2d18SAndreas Gohr * Strips source mapping declarations from given text #601
1791f8fb2d18SAndreas Gohr *
1792f8fb2d18SAndreas Gohr * @param &string $text reference to the CSS or JavaScript code to clean
1793f8fb2d18SAndreas Gohr */
1794f8fb2d18SAndreas Gohrfunction stripsourcemaps(&$text){
1795f8fb2d18SAndreas Gohr    $text = preg_replace('/^(\/\/|\/\*)[@#]\s+sourceMappingURL=.*?(\*\/)?$/im', '\\1\\2', $text);
1796f8fb2d18SAndreas Gohr}
1797f8fb2d18SAndreas Gohr
1798e3776c06SMichael Hamann//Setup VIM: ex: et ts=2 :
1799