[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/inc/ -> common.php (source)

   1  <?php
   2  /**
   3   * Common DokuWiki functions
   4   *
   5   * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
   6   * @author     Andreas Gohr <andi@splitbrain.org>
   7   */
   8  
   9  if(!defined('DOKU_INC')) define('DOKU_INC',fullpath(dirname(__FILE__).'/../').'/');
  10  require_once(DOKU_CONF.'dokuwiki.php');
  11  require_once (DOKU_INC.'inc/io.php');
  12  require_once (DOKU_INC.'inc/changelog.php');
  13  require_once (DOKU_INC.'inc/utf8.php');
  14  require_once (DOKU_INC.'inc/mail.php');
  15  require_once (DOKU_INC.'inc/parserutils.php');
  16  require_once (DOKU_INC.'inc/infoutils.php');
  17  
  18  /**
  19   * These constants are used with the recents function
  20   */
  21  define('RECENTS_SKIP_DELETED',2);
  22  define('RECENTS_SKIP_MINORS',4);
  23  define('RECENTS_SKIP_SUBSPACES',8);
  24  
  25  /**
  26   * Wrapper around htmlspecialchars()
  27   *
  28   * @author Andreas Gohr <andi@splitbrain.org>
  29   * @see    htmlspecialchars()
  30   */
  31  function hsc($string){
  32    return htmlspecialchars($string, ENT_QUOTES, 'UTF-8');
  33  }
  34  
  35  /**
  36   * print a newline terminated string
  37   *
  38   * You can give an indention as optional parameter
  39   *
  40   * @author Andreas Gohr <andi@splitbrain.org>
  41   */
  42  function ptln($string,$indent=0){
  43    echo str_repeat(' ', $indent)."$string\n";
  44  }
  45  
  46  /**
  47   * strips control characters (<32) from the given string
  48   *
  49   * @author Andreas Gohr <andi@splitbrain.org>
  50   */
  51  function stripctl($string){
  52    return preg_replace('/[\x00-\x1F]+/s','',$string);
  53  }
  54  
  55  /**
  56   * Return a secret token to be used for CSRF attack prevention
  57   *
  58   * @author  Andreas Gohr <andi@splitbrain.org>
  59   * @link    http://en.wikipedia.org/wiki/Cross-site_request_forgery
  60   * @link    http://christ1an.blogspot.com/2007/04/preventing-csrf-efficiently.html
  61   * @return  string
  62   */
  63  function getSecurityToken(){
  64    return md5(auth_cookiesalt().session_id());
  65  }
  66  
  67  /**
  68   * Check the secret CSRF token
  69   */
  70  function checkSecurityToken($token=null){
  71    if(is_null($token)) $token = $_REQUEST['sectok'];
  72    if(getSecurityToken() != $token){
  73      msg('Security Token did not match. Possible CSRF attack.',-1);
  74      return false;
  75    }
  76    return true;
  77  }
  78  
  79  /**
  80   * Print a hidden form field with a secret CSRF token
  81   *
  82   * @author  Andreas Gohr <andi@splitbrain.org>
  83   */
  84  function formSecurityToken($print=true){
  85    $ret = '<div class="no"><input type="hidden" name="sectok" value="'.getSecurityToken().'" /></div>'."\n";
  86    if($print){
  87      echo $ret;
  88    }else{
  89      return $ret;
  90    }
  91  }
  92  
  93  /**
  94   * Return info about the current document as associative
  95   * array.
  96   *
  97   * @author Andreas Gohr <andi@splitbrain.org>
  98   */
  99  function pageinfo(){
 100    global $ID;
 101    global $REV;
 102    global $RANGE;
 103    global $USERINFO;
 104    global $conf;
 105    global $lang;
 106  
 107    // include ID & REV not redundant, as some parts of DokuWiki may temporarily change $ID, e.g. p_wiki_xhtml
 108    // FIXME ... perhaps it would be better to ensure the temporary changes weren't necessary
 109    $info['id'] = $ID;
 110    $info['rev'] = $REV;
 111  
 112    if($_SERVER['REMOTE_USER']){
 113      $info['userinfo']     = $USERINFO;
 114      $info['perm']         = auth_quickaclcheck($ID);
 115      $info['subscribed']   = is_subscribed($ID,$_SERVER['REMOTE_USER'],false);
 116      $info['subscribedns'] = is_subscribed($ID,$_SERVER['REMOTE_USER'],true);
 117      $info['client']       = $_SERVER['REMOTE_USER'];
 118  
 119      // set info about manager/admin status
 120      $info['isadmin']   = false;
 121      $info['ismanager'] = false;
 122      if($info['perm'] == AUTH_ADMIN){
 123        $info['isadmin']   = true;
 124        $info['ismanager'] = true;
 125      }elseif(auth_ismanager()){
 126        $info['ismanager'] = true;
 127      }
 128  
 129      // if some outside auth were used only REMOTE_USER is set
 130      if(!$info['userinfo']['name']){
 131        $info['userinfo']['name'] = $_SERVER['REMOTE_USER'];
 132      }
 133  
 134    }else{
 135      $info['perm']       = auth_aclcheck($ID,'',null);
 136      $info['subscribed'] = false;
 137      $info['client']     = clientIP(true);
 138    }
 139  
 140    $info['namespace'] = getNS($ID);
 141    $info['locked']    = checklock($ID);
 142    $info['filepath']  = fullpath(wikiFN($ID));
 143    $info['exists']    = @file_exists($info['filepath']);
 144    if($REV){
 145      //check if current revision was meant
 146      if($info['exists'] && (@filemtime($info['filepath'])==$REV)){
 147        $REV = '';
 148      }elseif($RANGE){
 149        //section editing does not work with old revisions!
 150        $REV   = '';
 151        $RANGE = '';
 152        msg($lang['nosecedit'],0);
 153      }else{
 154        //really use old revision
 155        $info['filepath'] = fullpath(wikiFN($ID,$REV));
 156        $info['exists']   = @file_exists($info['filepath']);
 157      }
 158    }
 159    $info['rev'] = $REV;
 160    if($info['exists']){
 161      $info['writable'] = (is_writable($info['filepath']) &&
 162                           ($info['perm'] >= AUTH_EDIT));
 163    }else{
 164      $info['writable'] = ($info['perm'] >= AUTH_CREATE);
 165    }
 166    $info['editable']  = ($info['writable'] && empty($info['lock']));
 167    $info['lastmod']   = @filemtime($info['filepath']);
 168  
 169    //load page meta data
 170    $info['meta'] = p_get_metadata($ID);
 171  
 172    //who's the editor
 173    if($REV){
 174      $revinfo = getRevisionInfo($ID, $REV, 1024);
 175    }else{
 176      if (is_array($info['meta']['last_change'])) {
 177         $revinfo = $info['meta']['last_change'];
 178      } else {
 179        $revinfo = getRevisionInfo($ID, $info['lastmod'], 1024);
 180        // cache most recent changelog line in metadata if missing and still valid
 181        if ($revinfo!==false) {
 182          $info['meta']['last_change'] = $revinfo;
 183          p_set_metadata($ID, array('last_change' => $revinfo));
 184        }
 185      }
 186    }
 187    //and check for an external edit
 188    if($revinfo!==false && $revinfo['date']!=$info['lastmod']){
 189      // cached changelog line no longer valid
 190      $revinfo = false;
 191      $info['meta']['last_change'] = $revinfo;
 192      p_set_metadata($ID, array('last_change' => $revinfo));
 193    }
 194  
 195    $info['ip']     = $revinfo['ip'];
 196    $info['user']   = $revinfo['user'];
 197    $info['sum']    = $revinfo['sum'];
 198    // See also $INFO['meta']['last_change'] which is the most recent log line for page $ID.
 199    // Use $INFO['meta']['last_change']['type']===DOKU_CHANGE_TYPE_MINOR_EDIT in place of $info['minor'].
 200  
 201    if($revinfo['user']){
 202      $info['editor'] = $revinfo['user'];
 203    }else{
 204      $info['editor'] = $revinfo['ip'];
 205    }
 206  
 207    // draft
 208    $draft = getCacheName($info['client'].$ID,'.draft');
 209    if(@file_exists($draft)){
 210      if(@filemtime($draft) < @filemtime(wikiFN($ID))){
 211        // remove stale draft
 212        @unlink($draft);
 213      }else{
 214        $info['draft'] = $draft;
 215      }
 216    }
 217  
 218    // mobile detection
 219    $info['ismobile'] = clientismobile();
 220  
 221    return $info;
 222  }
 223  
 224  /**
 225   * Build an string of URL parameters
 226   *
 227   * @author Andreas Gohr
 228   */
 229  function buildURLparams($params, $sep='&amp;'){
 230    $url = '';
 231    $amp = false;
 232    foreach($params as $key => $val){
 233      if($amp) $url .= $sep;
 234  
 235      $url .= $key.'=';
 236      $url .= rawurlencode((string)$val);
 237      $amp = true;
 238    }
 239    return $url;
 240  }
 241  
 242  /**
 243   * Build an string of html tag attributes
 244   *
 245   * Skips keys starting with '_', values get HTML encoded
 246   *
 247   * @author Andreas Gohr
 248   */
 249  function buildAttributes($params,$skipempty=false){
 250    $url = '';
 251    foreach($params as $key => $val){
 252      if($key{0} == '_') continue;
 253      if($val === '' && $skipempty) continue;
 254  
 255      $url .= $key.'="';
 256      $url .= htmlspecialchars ($val);
 257      $url .= '" ';
 258    }
 259    return $url;
 260  }
 261  
 262  
 263  /**
 264   * This builds the breadcrumb trail and returns it as array
 265   *
 266   * @author Andreas Gohr <andi@splitbrain.org>
 267   */
 268  function breadcrumbs(){
 269    // we prepare the breadcrumbs early for quick session closing
 270    static $crumbs = null;
 271    if($crumbs != null) return $crumbs;
 272  
 273    global $ID;
 274    global $ACT;
 275    global $conf;
 276    $crumbs = $_SESSION[DOKU_COOKIE]['bc'];
 277  
 278    //first visit?
 279    if (!is_array($crumbs)){
 280      $crumbs = array();
 281    }
 282    //we only save on show and existing wiki documents
 283    $file = wikiFN($ID);
 284    if($ACT != 'show' || !@file_exists($file)){
 285      $_SESSION[DOKU_COOKIE]['bc'] = $crumbs;
 286      return $crumbs;
 287    }
 288  
 289    // page names
 290    $name = noNSorNS($ID);
 291    if ($conf['useheading']) {
 292      // get page title
 293      $title = p_get_first_heading($ID,true);
 294      if ($title) {
 295        $name = $title;
 296      }
 297    }
 298  
 299    //remove ID from array
 300    if (isset($crumbs[$ID])) {
 301      unset($crumbs[$ID]);
 302    }
 303  
 304    //add to array
 305    $crumbs[$ID] = $name;
 306    //reduce size
 307    while(count($crumbs) > $conf['breadcrumbs']){
 308      array_shift($crumbs);
 309    }
 310    //save to session
 311    $_SESSION[DOKU_COOKIE]['bc'] = $crumbs;
 312    return $crumbs;
 313  }
 314  
 315  /**
 316   * Filter for page IDs
 317   *
 318   * This is run on a ID before it is outputted somewhere
 319   * currently used to replace the colon with something else
 320   * on Windows systems and to have proper URL encoding
 321   *
 322   * Urlencoding is ommitted when the second parameter is false
 323   *
 324   * @author Andreas Gohr <andi@splitbrain.org>
 325   */
 326  function idfilter($id,$ue=true){
 327    global $conf;
 328    if ($conf['useslash'] && $conf['userewrite']){
 329      $id = strtr($id,':','/');
 330    }elseif (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' &&
 331        $conf['userewrite']) {
 332      $id = strtr($id,':',';');
 333    }
 334    if($ue){
 335      $id = rawurlencode($id);
 336      $id = str_replace('%3A',':',$id); //keep as colon
 337      $id = str_replace('%2F','/',$id); //keep as slash
 338    }
 339    return $id;
 340  }
 341  
 342  /**
 343   * This builds a link to a wikipage
 344   *
 345   * It handles URL rewriting and adds additional parameter if
 346   * given in $more
 347   *
 348   * @author Andreas Gohr <andi@splitbrain.org>
 349   */
 350  function wl($id='',$more='',$abs=false,$sep='&amp;'){
 351    global $conf;
 352    if(is_array($more)){
 353      $more = buildURLparams($more,$sep);
 354    }else{
 355      $more = str_replace(',',$sep,$more);
 356    }
 357  
 358    $id    = idfilter($id);
 359    if($abs){
 360      $xlink = DOKU_URL;
 361    }else{
 362      $xlink = DOKU_BASE;
 363    }
 364  
 365    if($conf['userewrite'] == 2){
 366      $xlink .= DOKU_SCRIPT.'/'.$id;
 367      if($more) $xlink .= '?'.$more;
 368    }elseif($conf['userewrite']){
 369      $xlink .= $id;
 370      if($more) $xlink .= '?'.$more;
 371    }elseif($id){
 372      $xlink .= DOKU_SCRIPT.'?id='.$id;
 373      if($more) $xlink .= $sep.$more;
 374    }else{
 375      $xlink .= DOKU_SCRIPT;
 376      if($more) $xlink .= '?'.$more;
 377    }
 378  
 379    return $xlink;
 380  }
 381  
 382  /**
 383   * This builds a link to an alternate page format
 384   *
 385   * Handles URL rewriting if enabled. Follows the style of wl().
 386   *
 387   * @author Ben Coburn <btcoburn@silicodon.net>
 388   */
 389  function exportlink($id='',$format='raw',$more='',$abs=false,$sep='&amp;'){
 390    global $conf;
 391    if(is_array($more)){
 392      $more = buildURLparams($more,$sep);
 393    }else{
 394      $more = str_replace(',',$sep,$more);
 395    }
 396  
 397    $format = rawurlencode($format);
 398    $id = idfilter($id);
 399    if($abs){
 400      $xlink = DOKU_URL;
 401    }else{
 402      $xlink = DOKU_BASE;
 403    }
 404  
 405    if($conf['userewrite'] == 2){
 406      $xlink .= DOKU_SCRIPT.'/'.$id.'?do=export_'.$format;
 407      if($more) $xlink .= $sep.$more;
 408    }elseif($conf['userewrite'] == 1){
 409      $xlink .= '_export/'.$format.'/'.$id;
 410      if($more) $xlink .= '?'.$more;
 411    }else{
 412      $xlink .= DOKU_SCRIPT.'?do=export_'.$format.$sep.'id='.$id;
 413      if($more) $xlink .= $sep.$more;
 414    }
 415  
 416    return $xlink;
 417  }
 418  
 419  /**
 420   * Build a link to a media file
 421   *
 422   * Will return a link to the detail page if $direct is false
 423   *
 424   * The $more parameter should always be given as array, the function then
 425   * will strip default parameters to produce even cleaner URLs
 426   *
 427   * @param string  $id     - the media file id or URL
 428   * @param mixed   $more   - string or array with additional parameters
 429   * @param boolean $direct - link to detail page if false
 430   * @param string  $sep    - URL parameter separator
 431   * @param boolean $abs    - Create an absolute URL
 432   */
 433  function ml($id='',$more='',$direct=true,$sep='&amp;',$abs=false){
 434    global $conf;
 435    if(is_array($more)){
 436      // strip defaults for shorter URLs
 437      if(isset($more['cache']) && $more['cache'] == 'cache') unset($more['cache']);
 438      if(!$more['w']) unset($more['w']);
 439      if(!$more['h']) unset($more['h']);
 440      if(isset($more['id']) && $direct) unset($more['id']);
 441      $more = buildURLparams($more,$sep);
 442    }else{
 443      $more = str_replace('cache=cache','',$more); //skip default
 444      $more = str_replace(',,',',',$more);
 445      $more = str_replace(',',$sep,$more);
 446    }
 447  
 448    if($abs){
 449      $xlink = DOKU_URL;
 450    }else{
 451      $xlink = DOKU_BASE;
 452    }
 453  
 454    // external URLs are always direct without rewriting
 455    if(preg_match('#^(https?|ftp)://#i',$id)){
 456      $xlink .= 'lib/exe/fetch.php';
 457      if($more){
 458        $xlink .= '?'.$more;
 459        $xlink .= $sep.'media='.rawurlencode($id);
 460      }else{
 461        $xlink .= '?media='.rawurlencode($id);
 462      }
 463      return $xlink;
 464    }
 465  
 466    $id = idfilter($id);
 467  
 468    // decide on scriptname
 469    if($direct){
 470      if($conf['userewrite'] == 1){
 471        $script = '_media';
 472      }else{
 473        $script = 'lib/exe/fetch.php';
 474      }
 475    }else{
 476      if($conf['userewrite'] == 1){
 477        $script = '_detail';
 478      }else{
 479        $script = 'lib/exe/detail.php';
 480      }
 481    }
 482  
 483    // build URL based on rewrite mode
 484     if($conf['userewrite']){
 485       $xlink .= $script.'/'.$id;
 486       if($more) $xlink .= '?'.$more;
 487     }else{
 488       if($more){
 489         $xlink .= $script.'?'.$more;
 490         $xlink .= $sep.'media='.$id;
 491       }else{
 492         $xlink .= $script.'?media='.$id;
 493       }
 494     }
 495  
 496    return $xlink;
 497  }
 498  
 499  
 500  
 501  /**
 502   * Just builds a link to a script
 503   *
 504   * @todo   maybe obsolete
 505   * @author Andreas Gohr <andi@splitbrain.org>
 506   */
 507  function script($script='doku.php'){
 508  #  $link = getBaseURL();
 509  #  $link .= $script;
 510  #  return $link;
 511    return DOKU_BASE.DOKU_SCRIPT;
 512  }
 513  
 514  /**
 515   * Spamcheck against wordlist
 516   *
 517   * Checks the wikitext against a list of blocked expressions
 518   * returns true if the text contains any bad words
 519   *
 520   * Triggers COMMON_WORDBLOCK_BLOCKED
 521   *
 522   *  Action Plugins can use this event to inspect the blocked data
 523   *  and gain information about the user who was blocked.
 524   *
 525   *  Event data:
 526   *    data['matches']  - array of matches
 527   *    data['userinfo'] - information about the blocked user
 528   *      [ip]           - ip address
 529   *      [user]         - username (if logged in)
 530   *      [mail]         - mail address (if logged in)
 531   *      [name]         - real name (if logged in)
 532   *
 533   * @author Andreas Gohr <andi@splitbrain.org>
 534   * Michael Klier <chi@chimeric.de>
 535   */
 536  function checkwordblock(){
 537    global $TEXT;
 538    global $conf;
 539    global $INFO;
 540  
 541    if(!$conf['usewordblock']) return false;
 542  
 543    // we prepare the text a tiny bit to prevent spammers circumventing URL checks
 544    $text = preg_replace('!(\b)(www\.[\w.:?\-;,]+?\.[\w.:?\-;,]+?[\w/\#~:.?+=&%@\!\-.:?\-;,]+?)([.:?\-;,]*[^\w/\#~:.?+=&%@\!\-.:?\-;,])!i','\1http://\2 \2\3',$TEXT);
 545  
 546    $wordblocks = getWordblocks();
 547    //how many lines to read at once (to work around some PCRE limits)
 548    if(version_compare(phpversion(),'4.3.0','<')){
 549      //old versions of PCRE define a maximum of parenthesises even if no
 550      //backreferences are used - the maximum is 99
 551      //this is very bad performancewise and may even be too high still
 552      $chunksize = 40;
 553    }else{
 554      //read file in chunks of 200 - this should work around the
 555      //MAX_PATTERN_SIZE in modern PCRE
 556      $chunksize = 200;
 557    }
 558    while($blocks = array_splice($wordblocks,0,$chunksize)){
 559      $re = array();
 560      #build regexp from blocks
 561      foreach($blocks as $block){
 562        $block = preg_replace('/#.*$/','',$block);
 563        $block = trim($block);
 564        if(empty($block)) continue;
 565        $re[]  = $block;
 566      }
 567      if(count($re) && preg_match('#('.join('|',$re).')#si',$text,$matches)) {
 568        //prepare event data
 569        $data['matches'] = $matches;
 570        $data['userinfo']['ip'] = $_SERVER['REMOTE_ADDR'];
 571        if($_SERVER['REMOTE_USER']) {
 572            $data<