*/ require_once("conf/dokuwiki.php"); require_once("inc/io.php"); require_once('inc/utf8.php'); require_once('inc/mail.php'); //set up error reporting to sane values error_reporting(E_ALL ^ E_NOTICE); //make session rewrites XHTML compliant //ini_set('arg_separator.output', '&'); //init session session_name("DokuWiki"); session_start(); //kill magic quotes if (get_magic_quotes_gpc()) { if (!empty($_GET)) remove_magic_quotes($_GET); if (!empty($_POST)) remove_magic_quotes($_POST); if (!empty($_COOKIE)) remove_magic_quotes($_COOKIE); if (!empty($_REQUEST)) remove_magic_quotes($_REQUEST); if (!empty($_SESSION)) remove_magic_quotes($_SESSION); // ini_set('magic_quotes_gpc', 0); } set_magic_quotes_runtime(0); // ini_set('magic_quotes_sybase',0); //disable gzip if not available if($conf['usegzip'] && !function_exists('gzopen')){ $conf['usegzip'] = 0; } //remember original umask $conf['oldumask'] = umask(); //make absolute mediaweb if(!preg_match('#^(https?://|/)#i',$conf['mediaweb'])){ $conf['mediaweb'] = getBaseURL().$conf['mediaweb']; } /** * remove magic quotes recursivly * * @author Andreas Gohr */ function remove_magic_quotes(&$array) { foreach (array_keys($array) as $key) { if (is_array($array[$key])) { remove_magic_quotes($array[$key]); }else { $array[$key] = stripslashes($array[$key]); } } } /** * Returns the full absolute URL to the directory where * DokuWiki is installed in (includes a trailing slash) * * @author Andreas Gohr */ function getBaseURL($abs=false){ global $conf; //if canonical url enabled always return absolute if($conf['canonical']) $abs = true; $dir = dirname($_SERVER['PHP_SELF']).'/'; $dir = str_replace('\\','/',$dir); #bugfix for weird WIN behaviour $dir = preg_replace('#//+#','/',$dir); //finish here for relative URLs if(!$abs) return $dir; $port = ':'.$_SERVER['SERVER_PORT']; //remove port from hostheader as sent by IE $host = preg_replace('/:.*$/','',$_SERVER['HTTP_HOST']); // see if HTTPS is enabled - apache leaves this empty when not available, // IIS sets it to 'off', 'false' and 'disabled' are just guessing if (preg_match('/^(|off|false|disabled)$/i',$_SERVER['HTTPS'])){ $proto = 'http://'; if ($_SERVER['SERVER_PORT'] == '80') { $port=''; } }else{ $proto = 'https://'; if ($_SERVER['SERVER_PORT'] == '443') { $port=''; } } return $proto.$host.$port.$dir; } /** * Return info about the current document as associative * array. * * @author Andreas Gohr */ function pageinfo(){ global $ID; global $REV; global $USERINFO; global $conf; if($GLOBALS['xoopsUser']){ $info['user'] = $GLOBALS['xoopsUser']->uname(); $info['userinfo'] = $USERINFO; $info['perm'] = auth_quickaclcheck($ID); }else{ $info['user'] = ''; $info['perm'] = auth_aclcheck($ID,'',null); } $info['namespace'] = getNS($ID); $info['locked'] = checklock($ID); $info['filepath'] = realpath(wikiFN($ID,$REV)); $info['exists'] = @file_exists($info['filepath']); if($REV && !$info['exists']){ //check if current revision was meant $cur = wikiFN($ID); if(@file_exists($cur) && (@filemtime($cur) == $REV)){ $info['filepath'] = realpath($cur); $info['exists'] = true; $REV = ''; } } if($info['exists']){ $info['writable'] = (is_writable($info['filepath']) && ($info['perm'] >= AUTH_EDIT)); }else{ $info['writable'] = ($info['perm'] >= AUTH_CREATE); } $info['editable'] = ($info['writable'] && empty($info['lock'])); $info['lastmod'] = @filemtime($info['filepath']); //who's the editor if($REV){ $revinfo = getRevisionInfo($ID,$REV); }else{ $revinfo = getRevisionInfo($ID,$info['lastmod']); } $info['ip'] = $revinfo['ip']; $info['user'] = $revinfo['user']; $info['sum'] = $revinfo['sum']; $info['editor'] = $revinfo['ip']; if($revinfo['user']) $info['editor'].= ' ('.$revinfo['user'].')'; return $info; } /** * print a message * * If HTTP headers were not sent yet the message is added * to the global message array else it's printed directly * using html_msgarea() * * * Levels can be: * * -1 error * 0 info * 1 success * * @author Andreas Gohr * @see html_msgarea */ function msg($message,$lvl=0){ global $MSG; $errors[-1] = 'error'; $errors[0] = 'info'; $errors[1] = 'success'; if(!headers_sent()){ if(!isset($MSG)) $MSG = array(); $MSG[]=array('lvl' => $errors[$lvl], 'msg' => $message); }else{ $MSG = array(); $MSG[]=array('lvl' => $errors[$lvl], 'msg' => $message); html_msgarea(); } } /** * This builds the breadcrumb trail and returns it as array * * @author Andreas Gohr */ function breadcrumbs(){ global $ID; global $ACT; global $conf; $crumbs = $_SESSION[$conf['title']]['bc']; //first visit? if (!is_array($crumbs)){ $crumbs = array(); } //we only save on show and existing wiki documents if($ACT != 'show' || !@file_exists(wikiFN($ID))){ $_SESSION[$conf['title']]['bc'] = $crumbs; return $crumbs; } //remove ID from array $pos = array_search($ID,$crumbs); if($pos !== false && $pos !== null){ array_splice($crumbs,$pos,1); } //add to array $crumbs[] =$ID; //reduce size while(count($crumbs) > $conf['breadcrumbs']){ array_shift($crumbs); } //save to session $_SESSION[$conf['title']]['bc'] = $crumbs; return $crumbs; } /** * Filter for page IDs * * This is run on a ID before it is outputted somewhere * currently used to replace the colon with something else * on Windows systems and to have proper URL encoding * * Urlencoding is ommitted when the second parameter is false * * @author Andreas Gohr */ function idfilter($id,$ue=true){ global $conf; if ($conf['useslash'] && $conf['userewrite']){ $id = strtr($id,':','/'); }elseif (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' && $conf['userewrite']) { $id = strtr($id,':',';'); } if($ue){ $id = urlencode($id); $id = str_replace('%3A',':',$id); //keep as colon $id = str_replace('%2F','/',$id); //keep as slash } return $id; } /** * This builds a link to a wikipage (using getBaseURL) * * @author Andreas Gohr */ function wl($id='',$more='',$script='doku.php',$canonical=false){ global $conf; $more = str_replace(',','&',$more); $id = idfilter($id); $xlink = getBaseURL($canonical); if(!$conf['userewrite']){ $xlink .= $script; $xlink .= '?id='.$id; if($more) $xlink .= '&'.$more; }else{ $xlink .= $id; if($more) $xlink .= '?'.$more; } return $xlink; } /** * Just builds a link to a script * * @author Andreas Gohr */ function script($script='doku.php'){ $link = getBaseURL(); $link .= $script; return $link; } /** * Return namespacepart of a wiki ID * * @author Andreas Gohr */ function getNS($id){ if(strpos($id,':')!==false){ return substr($id,0,strrpos($id,':')); } return false; } /** * Returns the ID without the namespace * * @author Andreas Gohr */ function noNS($id){ return preg_replace('/.*:/','',$id); } /** * Spamcheck against wordlist * * Checks the wikitext against a list of blocked expressions * returns true if the text contains any bad words * * @author Andreas Gohr */ function checkwordblock(){ global $TEXT; global $conf; if(!$conf['usewordblock']) return false; $blockfile = file('conf/wordblock.conf'); //how many lines to read at once (to work around some PCRE limits) if(version_compare(phpversion(),'4.3.0','<')){ //old versions of PCRE define a maximum of parenthesises even if no //backreferences are used - the maximum is 99 //this is very bad performancewise and may even be too high still $chunksize = 40; }else{ //read file in chunks of 600 - this should work around the //MAX_PATTERN_SIZE in modern PCRE $chunksize = 600; } while($blocks = array_splice($blockfile,0,$chunksize)){ $re = array(); #build regexp from blocks foreach($blocks as $block){ $block = preg_replace('/#.*$/','',$block); $block = trim($block); if(empty($block)) continue; $re[] = $block; } if(preg_match('#('.join('|',$re).')#si',$TEXT)) return true; } return false; } /** * Return the IP of the client * * Honours X-Forwarded-For Proxy Headers * * @author Andreas Gohr */ function clientIP(){ $my = $_SERVER['REMOTE_ADDR']; if($_SERVER['HTTP_X_FORWARDED_FOR']){ $my .= ' ('.$_SERVER['HTTP_X_FORWARDED_FOR'].')'; } return $my; } /** * Checks if a given page is currently locked. * * removes stale lockfiles * * @author Andreas Gohr */ function checklock($id){ global $conf; $lock = wikiFN($id).'.lock'; //no lockfile if(!@file_exists($lock)) return false; //lockfile expired if((time() - filemtime($lock)) > $conf['locktime']){ unlink($lock); return false; } //my own lock $ip = io_readFile($lock); if( ($ip == clientIP()) || ($ip == $_SERVER['REMOTE_USER']) ){ return false; } return $ip; } /** * Lock a page for editing * * @author Andreas Gohr */ function lock($id){ $lock = wikiFN($id).'.lock'; if($_SERVER['REMOTE_USER']){ io_saveFile($lock,$_SERVER['REMOTE_USER']); }else{ io_saveFile($lock,clientIP()); } } /** * Unlock a page if it was locked by the user * * @author Andreas Gohr * @return bool true if a lock was removed */ function unlock($id){ $lock = wikiFN($id).'.lock'; if(@file_exists($lock)){ $ip = io_readFile($lock); if( ($ip == clientIP()) || ($ip == $_SERVER['REMOTE_USER']) ){ @unlink($lock); return true; } } return false; } /** * Remove unwanted chars from ID * * Cleans a given ID to only use allowed characters. Accented characters are * converted to unaccented ones * * @author Andreas Gohr */ function cleanID($id){ global $conf; global $lang; $id = trim($id); $id = utf8_strtolower($id); //alternative namespace seperator $id = strtr($id,';',':'); if($conf['useslash']){ $id = strtr($id,'/',':'); }else{ $id = strtr($id,'/','_'); } if($conf['deaccent']) $id = utf8_deaccent($id,-1); //remove specials //$id = preg_replace('#[\x00-\x20 ¡!"§$%&()\[\]{}¿\\?`\'\#~*+=,<>\|^°@µ¹²³Â1/4Â1/2¬]#u','_',$id); $id = utf8_stripspecials($id,'_','_:.-'); //clean up $id = preg_replace('#__#','_',$id); $id = preg_replace('#:+#',':',$id); $id = trim($id,':._-'); $id = preg_replace('#:[:\._\-]+#',':',$id); return($id); } /** * returns the full path to the datafile specified by ID and * optional revision * * The filename is URL encoded to protect Unicode chars * * @author Andreas Gohr */ function wikiFN($id,$rev=''){ global $conf; $id = cleanID($id); $id = str_replace(':','/',$id); if(empty($rev)){ $fn = $conf['datadir'].'/'.$id.'.txt'; }else{ $fn = $conf['olddir'].'/'.$id.'.'.$rev.'.txt'; if($conf['usegzip'] && !@file_exists($fn)){ //return gzip if enabled and plaintext doesn't exist $fn .= '.gz'; } } $fn = utf8_encodeFN($fn); return $fn; } /** * Returns the full filepath to a localized textfile if local * version isn't found the english one is returned * * @author Andreas Gohr */ function localeFN($id){ global $conf; $file = './lang/'.$conf['lang'].'/'.$id.'.txt'; if(!@file_exists($file)){ //fall back to english $file = './lang/en/'.$id.'.txt'; } return cleanText($file); } /** * convert line ending to unix format * * @see formText() for 2crlf conversion * @author Andreas Gohr */ function cleanText($text){ $text = preg_replace("/(\015\012)|(\015)/","\012",$text); return $text; } /** * Prepares text for print in Webforms by encoding special chars. * It also converts line endings to Windows format which is * pseudo standard for webforms. * * @see cleanText() for 2unix conversion * @author Andreas Gohr */ function formText($text){ $text = preg_replace("/\012/","\015\012",$text); return htmlspecialchars($text); } /** * Returns the specified local text in parsed format * * @author Andreas Gohr */ function parsedLocale($id){ //disable section editing global $parser; $se = $parser['secedit']; $parser['secedit'] = false; //fetch parsed locale $html = io_cacheParse(localeFN($id)); //reset section editing $parser['secedit'] = $se; return $html; } /** * Returns the specified local text in raw format * * @author Andreas Gohr */ function rawLocale($id){ return io_readFile(localeFN($id)); } /** * Returns the parsed Wikitext for the given id and revision. * * If $excuse is true an explanation is returned if the file * wasn't found * * @author Andreas Gohr */ function parsedWiki($id,$rev='',$excuse=true){ $file = wikiFN($id,$rev); $ret = ''; //ensure $id is in global $ID (needed for parsing) global $ID; $ID = $id; if($rev){ if(@file_exists($file)){ $ret = parse(io_readFile($file)); }elseif($excuse){ $ret = parsedLocale('norev'); } }else{ if(@file_exists($file)){ $ret = io_cacheParse($file); }elseif($excuse){ $ret = parsedLocale('newpage'); } } return $ret; } /** * Returns the raw WikiText * * @author Andreas Gohr */ function rawWiki($id,$rev=''){ return io_readFile(wikiFN($id,$rev)); } /** * Returns the raw Wiki Text in three slices. * * The range parameter needs to have the form "from-to" * and gives the range of the section. * The returned order is prefix, section and suffix. * * @author Andreas Gohr */ function rawWikiSlices($range,$id,$rev=''){ list($from,$to) = split('-',$range,2); $text = io_readFile(wikiFN($id,$rev)); $text = split("\n",$text); if(!$from) $from = 0; if(!$to) $to = count($text); $slices[0] = join("\n",array_slice($text,0,$from)); $slices[1] = join("\n",array_slice($text,$from,$to + 1 - $from)); $slices[2] = join("\n",array_slice($text,$to+1)); return $slices; } /** * Joins wiki text slices * * function to join the text slices with correct lineendings again. * When the pretty parameter is set to true it adds additional empty * lines between sections if needed (used on saving). * * @author Andreas Gohr */ function con($pre,$text,$suf,$pretty=false){ if($pretty){ if($pre && substr($pre,-1) != "\n") $pre .= "\n"; if($suf && substr($text,-1) != "\n") $text .= "\n"; } if($pre) $pre .= "\n"; if($suf) $text .= "\n"; return $pre.$text.$suf; } /** * print debug messages * * little function to print the content of a var * * @author Andreas Gohr */ function dbg($msg,$hidden=false){ (!$hidden) ? print '
' : print "";
}

