<?php if (!defined('PmWiki')) exit();
/*  
	People's login information is stored on their profile page. 
	The group to which they belong corresponds to the group their profile page is in.
	To grant access to everyone in that group, simply set the password to @Group with 'Group' 
	being the group you want to grant access to. For individuals, just list their username in
	the password list. To exclude an individual, use -username. To exclude a group, use -@Group.

	Groups that are named $SitePrefix.${some default password key} are recognized as @_site_{some default password key}. 
	For example, if $SitePrefix=SitePre, then members of the group SitePreedit get all the privalages of the @_site_edit password.
	Similarly, the members of SitePreadmin get all the privalages of @_site_admin. You cannot set $SitePrefix to _site_. 
	Don't ask, you just can't.

	By the way, @_site_edit and the like will not work anymore. However you can still use @nopass.
	There is just no way around this. You will have to use the $SitePrefix thing mentioned above to get something equivalent.
	All in all it seemed like a small price to pay for the extra versatility. 

	People can always edit the page that stores their own information. 
	I thought it would be a safe bet that anyone that wanted to store login information in this fashion would want this to be the case.

	When using this recipe, the default password values do not really have any use anymore. 
	I thought about making use of them as a default password for the groups, and may impelement that in the future,
	but decided against it for the preseant because it just did not seem to add anything.

	You need to include this recipe before you include editattr.
*/

# let Site.AuthForm know that we're doing user-based authorization
$EnableAuthUser = 1;
SDVA($AuthUserSearchPatterns, array('!-deleted-\d+$!'));
SDVA($GroupPasswords, array());
SDV($SitePrefix, 'Site'); #cannot be _site_
SDV($GroupDelimiter, '@');

$ROSPatterns['/\\(:encrypt\\s+([^\\s:=]+).*?:\\)/e'] = "crypt(PSS('$1'),'$1')";

$EditAttrFields['username'] = array('attribute' => 1, 'markup' => "(:username:$1:)", 'filter' => 'check_username');

function check_username($pagename, $fn, &$new) {
  global $MessagesFmt, $EnablePost, $AuthUserSearchPatterns, $GroupDelimiter;
  if(!@$new[$fn]) return;
  if(PageTextVar($pagename,"password")){
    $pages=@ListPages($AuthUserSearchPatterns);
    foreach ($pages as $pn) {
      if($pn!=$pagename and PageTextVar($pn,"username")==$new[$fn]){
        $MessagesFmt[] = "<h3 class='wikimessage'>$[That username is taken. Please choose another.]</h3>";
        $EnablePost=false;
        return;
      }
    }
    if(preg_match('/('.$GroupDelimiter.'|[, ])/',$new[$fn])){
      $MessagesFmt[] = "<h3 class='wikimessage'>$[$GroupDelimiter, commas, and whitespaces are not allowed in your username.]</h3>";
      $EnablePost=false;
      return;
    }
  }
  else{
    $MessagesFmt[] = "<h3 class='wikimessage'>$[Enter a password using the (:password:(:encrypt MyNewPassword:):) markup.]</h3>";
    $EnablePost=false;
    return;
  }
}

if ( $action == 'edit' ) require_once("$FarmD/cookbook/editattr.php");

if (@$_POST['authid']) {
  $id=stripmagic(@$_POST['authid']);
  $pass=stripmagic(@$_POST['authpw']);
  $pw=crypt($pass,$pass);
  $authid='';
  
  $pages=@ListPages($AuthUserSearchPatterns);
  foreach ($pages as $pn) {
    if(PageTextVar($pn,"username")==$id && PageTextVar($pn,"password")==$pw){ 
      $authid = $id;
      $authgroup=FmtPageName('$Group', $pn);
      foreach((array)$authgroup as $g){ 
        $authlist[$GroupDelimiter.$g] = true;
        $authlist['-'.$GroupDelimiter.$g] = false;
      }
      break;
    }
  }
  if (!$authid) $GLOBALS['InvalidLogin'] = true;
  else{
    if (!isset($AuthId)) $AuthId = $authid;
    SessionAuth($pagename, array('authid' => $authid, 'authlist' => $authlist));
  }
}
else SessionAuth($pagename);

/*
if(PageTextVar($pagename,"username")){
  if(PageTextVar($pagename,"password")){
    if(PageTextVar($pagename,"username")==$AuthId && in_array(PageTextVar($pagename,"password"),$AuthPw)) $HandleAuth['edit']='';
    else $HandleAuth['source'] = 'admin';
  }
}
*/

