Wiki source for FreeCap


Show raw source

===== ""FreeCap"" CAPTCHA Integration =====

>>**See also:**
Ticket [[http://wush.net/trac/wikka/ticket/347 | #347]]

**URCaptchaModule**: A modular CAPTCHA plugin that requires no modifications to Wikka core code. Based upon YodaHome's implementation and the [[Docs:URRegistrationValidationFramework | UR framework]] available since 1.1.6.4.>>

I've recently had several attacks with bots registering dozens of users and then vandalizing my wiki where possible. While I could have just hidden the Registration I thought the obvious answer to this would be to add a CAPTCHA to the registration process. This is what I came up with.

Actually I looked for nice CAPTCHA scripts and found [[http://www.puremango.co.uk/cm_php_captcha_script_113.php | FreeCap]] which seemed quite strong to me and had some nice features. It is also released under the GPL. So I integrated it with my Wikka.

=== Prerequisites / Installation ===

First you need to download the [[http://www.puremango.co.uk/freecap1.4.1.zip | FreeCap package]] from the website. (Note that this link may not lead to the latest package). Then all the files went into a "freecap" folder below the "actions" tree. Of course you might put it somewhere else but be sure that it can be properly accessed and you changed the paths correctly where necessary (see note at the bottom).

I only changed the usersettings.php (this is a modified version from 1.1.6.3), look for "added for ""FreeCap""" in the comments. For convenience this is the whole file. (Scroll down for 1.1.7-compatible version.)

**actions/usersettings.php** (//version 1.1.6.3 only//)
%%(php)

<?php
/**
* Display a form to register, login and change user settings.
*
* @package Actions
* @version $Id$
* @license http://www.gnu.org/copyleft/gpl.html GNU General Public License
* @filesource
*
* @author {@link http://wikkawiki.org/MinusF MinusF} (code cleanup and validation)
* @author {@link http://wikkawiki.org/DarTar Dario Taraborelli} (further cleanup, i18n, replaced JS dialogs with server-generated messages)
*
* @uses Wakka::htmlspecialchars_ent()
*
* @todo use different actions for registration / login / user settings;
* @todo add documentation links or short explanations for each option;
* @todo use error handler for displaying messages and highlighting
* invalid input fields;
* @todo remove useless redirections;
* @todo [accessibility] make logout independent of JavaScript
*/

// defaults
if (!defined('PASSWORD_MIN_LENGTH')) define('PASSWORD_MIN_LENGTH', "5");
if (!defined('VALID_EMAIL_PATTERN')) define('VALID_EMAIL_PATTERN', "/^.+?\@.+?\..+$/"); //TODO: Use central regex library
if (!defined('REVISION_DISPLAY_LIMIT_MIN')) define('REVISION_DISPLAY_LIMIT_MIN', "0"); // 0 means no limit, 1 is the minimum number of revisions
if (!defined('REVISION_DISPLAY_LIMIT_MAX')) define('REVISION_DISPLAY_LIMIT_MAX', "20"); // keep this value within a reasonable limit to avoid an unnecessary long lists
if (!defined('RECENTCHANGES_DISPLAY_LIMIT_MIN')) define('RECENTCHANGES_DISPLAY_LIMIT_MIN', "0"); // 0 means no limit, 1 is the minimum number of changes
if (!defined('RECENTCHANGES_DISPLAY_LIMIT_MAX')) define('RECENTCHANGES_DISPLAY_LIMIT_MAX', "50"); // keep this value within a reasonable limit to avoid an unnecessary long list
if (!defined('INPUT_ERROR_STYLE')) define('INPUT_ERROR_STYLE', 'class="highlight"');

// i18n strings
if (!defined('USER_SETTINGS_HEADING')) define('USER_SETTINGS_HEADING', "User settings");
if (!defined('USER_LOGGED_OUT')) define('USER_LOGGED_OUT', "You have successfully logged out.");
if (!defined('USER_SETTINGS_STORED')) define('USER_SETTINGS_STORED', "User settings stored!");
if (!defined('ERROR_NO_BLANK')) define('ERROR_NO_BLANK', "Sorry, blanks are not permitted in the password.");
if (!defined('ERROR_PASSWORD_TOO_SHORT')) define('ERROR_PASSWORD_TOO_SHORT', "Sorry, the password must contain at least %s characters.");
if (!defined('PASSWORD_CHANGED')) define('PASSWORD_CHANGED', "Password successfully changed!");
if (!defined('ERROR_OLD_PASSWORD_WRONG')) define('ERROR_OLD_PASSWORD_WRONG', "The old password you entered is wrong.");
if (!defined('USER_EMAIL_LABEL')) define('USER_EMAIL_LABEL', "Your email address:");
if (!defined('DOUBLECLICK_LABEL')) define('DOUBLECLICK_LABEL', "Doubleclick Editing:");
if (!defined('SHOW_COMMENTS_LABEL')) define('SHOW_COMMENTS_LABEL', "Show comments by default:");
if (!defined('RECENTCHANGES_DISPLAY_LIMIT_LABEL')) define('RECENTCHANGES_DISPLAY_LIMIT_LABEL', "RecentChanges display limit:");
if (!defined('PAGEREVISION_LIST_LIMIT_LABEL')) define('PAGEREVISION_LIST_LIMIT_LABEL', "Page revisions list limit:");
if (!defined('UPDATE_SETTINGS_INPUT')) define('UPDATE_SETTINGS_INPUT', "Update Settings");
if (!defined('CHANGE_PASSWORD_HEADING')) define('CHANGE_PASSWORD_HEADING', "Change your password:");
if (!defined('CURRENT_PASSWORD_LABEL')) define('CURRENT_PASSWORD_LABEL', "Your current password:");
if (!defined('PASSWORD_REMINDER_LABEL')) define('PASSWORD_REMINDER_LABEL', "Password reminder:");
if (!defined('NEW_PASSWORD_LABEL')) define('NEW_PASSWORD_LABEL', "Your new password:");
if (!defined('NEW_PASSWORD_CONFIRM_LABEL')) define('NEW_PASSWORD_CONFIRM_LABEL', "Confirm new password:");
if (!defined('CHANGE_BUTTON_LABEL')) define('CHANGE_BUTTON_LABEL', "Change password");
if (!defined('REGISTER_BUTTON_LABEL')) define('REGISTER_BUTTON_LABEL', "Register");
if (!defined('QUICK_LINKS_HEADING')) define('QUICK_LINKS_HEADING', "Quick links");
if (!defined('QUICK_LINKS')) define('QUICK_LINKS', "See a list of pages you own (MyPages) and pages you've edited (MyChanges).");
if (!defined('ERROR_WRONG_PASSWORD')) define('ERROR_WRONG_PASSWORD', "Sorry, you entered the wrong password.");
if (!defined('ERROR_WRONG_HASH')) define('ERROR_WRONG_HASH', "Sorry, you entered a wrong password reminder.");
if (!defined('ERROR_EMPTY_USERNAME')) define('ERROR_EMPTY_USERNAME', "Please fill in your user name.");
if (!defined('ERROR_NON_EXISTENT_USERNAME')) define('ERROR_NON_EXISTENT_USERNAME', "Sorry, this user name doesn't exist.");
if (!defined('ERROR_RESERVED_PAGENAME')) define('ERROR_RESERVED_PAGENAME', "Sorry, this name is reserved for a page. Please choose a different name.");
if (!defined('ERROR_WIKINAME')) define('ERROR_WIKINAME', "Username must be formatted as a ##\"\"WikiName\"\"##, e.g. ##\"\"JohnDoe\"\"##.");
if (!defined('ERROR_EMPTY_PASSWORD')) define('ERROR_EMPTY_PASSWORD', "Please fill in a password.");
if (!defined('ERROR_EMPTY_PASSWORD_OR_HASH')) define('ERROR_EMPTY_PASSWORD_OR_HASH', "Please fill your password or hash.");
if (!defined('ERROR_EMPTY_CONFIRMATION_PASSWORD')) define('ERROR_EMPTY_CONFIRMATION_PASSWORD', "Please confirm your password in order to register a new account.");
if (!defined('ERROR_EMPTY_NEW_CONFIRMATION_PASSWORD')) define('ERROR_EMPTY_NEW_CONFIRMATION_PASSWORD', "Please confirm your new password in order to update your account.");
if (!defined('ERROR_EMPTY_NEW_PASSWORD')) define('ERROR_EMPTY_NEW_PASSWORD', "You must also fill in a new password.");
if (!defined('ERROR_PASSWORD_MATCH')) define('ERROR_PASSWORD_MATCH', "Passwords don't match.");
if (!defined('ERROR_EMAIL_ADDRESS_REQUIRED')) define('ERROR_EMAIL_ADDRESS_REQUIRED', "Please specify an email address.");
if (!defined('ERROR_INVALID_EMAIL_ADDRESS')) define('ERROR_INVALID_EMAIL_ADDRESS', "That doesn't quite look like an email address.");
if (!defined('ERROR_INVALID_REVISION_DISPLAY_LIMIT')) define('ERROR_INVALID_REVISION_DISPLAY_LIMIT', "The number of page revisions should not exceed %d.");
if (!defined('ERROR_INVALID_RECENTCHANGES_DISPLAY_LIMIT')) define('ERROR_INVALID_RECENTCHANGES_DISPLAY_LIMIT', "The number of recently changed pages should not exceed %d.");
if (!defined('REGISTRATION_SUCCEEDED')) define('REGISTRATION_SUCCEEDED', "You have successfully registered!");
if (!defined('REGISTERED_USER_LOGIN_LABEL')) define('REGISTERED_USER_LOGIN_LABEL', "If you're already a registered user, log in here!");
if (!defined('REGISTER_HEADING')) define('REGISTER_HEADING', "===Login/Register===");
if (!defined('WIKINAME_LABEL')) define('WIKINAME_LABEL', "Your <abbr title=\"A WikiName is formed by two or more capitalized words without space, e.g. JohnDoe\">WikiName</abbr>:");
if (!defined('PASSWORD_LABEL')) define('PASSWORD_LABEL', "Password (%s+ chars):");
if (!defined('LOGIN_BUTTON_LABEL')) define('LOGIN_BUTTON_LABEL', "Login");
if (!defined('LOGOUT_BUTTON_LABEL')) define('LOGOUT_BUTTON_LABEL', "Logout");
if (!defined('NEW_USER_REGISTER_LABEL')) define('NEW_USER_REGISTER_LABEL', "Stuff you only need to fill in when you're logging in for the first time (and thus signing up as a new user on this site).");
if (!defined('CONFIRM_PASSWORD_LABEL')) define('CONFIRM_PASSWORD_LABEL', "Confirm password:");
if (!defined('RETRIEVE_PASSWORD_HEADING')) define('RETRIEVE_PASSWORD_HEADING', "===Forgot your password?===");
if (!defined('RETRIEVE_PASSWORD_MESSAGE')) define('RETRIEVE_PASSWORD_MESSAGE', "If you need a password reminder, click [[PasswordForgotten | here]]. --- You can login here using your password reminder.");
if (!defined('TEMP_PASSWORD_LABEL')) define('TEMP_PASSWORD_LABEL', "Password reminder:");
// added for FreeCap CAPTCHA
if (!defined('CAPTCHA_LABEL')) define('CAPTCHA_LABEL', "letters in the above picture:");

//initialize variables
$params = '';
$url = '';
$email = '';
$doubleclickedit = '';
$show_comments = '';
$revisioncount = '';
$changescount = '';
$password = '';
$oldpass = '';
$password_confirm = '';
$pw_selected = '';
$hash_selected = '';
$username_highlight = '';
$username_temp_highlight = '';
$password_temp_highlight = '';
$email_highlight = '';
$password_highlight = '';
$password_new_highlight = '';
$password_confirm_highlight = '';
$revisioncount_highlight = '';
$changescount_highlight = '';

//create URL
$url = $this->config['base_url'].$this->tag;

// append URL params depending on rewrite_mode
$params = ($this->config['rewrite_mode'] == 1) ? '?' : '&';

// BEGIN *** Logout ***
// is user trying to log out?
#if (isset($_REQUEST['action']) && ($_REQUEST['action'] == 'logout')) // JavaScript button with GET
if (isset($_POST['logout']) && $_POST['logout'] == LOGOUT_BUTTON_LABEL) // replaced with normal form button #353, #312
{
$this->LogoutUser();
$params .= 'out=true';
$this->Redirect($url.$params);
}
// END *** Logout ***

// BEGIN *** Usersettings ***
// user is still logged in
else if ($user = $this->GetUser())
{
// is user trying to update user settings?
if (isset($_POST['action']) && ($_POST['action'] == 'update'))
{
// get POST parameters
$email = $this->GetSafeVar('email', 'post');
$doubleclickedit = $this->GetSafeVar('doubleclickedit', 'post');
$show_comments = $this->GetSafeVar('show_comments', 'post');
$revisioncount = (int) $this->GetSafeVar('revisioncount', 'post');
$changescount = (int) $this->GetSafeVar('changescount', 'post');

// validate form input
switch (TRUE)
{
case (strlen($email) == 0): //email is empty
$error = ERROR_EMAIL_ADDRESS_REQUIRED;
$email_highlight = INPUT_ERROR_STYLE;
break;
case (!preg_match(VALID_EMAIL_PATTERN, $email)): //invalid email
$error = ERROR_INVALID_EMAIL_ADDRESS;
$email_highlight = INPUT_ERROR_STYLE;
break;
case (($revisioncount < REVISION_DISPLAY_LIMIT_MIN) || ($revisioncount > REVISION_DISPLAY_LIMIT_MAX)): //invalid revision display limit
$error = sprintf(ERROR_INVALID_REVISION_DISPLAY_LIMIT, REVISION_DISPLAY_LIMIT_MAX);
$revisioncount_highlight = INPUT_ERROR_STYLE;
break;
case (($changescount < RECENTCHANGES_DISPLAY_LIMIT_MIN) || ($changescount > RECENTCHANGES_DISPLAY_LIMIT_MAX)): //invalid recentchanges display limit
$error = sprintf(ERROR_INVALID_RECENTCHANGES_DISPLAY_LIMIT, RECENTCHANGES_DISPLAY_LIMIT_MAX);
$changescount_highlight = INPUT_ERROR_STYLE;
break;
default: // input is valid
$this->Query('UPDATE '.$this->config['table_prefix'].'users SET '.
"email = '".mysql_real_escape_string($email)."', ".
"doubleclickedit = '".mysql_real_escape_string($doubleclickedit)."', ".
"show_comments = '".mysql_real_escape_string($show_comments)."', ".
"revisioncount = '".mysql_real_escape_string($revisioncount)."', ".
"changescount = '".mysql_real_escape_string($changescount)."' ".
"WHERE name = '".$user['name']."' LIMIT 1");
$this->SetUser($this->LoadUser($user["name"]));

// forward
$params .= 'stored=true';
$this->Redirect($url.$params);
}
}
//user just logged in
else
{
// get stored settings
$email = $user['email'];
$doubleclickedit = $user['doubleclickedit'];
$show_comments = $user['show_comments'];
$revisioncount = $user['revisioncount'];
$changescount = $user['changescount'];
}

// display user settings form
echo '<h3>'.USER_SETTINGS_HEADING.'</h3>';
echo $this->FormOpen();
?>
<input type="hidden" name="action" value="update" />
<table class="usersettings">
<tr>
<td> </td>
<td>Hello, <?php echo $this->Link($user['name']) ?>!</td>
</tr>
<?php

// create confirmation message if needed
switch(TRUE)
{
case (isset($_GET['registered']) && $_GET['registered'] == 'true'):
$success = REGISTRATION_SUCCEEDED;
break;
case (isset($_GET['stored']) && $_GET['stored'] == 'true'):
$success = USER_SETTINGS_STORED;
break;
case (isset($_GET['newpassword']) && $_GET['newpassword'] == 'true'):
$success = PASSWORD_CHANGED;
}

// display error or confirmation message
switch(TRUE)
{
case (isset($error)):
echo '<tr><td></td><td><em class="error">'.$this->Format($error).'</em></td></tr>'."\n";
break;
case (isset($success)):
echo '<tr><td></td><td><em class="success">'.$this->Format($success).'</em></td></tr>'."\n";
break;
default:
}
?>
<tr>
<td align="right"><?php echo USER_EMAIL_LABEL ?></td>
<td><input <?php echo $email_highlight; ?> name="email" value="<?php echo $this->htmlspecialchars_ent($email) ?>" size="40" /></td>
</tr>
<tr>
<td align="right"><?php echo DOUBLECLICK_LABEL ?></td>
<td><input type="hidden" name="doubleclickedit" value="N" /><input type="checkbox" name="doubleclickedit" value="Y" <?php echo $doubleclickedit == 'Y' ? 'checked="checked"' : '' ?> /></td>
</tr>
<tr>
<td align="right"><?php echo SHOW_COMMENTS_LABEL ?></td>
<td><input type="hidden" name="show_comments" value="N" /><input type="checkbox" name="show_comments" value="Y" <?php echo $show_comments == 'Y' ? 'checked="checked"' : '' ?> /></td>
</tr>
<tr>
<td align="right"><?php echo PAGEREVISION_LIST_LIMIT_LABEL ?></td>
<td><input <?php echo $revisioncount_highlight; ?> name="revisioncount" value="<?php echo $this->htmlspecialchars_ent($revisioncount) ?>" size="40" /></td>
</tr>
<tr>
<td align="right"><?php echo RECENTCHANGES_DISPLAY_LIMIT_LABEL ?></td>
<td><input <?php echo $changescount_highlight; ?> name="changescount" value="<?php echo $this->htmlspecialchars_ent($changescount) ?>" size="40" /></td>
</tr>
<tr>
<td> </td>
<td><input type="submit" value="<?php echo UPDATE_SETTINGS_INPUT ?>" /><!-- <input type="button" value="<?php echo LOGOUT_BUTTON_LABEL; ?>" onclick="document.location='<?php echo $this->href('', '', 'action=logout'); ?>'" /></td>-->
<input id="logout" name="logout" type="submit" value="<?php echo LOGOUT_BUTTON_LABEL; ?>" /><!--#353,#312-->
</td>
</tr>
</table>
<?php
echo $this->FormClose(); //close user settings form

if (isset($_POST['action']) && ($_POST['action'] == 'changepass'))
{
// check password
$oldpass = $_POST['oldpass']; //can be current password or hash sent as password reminder
$password = $_POST['password'];
$password_confirm = $_POST['password_confirm'];
$update_option = $this->GetSafeVar('update_option', 'post');

switch (TRUE)
{
case (strlen($oldpass) == 0):
$passerror = ERROR_EMPTY_PASSWORD_OR_HASH;
$password_highlight = INPUT_ERROR_STYLE;
break;
case (($update_option == 'pw') && md5($oldpass) != $user['password']): //wrong password
$passerror = ERROR_WRONG_PASSWORD;
$pw_selected = 'selected="selected"';
$password_highlight = INPUT_ERROR_STYLE;
break;
case (($update_option == 'hash') && $oldpass != $user['password']): //wrong hash
$passerror = ERROR_WRONG_HASH;
$hash_selected = 'selected="selected"';
$password_highlight = INPUT_ERROR_STYLE;
break;
case (strlen($password) == 0):
$passerror = ERROR_EMPTY_NEW_PASSWORD;
$password_highlight = INPUT_ERROR_STYLE;
$password_new_highlight = INPUT_ERROR_STYLE;
break;
case (preg_match("/ /", $password)):
$passerror = ERROR_NO_BLANK;
$password_highlight = INPUT_ERROR_STYLE;
$password_new_highlight = INPUT_ERROR_STYLE;
break;
case (strlen($password) < PASSWORD_MIN_LENGTH):
$passerror = sprintf(ERROR_PASSWORD_TOO_SHORT, PASSWORD_MIN_LENGTH);
$password_highlight = INPUT_ERROR_STYLE;
$password_new_highlight = INPUT_ERROR_STYLE;
break;
case (strlen($password_confirm) == 0):
$passerror = ERROR_EMPTY_NEW_CONFIRMATION_PASSWORD;
$password_highlight = INPUT_ERROR_STYLE;
$password_new_highlight = INPUT_ERROR_STYLE;
$password_confirm_highlight = INPUT_ERROR_STYLE;
break;
case ($password_confirm != $password):
$passerror = ERROR_PASSWORD_MATCH;
$password_highlight = INPUT_ERROR_STYLE;
$password_new_highlight = INPUT_ERROR_STYLE;
$password_confirm_highlight = INPUT_ERROR_STYLE;
break;
default:
$this->Query('UPDATE '.$this->config['table_prefix'].'users set '."password = md5('".mysql_real_escape_string($password)."') "."WHERE name = '".$user['name']."'");
$user['password'] = md5($password);
$this->SetUser($user);
$params .= 'newpassword=true';
$this->Redirect($url.$params);
}
}

//display password update form
echo '<hr />'."\n";
echo $this->FormOpen();
?>
<input type="hidden" name="action" value="changepass" />
<h5><?php echo CHANGE_PASSWORD_HEADING ?></h5>
<table class="usersettings">
<?php
if (isset($passerror))
{
print('<tr><td></td><td><em class="error">'.$this->Format($passerror).'</em></td></tr>'."\n");
}
?>
<tr>
<td align="right">
<select name="update_option">
<option value="pw" <?php echo $pw_selected; ?>><?php echo CURRENT_PASSWORD_LABEL; ?></option>
<option value="hash" <?php echo $hash_selected; ?>><?php echo PASSWORD_REMINDER_LABEL; ?></option>
</select></td>
<td><input <?php echo $password_highlight; ?> type="password" name="oldpass" size="40" /></td>
</tr>
<tr>
<td align="right"><?php echo NEW_PASSWORD_LABEL ?></td>
<td><input <?php echo $password_new_highlight; ?> type="password" name="password" size="40" /></td>
</tr>
<tr>
<td align="right"><?php echo NEW_PASSWORD_CONFIRM_LABEL ?></td>
<td><input <?php echo $password_confirm_highlight; ?> type="password" name="password_confirm" size="40" /></td>
</tr>
<tr>
<td></td>
<td><input type="submit" value="<?php echo CHANGE_BUTTON_LABEL ?>" size="40" /></td>
</tr>
</table>
<?php
echo '<hr />'."\n";
echo '<h5>'.QUICK_LINKS_HEADING.'</h5>'."\n";
echo $this->Format(QUICK_LINKS);
print($this->FormClose());
}
// user is not logged in
else
{
// print confirmation message on successful logout
if (isset($_GET['out']) && ($_GET['out'] == 'true'))
{
$success = USER_LOGGED_OUT;
}

// is user trying to log in or register?
if (isset($_POST['action']) && ($_POST['action'] == 'login'))
{
// if user name already exists, check password
if (isset($_POST['name']) && $existingUser = $this->LoadUser($_POST['name']))
{
// check password
switch(TRUE){
case (strlen($_POST['password']) == 0):
$error = ERROR_EMPTY_PASSWORD;
$password_highlight = INPUT_ERROR_STYLE;
break;
case (md5($_POST['password']) != $existingUser['password']):
$error = ERROR_WRONG_PASSWORD;
$password_highlight = INPUT_ERROR_STYLE;
break;
default:
$this->SetUser($existingUser);
$this->Redirect($url, '');
}
}
// BEGIN *** Register ***
else // otherwise, proceed to registration
{ //added for freecap CAPTCHA
if(!empty($_SESSION['freecap_word_hash']) && !empty($_POST['word']))
{
// all freeCap words are lowercase.
// font #4 looks uppercase, but trust me, it's not...
if($_SESSION['hash_func'](strtolower($_POST['word']))==$_SESSION['freecap_word_hash'])
{
// reset freeCap session vars
// cannot stress enough how important it is to do this
// defeats re-use of known image with spoofed session id
$_SESSION['freecap_attempts'] = 0;
$_SESSION['freecap_word_hash'] = false;

// now go somewhere else
// header("Location: somewhere.php");
$name = trim($_POST['name']);
$email = trim($this->GetSafeVar('email', 'post'));
$password = $_POST['password'];
$confpassword = $_POST['confpassword'];

// validate input
switch(TRUE)
{
case (strlen($name) == 0):
$error = ERROR_EMPTY_USERNAME;
$username_highlight = INPUT_ERROR_STYLE;
break;
case (!$this->IsWikiName($name)):
$error = ERROR_WIKINAME;
$username_highlight = INPUT_ERROR_STYLE;
break;
case ($this->ExistsPage($name)):
$error = ERROR_RESERVED_PAGENAME;
$username_highlight = INPUT_ERROR_STYLE;
break;
case (strlen($password) == 0):
$error = ERROR_EMPTY_PASSWORD;
$password_highlight = INPUT_ERROR_STYLE;
break;
case (preg_match("/ /", $password)):
$error = ERROR_NO_BLANK;
$password_highlight = INPUT_ERROR_STYLE;
break;
case (strlen($password) < PASSWORD_MIN_LENGTH):
$error = sprintf(ERROR_PASSWORD_TOO_SHORT, PASSWORD_MIN_LENGTH);
$password_highlight = INPUT_ERROR_STYLE;
break;
case (strlen($confpassword) == 0):
$error = ERROR_EMPTY_CONFIRMATION_PASSWORD;
$password_highlight = INPUT_ERROR_STYLE;
$password_confirm_highlight = INPUT_ERROR_STYLE;
break;
case ($confpassword != $password):
$error = ERROR_PASSWORD_MATCH;
$password_highlight = INPUT_ERROR_STYLE;
$password_confirm_highlight = INPUT_ERROR_STYLE;
break;
case (strlen($email) == 0):
$error = ERROR_EMAIL_ADDRESS_REQUIRED;
$email_highlight = INPUT_ERROR_STYLE;
$password_highlight = INPUT_ERROR_STYLE;
$password_confirm_highlight = INPUT_ERROR_STYLE;
break;
case (!preg_match(VALID_EMAIL_PATTERN, $email)):
$error = ERROR_INVALID_EMAIL_ADDRESS;
$email_highlight = INPUT_ERROR_STYLE;
$password_highlight = INPUT_ERROR_STYLE;
$password_confirm_highlight = INPUT_ERROR_STYLE;
break;
default: //valid input, create user
$this->Query("INSERT INTO ".$this->config['table_prefix']."users SET ".
"signuptime = now(), ".
"name = '".mysql_real_escape_string($name)."', ".
"email = '".mysql_real_escape_string($email)."', ".
"password = md5('".mysql_real_escape_string($_POST['password'])."')");

// log in
$this->SetUser($this->LoadUser($name));
$params .= 'registered=true';
$this->Redirect($url.$params);
}
} else {
$captcha="You entered the wrong letters, please try again.";
}
} else {
$captcha="You entered none of the letters. Please do so.";
}

}
// END *** Register ***
}

// BEGIN *** Usersettings ***
elseif (isset($_POST['action']) && ($_POST['action'] == 'updatepass'))
{
$name = trim($_POST['yourname']);
if (strlen($name) == 0) // empty username
{
$newerror = ERROR_EMPTY_USERNAME;
$username_temp_highlight = INPUT_ERROR_STYLE;
}
elseif (!$this->IsWikiName($name)) // check if name is WikiName style
{
$newerror = ERROR_WIKINAME;
$username_temp_highlight = INPUT_ERROR_STYLE;
}
elseif (!($this->LoadUser($_POST['yourname']))) //check if user exists
{
$newerror = ERROR_NON_EXISTENT_USERNAME;
$username_temp_highlight = INPUT_ERROR_STYLE;
}
elseif ($existingUser = $this->LoadUser($_POST['yourname'])) // if user name already exists, check password
{
// updatepassword
if ($existingUser['password'] == $_POST['temppassword'])
{
$this->SetUser($existingUser, $_POST['remember']);
$this->Redirect($url);
}
else
{
$newerror = ERROR_WRONG_PASSWORD;
$password_temp_highlight = INPUT_ERROR_STYLE;
}
}
}
// END *** Usersettings ***

// BEGIN *** Login/Register ***
print($this->FormOpen());
?>
<script language="javascript">
<!--
function new_freecap()
{
// loads new freeCap image
if(document.getElementById)
{
// extract image name from image source (i.e. cut off ?randomness)
thesrc = document.getElementById("freecap").src;
thesrc = thesrc.substring(0,thesrc.lastIndexOf(".")+4);
// add ?(random) to prevent browser/isp caching
document.getElementById("freecap").src = thesrc+"?"+Math.round(Math.random()*100000);
} else {
alert("Sorry, cannot autoreload freeCap image\nSubmit the form and a new freeCap will be loaded");
}
}
//-->
</script>
<input type="hidden" name="action" value="login" />
<table class="usersettings">
<tr>
<td colspan="2"><?php echo $this->Format(REGISTER_HEADING) ?></td>
<td> </td>
</tr>
<tr>
<td> </td>
<td><?php echo $this->Format(REGISTERED_USER_LOGIN_LABEL); ?></td>
</tr>
<?php
switch (true)
{
case (isset($error)):
echo '<tr><td></td><td><em class="error">'.$this->Format($error).'</em></td></tr>'."\n";
break;
case (isset($success)):
echo '<tr><td></td><td><em class="success">'.$this->Format($success).'</em></td></tr>'."\n";
break;
// added for FreeCap CAPTCHA
case (isset($captcha)):
echo '<tr><td></td><td><em class="error">'.$this->Format($captcha).'</em></td></tr>'."\n";
break;

}
?>
<tr>
<td align="right"><?php echo WIKINAME_LABEL ?></td>
<td><input <?php echo $username_highlight; ?> name="name" size="40" value="<?php echo $this->GetSafeVar('name', 'post'); ?>" /></td>
</tr>
<tr>
<td align="right"><?php echo sprintf(PASSWORD_LABEL, PASSWORD_MIN_LENGTH) ?></td>
<td><input <?php echo $password_highlight; ?> type="password" name="password" size="40" /></td>
</tr>
<tr>
<td> </td>
<td><input type="submit" value="<?php echo LOGIN_BUTTON_LABEL ?>" size="40" /></td>
</tr>
<tr>
<td> </td>
<td width="500"><?php echo $this->Format(NEW_USER_REGISTER_LABEL); ?></td>
</tr>
<tr>
<td align="right"><?php echo CONFIRM_PASSWORD_LABEL ?></td>
<td><input <?php echo $password_confirm_highlight; ?> type="password" name="confpassword" size="40" /></td>
</tr>
<tr>
<td align="right"><?php echo USER_EMAIL_LABEL ?></td>
<td><input <?php echo $email_highlight; ?> name="email" size="40" value="<?php echo $email; ?>" /></td>
</tr>
<?php //added for Freecap CAPTCHA
setcookie("sessname", session_name());
?>
<tr>
<td colspan="2"><img src="actions/freecap/freecap.php" id="freecap"></td>
</tr>
<tr>
<td colspan="2">If you can't read the word, <a href="#" onClick="this.blur();new_freecap();return false;">click here</a></td>
</tr>
<tr>
<td align="right"><?php echo CAPTCHA_LABEL ?></td>
<td><input type="text" name="word" /></td>
</tr>
<?php //added for Freecap CAPTCHA ?>
<tr>
<td> </td>
<td><input type="submit" value="<?php echo REGISTER_BUTTON_LABEL ?>" size="40" /></td>
</tr>
</table>
<?php
print($this->FormClose());
// END *** Login/Register ***

// BEGIN *** Login Temp Password ***
print($this->FormOpen());
?>
<input type="hidden" name="action" value="updatepass" />
<table class="usersettings">
<tr>
<td colspan="2"><br /><hr /><?php echo $this->Format(RETRIEVE_PASSWORD_HEADING) ?></td><td></td>
</tr>
<tr>
<td align="left"></td>
<td><?php echo $this->Format(RETRIEVE_PASSWORD_MESSAGE) ?></td>
</tr>
<?php
if (isset($newerror))
{
print('<tr><td></td><td><em class="error">'.$this->Format($newerror).'</em></td></tr>'."\n");
}
?>
<tr>
<td align="right"><?php echo WIKINAME_LABEL ?></td>
<td><input <?php echo $username_temp_highlight; ?> name="yourname" value="<?php echo $this->GetSafeVar('yourname', 'post'); ?>" size="40" /></td>
</tr>
<tr>
<td align="right"><?php echo TEMP_PASSWORD_LABEL ?></td>
<td><input <?php echo $password_temp_highlight; ?> name="temppassword" size="40" /></td>
</tr>
<tr>
<td> </td>
<td><input type="submit" value="<?php echo LOGIN_BUTTON_LABEL ?>" size="40" /></td>
</tr>
</table>
<?php
print($this->FormClose());
// END *** Login Temp Password ***
}
?>

%%

**actions/usersettings/usersettings.php** (//version 1.1.7 [trunk] only//)
%%(php)
<?php
/**
* Display a form to register, login and change user settings.
* Enchanced to include captchas, based upon work by YodaHome
* (http://wikkawiki.org/FreeCap). See that page for implementation
* details.
*
* Note: To enable captchas, set allow_user-registration in
* wikak.config.php to '3'.
*
* @package Actions
* @version $Id: usersettings.php,v 1.1 2007/07/14 05:32:14 brian Exp brian $
* @license http://www.gnu.org/copyleft/gpl.html GNU General Public License
* @filesource
* @since 1.1.7
*
* @author {@link http://wikkawiki.org/MinusF MinusF} (code cleanup and validation)
* @author {@link http://wikkawiki.org/DarTar Dario Taraborelli} (further cleanup, i18n, replaced JS dialogs with server-generated messages)
* @author {@link http://wikkawiki.org/NilsLindenberg Nils Lindenberg} (possibility to restrict registration)
*
* @uses Wakka::htmlspecialchars_ent()
*
* @todo use different actions for registration / login / user settings;
* @todo add documentation links or short explanations for each option;
* @todo use error handler for displaying messages and highlighting
* invalid input fields;
* @todo remove useless redirections;
* @todo [accessibility] make logout independent of JavaScript
* @todo replace $_REQUEST with either $_GET or $_POST (or both if really
* necessary) - #312
* Captcha TODOs:
* @todo Parameterize allow_user_registration in wikka.config.php
* to use defines rather than integers
* @todo Wrap in div blocks and style via CSS
* @todo Add option to bypass for manual registration
* (accessibility)
*/

// defaults
if (!defined('PASSWORD_MIN_LENGTH')) define('PASSWORD_MIN_LENGTH', "5");
if (!defined('VALID_EMAIL_PATTERN')) define('VALID_EMAIL_PATTERN', "/^.+?\@.+?\..+$/"); //TODO: Use central regex library
if (!defined('REVISION_DISPLAY_LIMIT_MIN')) define('REVISION_DISPLAY_LIMIT_MIN', "0"); // 0 means no limit, 1 is the minimum number of revisions
if (!defined('REVISION_DISPLAY_LIMIT_MAX')) define('REVISION_DISPLAY_LIMIT_MAX', "20"); // keep this value within a reasonable limit to avoid an unnecessary long lists
if (!defined('RECENTCHANGES_DISPLAY_LIMIT_MIN')) define('RECENTCHANGES_DISPLAY_LIMIT_MIN', "0"); // 0 means no limit, 1 is the minimum number of changes
if (!defined('RECENTCHANGES_DISPLAY_LIMIT_MAX')) define('RECENTCHANGES_DISPLAY_LIMIT_MAX', "50"); // keep this value within a reasonable limit to avoid an unnecessary long list
if (!defined('INPUT_ERROR_STYLE')) define('INPUT_ERROR_STYLE', 'class="highlight"');
// added for FreeCap CAPTCHA
if (!defined('CAPTCHA_LABEL')) define('CAPTCHA_LABEL', "Type the letters in the picture:");
if (!defined('CAPTCHA_NO_READ')) define('CAPTCHA_NO_READ', "If you cannot read the word,");
if (!defined('CAPTCHA_NO_READ_CLICK_HERE')) define('CAPTCHA_NO_READ_CLICK_HERE', "click here");

//initialize variables
$params = '';
$url = '';
$email = '';
$doubleclickedit = '';
$show_comments = '';
$default_comment_display = '';
$revisioncount = '';
$changescount = '';
$password = '';
$oldpass = '';
$password_confirm = '';
$pw_selected = '';
$hash_selected = '';
$username_highlight = '';
$username_temp_highlight = '';
$password_temp_highlight = '';
$email_highlight = '';
$password_highlight = '';
$password_new_highlight = '';
$password_confirm_highlight = '';
$revisioncount_highlight = '';
$changescount_highlight = '';
$invitation_code_highlight = '';

$wikiname_expanded = '<abbr title="'.WIKINAME_LONG.'">'.WIKINAME_SHORT.'</abbr>';

//create URL
$url = $this->Href();

//Remember referring page if internal.
// - Getting correct regex to find the tag of referring page
preg_match('/^(.*)ReferrerMarker/', $this->Href('', 'ReferrerMarker'), $match);
$regex_referrer = '/^'.preg_quote($match[1], '/')."([^\\/\\?&]*)/";
if (isset($_SERVER['HTTP_REFERER']) && preg_match($regex_referrer, $_SERVER['HTTP_REFERER'], $match))
{
if (strcasecmp($this->tag, $match[1]))
{
$_SESSION['go_back'] = $_SERVER['HTTP_REFERER'];
//We save the tag of the referring page, this tag is to be shown in label <Go back to ...>. We must use a session here because if the user
//Refresh the page by hitting <Enter> on the address bar, the value would be lost.
$_SESSION['go_back_tag'] = $match[1];
}
}

// append URL params depending on rewrite_mode
$params = ($this->config['rewrite_mode'] == 1) ? '?' : '&';

// BEGIN *** Logout ***
// is user trying to log out?
if (isset($_POST['logout']) && $_POST['logout'] == LOGOUT_BUTTON) // replaced with normal form button #353, #312
{
$this->LogoutUser();
}
// END *** Logout ***

// BEGIN *** Usersettings ***
// user is still logged in
if ($user = $this->GetUser())
{
// is user trying to update user settings?
if (isset($_POST['action']) && ($_POST['action'] == 'update'))
{
// get POST parameters
$email = $this->GetSafeVar('email', 'post');
$doubleclickedit = $this->GetSafeVar('doubleclickedit', 'post');
$show_comments = $this->GetSafeVar('show_comments', 'post');
$default_comment_display = $this->GetSafeVar('default_comment_display', 'post');
$revisioncount = (int) $this->GetSafeVar('revisioncount', 'post');
$changescount = (int) $this->GetSafeVar('changescount', 'post');

// validate form input
switch (TRUE)
{
case (strlen($email) == 0): //email is empty
$error = ERROR_EMPTY_EMAIL_ADDRESS;
$email_highlight = INPUT_ERROR_STYLE;
break;
case (!preg_match(VALID_EMAIL_PATTERN, $email)): //invalid email
$error = ERROR_INVALID_EMAIL_ADDRESS;
$email_highlight = INPUT_ERROR_STYLE;
break;
case (($revisioncount < REVISION_DISPLAY_LIMIT_MIN) || ($revisioncount > REVISION_DISPLAY_LIMIT_MAX)): //invalid revision display limit
$error = sprintf(ERROR_INVALID_REVISION_DISPLAY_LIMIT, REVISION_DISPLAY_LIMIT_MAX);
$revisioncount_highlight = INPUT_ERROR_STYLE;
break;
case (($changescount < RECENTCHANGES_DISPLAY_LIMIT_MIN) || ($changescount > RECENTCHANGES_DISPLAY_LIMIT_MAX)): //invalid recentchanges display limit
$error = sprintf(ERROR_INVALID_RECENTCHANGES_DISPLAY_LIMIT, RECENTCHANGES_DISPLAY_LIMIT_MAX);
$changescount_highlight = INPUT_ERROR_STYLE;
break;
default: // input is valid
$this->Query('UPDATE '.$this->config['table_prefix'].'users SET '.
"email = '".mysql_real_escape_string($email)."', ".
"doubleclickedit = '".mysql_real_escape_string($doubleclickedit)."', ".
"show_comments = '".mysql_real_escape_string($show_comments)."', ".
"default_comment_display = '".mysql_real_escape_string($default_comment_display)."', ".
"revisioncount = '".mysql_real_escape_string($revisioncount)."', ".
"changescount = '".mysql_real_escape_string($changescount)."' ".
"WHERE name = '".$user['name']."' LIMIT 1");
unset($this->specialCache['user'][strtolower($user['name'])]); //invalidate cache if exists #368
$this->SetUser($this->LoadUser($user["name"]));
}
}
//user just logged in
else
{
// get stored settings
$email = $user['email'];
$doubleclickedit = $user['doubleclickedit'];
$show_comments = $user['show_comments'];
$default_comment_display = $user['default_comment_display'];
$revisioncount = $user['revisioncount'];
$changescount = $user['changescount'];
}

// display user settings form
echo $this->FormOpen();
?>
<fieldset id="account"><legend><?php echo USER_ACCOUNT_LEGEND ?></legend>
<span id="account_info">
<?php printf(USER_LOGGED_IN_AS_CAPTION, $this->Link($user['name'])); ?>
</span><input id="logout" name="logout" type="submit" value="<?php echo LOGOUT_BUTTON; ?>" /><!-- #353,#312-->
<br class="clear" />
</fieldset>
<fieldset id="usersettings" class="usersettings"><legend><?php echo USER_SETTINGS_LEGEND ?></legend>
<?php

// create confirmation message if needed
switch(TRUE)
{
case (isset($_SESSION['usersettings_registered']) && $_SESSION['usersettings_registered'] == 'true'):
unset($_SESSION['usersettings_registered']);
$success = USER_REGISTERED_SUCCESS;
break;
//case (isset($_GET['stored']) && $_GET['stored'] == 'true'):
case (isset($_POST['action']) && $_POST['action'] == 'update' && !isset($error)):
$success = USER_SETTINGS_STORED_SUCCESS;
break;
}

// display error or confirmation message
switch(TRUE)
{
case (isset($error)):
echo '<em class="error">'.$error.'</em><br />'."\n";
break;
case (isset($success)):
echo '<em class="success">'.$success.'</em><br />'."\n";
break;
}

if (isset($_POST['action']) && ($_POST['action'] == 'changepass'))
{
// check password
$oldpass = $_POST['oldpass']; //can be current password or hash sent as password reminder
$password = $_POST['password'];
$password_confirm = $_POST['password_confirm'];
$update_option = $this->GetSafeVar('update_option', 'post');

switch (TRUE)
{
case (strlen($oldpass) == 0):
$passerror = ERROR_EMPTY_PASSWORD_OR_HASH;
$password_highlight = INPUT_ERROR_STYLE;
break;
case (($update_option == 'pw') && md5($oldpass) != $user['password']): //wrong old password
$passerror = ERROR_INVALID_OLD_PASSWORD;
$pw_selected = 'selected="selected"';
$password_highlight = INPUT_ERROR_STYLE;
break;
case (($update_option == 'hash') && $oldpass != $user['password']): //wrong reminder (hash)
$passerror = ERROR_INVALID_HASH;
$hash_selected = 'selected="selected"';
$password_highlight = INPUT_ERROR_STYLE;
break;
case (strlen($password) == 0):
$passerror = ERROR_EMPTY_NEW_PASSWORD;
$password_highlight = INPUT_ERROR_STYLE;
$password_new_highlight = INPUT_ERROR_STYLE;
break;
case (preg_match("/ /", $password)):
$passerror = ERROR_PASSWORD_NO_BLANK;
$password_highlight = INPUT_ERROR_STYLE;
$password_new_highlight = INPUT_ERROR_STYLE;
break;
case (strlen($password) < PASSWORD_MIN_LENGTH):
$passerror = sprintf(ERROR_PASSWORD_TOO_SHORT, PASSWORD_MIN_LENGTH);
$password_highlight = INPUT_ERROR_STYLE;
$password_new_highlight = INPUT_ERROR_STYLE;
break;
case (strlen($password_confirm) == 0):
$passerror = ERROR_EMPTY_NEW_CONFIRMATION_PASSWORD;
$password_highlight = INPUT_ERROR_STYLE;
$password_new_highlight = INPUT_ERROR_STYLE;
$password_confirm_highlight = INPUT_ERROR_STYLE;
break;
case ($password_confirm != $password):
$passerror = ERROR_PASSWORD_MATCH;
$password_highlight = INPUT_ERROR_STYLE;
$password_new_highlight = INPUT_ERROR_STYLE;
$password_confirm_highlight = INPUT_ERROR_STYLE;
break;
default:
$this->Query('UPDATE '.$this->config['table_prefix'].'users SET '."password = md5('".mysql_real_escape_string($password)."') "."WHERE name = '".$user['name']."'");
unset($this->specialCache['user'][strtolower($name)]); //invalidate cache if exists #368
$user['password'] = md5($password);
$this->SetUser($user);
$passsuccess = USER_PASSWORD_CHANGED_SUCCESS;
}
}

?>
<input type="hidden" name="action" value="update" />
<label for="email"><?php echo USER_EMAIL_LABEL ?></label>
<input id="email" type="text" <?php echo $email_highlight; ?> name="email" value="<?php echo $this->htmlspecialchars_ent($email) ?>" size="40" />
<br />
<label for="doubleclick"><?php echo DOUBLECLICK_LABEL ?></label>
<input type="hidden" name="doubleclickedit" value="N" />
<input id="doubleclick" type="checkbox" name="doubleclickedit" value="Y" <?php echo $doubleclickedit == 'Y' ? 'checked="checked"' : '' ?> />
<br />
<label for="showcomments"><?php echo SHOW_COMMENTS_LABEL ?></label>
<input type="hidden" name="show_comments" value="N" />
<input id="showcomments" type="checkbox" name="show_comments" value="Y" <?php echo $show_comments == 'Y' ? 'checked="checked"' : '' ?> />
<fieldset><legend><?php echo DEFAULT_COMMENT_STYLE_LABEL ?></legend>
<input id="default_comment_flat_asc" type="radio" name="default_comment_display" value="1" <?php echo ($default_comment_display==1) ? 'checked="checked"' : '' ?> /><label for="default_comment_flat_asc"><?php echo COMMENT_ASC_LABEL ?></label><br />
<input id="default_comment_flat_desc" type="radio" name="default_comment_display" value="2" <?php echo ($default_comment_display==2) ? 'checked="checked"' : '' ?> /><label for="default_comment_flat_desc"><?php echo COMMENT_DEC_LABEL ?></label><br />
<input id="default_comment_threaded" type="radio" name="default_comment_display" value="3" <?php echo ($default_comment_display==3) ? 'checked="checked"' : '' ?> /><label for="default_comment_threaded"><?php echo COMMENT_THREADED_LABEL ?></label><br />
</fieldset>
<br />
<label for="revisioncount"><?php echo PAGEREVISION_LIST_LIMIT_LABEL ?></label>
<input id="revisioncount" type="text" <?php echo $revisioncount_highlight; ?> name="revisioncount" value="<?php echo $this->htmlspecialchars_ent($revisioncount) ?>" size="40" />
<br />
<label for="changescount"><?php echo RECENTCHANGES_DISPLAY_LIMIT_LABEL ?></label>
<input id="changescount" type="text" <?php echo $changescount_highlight; ?> name="changescount" value="<?php echo $this->htmlspecialchars_ent($changescount) ?>" size="40" />
<br />
<input id="updatesettingssubmit" type="submit" value="<?php echo UPDATE_SETTINGS_BUTTON ?>" />
<br />
</fieldset>
<?php
echo $this->FormClose(); //close user settings form

//display password update form
echo $this->FormOpen();
?>
<fieldset class="usersettings" id="changepassword"><legend><?php echo CHANGE_PASSWORD_LEGEND ?></legend>
<input type="hidden" name="action" value="changepass" />
<?php
if (isset($passerror))
{
echo '<em class="error">'.$passerror.'</em><br />'."\n";
}
else if (isset($passsuccess))
{
echo '<em class="success">'.$passsuccess.'</em><br />'."\n";
}
?>
<select id="update_option" name="update_option">
<option value="pw" <?php echo $pw_selected; ?>><?php echo CURRENT_PASSWORD_OPTION; ?></option>
<option value="hash" <?php echo $hash_selected; ?>><?php echo PASSWORD_REMINDER_OPTION; ?></option>
</select>
<input <?php echo $password_highlight; ?> type="password" name="oldpass" size="40" />
<br />
<label for="password"><?php echo NEW_PASSWORD_LABEL ?></label>
<input id="password" <?php echo $password_new_highlight; ?> type="password" name="password" size="40" />
<br />
<label for="password_confirm"><?php echo NEW_PASSWORD_CONFIRM_LABEL ?></label>
<input id="password_confirm" <?php echo $password_confirm_highlight; ?> type="password" name="password_confirm" size="40" />
<br />
<input id="changepasswordsubmit" type="submit" value="<?php echo CHANGE_PASSWORD_BUTTON ?>" size="40" />
<br />
</fieldset>
<?php
echo $this->FormClose();
}
// END *** Usersettings ***
// BEGIN *** LOGIN/LOGOUT ***
else // user is not logged in
{
// print confirmation message on successful logout
if (isset($_POST['logout']) && $_POST['logout'] == LOGOUT_BUTTON)
{
$success = USER_LOGGED_OUT_SUCCESS;
}

// is user trying to log in or register?
$register = $this->GetConfigValue('allow_user_registration');
if (isset($_POST['action']) && ($_POST['action'] == 'login'))
{
// if user name already exists, check password
if (isset($_POST['name']) && $existingUser = $this->LoadUser($_POST['name']))
{
// check password
switch(TRUE){
case (strlen($_POST['password']) == 0):
$error = ERROR_EMPTY_PASSWORD;
$password_highlight = INPUT_ERROR_STYLE;
break;
case (md5($_POST['password']) != $existingUser['password']):
$error = ERROR_INVALID_PASSWORD;
$password_highlight = INPUT_ERROR_STYLE;
break;
default:
$this->SetUser($existingUser);
if ((isset($_SESSION['go_back'])) && (isset($_POST['do_redirect'])))
{
$go_back = $_SESSION['go_back'];
unset($_SESSION['go_back']);
unset($_SESSION['go_back_tag']);
$this->Redirect($go_back);
}
$this->Redirect($url, '');
}
}
// END *** Login/Logout ***
// BEGIN *** Register ***
// 3 = captcha registration
else if ($register == '1' || $register == '2' || $register == '3') // otherwise, proceed to registration
{
if($register == '3' &&
(empty($_SESSION['freecap_word_hash']) ||
empty($_POST['word'])))
{
$captcha="You entered none of the letters. Please do so.";
}
else if($register == '3' &&
!($_SESSION['hash_func'](strtolower($_POST['word']))==$_SESSION['freecap_word_hash']))
{
$captcha="You entered the wrong letters, please try again.";
} else
{
$name = trim($_POST['name']);
$email = trim($this->GetSafeVar('email', 'post'));
$password = $_POST['password'];
$confpassword = $_POST['confpassword'];

// validate input
switch(TRUE)
{
case (strlen($name) == 0):
$error = ERROR_EMPTY_USERNAME;
$username_highlight = INPUT_ERROR_STYLE;
break;
case (!$this->IsWikiName($name)):
$error = $this->Format(sprintf(ERROR_WIKINAME,'##""WikiName""##','##""'.WIKKA_SAMPLE_WIKINAME.'""##'));
$username_highlight = INPUT_ERROR_STYLE;
break;
case ($this->ExistsPage($name)):
$error = ERROR_RESERVED_PAGENAME;
$username_highlight = INPUT_ERROR_STYLE;
break;
case (strlen($password) == 0):
$error = ERROR_EMPTY_PASSWORD;
$password_highlight = INPUT_ERROR_STYLE;
break;
case (preg_match("/ /", $password)):
$error = ERROR_NO_BLANK;
$password_highlight = INPUT_ERROR_STYLE;
break;
case (strlen($password) < PASSWORD_MIN_LENGTH):
$error = sprintf(ERROR_PASSWORD_TOO_SHORT, PASSWORD_MIN_LENGTH);
$password_highlight = INPUT_ERROR_STYLE;
break;
case (strlen($confpassword) == 0):
$error = ERROR_EMPTY_CONFIRMATION_PASSWORD;
$password_highlight = INPUT_ERROR_STYLE;
$password_confirm_highlight = INPUT_ERROR_STYLE;
break;
case ($confpassword != $password):
$error = ERROR_PASSWORD_MATCH;
$password_highlight = INPUT_ERROR_STYLE;
$password_confirm_highlight = INPUT_ERROR_STYLE;
break;
case (strlen($email) == 0):
$error = ERROR_EMAIL_ADDRESS_REQUIRED;
$email_highlight = INPUT_ERROR_STYLE;
$password_highlight = INPUT_ERROR_STYLE;
$password_confirm_highlight = INPUT_ERROR_STYLE;
break;
case (!preg_match(VALID_EMAIL_PATTERN, $email)):
$error = ERROR_INVALID_EMAIL_ADDRESS;
$email_highlight = INPUT_ERROR_STYLE;
$password_highlight = INPUT_ERROR_STYLE;
$password_confirm_highlight = INPUT_ERROR_STYLE;
break;
case ($register == '2' && $_POST['invitation_code'] !== $this->GetConfigValue('invitation_code')):
$error = ERROR_INVALID_INVITATION_CODE;
$invitation_code_highlight = INPUT_ERROR_STYLE;
break;
default: //valid input, create user
$this->Query("INSERT INTO ".$this->config['table_prefix']."users SET ".
"signuptime = now(), ".
"name = '".mysql_real_escape_string($name)."', ".
"email = '".mysql_real_escape_string($email)."', ".
"password = md5('".mysql_real_escape_string($_POST['password'])."')");
unset($this->specialCache['user'][strtolower($name)]); //invalidate cache if exists #368

// log in
$this->SetUser($this->LoadUser($name));
if ((isset($_SESSION['go_back'])) && (isset($_POST['do_redirect'])))
{
$go_back = $_SESSION['go_back'];
unset($_SESSION['go_back']);
$this->Redirect($go_back);
}
$_SESSION['usersettings_registered'] = true;
$this->Redirect($url.$params);
}
}
}
}
// END *** Register ***
// BEGIN *** Usersettings ***
elseif (isset($_POST['action']) && ($_POST['action'] == 'updatepass'))
{
$name = trim($_POST['yourname']);
if (strlen($name) == 0) // empty username
{
$newerror = WIKKA_ERROR_EMPTY_USERNAME;
$username_temp_highlight = INPUT_ERROR_STYLE;
}
elseif (!$this->IsWikiName($name)) // check if name is WikiName style
{
$newerror = ERROR_WIKINAME;
$username_temp_highlight = INPUT_ERROR_STYLE;
}
elseif (!($this->LoadUser($_POST['yourname']))) //check if user exists
{
$newerror = ERROR_NONEXISTENT_USERNAME;
$username_temp_highlight = INPUT_ERROR_STYLE;
}
elseif ($existingUser = $this->LoadUser($_POST['yourname'])) // if user name already exists, check password
{
// updatepassword
if ($existingUser['password'] == $_POST['temppassword'])
{
$this->SetUser($existingUser, $_POST['remember']);
$this->Redirect($url);
}
else
{
$newerror = ERROR_WRONG_PASSWORD;
$password_temp_highlight = INPUT_ERROR_STYLE;
}
}
}
// END *** Usersettings ***
// BEGIN *** Login/Logout ***
// BEGIN *** Register ***
print($this->FormOpen());
if($register == '3')
{
?>
<script language="javascript">
<!--
function new_freecap()
{
// loads new freeCap image
if(document.getElementById)
{
// extract image name from image source (i.e. cut off
// ?randomness)
thesrc = document.getElementById("freecap").src;
thesrc = thesrc.substring(0,thesrc.lastIndexOf(".")+4);
// add ?(random) to prevent browser/isp caching
document.getElementById("freecap").src =
thesrc+"?"+Math.round(Math.random()*100000);
} else {
alert("Sorry, cannot autoreload freeCap image\nSubmit the form
and a new freeCap will be loaded");
}
}
//-->
</script>
<?php
}
?>
<fieldset id="register" class="usersettings"><legend><?php echo ($register == '1' || $register == '2') ? LOGIN_REGISTER_LEGEND : LOGIN_LEGEND; ?></legend>
<input type="hidden" name="action" value="login" />
<?php
switch (true)
{
case (isset($error)):
echo '<em class="error">'.$error.'</em><br />'."\n";
break;
case (isset($success)):
echo '<em class="success">'.$success.'</em><br />'."\n";
break;
case (isset($captcha)):
echo '<em class="error">'.$this->Format($captcha).'</em>'."\n";
break;
}
?>
<em class="usersettings_info"><?php echo REGISTERED_USER_LOGIN_CAPTION; ?></em>
<br />
<label for="name"><?php printf(WIKINAME_LABEL,$wikiname_expanded) ?></label>
<input id="name" type="text" <?php echo $username_highlight; ?> name="name" size="40" value="<?php echo $this->GetSafeVar('name', 'post'); ?>" />
<br />
<label for="password"><?php printf(PASSWORD_LABEL, PASSWORD_MIN_LENGTH) ?></label>
<input id="password" <?php echo $password_highlight; ?> type="password" name="password" size="40" />
<br />
<?php
if (isset($_SESSION['go_back']))
{
// FIXME @@@ label for a checkbox should come AFTER it, not before
?>
<label for="do_redirect"><?php printf(USERSETTINGS_REDIRECT_AFTER_LOGIN, $_SESSION['go_back_tag']); ?></label>
<input type="checkbox" name="do_redirect" id="do_redirect"<?php if (isset($_POST['do_redirect']) || empty($_POST)) echo ' checked="checked"';?> />
<br />
<?php
}
?>
<input id="loginsubmit" type="submit" value="<?php echo LOGIN_BUTTON ?>" size="40" />
<br /><br />
<?php
// END *** Login/Logout ***
$register = $this->GetConfigValue('allow_user_registration');
if ($register == '1' || $register == '2' || $register == '3')
{
?>
<em class="usersettings_info"><?php echo NEW_USER_REGISTER_CAPTION; ?></em>
<br />
<label for="confpassword"><?php echo CONFIRM_PASSWORD_LABEL ?></label>
<input id="confpassword" <?php echo $password_confirm_highlight; ?> type="password" name="confpassword" size="40" />
<br />
<label for="email"><?php echo USER_EMAIL_LABEL ?></label>
<input id="email" type="text" <?php echo $email_highlight; ?> name="email" size="40" value="<?php echo $email; ?>" />
<br />
<?php
if($register == '3')
{
setcookie("sessname", session_name(), 0, '/');
?>
<?php // <img src="../freecap/freecap.php" id="freecap"/><br/> ?>
<div id="captcha">
<img src="3rdparty/plugins/freecap/freecap.php" id="freecap"/><br/>
<?php echo CAPTCHA_NO_READ ?><a href="<?php echo $this->Href('', 'UserSettings'); ?>" onClick="this.blur();new_freecap();return false;"> <?php echo CAPTCHA_NO_READ_CLICK_HERE ?>.</a>
</div>
<label for="confpassword"><?php echo CAPTCHA_LABEL ?></label>
<input id="confpassword" name="word" type="text" size="40" />
<?php
}
else if ($register == '2')
{
$invitation_code_expanded = '<abbr title="'.INVITATION_CODE_LONG.'">'.INVITATION_CODE_SHORT.'</abbr>'
?>
<label for="invitation_code"><?php printf(INVITATION_CODE_LABEL,$invitation_code_expanded) ?></label>
<input id="invitation_code" type="text" <?php echo $invitation_code_highlight; ?> size="20" name="invitation_code" />
<br />
<?php
}
?>
<input id="loginsubmit" type="submit" value="<?php echo REGISTER_BUTTON ?>" size="40" />
<br />
<?php
}
echo ' </fieldset>'."\n";
print($this->FormClose());
// END *** Register ***
print($this->FormOpen());
?>
<fieldset id="password_forgotten" class="usersettings"><legend><?php echo RETRIEVE_PASSWORD_LEGEND ?></legend>
<input type="hidden" name="action" value="updatepass" />
<?php
if (isset($newerror))
{
echo '<em class="error">'.$newerror.'</em><br />'."\n";
}
$retrieve_password_link = 'PasswordForgotten';
$retrieve_password_caption = $this->Format(sprintf(RETRIEVE_PASSWORD_CAPTION,$retrieve_password_link));
?>
<em class="usersettings_info"><?php echo $retrieve_password_caption ?></em>
<br />
<label for="yourname"><?php printf(WIKINAME_LABEL,$wikiname_expanded) ?></label>
<input id="yourname" type="text" <?php echo $username_temp_highlight; ?> name="yourname" value="<?php echo $this->GetSafeVar('yourname', 'post'); ?>" size="40" />
<br />
<label for="temppassword"><?php echo TEMP_PASSWORD_LABEL ?></label>
<input id="temppassword" type="text" <?php echo $password_temp_highlight; ?> name="temppassword" size="40" />
<br />
<input id="temppassloginsubmit" type="submit" value="<?php echo LOGIN_BUTTON ?>" size="40" />
<br class="clear" />
</fieldset>
<?php
print($this->FormClose());
}
?>
%%

=== Problems ===

Integration went very straight after the example that comes with the package except for one thing. The ""FreeCap"" script assumed a standard php session to use but because Wikka uses a custom session name it missed this. Since this is no link (no href but rather a src where the freecap.php is linked) I couldn't give the name to the script via URL (also it would be visible then) so I put the name in a cookie. This works and I don't think it's insecure for the cookie can't be read by anyone else but the one who placed it. Of course it doesn't work when cookies are off.
If anyone could come up with a better solution that would be great.

~& You will also need to pass the session name saved in the cookie to the freecap.php script by including the following statement **before** session_start() in freecap.php: %%session_name($_COOKIE['sessname']);%%. Also, if you decide to relocate your freecap dir to another location (for instance, under ##3rdparty/plugins/freenode##), you must also modify the setcookie() call so that the cookie is accessible by the freecap.php script. The quick and dirty way to do this would be: %%setcookie('sessname', session_name(), 0, '/');%%.


=== Note for Rewrite Users ===

The image will not show up unless you exclude it in your mod_rewrite settings. For example, I initially had a rewrite rule found elsewhere on this site that didn't exclude rewriting for the actions directory.

%%
RewriteRule ^(css|images|wikiedit2)/(.*)$ $1/$2 [L]
%%

Changing that rule to include the actions directory, like as follows, fixed the problem.

%%
RewriteRule ^(actions|css|images|wikiedit2)/(.*)$ $1/$2 [L]
%%

~& If you didn't define a list with directories to exclude from rewrite, you can also copy an .htaccess -file into the actions directory - or any other directory you want to be accessable - containing the simple rule to turn rewrite off for that directory:

%%
<IfModule mod_rewrite.c>
RewriteEngine off
</IfModule>
%%

=== Customization ===

You can edit the freecap.php to customize the CAPTCHAs, change the hash encryption and type of image format. The script can use a dictionary to read the words from but I prefer random letters although they're hard to read sometimes. The dictionary must NOT be readable on your webspace (check it!) if you use it.

In addition there should be an option for people who can't see the CAPTCHA to register on the site (the ""FreeCap"" author suggests to let them register and check those registrations manually but audio CAPTCHA would also be possible).


Any comments are appreciated!

----
CategoryUserContributions
Valid XHTML :: Valid CSS: :: Powered by WikkaWiki