/**
 * Add's an entry to the changelog
 *
 * @author Andreas Gohr 
 */
function addLogEntry($date,$id,$summary=""){
  global $conf;
  $id     = cleanID($id);//FIXME not needed anymore?

  if(!@is_writable($conf['changelog'])){
    msg($conf['changelog'].' is not writable!',-1);
    return;
  }

  if(!$date) $date = time(); //use current time if none supplied
  $remote = $_SERVER['REMOTE_ADDR'];
  $user   = $_SERVER['REMOTE_USER'];

  $logline = join("\t",array($date,$remote,$id,$user,$summary))."\n";

  //FIXME: use adjusted io_saveFile instead
  $fh = fopen($conf['changelog'],'a');
  if($fh){
    fwrite($fh,$logline);
    fclose($fh);
  }
}

/**
 * returns an array of recently changed files using the
 * changelog
 *
 * @author Andreas Gohr 
 */
function getRecents($num=0,$incdel=false){
  global $conf;
  $recent = array();
  if(!$num) $num = $conf['recent'];

  if(!@is_readable($conf['changelog'])){
    msg($conf['changelog'].' is not readable',-1);
    return $recent;
  }

  $loglines = file($conf['changelog']);
  rsort($loglines); //reverse sort on timestamp

  foreach ($loglines as $line){
    $line = rtrim($line);        //remove newline
    if(empty($line)) continue;   //skip empty lines
    $info = split("\t",$line);   //split into parts
    //add id if not in yet and file still exists and is allowed to read
    if(!$recent[$info[2]] && 
       (@file_exists(wikiFN($info[2])) || $incdel) &&
       (auth_quickaclcheck($info[2]) >= AUTH_READ)
      ){
      $recent[$info[2]]['date'] = $info[0];
      $recent[$info[2]]['ip']   = $info[1];
      $recent[$info[2]]['user'] = $info[3];
      $recent[$info[2]]['sum']  = $info[4];
      $recent[$info[2]]['del']  = !@file_exists(wikiFN($info[2]));
    }
    if(count($recent) >= $num){
      break; //finish if enough items found
    }
  }
  return $recent;
}

