<?php if (!defined('PmWiki')) exit();

/*	=== AuthUserProfiles ===
 *	Copyright 2010 Eemeli Aro <eemeli@gmail.com>
 *
 *	AuthUser account self-registration and management
 *
 *	To install, add the following line to your configuration file :
		include_once("$FarmD/cookbook/authuserprofiles.php");
 *
 *	For more information, please see the online documentation at
 *		http://www.pmwiki.org/wiki/Cookbook/AuthUserProfiles
 *
 *
 *  This script is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This script is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

$RecipeInfo['AuthUserProfiles']['Version'] = '2010-05-25';

if (!IsEnabled($EnableAuthorTracking,1)) Abort('$EnableAuthorTracking required for AuthUserProfiles');

SDV($AuthUserFunctions['userprofilegroup'], 'AuthUserProfiles');

function AuthUserProfiles($pagename, $id, $pw, $pwlist, &$authlist) {
	foreach ((array)$pwlist as $pgroup) {
		$pn = MakePageName($pagename, "$pgroup.$id");
		$page = ReadPage($pn, READPAGE_CURRENT);
		if ($page
			&& !empty($page['userid'])
			&& ($id == $page['userid'])
			&& !empty($page['passwdhash'])
			&& (_crypt($pw, $page['passwdhash']) == $page['passwdhash'])
		) {
			if (!empty($page['usergroups']) && preg_match_all('/@[^,\s]+/', $page['usergroups'], $m))
				foreach ($m[0] as $g) $authlist[$g] = 1;
			return true;
		}
	}
	return false;
}


if ($action != 'newuser') return;


XLSDV('en', array(
	'newuser_success_mail' => 'Account created, email sent with activation link',
	'newuser_success_done' => 'Account created',
	'newuser_success_key' => 'E-mail address verified, account activated',
	'newuser_fail' => 'Error creating account, please contact site admin',
	'newuser_fail_key' => 'Wrong activation key for this user',
	'newuser_empty_userid' => 'Username is required',
	'newuser_empty_userpw' => 'Password is required',
	'newuser_empty_userpw2' => 'Please enter your password twice',
	'newuser_empty_useremail' => 'An e-mail address is required',
	'newuser_diff_userpw' => 'Passwords don\'t match',
	'newuser_invalid_userid' => 'Username is not valid, please use alphanumeric characters only',
	'newuser_invalid_email' => 'E-mail address is not valid',
	'newuser_invalid_password' => 'Password is not valid',
	'newuser_exists' => 'Username already exits',
	'newuser_csum_done' => 'New user account created',
	'newuser_csum_wait' => 'New user account: waiting for confirmation',
	'newuser_email_error' => 'Error sending confirmation email, please contact site admin',
	'newuser_email_from' => "no-reply@{$_SERVER['HTTP_HOST']}",
	'newuser_email_subject' => "Welcome to $WikiTitle",
	'newuser_email_body' =>
"Welcome \$newuser!

Thank you for registering at $WikiTitle.

To activate your account and confirm your e-mail address, please visit
the following location:

\$link
"
));

SDV($HandleActions['newuser'], 'HandleNewUser');

SDV($NewUserFields, array(
	'userid' => '$[Username]',
	'userpw' => '$[Password]',
	'userpw2' => '$[Password, again]',
	'useremail' => '$[E-mail address]'
));


## based on HandleEdit
function NewUserSavePage($pagename, $opt, $auth = 'read') {
	global $Now, $EditFunctions, $IsPagePosted;
	Lock(2);
		$page = RetrieveAuthPage($pagename, $auth, TRUE);
		if (!$page) Abort("?cannot edit $pagename"); 
		$new = $page;
		foreach((array)$opt as $k => $v) {
			if ($v) $new[$k] = $v;
			else unset($new[$k]);
		}
		if ($new['csum']) $new["csum:$Now"] = $new['csum'];
		PCache($pagename, $new);
		$k = array_search('SaveAttributes', $EditFunctions);
		if ($k !== FALSE) unset($EditFunctions[$k]);
		UpdatePage($pagename, $page, $new);
	Lock(0);
	return $IsPagePosted;
}


## Profiles/User?action=newuser&key=1234567890
function NewUserConfirmKey($pagename, $key) {
	global $PCache, $ChangeSummary, $EditFields, $EditFunctions, $SaveProperties;
	$page = $PCache[$pagename];
	if (empty($page['userkey']) || !preg_match("/^$key (.*)$/", $page['userkey'], $match)) return 'newuser_fail_key';

	$opt = array(
		'csum' => XL('newuser_csum_done'),
		'passwdhash' => $match[1],
		'userkey' => '',
	);
	return NewUserSavePage($pagename, $opt) ? 'newuser_success_key' : 'newuser_fail';
}


## validate inputs
SDV($UserNameValidFunction, 'UserNameValidSimple');
function UserNameValidSimple(&$pagename, $id) {
	global $AuthorGroup, $UserNameChars, $UserNameDeniedPages;

	SDV($UserNameChars,'-[:alnum:]');
	$id = preg_replace("/[^$UserNameChars]+/", '', $id);
	if (!$id || ($id != $_POST['userid'])) return FALSE;

	SDV($UserNameDeniedPages, "$AuthorGroup|SideBar|(Group(Print)?(Attributes|Header|Footer))");
	$pagename = MakePageName($pagename, "$AuthorGroup.$id");
	if (preg_match("/\.($UserNameDeniedPages)$/", $pagename)) return FALSE;

	return TRUE;
}

SDV($UserEmailValidFunction, 'UserEmailValidSimple');
function UserEmailValidSimple($address) { return preg_match('/^.+@.+\..+$/', $address); }

SDV($UserPasswordValidFunction, 'UserPasswordValidSimple');
function UserPasswordValidSimple($pw) { return (boolean)$pw; }

SDV($UserExistsFunction, 'UserProfileExists');
function UserProfileExists($pagename, $id) {
	$page = ReadPage($pagename, READPAGE_CURRENT);
	return $page && (!empty($page['userid']) || preg_grep('/^passwd/', array_keys($page)));
}

function NewUserValidateInput($data, &$pn) {
	global $NewUserFields, $UserNameValidFunction, $UserEmailValidFunction, $UserPasswordValidFunction, $UserExistsFunction;

	foreach ($NewUserFields as $f => $p) {
		if (empty($data[$f])) return "newuser_empty_$f";
		else if (preg_match('/^(.*)2$/', $f, $m)) {
			if ($data[$m[1]] != $data[$f]) return "newuser_diff_{$m[1]}";
		}
	}

	$id = stripmagic($data['userid']);
	if (!$UserNameValidFunction($pn, $id)) return 'newuser_invalid_userid';
	if ($UserExistsFunction($pn, $id)) return 'newuser_exists';

	if (!$UserEmailValidFunction(@$data['useremail'])) return 'newuser_invalid_email';
	if (!$UserPasswordValidFunction($data['userpw'])) return 'newuser_invalid_password';

	return NULL;
}


## send email with activation link
function NewUserMailKey($pagename, $opt) {
	if (empty($opt['email'])) return 'newuser_err_send';

	if (empty($opt['link'])) {
		$opt['link'] = FmtPageName('{$PageUrl}', $pagename);
		$opt['link'] .= (strpos($opt['link'], '?') ? '&' : '?'). "action=newuser&key={$opt['key']}";
	}

	if (empty($opt['newuser'])) $opt['newuser'] = $_POST['userid'];

	$msg = XL('newuser_email_body');
	foreach($opt as $k => $v) $msg = preg_replace("/\\$$k\b`?/", $v, $msg);

	$head = 'From: ' . XL('newuser_email_from');

	return mail($opt['email'], XL('newuser_email_subject'), $msg, $head);
}


## save the new user data & optionally mail the user an activation link
function NewUserSubmit($pagename) {
	global $NewUserConfirmEmail;

	$hash = crypt(stripmagic($_POST['userpw']));

	if (IsEnabled($NewUserConfirmEmail, 1)) {
		$key = strval(mt_rand() + 1);

		$mail_opt = array('key' => $key, 'email' => $_POST['useremail']);
		if (!NewUserMailKey($pagename, $mail_opt)) return 'newuser_email_error';

		$opt = array(
			'csum' => XL('newuser_csum_wait'),
			'useremail' => stripmagic($_POST['useremail']),
			'userid' => stripmagic($_POST['userid']),
			'userkey' => "$key $hash",
		);
		return NewUserSavePage($pagename, $opt) ? 'newuser_success_mail' : 'newuser_fail';
	} else {
		$opt = array(
			'csum' => XL('newuser_csum_done'),
			'passwdhash' => $hash,
			'useremail' => stripmagic(@$_POST['useremail']),
			'userid' => stripmagic($_POST['userid']),
		);
		return NewUserSavePage($pagename, $opt) ? 'newuser_success_done' : 'newuser_fail';
	}
}


function PrintNewUserForm($pagename, $result) {
	global $MessagesFmt, $NewUserFields;

	if (!empty($MessagesFmt)) echo FmtPageName(implode('', (array)$MessagesFmt), $pagename);

	if (preg_match('/^newuser_(fail|success)/', $result)) {
		echo FmtPageName("\n<a href='\$PageUrl'>View page</a>", $pagename);
		return;
	}

	echo FmtPageName("\n<form action='\$PageUrl' method='post'>
	<input type='hidden' name='action' value='newuser' />
	<input type='hidden' name='n' value='\$FullName' />
	<table>", $pagename);

	foreach ($NewUserFields as $f => $p) {
		if (strpos($f, 'pw') !== FALSE) {
			$type = 'password';
			$value = '';
		} else {
			$type = 'text';
			$value = isset($_POST[$f]) ? $_POST[$f] : '';
		}
		$prompt = FmtPageName($p, $pagename);
		echo "\n\t\t<tr><td>$prompt</td><td><input type='$type' name='$f' value='$value' /></td></tr>";
	}

	echo FmtPageName("\n\t</table>\n\t<input type='submit' value='$[Sign up]' />\n</form>", $pagename);
}


function HandleNewUser($pagename, $auth = 'read') {
	global $HandleNewUserFmt, $PageStartFmt, $PageEndFmt, $MessagesFmt;

	$pform = RetrieveAuthPage($pagename, $auth, TRUE);
	if (!$pform) Abort("?cannot add new user");
	PCache($pagename, $pform);

	$result = NULL;

	if (!empty($_REQUEST['key'])) $result = NewUserConfirmKey($pagename, preg_replace('/[^0-9]+/', '', $_REQUEST['key']));

	if (!$result && !empty($_POST)) {
		if (!$result) $result = NewUserValidateInput($_POST, $pn);
		if (!$result) $result = NewUserSubmit($pn);
	}

	if ($result) $MessagesFmt[] = "<h3 class='wikimessage'>$[$result]</h3>";

	SDV($HandleNewUserFmt, array(&$PageStartFmt, "function:PrintNewUserForm $result", &$PageEndFmt));
	PrintFmt($pagename, $HandleNewUserFmt);
}