$AuthFunction='UserAuth';
function UserAuth($pagename, $level, $authprompt=true, $since=0) {
  global $DefaultPasswords, $GroupAttributesFmt, $AllowName, $SitePrefix, $GroupDelimiter,
    $AuthCascade, $FmtV, $AuthPromptFmt, $PageStartFmt, $PageEndFmt, 
    $AuthId, $AuthList, $NoHTMLCache;
  static $authcache;
  SDV($GroupAttributesFmt,'$Group/GroupAttributes');
  SDV($AllowName,'@nopass');
  $page = ReadPage($pagename, $since);
  if (!$page) { return false; }
  if (!isset($authcache)) 
    SessionAuth($pagename, (@$_POST['authpw']) 
                           ? array('authpw' => array($_POST['authpw'] => 1))
                           : '');
  if (@$AuthId) {
    $AuthList[$AuthId] = true;
    $AuthList["-$AuthId"] = false;
  }
  
  ## To allow @edit in GroupAttributes, we cache it first
  if (!isset($authcache['site'])) {
    foreach(array_keys($DefaultPasswords) as $k) {
      $page['=passwd'][$k]=$GroupDelimiter.$SitePrefix.$k;
      if(@$AuthList[$page['=passwd'][$k]]) $authcache['site'][$k]=$AuthList[$GroupDelimiter.$SitePrefix.$k];
      elseif($DefaultPasswords[$k]==$AllowName or !$DefaultPasswords[$k]) $authcache['site'][$k]=true;
      else $authcache['site'][$k]=false;
      $page['=pwsource'][$k]='site';
    }
  }
  
  $gn = FmtPageName($GroupAttributesFmt, $pagename);
  if (!isset($authcache[$gn])) {
    $gp = ReadPage($gn, READPAGE_CURRENT);
    foreach(array_keys($DefaultPasswords) as $k) {
      if(@$gp["passwd$k"]) {
        $authcache[$gn][$k] = UserIsAuthorized($gp["passwd$k"]);
        $page['=pwsource'][$k]='group';
        $page['=passwd'][$k]=$gp["passwd$k"];
      }
      else $authcache[$gn][$k]=$authcache['site'][$k];
    }
  }
  foreach(array_keys($DefaultPasswords) as $k){
    if(@$page["passwd$k"]) {
      $page['=auth'][$k]=UserIsAuthorized($page["passwd$k"]);
      $page['=pwsource'][$k]='page';
      $page['=passwd'][$k]=$page["passwd$k"];
    }
    else $page['=auth'][$k]=$authcache[$gn][$k];
    if(@$AuthList[$GroupDelimiter.$SitePrefix.$k]) $page['=auth'][$k]=true;
  }

  AuthCascade($page);
  
  if (@$AuthList[$GroupDelimiter.$SitePrefix.'admin']) { 
    foreach(array_keys($DefaultPasswords) as $k){$page['=auth'][$k]=true;}
  }
  
  if (@$page['=passwd']['read']) $NoHTMLCache |= 2;
  if ($level=='ALWAYS' || @$page['=auth'][$level] || (@$AuthId && @$AuthId==PageTextVar($pagename, "username"))) return $page;
  if (!$authprompt) return false;
  $GLOBALS['AuthNeeded'] = (@$_POST['authpw']) 
    ? $page['=pwsource'][$level] . ' ' . $level : '';
  PCache($pagename, $page);
  $postvars = '';
  foreach($_POST as $k=>$v) {
    if ($k == 'authpw' || $k == 'authid') continue;
    $k = htmlspecialchars(stripmagic($k), ENT_QUOTES);
    $v = str_replace('$', '&#036;', 
             htmlspecialchars(stripmagic($v), ENT_COMPAT));
    $postvars .= "<input type='hidden' name='$k' value=\"$v\" />\n";
  }
  $FmtV['$PostVars'] = $postvars;
  $r = str_replace("'", '%37', stripmagic($_SERVER['REQUEST_URI']));
  SDV($AuthPromptFmt,array(&$PageStartFmt,
    "<p><b>$[Password required]</b></p>
      <form name='authform' action='$r' method='post'>
        $[Password]: <input tabindex='1' type='password' name='authpw' 
          value='' />
        <input type='submit' value='OK' />\$PostVars</form>
        <script language='javascript' type='text/javascript'><!--
          document.authform.authpw.focus() //--></script>", &$PageEndFmt));
  PrintFmt($pagename,$AuthPromptFmt);
  exit;
}

function UserIsAuthorized($args) {
  global $AuthList, $AllowName;
  foreach(preg_split("/[\s,]+/", $args) as $name) {
    if(@$AuthList[$name] || $name==$AllowName) {return true;}
  }
  return false;
}

function AuthCascade(&$page){
  global $AuthCascade;
  foreach($AuthCascade as $k=>$v){
    if (@$page['=auth'][$k]){
      foreach((array)$v as $value){
        if(!@$page['=auth'][$value]){
          $page['=auth'][$value] = $page['=auth'][$k];
          if ($page['=passwd'][$value] = $page['=passwd'][$k])         # assign
            $page['=pwsource'][$value] = "cascade:$k";
          AuthCascade($page);
        }
      }
    }
  }
}