/**
 * gets additonal informations for a certain pagerevison
 * from the changelog
 *
 * @author Andreas Gohr 
 */
function getRevisionInfo($id,$rev){
  global $conf;
  $info = array();
  if(!@is_readable($conf['changelog'])){
    msg($conf['changelog'].' is not readable',-1);
    return $recent;
  }
  $loglines = file($conf['changelog']);
  $loglines = preg_grep("/$rev\t\d+\.\d+\.\d+\.\d+\t$id\t/",$loglines);
  rsort($loglines); //reverse sort on timestamp (shouldn't be needed)
  $line = split("\t",$loglines[0]);
  $info['date'] = $line[0];
  $info['ip']   = $line[1];
  $info['user'] = $line[3];
  $info['sum']   = $line[4];
  return $info;
}

/**
 * Saves a wikitext by calling io_saveFile
 *
 * @author Andreas Gohr 
 */
function saveWikiText($id,$text,$summary){
  global $conf;
  global $lang;
  umask($conf['umask']);
  // ignore if no changes were made
  if($text == rawWiki($id,'')){
    return;
  }

  $file = wikiFN($id);
  $old  = saveOldRevision($id);

  if (empty($text)){
    // remove empty files
    @unlink($file);
    $del = true;
    //autoset summary on deletion
    if(empty($summary)) $summary = $lang['deleted'];
  }else{
    // save file (datadir is created in io_saveFile)
    io_saveFile($file,$text);
    $del = false;
  }

  addLogEntry(@filemtime($file),$id,$summary);
  notify($id,$old,$summary);
  
  //purge cache on add by updating the purgefile
  if($conf['purgeonadd'] && (!$old || $del)){
    io_saveFile($conf['datadir'].'/.cache/purgefile',time());
  }
}

/**
 * moves the current version to the attic and returns its
 * revision date
 *
 * @author Andreas Gohr 
 */
function saveOldRevision($id){
	global $conf;
  umask($conf['umask']);
  $oldf = wikiFN($id);
  if(!@file_exists($oldf)) return '';
  $date = filemtime($oldf);
  $newf = wikiFN($id,$date);
  if(substr($newf,-3)=='.gz'){
    io_saveFile($newf,rawWiki($id));
  }else{
    io_makeFileDir($newf);
    copy($oldf, $newf);
  }
  return $date;
}

/**
 * Sends a notify mail to the wikiadmin when a page was
 * changed
 *
 * @author Andreas Gohr 
 */
function notify($id,$rev="",$summary=""){
  global $lang;
  global $conf;
  $hdrs ='';
  if(empty($conf['notify'])) return; //notify enabled?
  
  $text = rawLocale('mailtext');
  $text = str_replace('@DATE@',date($conf['dformat']),$text);
  $text = str_replace('@BROWSER@',$_SERVER['HTTP_USER_AGENT'],$text);
  $text = str_replace('@IPADDRESS@',$_SERVER['REMOTE_ADDR'],$text);
  $text = str_replace('@HOSTNAME@',gethostbyaddr($_SERVER['REMOTE_ADDR']),$text);
  $text = str_replace('@NEWPAGE@',wl($id,'','doku.php',true),$text);
  $text = str_replace('@DOKUWIKIURL@',getBaseURL(true),$text);
  $text = str_replace('@SUMMARY@',$summary,$text);
  $text = str_replace('@USER@',$_SERVER['REMOTE_USER'],$text);
  
  if($rev){
    $subject = $lang['mail_changed'].' '.$id;
    $text = str_replace('@OLDPAGE@',wl($id,"rev=$rev",'doku.php',true),$text);
    require_once("inc/DifferenceEngine.php");
    $df  = new Diff(split("\n",rawWiki($id,$rev)),
                    split("\n",rawWiki($id)));
    $dformat = new UnifiedDiffFormatter();
    $diff    = $dformat->format($df);
  }else{
    $subject=$lang['mail_newpage'].' '.$id;
    $text = str_replace('@OLDPAGE@','none',$text);
    $diff = rawWiki($id);
  }
  $text = str_replace('@DIFF@',$diff,$text);

  mail_send($conf['notify'],$subject,$text,$conf['mailfrom']);
}

/**
 * Return a list of available page revisons
 *
 * @author Andreas Gohr 
 */
function getRevisions($id){
  $revd = dirname(wikiFN($id,'foo'));
  $revs = array();
  $clid = cleanID($id);
  if(strrpos($clid,':')) $clid = substr($clid,strrpos($clid,':')+1); //remove path

  if (is_dir($revd) && $dh = opendir($revd)) {
    while (($file = readdir($dh)) !== false) {
      if (is_dir($revd.'/'.$file)) continue;
      if (preg_match('/^'.$clid.'\.(\d+)\.txt(\.gz)?$/',$file,$match)){
        $revs[]=$match[1];
      }
    }
    closedir($dh);
  }
  rsort($revs);
  return $revs;
}

