************************************************ UPDATE *****************************************************************

THIS POST IS IRRELEVANT TO A CERTAIN POINT — THE NEWEST VERSION OF ROUDCUBE HAS A PASSWORD PLUGIN THAT PERFORMS THIS SAME ACTION.  YOU STILL NEED TO EDIT THE SQL STRING TO UPDATE YOUR USERS TABLE ACCORDINGLY.  PLEASE REFERENCE THE PLUGIN API OR ROUNDCUBE FORUM IF YOU HAVE ANY FURTHER QUESTIONS OR CONCERNS!!!

*************************************************************************************************************************

I have had quite a struggle over the past few weeks trying to get a good webmail client working properly for my email domains. In times past I have used Roundcube, but that was only with a single domain and UNIX based user accounts. My new setup was configured for virtual domains and virtual users and unfortunately I was struggling to get Roundcube functioning for me in this context. After several long nights I finally got an instance of Roundcube to represent each of my virtual domains. My relief quickly turned to despair as I realized that Roundcube didn’t have a plugin that allowed my users to change their encrypted database password. I certainly did not want the responsibility of knowing all my users passwords and having them email me whenever they wanted to change their password. I came to the conclusion that this was a deal breaker for me — I either had to find a way to allow my users to change their passwords independent of me via Roundcube or I was going to go back to SquirrelMail. In all fairness SquirrelMail has an awesome array of plugins and functionality that Roundcube doesn’t even begin to offer — BUT SquirrelMail is still running on Web 1.0 (aka it looks like a 1990′s webmail client).

After a lot of google searching and A LOT of failed attempts at getting Roundcube to change the user’s passwords I finally came upon some code that I was able to piece together and make work. This code is not written or maintained by me — credit goes to Nemesis02 and xul from the Roundcube forums as well as credit goes to Jonny Grover who helped me find a few misplaced quotation marks.  So without further adieu here it is:

Requirements:

Working email server (either POP/IMAP and SMTP)

Working installation of Roundcube (version 0.2-stable)

Write access to your database server

Database that contains user names, domains and passwords

Write access to your Roundcube files

Ok we are going to start editing a few files so be patient.

1. Open roundcube/index.php

==========================================================

Find               [~ Line 188  ]


'settings' => array(
    'folders'       => 'manage_folders.inc',
    'create-folder' => 'manage_folders.inc',
    'rename-folder' => 'manage_folders.inc',
    'delete-folder' => 'manage_folders.inc',
    'subscribe'     => 'manage_folders.inc',
    'unsubscribe'   => 'manage_folders.inc',
    'add-identity'  => 'edit_identity.inc',
  )

==========================================================

Change it to say:


'settings' => array(
    'folders'       => 'manage_folders.inc',
    'create-folder' => 'manage_folders.inc',
    'rename-folder' => 'manage_folders.inc',
    'delete-folder' => 'manage_folders.inc',
    'subscribe'     => 'manage_folders.inc',
    'unsubscribe'   => 'manage_folders.inc',
    'add-identity'  => 'edit_identity.inc',
    'password'      => 'password.inc',
    'save-password' => 'password.inc',
  )

2. Open roundcube/program/js/app.js

==========================================================

Find          [ ~ Line 238 ]


case 'settings':
        this.enable_command("preferences", "identities", "save", "folders", true);
==========================================================

edit it to say:


case 'settings':
        this.enable_command("preferences", "identities", "save", "folders", "password", true);
==========================================================

Find this line           [ ~ Line 239 ]


if(this.env.action=="identities"||this.env.action=="edit-identity"||this.env.action=="add-identity"){
  this.enable_command("add",this.env.identities_level<2);
  this.enable_command("delete","edit",true);
        }
==========================================================

and then insert above it:


if (this.env.action=='password' || this.env.action=='save-password')
        {
          var input_current_password = rcube_find_object('_current_password');
          var input_new_password = rcube_find_object('_new_password');
          var input_repeat_password = rcube_find_object('_repeat_password');
          if (input_current_password && input_current_password.value=='')
              input_current_password.focus();
          else if (input_repeat_password)
              input_repeat_password.focus();

          this.enable_command('save-password', true);
        }
==========================================================

Find this line             [ ~ Line 904]


      case 'delete-folder':
        this.delete_folder(props);
        break;
==========================================================

Insert after it the following:


      case 'password':
        this.goto_url('password');
        break;

      case 'save-password':
        var input_current_password = rcube_find_object('_current_password');
        var input_new_password = rcube_find_object('_new_password');
        var input_repeat_password = rcube_find_object('_repeat_password');
        if ((input_new_password && input_new_password.value=='') && (input_repeat_password && input_repeat_password.value=='')
                || (input_current_password && input_current_password.value==''))
            {
                alert(this.get_label('nopassword'));
                input_current_password.value='';
                input_new_password.value='';
                input_repeat_password.value='';
                input_current_password.focus();
            }
        else if ((input_new_password && input_repeat_password) && ( input_new_password.value != input_repeat_password.value))
            {
                alert(this.get_label('passwordinconsistency'));
                input_new_password.value='';
                input_repeat_password.value='';
                input_new_password.focus();
            }
        else
                this.gui_objects.editform.submit();
        break;

3. Open program/localization/en_US/labels.inc

Insert at the end of the file:

$labels['changepassword']  = 'Change Password';
$labels['current_password']  = 'Current Password';
$labels['new_password']  = 'New Password';
$labels['repeat_password']  = 'Repeat Password';

4. Open program/localization/en_US/messages.inc

Insert at the end of the file:


$messages['nopassword'] = "Please input new password.";
$messages['passwordinconsistency'] = "Inconsistency of password, please try again.";

5. Open program/localization/en_GB/labels.inc

Insert at the end of the file:

$labels['changepassword']  = 'Change Password';
$labels['current_password']  = 'Current Password';
$labels['new_password']  = 'New Password';
$labels['repeat_password']  = 'Repeat Password';

6. Open program/localization/en_GB/messages.inc

Insert at the end of the file:


$messages['nopassword'] = "Please input new password.";
$messages['passwordinconsistency'] = "Inconsistency of password, please try again.";

7. Open skins/default/includes/settingstab.html

Insert at the end of the file:


<span id="settingstabpassword" class="tablink"><roundcube:button command="password" type="link" label="password" title="changepassword" class="tablink" /></span>

8. Create this file roundcube/skins/default/templates/password.html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
	<head>
		<title><roundcube:object name="pagetitle" /></title>
		<roundcube:include file="/includes/links.html" />
		<link rel="stylesheet" type="text/css" href="/settings.css" />
	</head>
	<body>

		<roundcube:include file="/includes/taskbar.html" />
		<roundcube:include file="/includes/header.html" />
		<roundcube:include file="/includes/settingstabs.html" />

		<div id="userprefs-box">
			<div id="userprefs-title"><roundcube:label name="changepassword" /></div>

			<div style="padding:15px">
				<roundcube:object name="passwordform">

				<p><br /><roundcube:button command="save-password" type="input" class="button" label="save" /></p>
			</div>
		</div>

		<roundcube:include file="/includes/settingscripts.html" />

	</body>
</html>

9. Create roundcube/program/steps/settings/password.inc

You MUST update the query string for your database tables and fields !!!

<?PHP

/****************************************************************
 * Roundcube password manager
 *  works with v.2-stable
/****************************************************************/

function rcube_password_form($attrib)
{
	global $RCMAIL, $OUTPUT, $CONFIG;

	$a_show_cols = array( 	'current_password' => array( 'type' => 'text'),
				'new_password' => array( 'type' => 'text'),
				'repeat_password' => array( 'type' => 'text'));

	list($form_start, $form_end) = get_form_tags($attrib, 'save-password', array() );

	$out = "{$form_start}<table>\r\n\r\n";

	foreach ($a_show_cols as $col => $colprop)
	{
		$label = strlen($colprop['label']) ? $colprop['label'] : $col;
		$value = rcmail_get_edit_field($col, '', $attrib + array('type' => 'password', 'size' => 30), $colprop['type']);

		$out .= sprintf("<tr><td class=\"title\"><label for=\"%s\">%s</label></td><td>%s</td></tr>\n",
              		$attrib['id'],
              		Q(rcube_label($label)),
               		$value);
	}

	$out .= "\n</table>$form_end";

	return $out;
}

function rcube_save_password($current_password,$new_password)
{
	global $OUTPUT, $RCMAIL;

	$emailuser=$_SESSION['username'];
    	$db = rcmail::get_instance()->get_dbh();

     	// YOU MUST CHANGE THIS QUERY LINE TO MATCH YOUR DATABASE SETTINGS !!!!!
    	$passwordquery = "UPDATE database.table SET password_field =ENCRYPT('".$new_password."') WHERE user_name = '".$emailuser."';";   

    	$db->query($passwordquery);

	$_SESSION['password'] = encrypt_password($new_password);

	$OUTPUT->show_message('successfullysaved', 'confirmation');
}

function encrypt_password($pass)
{
    if (function_exists('mcrypt_module_open') && ($td = mcrypt_module_open(MCRYPT_TripleDES, "", MCRYPT_MODE_ECB, ""))) {
      $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
      mcrypt_generic_init($td, rcmail::get_instance()->config->get_des_key(), $iv);
      $cypher = mcrypt_generic($td, $pass);
      mcrypt_generic_deinit($td);
      mcrypt_module_close($td);
    }
    else if (function_exists('des')) {
      $cypher = des(rcmail::get_instance()->config->get_des_key(), $pass, 1, 0, NULL);
    }
    else {
      $cypher = $pass;

      raise_error(array(
        'code' => 500,
        'type' => 'php',
        'file' => __FILE__,
        'message' => "Could not convert encrypt password. Make sure Mcrypt is installed or lib/des.inc is available"
        ), true, false);
    }

    return base64_encode($cypher);
}

$OUTPUT->set_pagetitle(rcube_label("changepassword"));
$OUTPUT->add_handler('passwordform', 'rcube_password_form');

$OUTPUT->add_label('passwordinconsistency', 'nopassword');

switch ($RCMAIL->action)
{

	case "password":
		$OUTPUT->send("password");
	break;

	case "save-password":
		$curpass = get_input_value('_current_password', RCUBE_INPUT_POST, false);
		$newpass = get_input_value('_new_password', RCUBE_INPUT_POST, false);
		$repeatpass = get_input_value('_repeat_password', RCUBE_INPUT_POST, false);

		if ($newpass == $repeatpass && $_SESSION['password'] == encrypt_password($curpass))
			rcube_save_password($curpass,$newpass);
		else
			$OUTPUT->show_message('errorsaving', 'error');

		rcmail_overwrite_action("password");
		$OUTPUT->send("password");
	break;

}

?>

Phew 8) We are finally done.  The change password functionality is available under the “Personal Settings” link.  I have attached a screenshot below!  Hope this works for you, if not please carefully go back and make sure you have copied the above code very carefully — one missed bracket, parenthesis or semi-colon can break the code!

Please feel free to post up any comments or questions!

Roundcube Change Password