/**
 * downloads a file from the net and saves it to the given location
 *
 * @author Andreas Gohr 
 */
function download($url,$file){
  $fp = @fopen($url,"rb");
  if(!$fp) return false;

  while(!feof($fp)){
    $cont.= fread($fp,1024);
  }
  fclose($fp);

  $fp2 = @fopen($file,"w");
  if(!$fp2) return false;
  fwrite($fp2,$cont);
  fclose($fp2);
  return true;
} 

/**
 * extracts the query from a google referer
 *
 * @author Andreas Gohr 
 */
function getGoogleQuery(){
  $url = parse_url($_SERVER['HTTP_REFERER']);

  if(!preg_match("#google\.#i",$url['host'])) return '';
  $query = array();
  parse_str($url['query'],$query);

  return $query['q'];
}

/**
 * Try to set correct locale
 *
 * @deprecated No longer used
 * @author     Andreas Gohr 
 */
function setCorrectLocale(){
  global $conf;
  global $lang;

  $enc = strtoupper($lang['encoding']);
  foreach ($lang['locales'] as $loc){
    //try locale
    if(@setlocale(LC_ALL,$loc)) return;
    //try loceale with encoding
    if(@setlocale(LC_ALL,"$loc.$enc")) return;
  }
  //still here? try to set from environment
  @setlocale(LC_ALL,"");
}

/**
 * Return the human readable size of a file
 *
 * @param       int    $size   A file size
 * @param       int    $dec    A number of decimal places
 * @author      Martin Benjamin 
 * @author      Aidan Lister 
 * @version     1.0.0
 */
function filesize_h($size, $dec = 1){
  $sizes = array('B', 'KB', 'MB', 'GB');
  $count = count($sizes);
  $i = 0;
    
  while ($size >= 1024 && ($i < $count - 1)) {
    $size /= 1024;
    $i++;
  }

  return round($size, $dec) . ' ' . $sizes[$i];
}

/**
 * Run a few sanity checks
 *
 * @author Andreas Gohr 
 */
function getVersion(){
  //import version string
  if(@file_exists('VERSION')){
    //official release
    return 'Release '.io_readfile('VERSION');
  }elseif(is_dir('_darcs')){
    //darcs checkout
    $inv = file('_darcs/inventory');
    $inv = preg_grep('#andi@splitbrain\.org\*\*\d{14}#',$inv);
    $cur = array_pop($inv);
    preg_match('#\*\*(\d{4})(\d{2})(\d{2})#',$cur,$matches);
    return 'Darcs '.$matches[1].'-'.$matches[2].'-'.$matches[3];
  }else{
    return 'snapshot?';
  }
}

/**
 * Run a few sanity checks
 *
 * @author Andreas Gohr 
 */
function check(){
  global $conf;
  global $INFO;

  msg('DokuWiki version: '.getVersion(),1);

  if(version_compare(phpversion(),'4.3.0','<')){
    msg('Your PHP version is too old ('.phpversion().' vs. 4.3.+ recommended)',-1);
  }elseif(version_compare(phpversion(),'4.3.10','<')){
    msg('Consider upgrading PHP to 4.3.10 or higher for security reasons (your version: '.phpversion().')',0);
  }else{
    msg('PHP version '.phpversion(),1);
  }

  if(is_writable($conf['changelog'])){
    msg('Changelog is writable',1);
  }else{
    msg('Changelog is not writable',-1);
  }

  if(is_writable($conf['datadir'])){
    msg('Datadir is writable',1);
  }else{
    msg('Datadir is not writable',-1);
  }

  if(is_writable($conf['olddir'])){
    msg('Attic is writable',1);
  }else{
    msg('Attic is not writable',-1);
  }

  if(is_writable($conf['mediadir'])){
    msg('Mediadir is writable',1);
  }else{
    msg('Mediadir is not writable',-1);
  }

  if(is_writable('conf/users.auth')){
    msg('conf/users.auth is writable',1);
  }else{
    msg('conf/users.auth is not writable',0);
  }

  if(function_exists('mb_strpos')){
    if(defined('UTF8_NOMBSTRING')){
      msg('mb_string extension is available but will not be used',0);
    }else{
      msg('mb_string extension is available and will be used',1);
    }
  }else{
    msg('mb_string extension not available - PHP only replacements will be used',0);
  }
 
  msg('Your current permission for this page is '.$INFO['perm'],0);

  if(is_writable($INFO['filepath'])){
    msg('The current page is writable by the webserver',0);
  }else{
    msg('The current page is not writable by the webserver',0);
  }

  if($INFO['writable']){
    msg('The current page is writable by you',0);
  }else{
    msg('The current page is not writable you',0);
  }
}
?>


 */

  include_once("inc/common.php");
  include_once("inc/html.php");
  include_once("inc/format.php");
  require_once("lang/en/lang.php");
  require_once("lang/".$conf['lang']."/lang.php");

/**
 * The main parser function.
 *
 * Accepts raw data and returns valid xhtml
 *
 * @author  Andreas Gohr 
 */  
function parse($text){
  global $parser;
  global $conf;
  $table   = array();
  $hltable = array();

  //preparse
  $text = preparse($text,$table,$hltable);

  //padding with a newline
  $text  = "\n".$text."\n";

  #for link matching
  $urls = '(https?|telnet|gopher|file|wais|ftp|ed2k|irc)';
  $ltrs = '\w';
  $gunk = '/\#~:.?+=&%@!\-';
  $punc = '.:?\-;,';
  $host = $ltrs.$punc;
  $any  = $ltrs.$gunk.$punc;

  /* first pass */

  //preformated texts
  firstpass($table,$text,"#(.*?)#se","preformat('\\1','nowiki')");
  firstpass($table,$text,"#%%(.*?)%%#se","preformat('\\1','nowiki')");
  firstpass($table,$text,"#(.*?)#se","preformat('\\3','code','\\2')");
  firstpass($table,$text,"#(.*?)#se","preformat('\\1','file')");

  // html and php includes
  firstpass($table,$text,"#(.*?)#se","preformat('\\1','html')");
  firstpass($table,$text,"#(.*?)#se","preformat('\\1','php')");

  // codeblocks
  firstpass($table,$text,"/(\n( {2,}|\t)[^\*\-\n ][^\n]+)(\n( {2,}|\t)[^\n]*)*/se","preformat('\\0','block')","\n");

  //check if toc is wanted
  if(!isset($parser['toc'])){
    if(strpos($text,'~~NOTOC~~')!== false){
      $text = str_replace('~~NOTOC~~','',$text);
      $parser['toc']  = false;
    }else{
      $parser['toc']  = true;
    }
  }

  //check if this file may be cached
  if(!isset($parser['cache'])){
    if(strpos($text,'~~NOCACHE~~')!=false){
      $text = str_replace('~~NOCACHE~~','',$text);
      $parser['cache']  = false;
    }else{
      $parser['cache']  = true;
    }
  }

  //headlines
  format_headlines($table,$hltable,$text);

  //links
  firstpass($table,$text,"#\[\[([^\]]+?)\]\]#ie","linkformat('\\1')");

  //media
  firstpass($table,$text,"/\{\{([^\}]+)\}\}/se","mediaformat('\\1')");

  //match full URLs (adapted from Perl cookbook)
  firstpass($table,$text,"#(\b)($urls://[$any]+?)([$punc]*[^$any])#ie","linkformat('\\2')",'\1','\4');

  //short www URLs 
  firstpass($table,$text,"#(\b)(www\.[$host]+?\.[$host]+?[$any]+?)([$punc]*[^$any])#ie","linkformat('http://\\2|\\2')",'\1','\3');

  //windows shares 
  firstpass($table,$text,"#([$gunk$punc\s])(\\\\\\\\[$host]+?\\\\[$any]+?)([$punc]*[^$any])#ie","linkformat('\\2')",'\1','\3');

  //short ftp URLs 
  firstpass($table,$text,"#(\b)(ftp\.[$host]+?\.[$host]+?[$any]+?)([$punc]*[^$any])#ie","linkformat('ftp://\\2')",'\1','\3');

  // email@domain.tld
  firstpass($table,$text,"#<([\w0-9\-_.]+?)@([\w\-]+\.([\w\-\.]+\.)*[\w]+)>#ie", "linkformat('\\1@\\2')");

  //CamelCase if wanted
  if($conf['camelcase']){
    firstpass($table,$text,"#(\b)([A-Z]+[a-z]+[A-Z][A-Za-z]*)(\b)#se","linkformat('\\2')",'\1','\3');
  }

  $text = htmlspecialchars($text);

  //smileys
  smileys($table,$text);

  //acronyms
  acronyms($table,$text);

  /* second pass for simple formating */
  $text = simpleformat($text);
  
  /* third pass - insert the matches from 1st pass */
  reset($table);
  while (list($key, $val) = each($table)) {
    $text = str_replace($key,$val,$text);
  }

  /* remove empty paragraphs */
  $text = preg_replace('"

\n*

"','',$text); /* remove padding */ $text = trim($text); return $text; } /** * Line by line preparser * * This preparses the text by walking it line by line. This * is the only place where linenumbers are still available (needed * for section edit. Some precautions have to be taken to not change * any noparse block. * * @author Andreas Gohr */ function preparse($text,&$table,&$hltable){ $lines = split("\n",$text); //prepare a tokens for paragraphs $po = mkToken(); $table[$po] = "

"; $pc = mkToken(); $table[$pc] = "

"; for ($l=0; $l(.*?)#","",$line); $line = preg_replace("#%%(.*?)%%#","",$line); $line = preg_replace("#(.*?)#","",$line); $line = preg_replace("#(.*?)#","",$line); $line = preg_replace("#(.*?)#","",$line); $line = preg_replace("#(.*?)#","",$line); //check for start of multiline noparse areas if(preg_match('#^.*?<(nowiki|code|php|html|file)( (\w+))?>#',$line,$matches)){ list($noparse) = split(" ",$matches[1]); //remove options $noparse = ''; continue; }elseif(preg_match('#^.*?%%#',$line)){ $noparse = '%%'; continue; } } //handle headlines if(preg_match('/^(\s)*(==+)(.+?)(==+)(\s*)$/',$lines[$l],$matches)){ //get token $tk = tokenize_headline($hltable,$matches[2],$matches[3],$l); //replace line with token $lines[$l] = $tk; } //handle paragraphs if(empty($lines[$l])){ $lines[$l] = "$pc\n$po"; } } //reassemble full text $text = join("\n",$lines); //open first and close last paragraph $text = "$po\n$text\n$pc"; return $text; } /** * Build TOC lookuptable * * This function adds some information about the given headline * to a lookuptable to be processed later. Returns a unique token * that idetifies the headline later * * @author Andreas Gohr */ function tokenize_headline(&$hltable,$pre,$hline,$lno){ switch (strlen($pre)){ case 2: $lvl = 5; break; case 3: $lvl = 4; break; case 4: $lvl = 3; break; case 5: $lvl = 2; break; default: $lvl = 1; break; } $token = mkToken(); $hltable[] = array( 'name' => htmlspecialchars(trim($hline)), 'level' => $lvl, 'line' => $lno, 'token' => $token ); return $token; } /** * Headline formatter * * @author Andreas Gohr */ function format_headlines(&$table,&$hltable,&$text){ global $parser; global $conf; global $lang; global $ID; // walk the headline table prepared in preparsing $last = 0; $cnt = 0; $hashs = array(); foreach($hltable as $hl){ $cnt++; //make unique headlinehash $hash = cleanID($hl['name']); $i=2; while(in_array($hash,$hashs)) $hash = cleanID($hl['name']).$i++; $hashs[] = $hash; // build headline $headline = "

\n"; //close paragraph if($cnt - 1) $headline .= ''; //no close on first HL $headline .= ''; $headline .= ''; $headline .= $hl['name']; $headline .= ''; $headline .= '
'; $headline .= "\n

"; //open new paragraph //remember for autoTOC if($hl['level'] <= $conf['maxtoclevel']){ $content[] = array('id' => $hash, 'name' => $hl['name'], 'level' => $hl['level']); } //add link for section edit for HLs 1, and 3 if( ($hl['level'] <= $conf['maxseclevel']) && ($hl['line'] - $last > 1)){ $secedit = ''; $headline = $secedit.$headline; $last = $hl['line']; } //put headline into firstpasstable $table[$hl['token']] = $headline; } //add link for editing the last section if($last){ $secedit = ''; $token = mktoken(); $text .= $token; $table[$token] = $secedit; } //close last div if ($cnt){ $token = mktoken(); $text .= $token; $table[$token] = '

'; } //prepend toc if ($parser['toc'] && count($content) > 2){ $token = mktoken(); $text = $token.$text; $table[$token] = html_toc($content); } } /** * Formats various link types using the functions from format.php * * @author Andreas Gohr */ function linkformat($match){ global $conf; //unescape $match = str_replace('\\"','"',$match); //prepare variables for the formaters $link = array(); list($link['url'],$link['name']) = split('\|',$match,2); $link['url'] = trim($link['url']); $link['name'] = trim($link['name']); $link['class'] = ''; $link['target'] = ''; $link['style'] = ''; $link['pre'] = ''; $link['suf'] = ''; $link['more'] = ''; //save real name for image check $realname = $link['name']; /* put it into the right formater */ if(strpos($link['url'],'>')){ // InterWiki $link = format_link_interwiki($link); }elseif(preg_match('#^([a-z0-9]+?){1}://#i',$link['url'])){ // external URL $link = format_link_externalurl($link); }elseif(preg_match("/^\\\\\\\\([a-z0-9\-_.]+)\\\\(.+)$/",$link['url'])){ // windows shares $link = format_link_windows($link); }elseif(preg_match('#([a-z0-9\-_.]+?)@([\w\-]+\.([\w\-\.]+\.)*[\w]+)#i',$link['url'])){ // email $link = format_link_email($link); }else{ // wiki link $link = format_link_wiki($link); } //is realname an image? use media formater if(preg_match('#^{{.*?\.(gif|png|jpe?g)(\?.*?)?\s*(\|.*?)?}}$#',$realname)){ $link['name'] = substr($realname,2,-2); $link = format_link_media($link); } // build the replacement with the variables set by the formaters return format_link_build($link); } /** * Simple text formating and typography is done here * * @author Andreas Gohr */ function simpleformat($text){ global $conf; $text = preg_replace('/__(.+?)__/s','\1',$text); //underline $text = preg_replace('/\/\/(.+?)\/\//s','\1',$text); //emphasize $text = preg_replace('/\*\*(.+?)\*\*/s','\1',$text); //bold $text = preg_replace('/\'\'(.+?)\'\'/s','\1',$text); //code $text = preg_replace('#<del>(.*?)</del>#is','\1',$text); //deleted $text = preg_replace('/^(\s)*----+(\s*)$/m',"

\n
\n

",$text); //hr //sub and superscript $text = preg_replace('#<sub>(.*?)</sub>#is','\1',$text); $text = preg_replace('#<sup>(.*?)</sup>#is','\1',$text); //do quoting $text = preg_replace("/\n((>)[^\n]*?\n)+/se","'\n'.quoteformat('\\0').'\n'",$text); // Typography if($conf['typography']){ $text = preg_replace('/([^-])--([^-])/s','\1–\2',$text); //endash $text = preg_replace('/([^-])---([^-])/s','\1—\2',$text); //emdash $text = preg_replace('/"([^\"]+?)"/s','“\1”',$text); //curly quotes $text = preg_replace('/(\s)\'(\S)/m','\1‘\2',$text); //single open quote $text = preg_replace('/(\S)\'/','\1’',$text); //single closing quote or apostroph $text = preg_replace('/\.\.\./','\1…\2',$text); //ellipse $text = preg_replace('/(\d+)x(\d+)/i','\1×\2',$text); //640x480 $text = preg_replace('/>>/i','»',$text); // >> $text = preg_replace('/<</i','«',$text); // << $text = preg_replace('/<->/i','↔',$text); // <-> $text = preg_replace('/<-/i','←',$text); // <- $text = preg_replace('/->/i','→',$text); // -> $text = preg_replace('/<=>/i','⇔',$text); // <=> $text = preg_replace('/<=/i','⇐',$text); // <= $text = preg_replace('/=>/i','⇒',$text); // => $text = preg_replace('/\(c\)/i','©',$text); // copyrigtht $text = preg_replace('/\(r\)/i','®',$text); // registered $text = preg_replace('/\(tm\)/i','™',$text); // trademark } //forced linebreaks $text = preg_replace('#\\\\\\\\(\s)#',"
\\1",$text); // lists (blocks leftover after blockformat) $text = preg_replace("/(\n( {2,}|\t)[\*\-][^\n]+)(\n( {2,}|\t)[^\n]*)*/se","\"\\n\".listformat('\\0')",$text); // tables $text = preg_replace("/\n(([\|\^][^\n]*?)+[\|\^] *\n)+/se","\"\\n\".tableformat('\\0')",$text); // footnotes $text = footnotes($text); // run custom text replacements $text = customs($text); return $text; } /** * Footnote formating * * @author Andreas Gohr */ function footnotes($text){ $num = 0; while (preg_match('/\(\((.+?)\)\)/s',$text,$match)){ $num++; $fn = $match[1]; $linkt = ''.$num.')'; $linkb = ''.$num.')'; $text = preg_replace('/ ?\(\((.+?)\)\)/s',$linkt,$text,1); if($num == 1) $text .= '

'; $text .= '
'.$linkb.' '.$fn.'
'; } if($num) $text .= '
'; return $text; } /** * Replace smileys with their graphic equivalents * * @author Andreas Gohr */ function smileys(&$table,&$text){ $smileys = file('conf/smileys.conf'); foreach($smileys as $smiley){ $smiley = preg_replace('/#.*$/','',$smiley); //ignore comments $smiley = trim($smiley); if(empty($smiley)) continue; $sm = preg_split('/\s+/',$smiley,2); $sm[1] = ''.$sm[0].''; $sm[0] = preg_quote($sm[0],'/'); firstpass($table,$text,'/(\W)'.$sm[0].'(\W)/s',$sm[1],"\\1","\\2"); } } /** * Add acronym tags to known acronyms * * @author Andreas Gohr */ function acronyms(&$table,&$text){ $acronyms = file('conf/acronyms.conf'); foreach($acronyms as $acro){ $acro = preg_replace('/#.*$/','',$acro); //ignore comments $acro = trim($acro); if(empty($acro)) continue; list($ac,$desc) = preg_split('/\s+/',$acro,2); $ac = preg_quote($ac,'/'); firstpass($table,$text,'/(\b)('.$ac.')(\b)/s',"\\2","\\1","\\3"); } } /** * Apply custom text replacements * * @author Andreas Gohr */ function customs($text){ $reps = file ('conf/custom.conf'); foreach($reps as $rep){ //strip comments only outside a regexp $rep = preg_replace('/#[^\/]*$/','',$rep); //ignore comments $rep = trim($rep); if(empty($rep)) continue; if(preg_match('#^(/.+/\w*)\s+\'(.*)\'$#',$rep,$matches)){ $text = preg_replace($matches[1],$matches[2],$text); } } return $text; } /** * Replace regexp with token * * @author Andreas Gohr */ function firstpass(&$table,&$text,$regexp,$replace,$lpad='',$rpad=''){ //extended regexps have to be disabled for inserting the token //and later reenabled when handling the actual code: $ext=''; if(substr($regexp,-1) == 'e'){ $ext='e'; $regexp = substr($regexp,0,-1); } while(preg_match($regexp,$text,$matches)){ $token = mkToken(); $match = $matches[0]; $text = preg_replace($regexp,$lpad.$token.$rpad,$text,1); $table[$token] = preg_replace($regexp.$ext,$replace,$match); } } /** * create a random and hopefully unique token * * @author Andreas Gohr */ function mkToken(){ return '~'.md5(uniqid(rand(), true)).'~'; } /** * Do quote blocks * * @author Andreas Gohr */ function quoteformat($block){ $block = trim($block); $lines = split("\n",$block); $lvl = 0; $ret = ""; foreach ($lines as $line){ //remove '>' and count them $cnt = 0; while(substr($line,0,4) == '>'){ $line = substr($line,4); $cnt++; } //compare to last level and open or close new divs if needed if($cnt > $lvl){ $ret .= "

\n"; for ($i=0; $i< $cnt - $lvl; $i++){ $ret .= '
'; } $ret .= "\n

"; }elseif($cnt < $lvl){ $ret .= "\n

"; for ($i=0; $i< $lvl - $cnt; $i++){ $ret .= "
\n"; } $ret .= "

\n"; }elseif(empty($line)){ $ret .= "

\n

"; } //keep rest of line but trim left whitespaces $ret .= ltrim($line)."\n"; //remember level $lvl = $cnt; } //close remaining divs $ret .= "

\n"; for ($i=0; $i< $lvl; $i++){ $ret .= "\n"; } $ret .= "

\n"; return "$ret"; } /** * format inline tables * * @author Andreas Gohr * @author Aaron Evans */ function tableformat($block) { $block = trim($block); $lines = split("\n",$block); $ret = ""; //build a row array $rows = array(); for($r=0; $r < count($lines); $r++){ $line = $lines[$r]; //remove last seperator and trailing whitespace $line = preg_replace('/[\|\^]\s*$/', '', $line); $c = -1; //prepare colcounter) for($chr=0; $chr < strlen($line); $chr++){ if($line[$chr] == '^'){ $c++; $rows[$r][$c]['head'] = true; $rows[$r][$c]['data'] = ''; }elseif($line[$chr] == '|'){ $c++; $rows[$r][$c]['head'] = false; $rows[$r][$c]['data'] = ''; }else{ $rows[$r][$c]['data'].= $line[$chr]; } } } //build table $ret .= "

\n\n"; for($r=0; $r < count($rows); $r++){ $ret .= " \n"; for ($c=0; $c < count($rows[$r]); $c++){ $cspan=1; $data = $rows[$r][$c]['data']; $head = $rows[$r][$c]['head']; //join cells if next is empty while($c < count($rows[$r])-1 && $rows[$r][$c+1]['data'] == ''){ $c++; $cspan++; } if($cspan > 1){ $cspan = 'colspan="'.$cspan.'"'; }else{ $cspan = ''; } //determine alignment from whitespace if (preg_match('/^\s\s/', $data)) { // right indentation $td_class = "rightalign"; if (preg_match('/\s\s$/', $data)) { // both left and right indentation $td_class = "centeralign"; } } else { // left indentation (default) $td_class = "leftalign"; } $data = trim($data); if ($head) { $ret .= " \n"; // set css class for alignment } else { $ret .= " \n"; // set css class for alignment } } $ret .= " \n"; } $ret .= "
$data $data

\n

"; return $ret; } /** * format lists * * @author Andreas Gohr */ function listformat($block){ //remove 1st newline $block = substr($block,1); //unescape $block = str_replace('\\"','"',$block); //dbg($block); //walk line by line $ret=''; $lst=0; $lvl=0; $enc=0; $lines = split("\n",$block); //build an item array $cnt=0; $items = array(); foreach ($lines as $line){ //get intendion level $lvl = 0; $lvl += floor(strspn($line,' ')/2); $lvl += strspn($line,"\t"); //remove indents $line = preg_replace('/^[ \t]+/','',$line); //get type of list (substr($line,0,1) == '-') ? $type='ol' : $type='ul'; // remove bullet and following spaces $line = preg_replace('/^[*\-]\s*/','',$line); //add item to the list $items[$cnt]['level'] = $lvl; $items[$cnt]['type'] = $type; $items[$cnt]['text'] = $line; //increase counter $cnt++; } $level = 0; $opens = array(); foreach ($items as $item){ if( $item['level'] > $level ){ //open new list $ret .= "\n<".$item['type'].">\n"; array_push($opens,$item['type']); }elseif( $item['level'] < $level ){ //close last item $ret .= "\n"; for ($i=0; $i<($level - $item['level']); $i++){ //close higher lists $ret .= '\n\n"; } }elseif($item['type'] != $opens[count($opens)-1]){ //close last list and open new $ret .= '\n\n"; $ret .= "\n<".$item['type'].">\n"; array_push($opens,$item['type']); }else{ //close last item $ret .= "\n"; } //remember current level and type $level = $item['level']; //print item $ret .= '

  • '; $ret .= ''.$item['text'].''; } //close remaining items and lists while ($open = array_pop($opens)){ $ret .= "
  • \n"; $ret .= '\n"; } return "

    \n".$ret."\n

    "; } /** * Handle preformatted blocks * * Uses GeSHi for syntax highlighting * * @author Andreas Gohr */ function preformat($text,$type,$option=''){ global $conf; global $lang; //unescape $text = str_replace('\\"','"',$text); if($type == 'php' && !$conf['phpok']) $type='file'; if($type == 'html' && !$conf['htmlok']) $type='file'; switch ($type){ case 'php': ob_start(); eval($text); $text = ob_get_contents(); ob_end_clean(); break; case 'html': break; case 'nowiki': $text = htmlspecialchars($text); break; case 'file': $text = htmlspecialchars($text); $text = "

    \n
    ".$text."
    \n

    "; break; case 'code': if(empty($option)){ $text = htmlspecialchars($text); $text = '

    '.$text.'
    '; }else{ //strip leading blank line $text = preg_replace('/^\s*?\n/','',$text); //use geshi for highlighting require_once("inc/geshi.php"); $geshi = new GeSHi($text, strtolower($option), "inc/geshi"); $geshi->set_encoding($lang['encoding']); $geshi->enable_classes(); $geshi->set_header_type(GESHI_HEADER_PRE); $geshi->set_overall_class('code'); $geshi->set_link_target($conf['target']['extern']); $text = $geshi->parse_code(); } $text = "

    \n".$text."\n

    "; break; case 'block': $text = substr($text,1); //remove 1st newline $lines = split("\n",$text); //break into lines $text = ''; foreach($lines as $line){ $text .= substr($line,2)."\n"; //remove indents } $text = htmlspecialchars($text); $text = "

    \n
    ".$text."
    \n

    "; break; } return $text; } /** * Format embedded media (images) * * @author Andreas Gohr */ function mediaformat($text){ global $conf; //unescape $text = str_replace('\\"','"',$text); // format RSS if(substr($text,0,4) == 'rss>'){ return format_rss(substr($text,4)); } //handle normal media stuff $link = array(); $link['name'] = $text; $link = format_link_media($link); return format_link_build($link); } ?> */ require_once("inc/common.php"); require_once("inc/io.php"); require_once("inc/blowfish.php"); require_once("inc/mail.php"); // load the the auth functions require_once('inc/auth_'.$conf['authtype'].'.php'); // some ACL level defines define('AUTH_NONE',0); define('AUTH_READ',1); define('AUTH_EDIT',2); define('AUTH_CREATE',4); define('AUTH_UPLOAD',8); define('AUTH_GRANT',255); if($conf['useacl']){ auth_login($_REQUEST['u'],$_REQUEST['p'],$_REQUEST['r']); // load ACL into a global array $AUTH_ACL = split("\n", $xoopsModuleConfig['authdata']); } /** * This tries to login the user based on the sent auth credentials * * The authentication works like this: if a username was given * a new login is assumed and user/password are checked. If they * are correct the password is encrypted with blowfish and stored * together with the username in a cookie - the same info is stored * in the session, too. Additonally a browserID is stored in the * session. * * If no username was given the cookie is checked: if the username, * crypted password and browserID match between session and cookie * no further testing is done and the user is accepted * * If a cookie was found but no session info was availabe the * blowish encrypted password from the cookie is decrypted and * together with username rechecked by calling this function again. * * On a successful login $_SERVER[REMOTE_USER] and $USERINFO * are set. * * @author Andreas Gohr * * @param string $user Username * @param string $pass Cleartext Password * @param bool $sticky Cookie should not expire * @return bool true on successful auth */ function auth_login($user,$pass,$sticky=false){ global $USERINFO; global $conf; global $lang; $sticky ? $sticky = true : $sticky = false; //sanity check if(isset($user)){ //usual login if (auth_checkPass($user,$pass)){ // make logininfo globally available $_SERVER['REMOTE_USER'] = $user; $USERINFO = auth_getUserData($user); //FIXME move all references to session // set cookie $pass = PMA_blowfish_encrypt($pass,auth_cookiesalt()); $cookie = base64_encode("$user|$sticky|$pass"); if($sticky) $time = time()+60*60*24*365; //one year setcookie('DokuWikiAUTH',$cookie,$time); // set session $_SESSION[$conf['title']]['auth']['user'] = $user; $_SESSION[$conf['title']]['auth']['pass'] = $pass; $_SESSION[$conf['title']]['auth']['buid'] = auth_browseruid(); $_SESSION[$conf['title']]['auth']['info'] = $USERINFO; return true; }else{ //invalid credentials - log off msg($lang['badlogin'],-1); auth_logoff(); return false; } }else{ // read cookie information $cookie = base64_decode($_COOKIE['DokuWikiAUTH']); list($user,$sticky,$pass) = split('\|',$cookie,3); // get session info $session = $_SESSION[$conf['title']]['auth']; if($user && $pass){ // we got a cookie - see if we can trust it if(isset($session) && ($session['user'] == $user) && ($session['pass'] == $pass) && //still crypted ($session['buid'] == auth_browseruid()) ){ // he has session, cookie and browser right - let him in $_SERVER['REMOTE_USER'] = $user; $USERINFO = $session['info']; //FIXME move all references to session return true; } // no we don't trust it yet - recheck pass $pass = PMA_blowfish_decrypt($pass,auth_cookiesalt()); return auth_login($user,$pass,$sticky); } } //just to be sure auth_logoff(); return false; } /** * Builds a pseudo UID from browserdata * * This is neither unique nor unfakable - still it adds some * security * * @author Andreas Gohr * * @return string a MD5 sum of various browser headers */ function auth_browseruid(){ $uid = ''; $uid .= $_SERVER['HTTP_USER_AGENT']; $uid .= $_SERVER['HTTP_ACCEPT_ENCODING']; $uid .= $_SERVER['HTTP_ACCEPT_LANGUAGE']; $uid .= $_SERVER['HTTP_ACCEPT_CHARSET']; return md5($uid); } /** * Creates a random key to encrypt the password in cookies * * This function tries to read the password for encrypting * cookies from $conf['datadir'].'/.cache/.htcookiesalt' * if no such file is found a random key is created and * and stored in this file. * * @author Andreas Gohr * * @return string */ function auth_cookiesalt(){ global $conf; $file = $conf['datadir'].'/.cache/.htcookiesalt'; $salt = io_readFile($file); if(empty($salt)){ $salt = uniqid(rand(),true); io_saveFile($file,$salt); } return $salt; } /** * This clears all authenticationdata and thus log the user * off * * @author Andreas Gohr */ function auth_logoff(){ global $conf; global $USERINFO; unset($_SESSION[$conf['title']]['auth']['user']); unset($_SESSION[$conf['title']]['auth']['pass']); unset($_SESSION[$conf['title']]['auth']['info']); unset($_SERVER['REMOTE_USER']); $USERINFO=null; //FIXME setcookie('DokuWikiAUTH','',time()-3600); } /** * Convinience function for auth_aclcheck() * * This checks the permissions for the current user * * @author Andreas Gohr * * @param string $id page ID * @return int permission level */ function auth_quickaclcheck($id){ global $conf; global $USERINFO; # if no ACL is used always return upload rights # if no ACL is used always return upload rights if(!$conf['useacl']) return AUTH_UPLOAD; if ($GLOBALS['xoopsUser']) return auth_aclcheck($id,$GLOBALS['xoopsUser']->uname(),$GLOBALS['xoopsUser']->groups()); else return auth_aclcheck($id, null, null); } /** * Returns the maximum rights a user has for * the given ID or its namespace * * @author Andreas Gohr * * @param string $id page ID * @param string $user Username * @param array $groups Array of groups the user is in * @return int permission level */ function auth_aclcheck($id,$user,$groups){ global $conf; global $AUTH_ACL; # if no ACL is used always return upload rights if(!$conf['useacl']) return AUTH_UPLOAD; $ns = getNS($id); $perm = -1; if($user){ // Getting the group names $groupNames = array(); foreach ($groups as $group) { $xoopsGroup = $GLOBALS['member_handler']->getGroup($group); $groupNames[] = $xoopsGroup->getVar('name'); } $groups = $groupNames; //prepend groups with @ for($i=0; $i $perm){ $perm = $acl[2]; } } if($perm > -1){ //we had a match - return it return $perm; } } //still here? do the namespace checks if($ns){ $path = $ns.':\*'; }else{ $path = '\*'; //root document } do{ $matches = preg_grep('/^'.$path.'\s+('.$regexp.')\s+/',$AUTH_ACL); if(count($matches)){ foreach($matches as $match){ $match = preg_replace('/#.*$/','',$match); //ignore comments $acl = preg_split('/\s+/',$match); if($acl[2] > $perm){ $perm = $acl[2]; } } //we had a match - return it return $perm; } //get next higher namespace $ns = getNS($ns); if($path != '\*'){ $path = $ns.':\*'; if($path == ':\*') $path = '\*'; }else{ //we did this already //looks like there is something wrong with the ACL //break here return $perm; } }while(1); //this should never loop endless } /** * Create a pronouncable password * * @author Andreas Gohr * @link http://www.phpbuilder.com/annotate/message.php3?id=1014451 * * @return string pronouncable password */ function auth_pwgen(){ $pw = ''; $c = 'bcdfghjklmnprstvwz'; //consonants except hard to speak ones $v = 'aeiou'; //vowels $a = $c.$v; //both //use two syllables... for($i=0;$i < 2; $i++){ $pw .= $c[rand(0, strlen($c)-1)]; $pw .= $v[rand(0, strlen($v)-1)]; $pw .= $a[rand(0, strlen($a)-1)]; } //... and add a nice number $pw .= rand(10,99); return $pw; } /** * Sends a password to the given user * * @author Andreas Gohr * * @return bool true on success */ function auth_sendPassword($user,$password){ global $conf; global $lang; $hdrs = ''; $userinfo = auth_getUserData($user); if(!$userinfo['mail']) return false; $text = rawLocale('password'); $text = str_replace('@DOKUWIKIURL@',getBaseURL(true),$text); $text = str_replace('@FULLNAME@',$userinfo['name'],$text); $text = str_replace('@LOGIN@',$user,$text); $text = str_replace('@PASSWORD@',$password,$text); $text = str_replace('@TITLE@',$conf['title'],$text); return mail_send($userinfo['name'].' <'.$userinfo['mail'].'>', $lang['regpwmail'], $text, $conf['mailfrom']); } /** * Register a new user * * This registers a new user - Data is read directly from $_POST * * @author Andreas Gohr * * @return bool true on success, false on any error */ function register(){ global $lang; global $conf; if(!$_POST['save']) return false; if(!$conf['openregister']) return false; //clean username $_POST['login'] = preg_replace('/.*:/','',$_POST['login']); $_POST['login'] = cleanID($_POST['login']); //clean fullname and email $_POST['fullname'] = trim(str_replace(':','',$_POST['fullname'])); $_POST['email'] = trim(str_replace(':','',$_POST['email'])); if( empty($_POST['login']) || empty($_POST['fullname']) || empty($_POST['email']) ){ msg($lang['regmissing'],-1); return false; } //check mail if(!mail_isvalid($_POST['email'])){ msg($lang['regbadmail'],-1); return false; } //okay try to create the user $pass = auth_createUser($_POST['login'],$_POST['fullname'],$_POST['email']); if(empty($pass)){ msg($lang['reguexists'],-1); return false; } //send him the password if (auth_sendPassword($_POST['login'],$pass)){ msg($lang['regsuccess'],1); return true; }else{ msg($lang['regmailfail'],-1); return false; } } ?>