Import initial du site depuis le serveur

This commit is contained in:
WhyKorp's server
2026-04-27 06:52:16 +00:00
parent 81b39e856b
commit f57f9fe1d5
1871 changed files with 214417 additions and 32138 deletions
@@ -0,0 +1,53 @@
<?php
/**
* chpasswd driver
*
* Driver that adds functionality to change the systems user password via
* the 'chpasswd' command.
*
* For installation instructions please read the README file.
*
* @version 2.0
* @author Alex Cartwright <acartwright@mutinydesign.co.uk>
*
* Copyright (C) The Roundcube Dev Team
*
* This program 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 program 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/.
*/
class rcube_chpasswd_password
{
public function save($currpass, $newpass, $username)
{
$cmd = rcmail::get_instance()->config->get('password_chpasswd_cmd');
$handle = popen($cmd, "w");
fwrite($handle, "$username:$newpass\n");
if (pclose($handle) == 0) {
return PASSWORD_SUCCESS;
}
rcube::raise_error([
'code' => 600,
'file' => __FILE__,
'line' => __LINE__,
'message' => "Password plugin: Unable to execute $cmd"
], true, false
);
return PASSWORD_ERROR;
}
}
@@ -0,0 +1,146 @@
<?php
/**
* cPanel Password Driver
*
* It uses Cpanel's Webmail UAPI to change the users password.
*
* This driver has been tested successfully with Digital Pacific hosting.
*
* @author Maikel Linke <maikel@email.org.au>
*
* Copyright (C) The Roundcube Dev Team
*
* This program 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 program 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/.
*/
class rcube_cpanel_password
{
/**
* Changes the user's password. It is called by password.php.
* See "Driver API" README and password.php for the interface details.
*
* @param string $curpas Current (old) password
* @param string $newpass New password
*
* @return int|array Error code or assoc array with 'code' and 'message', see
* "Driver API" README and password.php
*/
public function save($curpas, $newpass)
{
$url = self::url();
$user = password::username();
$userpwd = "$user:$curpas";
$data = [
'email' => password::username('%l'),
'password' => $newpass
];
$response = $this->curl_auth_post($userpwd, $url, $data);
return self::decode_response($response);
}
/**
* Provides the UAPI URL of the Email::passwd_pop function.
*
* @return string HTTPS URL
*/
public static function url()
{
$config = rcmail::get_instance()->config;
$storage_host = $_SESSION['storage_host'];
$host = $config->get('password_cpanel_host', $storage_host);
$port = $config->get('password_cpanel_port', 2096);
return "https://$host:$port/execute/Email/passwd_pop";
}
/**
* Converts a UAPI response to a password driver response.
*
* @param string $response JSON response by the Cpanel UAPI
*
* @return mixed Response code or array, see <code>save</code>
*/
public static function decode_response($response)
{
if (!$response) {
return PASSWORD_CONNECT_ERROR;
}
// $result should be `null` or `stdClass` object
$result = json_decode($response);
// The UAPI may return HTML instead of JSON on missing authentication
if ($result && isset($result->status) && $result->status === 1) {
return PASSWORD_SUCCESS;
}
if ($result && !empty($result->errors) && is_array($result->errors)) {
return [
'code' => PASSWORD_ERROR,
'message' => $result->errors[0],
];
}
return PASSWORD_ERROR;
}
/**
* Post data to the given URL using basic authentication.
*
* Example:
*
* <code>
* curl_auth_post('john:Secr3t', 'https://example.org', [
* 'param' => 'value',
* 'param' => 'value'
* ]);
* </code>
*
* @param string $userpwd User name and password separated by a colon
* <code>:</code>
* @param string $url The URL to post data to
* @param array $postdata The data to post
*
* @return string|false The body of the reply, False on error
*/
private function curl_auth_post($userpwd, $url, $postdata)
{
$ch = curl_init();
$postfields = http_build_query($postdata, '', '&');
// see http://php.net/manual/en/function.curl-setopt.php
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_BUFFERSIZE, 131072);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postfields);
curl_setopt($ch, CURLOPT_USERPWD, $userpwd);
$result = curl_exec($ch);
$error = curl_error($ch);
curl_close($ch);
if ($result === false) {
rcube::raise_error("curl error: $error", true, false);
}
return $result;
}
}
@@ -0,0 +1,59 @@
<?php
/**
* DBMail Password Driver
*
* Driver that adds functionality to change the users DBMail password.
* The code is derived from the Squirrelmail "Change SASL Password" Plugin
* by Galen Johnson.
*
* It only works with dbmail-users on the same host where Roundcube runs
* and requires shell access and gcc in order to compile the binary.
*
* For installation instructions please read the README file.
*
* @version 1.0
*
* Copyright (C) The Roundcube Dev Team
*
* This program 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 program 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/.
*/
class rcube_dbmail_password
{
function save($currpass, $newpass, $username)
{
$curdir = RCUBE_PLUGINS_DIR . 'password/helpers';
$username = escapeshellarg($username);
$password = escapeshellarg($newpass);
$args = rcmail::get_instance()->config->get('password_dbmail_args', '');
$command = "$curdir/chgdbmailusers -c $username -w $password $args";
exec($command, $output, $return_value);
if ($return_value == 0) {
return PASSWORD_SUCCESS;
}
rcube::raise_error([
'code' => 600,
'file' => __FILE__,
'line' => __LINE__,
'message' => "Password plugin: Unable to execute $curdir/chgdbmailusers"
], true, false
);
return PASSWORD_ERROR;
}
}
@@ -0,0 +1,456 @@
<?php
/**
* DirectAdmin Password Driver
*
* Driver to change passwords via DirectAdmin Control Panel
*
* @version 2.2
* @author Victor Benincasa <vbenincasa @ gmail.com>
*
* Copyright (C) The Roundcube Dev Team
*
* This program 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 program 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/.
*/
class rcube_directadmin_password
{
public function save($curpass, $passwd)
{
$rcmail = rcmail::get_instance();
$Socket = new HTTPSocket;
$da_user = $_SESSION['username'];
$da_curpass = $curpass;
$da_newpass = $passwd;
$da_host = $rcmail->config->get('password_directadmin_host');
$da_port = $rcmail->config->get('password_directadmin_port');
if (strpos($da_user, '@') === false) {
return ['code' => PASSWORD_ERROR, 'message' => 'Change the SYSTEM user password through control panel!'];
}
$da_host = str_replace('%h', $_SESSION['imap_host'], $da_host);
$da_host = str_replace('%d', $rcmail->user->get_username('domain'), $da_host);
$Socket->connect($da_host,$da_port);
$Socket->set_method('POST');
$Socket->query('/CMD_CHANGE_EMAIL_PASSWORD', [
'email' => $da_user,
'oldpassword' => $da_curpass,
'password1' => $da_newpass,
'password2' => $da_newpass,
'api' => '1'
]);
$response = $Socket->fetch_parsed_body();
//DEBUG
//rcube::console("Password Plugin: [USER: $da_user] [HOST: $da_host] - Response: [SOCKET: ".$Socket->result_status_code."] [DA ERROR: ".strip_tags($response['error'])."] [TEXT: ".$response[text]."]");
if ($Socket->result_status_code != 200) {
return ['code' => PASSWORD_CONNECT_ERROR, 'message' => $Socket->error[0]];
}
if ($response['error'] == 1) {
return ['code' => PASSWORD_ERROR, 'message' => strip_tags($response['text'])];
}
return PASSWORD_SUCCESS;
}
}
/**
* Socket communication class.
*
* Originally designed for use with DirectAdmin's API, this class will fill any HTTP socket need.
*
* Very, very basic usage:
* $Socket = new HTTPSocket;
* echo $Socket->get('http://user:pass@somesite.com/somedir/some.file?query=string&this=that');
*
* @author Phi1 'l0rdphi1' Stier <l0rdphi1@liquenox.net>
* @package HTTPSocket
* @version 3.0.2
*/
class HTTPSocket
{
var $version = '3.0.2';
// all vars are private except $error, $query_cache, and $doFollowLocationHeader
var $method = 'GET';
var $remote_host;
var $remote_port;
var $remote_uname;
var $remote_passwd;
var $result;
var $result_header;
var $result_body;
var $result_status_code;
var $lastTransferSpeed;
var $bind_host;
var $error = [];
var $warn = [];
var $query_cache = [];
var $doFollowLocationHeader = true;
var $redirectURL;
var $max_redirects = 5;
var $ssl_setting_message = 'DirectAdmin appears to be using SSL. Change your script to connect to ssl://';
var $extra_headers = [];
/**
* Create server "connection".
*
*/
function connect($host, $port = '')
{
if (!is_numeric($port)) {
$port = 2222;
}
$this->remote_host = $host;
$this->remote_port = $port;
}
function bind($ip = '')
{
if ($ip == '') {
$ip = $_SERVER['SERVER_ADDR'];
}
$this->bind_host = $ip;
}
/**
* Change the method being used to communicate.
*
* @param string|null request method. supports GET, POST, and HEAD. default is GET
*/
function set_method($method = 'GET')
{
$this->method = strtoupper($method);
}
/**
* Specify a username and password.
*
* @param string|null username. default is null
* @param string|null password. default is null
*/
function set_login($uname = '', $passwd = '')
{
if (strlen($uname) > 0) {
$this->remote_uname = $uname;
}
if (strlen($passwd) > 0) {
$this->remote_passwd = $passwd;
}
}
/**
* Query the server
*
* @param string containing properly formatted server API. See DA API docs and examples. Http:// URLs O.K. too.
* @param string|array query to pass to url
*/
function query($request, $content = '')
{
$this->error = $this->warn = [];
$this->result_status_code = null;
$is_ssl = false;
// is our request a http:// ... ?
if (preg_match('!^http://!i',$request) || preg_match('!^https://!i',$request)) {
$location = parse_url($request);
if (preg_match('!^https://!i',$request)) {
$this->connect('https://'.$location['host'],$location['port']);
}
else {
$this->connect('http://'.$location['host'],$location['port']);
}
$this->set_login($location['user'], $location['pass']);
$request = $location['path'];
if ($content == '') {
$content = $location['query'];
}
if (strlen($request) < 1) {
$request = '/';
}
}
if (preg_match('!^ssl://!i', $this->remote_host)) {
$this->remote_host = 'https://'.substr($this->remote_host, 6);
}
if (preg_match('!^tcp://!i', $this->remote_host)) {
$this->remote_host = 'http://'.substr($this->remote_host, 6);
}
if (preg_match('!^https://!i', $this->remote_host)) {
$is_ssl = true;
}
$array_headers = [
'Host' => $this->remote_port == 80 ? $this->remote_host : "$this->remote_host:$this->remote_port",
'Accept' => '*/*',
'Connection' => 'Close'
];
foreach ($this->extra_headers as $key => $value) {
$array_headers[$key] = $value;
}
$this->result = $this->result_header = $this->result_body = '';
// was content sent as an array? if so, turn it into a string
if (is_array($content)) {
$pairs = [];
foreach ($content as $key => $value) {
$pairs[] = "$key=".urlencode($value);
}
$content = join('&',$pairs);
unset($pairs);
}
$OK = true;
if ($this->method == 'GET') {
$request .= '?'.$content;
}
$ch = curl_init($this->remote_host.':'.$this->remote_port.$request);
if ($is_ssl) {
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //1
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); //2
//curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
}
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch, CURLOPT_USERAGENT, "HTTPSocket/$this->version");
curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 100);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_LOW_SPEED_LIMIT, 512);
curl_setopt($ch, CURLOPT_LOW_SPEED_TIME, 120);
// instance connection
if ($this->bind_host) {
curl_setopt($ch, CURLOPT_INTERFACE, $this->bind_host);
}
// if we have a username and password, add the header
if (isset($this->remote_uname) && isset($this->remote_passwd)) {
curl_setopt($ch, CURLOPT_USERPWD, $this->remote_uname.':'.$this->remote_passwd);
}
// for DA skins: if $this->remote_passwd is NULL, try to use the login key system
if (isset($this->remote_uname) && $this->remote_passwd == NULL) {
$array_headers['Cookie'] = "session={$_SERVER['SESSION_ID']}; key={$_SERVER['SESSION_KEY']}";
}
// if method is POST, add content length & type headers
if ($this->method == 'POST') {
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $content);
//$array_headers['Content-type'] = 'application/x-www-form-urlencoded';
$array_headers['Content-length'] = strlen($content);
}
curl_setopt($ch, CURLOPT_HTTPHEADER, $array_headers);
if (!($this->result = curl_exec($ch))) {
$this->error[] = curl_error($ch);
$OK = false;
}
$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$this->result_header = substr($this->result, 0, $header_size);
$this->result_body = substr($this->result, $header_size);
$this->result_status_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$this->lastTransferSpeed = curl_getinfo($ch, CURLINFO_SPEED_DOWNLOAD) / 1024;
curl_close($ch);
$this->query_cache[] = $this->remote_host.':'.$this->remote_port.$request;
$headers = $this->fetch_header();
// did we get the full file?
if (!empty($headers['content-length']) && $headers['content-length'] != strlen($this->result_body)) {
$this->result_status_code = 206;
}
// now, if we're being passed a location header, should we follow it?
if ($this->doFollowLocationHeader) {
//dont bother if we didn't even setup the script correctly
if (isset($headers['x-use-https']) && $headers['x-use-https'] == 'yes') {
die($this->ssl_setting_message);
}
if (isset($headers['location'])) {
if ($this->max_redirects <= 0) {
die("Too many redirects on: ".$headers['location']);
}
$this->max_redirects--;
$this->redirectURL = $headers['location'];
$this->query($headers['location'], $content);
}
}
}
function getTransferSpeed()
{
return $this->lastTransferSpeed;
}
/**
* The quick way to get a URL's content :)
*
* @param string $location URL
* @param bool $asArray return as array? (like PHP's file() command)
*
* @return string result body
*/
function get($location, $asArray = false)
{
$this->query($location);
if ($this->get_status_code() == 200) {
if ($asArray) {
return preg_split("/\n/", $this->fetch_body());
}
return $this->fetch_body();
}
return false;
}
/**
* Returns the last status code.
* 200 = OK;
* 403 = FORBIDDEN;
* etc.
*
* @return int status code
*/
function get_status_code()
{
return $this->result_status_code;
}
/**
* Adds a header, sent with the next query.
*
* @param string header name
* @param string header value
*/
function add_header($key, $value)
{
$this->extra_headers[$key] = $value;
}
/**
* Clears any extra headers.
*
*/
function clear_headers()
{
$this->extra_headers = [];
}
/**
* Return the result of a query.
*
* @return string result
*/
function fetch_result()
{
return $this->result;
}
/**
* Return the header of result (stuff before body).
*
* @param string (optional) header to return
* @return array result header
*/
function fetch_header($header = '')
{
$array_headers = preg_split("/\r\n/", $this->result_header);
$array_return = [0 => $array_headers[0]];
unset($array_headers[0]);
foreach ($array_headers as $pair) {
if ($pair == '' || $pair == "\r\n") continue;
list($key,$value) = preg_split("/: /", $pair, 2);
$array_return[strtolower($key)] = $value;
}
if ($header != '') {
return $array_return[strtolower($header)];
}
return $array_return;
}
/**
* Return the body of result (stuff after header).
*
* @return string result body
*/
function fetch_body()
{
return $this->result_body;
}
/**
* Return parsed body in array format.
*
* @return array result parsed
*/
function fetch_parsed_body()
{
parse_str($this->result_body, $x);
return $x;
}
/**
* Set a specific message on how to change the SSL setting, in the event that it's not set correctly.
*/
function set_ssl_setting_message($str)
{
$this->ssl_setting_message = $str;
}
}
@@ -0,0 +1,94 @@
<?php
/**
* domainFACTORY Password Driver
*
* Driver to change passwords with the hosting provider domainFACTORY.
* http://www.df.eu/
*
* @version 2.1
* @author Till Krüss <me@tillkruess.com>
* @link http://tillkruess.com/projects/roundcube/
*
* Copyright (C) The Roundcube Dev Team
*
* This program 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 program 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/.
*/
class rcube_domainfactory_password
{
function save($curpass, $passwd, $username)
{
if ($ch = curl_init()) {
// initial login
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_URL => 'https://ssl.df.eu/chmail.php',
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query([
'login' => $username,
'pwd' => $curpass,
'action' => 'change'
])
]);
if ($result = curl_exec($ch)) {
// login successful, get token!
$postfields = [
'pwd1' => $passwd,
'pwd2' => $passwd,
'action[update]' => 'Speichern'
];
preg_match_all('~<input name="(.+?)" type="hidden" value="(.+?)">~i', $result, $fields);
foreach ($fields[1] as $field_key => $field_name) {
$postfields[$field_name] = $fields[2][$field_key];
}
// change password
$ch = curl_copy_handle($ch);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postfields));
if ($result = curl_exec($ch)) {
// has the password been changed?
if (strpos($result, 'Einstellungen erfolgreich') !== false) {
return PASSWORD_SUCCESS;
}
// show error message(s) if possible
if (strpos($result, '<div class="d-msg-text">') !== false) {
preg_match_all('#<div class="d-msg-text">(.*?)</div>#s', $result, $errors);
if (isset($errors[1])) {
$error_message = '';
foreach ($errors[1] as $error) {
$error_message .= trim(rcube_charset::convert($error, 'ISO-8859-15')).' ';
}
return ['code' => PASSWORD_ERROR, 'message' => $error_message];
}
}
}
else {
return PASSWORD_CONNECT_ERROR;
}
}
else {
return PASSWORD_CONNECT_ERROR;
}
}
else {
return PASSWORD_CONNECT_ERROR;
}
return PASSWORD_ERROR;
}
}
@@ -0,0 +1,91 @@
<?php
/**
* Dovecot passwdfile Password Driver
*
* Driver that adds functionality to change the passwords in dovecot v2 passwd-file files.
* The code is derived from the Plugin examples by The Roundcube Dev Team
*
* On vanilla dovecot v2 environments, use the correct values for these config settings, too:
*
* $config['password_dovecot_passwdfile_path']: The path of your dovecot passwd-file '/path/to/filename'
* $config['password_dovecotpw']: Full path and 'pw' command of doveadm binary - like '/usr/local/bin/doveadm pw'
* $config['password_dovecotpw_method']: Dovecot hashing algo (http://wiki2.dovecot.org/Authentication/PasswordSchemes)
* $config['password_dovecotpw_with_method']: True if you want the hashing algo as prefix in your passwd-file
*
* @version 1.1
*
* Copyright (C) 2017, hostNET Medien GmbH, www.hostnet.de
* Copyright (C) The Roundcube Dev Team
*
* This program 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 program 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/.
*/
class rcube_dovecot_passwdfile_password
{
public function save($currpass, $newpass, $username)
{
$rcmail = rcmail::get_instance();
$mailuserfile = $rcmail->config->get('password_dovecot_passwdfile_path') ?: '/etc/mail/imap.passwd';
$password = password::hash_password($newpass);
$username = escapeshellcmd($username); // FIXME: Do we need this?
$content = '';
// read the entire mail user file
$fp = fopen($mailuserfile, 'r');
if (empty($fp)) {
rcube::raise_error([
'code' => 600, 'file' => __FILE__, 'line' => __LINE__,
'message' => "Password plugin: Unable to read password file $mailuserfile."
],
true, false
);
return PASSWORD_CONNECT_ERROR;
}
if (flock($fp, LOCK_EX)) {
// Read the file and replace the user password
while (($line = fgets($fp, 40960)) !== false) {
if (strpos($line, "$username:") === 0) {
$pos = strpos($line, ':', strlen("$username:") + 1);
$line = "$username:$password" . substr($line, $pos);
}
$content .= $line;
}
// Write back the entire file
if (file_put_contents($mailuserfile, $content)) {
flock($fp, LOCK_UN);
fclose($fp);
return PASSWORD_SUCCESS;
}
}
fclose($fp);
rcube::raise_error([
'code' => 600, 'file' => __FILE__, 'line' => __LINE__,
'message' => "Password plugin: Failed to save file $mailuserfile."
],
true, false
);
return PASSWORD_ERROR;
}
}
@@ -0,0 +1,72 @@
<?php
/**
* expect driver
*
* Driver that adds functionality to change the systems user password via
* the 'expect' command.
*
* For installation instructions please read the README file.
*
* @version 2.0
* @author Andy Theuninck <gohanman@gmail.com)
*
* Based on chpasswd roundcubemail password driver by
* @author Alex Cartwright <acartwright@mutinydesign.co.uk)
* and expect horde passwd driver by
* @author Gaudenz Steinlin <gaudenz@soziologie.ch>
*
* Configuration settings:
* password_expect_bin => location of expect (e.g. /usr/bin/expect)
* password_expect_script => path to "password-expect" file
* password_expect_params => arguments for the expect script
* see the password-expect file for details. This is probably
* a good starting default:
* -telnet -host localhost -output /tmp/passwd.log -log /tmp/passwd.log
*
* Copyright (C) The Roundcube Dev Team
*
* This program 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 program 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/.
*/
class rcube_expect_password
{
public function save($currpass, $newpass, $username)
{
$rcmail = rcmail::get_instance();
$bin = $rcmail->config->get('password_expect_bin');
$script = $rcmail->config->get('password_expect_script');
$params = $rcmail->config->get('password_expect_params');
$cmd = $bin . ' -f ' . $script . ' -- ' . $params;
$handle = popen($cmd, "w");
fwrite($handle, "$username\n");
fwrite($handle, "$currpass\n");
fwrite($handle, "$newpass\n");
if (pclose($handle) == 0) {
return PASSWORD_SUCCESS;
}
rcube::raise_error([
'code' => 600,
'file' => __FILE__,
'line' => __LINE__,
'message' => "Password plugin: Unable to execute $cmd"
], true, false
);
return PASSWORD_ERROR;
}
}
@@ -0,0 +1,70 @@
<?php
/**
* Gearman Password Driver
*
* Payload is json string containing username, oldPassword and newPassword
* Return value is a json string saying result: true if success.
*
* @version 1.0
* @author Mohammad Anwari <mdamt@mdamt.net>
*
* Copyright (C) The Roundcube Dev Team
*
* This program 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 program 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/.
*/
class rcube_gearman_password
{
function save($currpass, $newpass, $username)
{
if (extension_loaded('gearman')) {
$rcmail = rcmail::get_instance();
$payload = [
'username' => $username,
'oldPassword' => $currpass,
'newPassword' => $newpass,
];
$gmc = new GearmanClient();
$gmc->addServer($rcmail->config->get('password_gearman_host', 'localhost'));
$result = $gmc->doNormal('setPassword', json_encode($payload));
$success = json_decode($result);
if ($success && $success->result == 1) {
return PASSWORD_SUCCESS;
}
rcube::raise_error([
'code' => 600,
'file' => __FILE__,
'line' => __LINE__,
'message' => "Password plugin: Gearman authentication failed for user $username"
], true, false
);
}
else {
rcube::raise_error([
'code' => 600,
'file' => __FILE__,
'line' => __LINE__,
'message' => "Password plugin: PECL Gearman module not loaded"
], true, false
);
}
return PASSWORD_ERROR;
}
}
@@ -0,0 +1,76 @@
<?php
/**
* hMailserver password driver
*
* @version 2.0
* @author Roland 'rosali' Liebl <myroundcube@mail4us.net>
*
* Copyright (C) The Roundcube Dev Team
*
* This program 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 program 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/.
*/
class rcube_hmail_password
{
public function save($curpass, $passwd, $username)
{
$rcmail = rcmail::get_instance();
try {
$remote = $rcmail->config->get('hmailserver_remote_dcom', false);
if ($remote) {
$obApp = new COM("hMailServer.Application", $rcmail->config->get('hmailserver_server'));
}
else {
$obApp = new COM("hMailServer.Application");
}
}
catch (Exception $e) {
rcube::write_log('errors', "Plugin password (hmail driver): " . trim(strip_tags($e->getMessage())));
rcube::write_log('errors', "Plugin password (hmail driver): This problem is often caused by DCOM permissions not being set.");
return PASSWORD_ERROR;
}
if (strstr($username,'@')) {
list(, $domain) = explode('@', $username);
}
else {
$domain = $rcmail->config->get('username_domain',false);
if (!$domain) {
rcube::write_log('errors','Plugin password (hmail driver): $config[\'username_domain\'] is not defined.');
return PASSWORD_ERROR;
}
$username = $username . "@" . $domain;
}
try {
$obApp->Authenticate($username, $curpass);
$obDomain = $obApp->Domains->ItemByName($domain);
$obAccount = $obDomain->Accounts->ItemByAddress($username);
$obAccount->Password = $passwd;
$obAccount->Save();
return PASSWORD_SUCCESS;
}
catch (Exception $e) {
rcube::write_log('errors', "Plugin password (hmail driver): " . trim(strip_tags($e->getMessage())));
rcube::write_log('errors', "Plugin password (hmail driver): This problem is often caused by DCOM permissions not being set.");
return PASSWORD_ERROR;
}
}
}
@@ -0,0 +1,139 @@
<?php
/**
* Roundcube password driver for generic HTTP APIs.
*
* This driver changes the e-mail password via any generic HTTP/HTTPS API.
*
* @author David Croft
*
* Copyright (C) The Roundcube Dev Team
*
* Config variables:
* $config['password_httpapi_url'] = 'https://passwordserver.example.org'; // required
* $config['password_httpapi_method'] = 'POST'; // default
* $config['password_httpapi_var_user'] = 'user'; // optional
* $config['password_httpapi_var_curpass'] = 'curpass'; // optional
* $config['password_httpapi_var_newpass'] = 'newpass'; // optional
* $config['password_httpapi_expect'] = '/^ok$/i'; // optional
*
* This program 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 program 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/.
*/
class rcube_httpapi_password
{
/**
* This method is called from roundcube to change the password
*
* roundcube already validated the old password so we just need to change it at this point
*
* @param string $curpass Current password
* @param string $newpass New password
* @param string $username Login username
*
* @return int PASSWORD_SUCCESS|PASSWORD_ERROR|PASSWORD_CONNECT_ERROR
*/
function save($curpass, $newpass, $username)
{
$rcmail = rcmail::get_instance();
$client = password::get_http_client();
// Get configuration with defaults
$url = $rcmail->config->get('password_httpapi_url');
$method = $rcmail->config->get('password_httpapi_method', 'POST');
$var_user = $rcmail->config->get('password_httpapi_var_user');
$var_curpass = $rcmail->config->get('password_httpapi_var_curpass');
$var_newpass = $rcmail->config->get('password_httpapi_var_newpass');
$expect = $rcmail->config->get('password_httpapi_expect');
// Set the variables on the GET query string or POST vars
$vars = [];
if ($var_user) {
$vars[$var_user] = $username;
}
if ($var_curpass) {
$vars[$var_curpass] = $curpass;
}
if ($var_newpass) {
$vars[$var_newpass] = $newpass;
}
$method = strtoupper($method);
$params = [];
if ($method == 'POST') {
$params['form_params'] = $vars;
}
else if ($method == 'GET') {
$params['query'] = $vars;
}
else {
rcube::raise_error([
'code' => 600, 'file' => __FILE__, 'line' => __LINE__,
'message' => "Password plugin: Invalid httpapi method",
],
true, false
);
return PASSWORD_CONNECT_ERROR;
}
try {
$response = $client->request($method, $url, $params);
$response_code = $response->getStatusCode();
$result = $response->getBody();
}
catch (Exception $e) {
rcube::raise_error([
'code' => 600, 'file' => __FILE__, 'line' => __LINE__,
'message' => "Password plugin: " . $e->getMessage()
],
true, false
);
return PASSWORD_CONNECT_ERROR;
}
// Non-2xx response codes mean the password change failed
if ($response_code < 200 || $response_code > 299) {
rcube::raise_error([
'code' => 600, 'file' => __FILE__, 'line' => __LINE__,
'message' => "Password plugin: Unexpected response code {$response_code}: "
. substr($result, 0, 1024)
],
true, false
);
return ($response_code == 404 || $response_code > 499) ? PASSWORD_CONNECT_ERROR : PASSWORD_ERROR;
}
// If configured, check the body of the response
if ($expect && !preg_match($expect, $result)) {
rcube::raise_error([
'code' => 600, 'file' => __FILE__, 'line' => __LINE__,
'message' => "Password plugin: Unexpected response body: " . substr($result, 0, 1024)
],
true, false
);
return PASSWORD_ERROR;
}
return PASSWORD_SUCCESS;
}
}
@@ -0,0 +1,44 @@
<?php
/**
* kpasswd Driver
*
* Driver that adds functionality to change the systems user password via
* the 'kpasswd' command.
*
* For installation instructions please read the README file.
*
* @version 1.0
* @author Peter Allgeyer <peter.allgeyer@salzburgresearch.at>
*
* Based on chpasswd roundcubemail password driver by
* @author Alex Cartwright <acartwright@mutinydesign.co.uk>
*/
class rcube_kpasswd_password
{
public function save($currpass, $newpass, $username)
{
$bin = rcmail::get_instance()->config->get('password_kpasswd_cmd', '/usr/bin/kpasswd');
$cmd = $bin . ' ' . escapeshellarg($username) . ' 2>&1';
$handle = popen($cmd, "w");
fwrite($handle, $currpass."\n");
fwrite($handle, $newpass."\n");
fwrite($handle, $newpass."\n");
if (pclose($handle) == 0) {
return PASSWORD_SUCCESS;
}
rcube::raise_error([
'code' => 600,
'file' => __FILE__,
'line' => __LINE__,
'message' => "Password plugin: Unable to execute $cmd"
], true, false
);
return PASSWORD_ERROR;
}
}
@@ -0,0 +1,202 @@
<?php
/**
* LDAP Password Driver
*
* Driver for passwords stored in LDAP
* This driver use the PEAR Net_LDAP2 class (http://pear.php.net/package/Net_LDAP2).
*
* @version 2.0
* @author Edouard MOREAU <edouard.moreau@ensma.fr>
*
* method hashPassword based on code from the phpLDAPadmin development team (http://phpldapadmin.sourceforge.net/).
* method randomSalt based on code from the phpLDAPadmin development team (http://phpldapadmin.sourceforge.net/).
*
* Copyright (C) The Roundcube Dev Team
*
* This program 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 program 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/.
*/
class rcube_ldap_password
{
public function save($curpass, $passwd)
{
$rcmail = rcmail::get_instance();
require_once 'Net/LDAP2.php';
require_once __DIR__ . '/ldap_simple.php';
// Building user DN
if ($userDN = $rcmail->config->get('password_ldap_userDN_mask')) {
$userDN = rcube_ldap_simple_password::substitute_vars($userDN);
}
else {
$userDN = $this->search_userdn($rcmail);
}
if (empty($userDN)) {
return PASSWORD_CONNECT_ERROR;
}
// Connection Method
switch ($rcmail->config->get('password_ldap_method')) {
case 'admin':
$binddn = $rcmail->config->get('password_ldap_adminDN');
$bindpw = $rcmail->config->get('password_ldap_adminPW');
break;
case 'user':
default:
$binddn = $userDN;
$bindpw = $curpass;
break;
}
// Configuration array
$ldapConfig = [
'binddn' => $binddn,
'bindpw' => $bindpw,
'basedn' => $rcmail->config->get('password_ldap_basedn'),
'host' => $rcmail->config->get('password_ldap_host', 'localhost'),
'port' => $rcmail->config->get('password_ldap_port', '389'),
'starttls' => $rcmail->config->get('password_ldap_starttls'),
'version' => $rcmail->config->get('password_ldap_version', '3'),
];
// Connecting using the configuration array
$ldap = Net_LDAP2::connect($ldapConfig);
// Checking for connection error
if (is_a($ldap, 'PEAR_Error')) {
return PASSWORD_CONNECT_ERROR;
}
$force = $rcmail->config->get('password_ldap_force_replace', true);
$pwattr = $rcmail->config->get('password_ldap_pwattr', 'userPassword');
$lchattr = $rcmail->config->get('password_ldap_lchattr');
$smbpwattr = $rcmail->config->get('password_ldap_samba_pwattr');
$smblchattr = $rcmail->config->get('password_ldap_samba_lchattr');
$samba = $rcmail->config->get('password_ldap_samba');
$encodage = $rcmail->config->get('password_ldap_encodage', 'md5-crypt');
// Support multiple userPassword values where desired.
// multiple encodings can be specified separated by '+' (e.g. "cram-md5+ssha")
$encodages = explode('+', $encodage);
$crypted_pass = [];
foreach ($encodages as $enc) {
if ($cpw = password::hash_password($passwd, $enc)) {
$crypted_pass[] = $cpw;
}
}
// Support password_ldap_samba option for backward compat.
if ($samba && !$smbpwattr) {
$smbpwattr = 'sambaNTPassword';
$smblchattr = 'sambaPwdLastSet';
}
// Crypt new password
if (empty($crypted_pass)) {
return PASSWORD_CRYPT_ERROR;
}
// Crypt new samba password
if ($smbpwattr && !($samba_pass = password::hash_password($passwd, 'samba'))) {
return PASSWORD_CRYPT_ERROR;
}
// Writing new crypted password to LDAP
$userEntry = $ldap->getEntry($userDN);
if (Net_LDAP2::isError($userEntry)) {
return PASSWORD_CONNECT_ERROR;
}
if (!$userEntry->replace([$pwattr => $crypted_pass], $force)) {
return PASSWORD_CONNECT_ERROR;
}
// Updating PasswordLastChange Attribute if desired
if ($lchattr) {
$current_day = (int) (time() / 86400);
if (!$userEntry->replace([$lchattr => $current_day], $force)) {
return PASSWORD_CONNECT_ERROR;
}
}
// Update Samba password and last change fields
if ($smbpwattr) {
$userEntry->replace([$smbpwattr => $samba_pass], $force);
}
// Update Samba password last change field
if ($smblchattr) {
$userEntry->replace([$smblchattr => time()], $force);
}
if (Net_LDAP2::isError($userEntry->update())) {
return PASSWORD_CONNECT_ERROR;
}
// All done, no error
return PASSWORD_SUCCESS;
}
/**
* Bind with searchDN and searchPW and search for the user's DN.
* Use search_base and search_filter defined in config file.
* Return the found DN.
*/
function search_userdn($rcmail)
{
$binddn = $rcmail->config->get('password_ldap_searchDN');
$bindpw = $rcmail->config->get('password_ldap_searchPW');
$ldapConfig = [
'basedn' => $rcmail->config->get('password_ldap_basedn'),
'host' => $rcmail->config->get('password_ldap_host', 'localhost'),
'port' => $rcmail->config->get('password_ldap_port', '389'),
'starttls' => $rcmail->config->get('password_ldap_starttls'),
'version' => $rcmail->config->get('password_ldap_version', '3'),
];
// allow anonymous searches
if (!empty($binddn)) {
$ldapConfig['binddn'] = $binddn;
$ldapConfig['bindpw'] = $bindpw;
}
$ldap = Net_LDAP2::connect($ldapConfig);
if (is_a($ldap, 'PEAR_Error')) {
return '';
}
$base = rcube_ldap_simple_password::substitute_vars($rcmail->config->get('password_ldap_search_base'));
$filter = rcube_ldap_simple_password::substitute_vars($rcmail->config->get('password_ldap_search_filter'));
$options = [
'scope' => 'sub',
'attributes' => [],
];
$result = $ldap->search($base, $filter, $options);
if (is_a($result, 'PEAR_Error') || ($result->count() != 1)) {
$ldap->done();
return '';
}
$userDN = $result->current()->dn();
$ldap->done();
return $userDN;
}
}
@@ -0,0 +1,75 @@
<?php
/**
* LDAP - Password Modify Extended Operation Driver
*
* Driver for passwords stored in LDAP
* This driver is based on Simple LDAP Password Driver, but uses
* Password Modify Extended Operation
* PHP >= 7.2 required
*
* @version 1.0
* @author Peter Kubica <peter@kubica.ch>
*
* Copyright (C) The Roundcube Dev Team
*
* This program 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 program 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/.
*/
require_once __DIR__ . '/ldap_simple.php';
class rcube_ldap_exop_password extends rcube_ldap_simple_password
{
function save($curpass, $passwd)
{
if (!function_exists('ldap_exop_passwd')) {
rcube::raise_error([
'code' => 100, 'type' => 'ldap',
'file' => __FILE__, 'line' => __LINE__,
'message' => "ldap_exop_passwd not supported"
],
true
);
return PASSWORD_ERROR;
}
// Connect and bind
$ret = $this->connect($curpass);
if ($ret !== true) {
return $ret;
}
if (!ldap_exop_passwd($this->conn, $this->user, $curpass, $passwd)) {
$this->_debug("S: ".ldap_error($this->conn));
$errno = ldap_errno($this->conn);
ldap_unbind($this->conn);
if ($errno == 0x13) {
return PASSWORD_CONSTRAINT_VIOLATION;
}
return PASSWORD_CONNECT_ERROR;
}
$this->_debug("S: OK");
// All done, no error
ldap_unbind($this->conn);
return PASSWORD_SUCCESS;
}
}
@@ -0,0 +1,104 @@
<?php
/**
* ldap_ppolicy driver
*
* Driver that adds functionality to change the user password via
* the 'change_ldap_pass.pl' command respecting password policy (history) in LDAP.
*
* @version 1.0
* @author Zbigniew Szmyd <zbigniew.szmyd@linseco.pl>
*/
class rcube_ldap_ppolicy_password
{
protected $debug = false;
public function save($currpass, $newpass, $username)
{
$rcmail = rcmail::get_instance();
$this->debug = $rcmail->config->get('ldap_debug');
$cmd = $rcmail->config->get('password_ldap_ppolicy_cmd');
$uri = $rcmail->config->get('password_ldap_ppolicy_uri');
$baseDN = $rcmail->config->get('password_ldap_ppolicy_basedn');
$filter = $rcmail->config->get('password_ldap_ppolicy_search_filter');
$bindDN = $rcmail->config->get('password_ldap_ppolicy_searchDN');
$bindPW = $rcmail->config->get('password_ldap_ppolicy_searchPW');
$cafile = $rcmail->config->get('password_ldap_ppolicy_cafile');
$log_dir = $rcmail->config->get('log_dir');
if (empty($log_dir)) {
$log_dir = RCUBE_INSTALL_PATH . 'logs';
}
$descriptorspec = [
0 => ["pipe", "r"], // stdin is a pipe that the child will read from
1 => ["pipe", "w"], // stdout is a pipe that the child will write to
2 => ["pipe", "w"] // stderr is a pipe that the child will write to
];
$cmd = 'plugins/password/helpers/'. $cmd;
$this->_debug('Policy request: ' . json_encode([
'user' => $username,
'cmd' => $cmd,
'uri' => $uri,
'baseDN' => $baseDN,
'filter' => $filter,
]));
$process = proc_open($cmd, $descriptorspec, $pipes);
if ($process) {
// $pipes now looks like this:
// 0 => writeable handle connected to child stdin
// 1 => readable handle connected to child stdout
// Any error output will be appended to /tmp/error-output.txt
fwrite($pipes[0], $uri."\n");
fwrite($pipes[0], $baseDN."\n");
fwrite($pipes[0], $filter."\n");
fwrite($pipes[0], $bindDN."\n");
fwrite($pipes[0], $bindPW."\n");
fwrite($pipes[0], $username."\n");
fwrite($pipes[0], $currpass."\n");
fwrite($pipes[0], $newpass."\n");
fwrite($pipes[0], $cafile);
$result = trim(stream_get_contents($pipes[1]));
$stderr = trim(stream_get_contents($pipes[2]));
fclose($pipes[0]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
$this->_debug('Policy result: ' . $result);
switch ($result) {
case "OK":
return PASSWORD_SUCCESS;
case "Password is in history of old passwords":
return PASSWORD_IN_HISTORY;
case "Cannot connect to any server":
return PASSWORD_CONNECT_ERROR;
default:
rcube::raise_error([
'code' => 600, 'file' => __FILE__, 'line' => __LINE__,
'message' => "Password plugin: Failed to execute command: $cmd. Output: $result. Error: $stderr"
], true, false);
}
}
return PASSWORD_ERROR;
}
private function _debug($str)
{
if ($this->debug) {
rcube::write_log('ldap', $str);
}
}
}
@@ -0,0 +1,314 @@
<?php
/**
* Simple LDAP Password Driver
*
* Driver for passwords stored in LDAP
* This driver is based on Edouard's LDAP Password Driver, but does not
* require PEAR's Net_LDAP2 to be installed
*
* @version 2.1
* @author Wout Decre <wout@canodus.be>
* @author Aleksander Machniak <machniak@kolabsys.com>
*
* Copyright (C) The Roundcube Dev Team
*
* This program 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 program 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/.
*/
class rcube_ldap_simple_password
{
protected $debug = false;
protected $user;
protected $conn;
public function save($curpass, $passwd)
{
$rcmail = rcmail::get_instance();
$lchattr = $rcmail->config->get('password_ldap_lchattr');
$pwattr = $rcmail->config->get('password_ldap_pwattr', 'userPassword');
$smbpwattr = $rcmail->config->get('password_ldap_samba_pwattr');
$smblchattr = $rcmail->config->get('password_ldap_samba_lchattr');
$samba = $rcmail->config->get('password_ldap_samba');
$pass_mode = $rcmail->config->get('password_ldap_encodage', 'md5-crypt');
$crypted_pass = password::hash_password($passwd, $pass_mode);
// Support password_ldap_samba option for backward compat.
if ($samba && !$smbpwattr) {
$smbpwattr = 'sambaNTPassword';
$smblchattr = 'sambaPwdLastSet';
}
// Crypt new password
if (!$crypted_pass) {
return PASSWORD_CRYPT_ERROR;
}
// Crypt new Samba password
if ($smbpwattr && !($samba_pass = password::hash_password($passwd, 'samba'))) {
return PASSWORD_CRYPT_ERROR;
}
// Connect and bind
$ret = $this->connect($curpass);
if ($ret !== true) {
return $ret;
}
$entry[$pwattr] = $crypted_pass;
// Update PasswordLastChange Attribute if desired
if ($lchattr) {
$entry[$lchattr] = (int)(time() / 86400);
}
// Update Samba password
if ($smbpwattr) {
$entry[$smbpwattr] = $samba_pass;
}
// Update Samba password last change
if ($smblchattr) {
$entry[$smblchattr] = time();
}
$this->_debug("C: Modify {$this->user}: " . print_r($entry, true));
if (!ldap_modify($this->conn, $this->user, $entry)) {
$this->_debug("S: ".ldap_error($this->conn));
$errno = ldap_errno($this->conn);
ldap_unbind($this->conn);
if ($errno == 0x13) {
return PASSWORD_CONSTRAINT_VIOLATION;
}
return PASSWORD_CONNECT_ERROR;
}
$this->_debug("S: OK");
// All done, no error
ldap_unbind($this->conn);
return PASSWORD_SUCCESS;
}
/**
* Connect and bind to LDAP server
*/
function connect($curpass)
{
$rcmail = rcmail::get_instance();
$this->debug = $rcmail->config->get('ldap_debug');
$ldap_host = $rcmail->config->get('password_ldap_host', 'localhost');
$ldap_port = $rcmail->config->get('password_ldap_port', '389');
$ldap_uri = $this->_host2uri($ldap_host, $ldap_port);
$this->_debug("C: Connect [{$ldap_uri}]");
// Connect
if (!($ds = ldap_connect($ldap_uri))) {
$this->_debug("S: NOT OK");
rcube::raise_error([
'code' => 100, 'type' => 'ldap',
'file' => __FILE__, 'line' => __LINE__,
'message' => "Could not connect to LDAP server"
],
true
);
return PASSWORD_CONNECT_ERROR;
}
$this->_debug("S: OK");
// Set protocol version
ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION,
$rcmail->config->get('password_ldap_version', '3'));
// Start TLS
if ($rcmail->config->get('password_ldap_starttls')) {
if (!ldap_start_tls($ds)) {
ldap_unbind($ds);
return PASSWORD_CONNECT_ERROR;
}
}
// other plugins might want to modify user DN
$plugin = $rcmail->plugins->exec_hook('password_ldap_bind',
['user_dn' => '', 'conn' => $ds]
);
// Build user DN
if (!empty($plugin['user_dn'])) {
$user_dn = $plugin['user_dn'];
}
else if ($user_dn = $rcmail->config->get('password_ldap_userDN_mask')) {
$user_dn = self::substitute_vars($user_dn);
}
else {
$user_dn = $this->search_userdn($rcmail, $ds);
}
if (empty($user_dn)) {
ldap_unbind($ds);
return PASSWORD_CONNECT_ERROR;
}
// Connection method
switch ($rcmail->config->get('password_ldap_method')) {
case 'admin':
$binddn = $rcmail->config->get('password_ldap_adminDN');
$bindpw = $rcmail->config->get('password_ldap_adminPW');
break;
case 'user':
default:
$binddn = $user_dn;
$bindpw = $curpass;
break;
}
$this->_debug("C: Bind $binddn, pass: **** [" . strlen($bindpw) . "]");
// Bind
if (!ldap_bind($ds, $binddn, $bindpw)) {
$this->_debug("S: ".ldap_error($ds));
ldap_unbind($ds);
return PASSWORD_CONNECT_ERROR;
}
$this->_debug("S: OK");
$this->conn = $ds;
$this->user = $user_dn;
return true;
}
/**
* Bind with searchDN and searchPW and search for the user's DN
* Use search_base and search_filter defined in config file
* Return the found DN
*/
function search_userdn($rcmail, $ds)
{
$search_user = $rcmail->config->get('password_ldap_searchDN');
$search_pass = $rcmail->config->get('password_ldap_searchPW');
$search_base = $rcmail->config->get('password_ldap_search_base');
$search_filter = $rcmail->config->get('password_ldap_search_filter');
if (empty($search_filter)) {
return false;
}
$this->_debug("C: Bind " . ($search_user ? $search_user : '[anonymous]'));
// Bind
if (!ldap_bind($ds, $search_user, $search_pass)) {
$this->_debug("S: ".ldap_error($ds));
return false;
}
$this->_debug("S: OK");
$search_base = self::substitute_vars($search_base);
$search_filter = self::substitute_vars($search_filter);
$this->_debug("C: Search $search_base for $search_filter");
// Search for the DN
if (!($sr = ldap_search($ds, $search_base, $search_filter))) {
$this->_debug("S: ".ldap_error($ds));
return false;
}
$found = ldap_count_entries($ds, $sr);
$this->_debug("S: OK [found $found records]");
// If no or more entries were found, return false
if ($found != 1) {
return false;
}
return ldap_get_dn($ds, ldap_first_entry($ds, $sr));
}
/**
* Substitute %login, %name, %domain, %dc in $str
* See plugin config for details
*/
public static function substitute_vars($str)
{
$str = str_replace('%login', $_SESSION['username'], $str);
$str = str_replace('%l', $_SESSION['username'], $str);
$parts = explode('@', $_SESSION['username']);
if (count($parts) == 2) {
$dc = 'dc='.strtr($parts[1], ['.' => ',dc=']); // hierarchal domain string
$str = str_replace('%name', $parts[0], $str);
$str = str_replace('%n', $parts[0], $str);
$str = str_replace('%dc', $dc, $str);
$str = str_replace('%domain', $parts[1], $str);
$str = str_replace('%d', $parts[1], $str);
}
else if (count($parts) == 1) {
$str = str_replace('%name', $parts[0], $str);
$str = str_replace('%n', $parts[0], $str);
}
return $str;
}
/**
* Prints debug info to the log
*/
protected function _debug($str)
{
if ($this->debug) {
rcube::write_log('ldap', $str);
}
}
/**
* Convert LDAP host/port into URI
*/
private static function _host2uri($host, $port = null)
{
if (stripos($host, 'ldapi://') === 0) {
return $host;
}
if (strpos($host, '://') === false) {
$host = ($port == 636 ? 'ldaps' : 'ldap') . '://' . $host;
}
if ($port && !preg_match('/:[0-9]+$/', $host)) {
$host .= ':' . $port;
}
return $host;
}
}
@@ -0,0 +1,83 @@
<?php
/**
* Mailcow Password Driver
*
* @version 1.0
* @author Lukas "Hexaris" Matula <hexaris@gbely.net>
*
* This program 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 program 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/.
*
* It is necessary to set the following variables in plugin/password/config.inc.php
* $config['password_driver'] = 'mailcow';
* $config['password_mailcow_api_host'] = '';
* $config['password_mailcow_api_token'] = '';
*/
class rcube_mailcow_password
{
function save($curpass, $passwd, $username)
{
$rcmail = rcmail::get_instance();
$host = $rcmail->config->get('password_mailcow_api_host');
$token = $rcmail->config->get('password_mailcow_api_token');
try {
$client = password::get_http_client();
$headers = [
'X-API-Key' => $token,
'accept' => 'application/json'
];
$cowdata = [
'attr' => [
'password' => $passwd,
'password2' => $passwd
],
'items' => [ $username ]
];
if (!strpos($host, '://')) {
$host = "https://{$host}";
}
$response = $client->post("{$host}/api/v1/edit/mailbox", [
'headers' => $headers,
'json' => $cowdata
]);
$cowreply = json_decode($response->getBody(),true);
if ($cowreply[0]['type'] == 'success') {
return PASSWORD_SUCCESS;
}
return PASSWORD_ERROR;
}
catch (Exception $e) {
$result = $e->getMessage();
}
rcube::raise_error([
'code' => 600, 'file' => __FILE__, 'line' => __LINE__,
'message' => "Password plugin: Problem with Mailcow API: $result",
],
true, false
);
return PASSWORD_CONNECT_ERROR;
}
}
@@ -0,0 +1,78 @@
<?php
/**
* Mail-in-a-Box Driver
*
* Driver that adds functionality to change the user password via the
* API endpoint of Mail-in-a-Box (https://mailinabox.email/).
*
* For installation instructions please read the README file. It requires
* following parameters in configuration:
*
* - password_miab_username - name of the admin user used to access api
* - password_miab_password - password of the admin user used to access api
* - password_miab_url - the url to the control panel of Mail-in-a-Box
*
* @version 1.0
* @author Alexey Shtokalo <alexey@shtokalo.net>
*
* Copyright (C) The Roundcube Dev Team
*
* This program 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 program 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/.
*/
class rcube_miab_password
{
public function save($currpass, $newpass, $username)
{
$config = rcmail::get_instance()->config;
$host = rtrim($config->get('password_miab_url'), '/') . '/mail/users/password';
try {
$client = password::get_http_client();
$request = [
'form_params' => [
'email' => $username,
'password' => $newpass,
],
'auth' => [
$config->get('password_miab_user') ?: $username,
$config->get('password_miab_pass') ?: $currpass,
],
];
$response = $client->post($host, $request);
if (
$response->getStatusCode() == 200
&& trim($result = $response->getBody()) === 'OK'
) {
return PASSWORD_SUCCESS;
}
}
catch (Exception $e) {
$result = $e->getMessage();
}
rcube::raise_error([
'code' => 600, 'file' => __FILE__, 'line' => __LINE__,
'message' => "Password plugin: Unable to change password. $result",
],
true, false
);
return PASSWORD_ERROR;
}
}
@@ -0,0 +1,120 @@
<?php
/**
* Modoboa Password Driver
*
* Payload is json string containing username, oldPassword and newPassword
* Return value is a json string saying result: true if success.
*
* @version 1.0.1
* @author stephane @actionweb.fr
*
* Copyright (C) The Roundcube Dev Team
*
* This program 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 program 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/.
*
* The driver need modoboa core 1.10.6 or later
*
* You need to define theses variables in plugin/password/config.inc.php
*
* $config['password_driver'] = 'modoboa'; // use modoboa as driver
* $config['password_modoboa_api_token'] = ''; // put token number from Modoboa server
* $config['password_minimum_length'] = 8; // select same number as in Modoboa server
*/
class rcube_modoboa_password
{
function save($curpass, $passwd)
{
// Init config access
$rcmail = rcmail::get_instance();
$ModoboaToken = $rcmail->config->get('password_modoboa_api_token');
$RoudCubeUsername = $_SESSION['username'];
$IMAPhost = $_SESSION['imap_host'];
// Call GET to fetch values from modoboa server
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://" . $IMAPhost . "/api/v1/accounts/?search=" . urlencode($RoudCubeUsername),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Token " . $ModoboaToken,
"Cache-Control: no-cache",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
return PASSWORD_CONNECT_ERROR;
}
// Decode json string
$decoded = json_decode($response);
if (!is_array($decoded)) {
return PASSWORD_CONNECT_ERROR;
}
// Get user ID (pk)
$userid = $decoded[0]->pk;
// Encode json with new password
$ret['username'] = $decoded[0]->username;
$ret['mailbox'] = $decoded[0]->mailbox;
$ret['role'] = $decoded[0]->role;
$ret['password'] = $passwd; // new password
$encoded = json_encode($ret);
// Call HTTP API Modoboa
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://" . $IMAPhost . "/api/v1/accounts/" . $userid . "/",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PUT",
CURLOPT_POSTFIELDS => "" . $encoded . "",
CURLOPT_HTTPHEADER => [
"Authorization: Token " . $ModoboaToken,
"Cache-Control: no-cache",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
return PASSWORD_CONNECT_ERROR;
}
return PASSWORD_SUCCESS;
}
}
@@ -0,0 +1,59 @@
<?php
/**
* PAM Password Driver
*
* @version 2.0
* @author Aleksander Machniak
*
* Copyright (C) The Roundcube Dev Team
*
* This program 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 program 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/.
*/
class rcube_pam_password
{
function save($currpass, $newpass, $username)
{
$error = '';
if (extension_loaded('pam') || extension_loaded('pam_auth')) {
if (pam_auth($username, $currpass, $error, false)) {
if (pam_chpass($username, $currpass, $newpass)) {
return PASSWORD_SUCCESS;
}
}
else {
rcube::raise_error([
'code' => 600,
'file' => __FILE__,
'line' => __LINE__,
'message' => "Password plugin: PAM authentication failed for user $username: $error"
], true, false
);
}
}
else {
rcube::raise_error([
'code' => 600,
'file' => __FILE__,
'line' => __LINE__,
'message' => "Password plugin: PECL-PAM module not loaded"
], true, false
);
}
return PASSWORD_ERROR;
}
}
@@ -0,0 +1,260 @@
<?php
/**
* Roundcube Password Driver for Plesk-RPC.
*
* This driver changes a E-Mail-Password via Plesk-RPC
* Deps: PHP-Curl, SimpleXML
*
* @author Cyrill von Wattenwyl <cyrill.vonwattenwyl@adfinis-sygroup.ch>
* @copyright Adfinis SyGroup AG, 2014
*
* Config needed:
* $config['password_plesk_host'] = '10.0.0.5';
* $config['password_plesk_user'] = 'admin';
* $config['password_plesk_pass'] = 'pass';
* $config['password_plesk_rpc_port'] = 8443;
* $config['password_plesk_rpc_path'] = enterprise/control/agent.php;
*
* This program 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 program 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/.
*/
/**
* Roundcube Password Driver Class
*
* See {ROUNDCUBE_ROOT}/plugins/password/README for API description
*
* @author Cyrill von Wattenwyl <cyrill.vonwattenwyl@adfinis-sygroup.ch>
*/
class rcube_plesk_password
{
/**
* This method is called from roundcube to change the password
*
* roundcube already validated the old password so we just need to change it at this point
*
* @author Cyrill von Wattenwyl <cyrill.vonwattenwyl@adfinis-sygroup.ch>
* @param string $curpass Current password
* @param string $newpass New password
* @returns int PASSWORD_SUCCESS|PASSWORD_ERROR
*/
function save($currpass, $newpass, $username)
{
// get config
$rcmail = rcmail::get_instance();
$host = $rcmail->config->get('password_plesk_host');
$user = $rcmail->config->get('password_plesk_user');
$pass = $rcmail->config->get('password_plesk_pass');
$port = $rcmail->config->get('password_plesk_rpc_port');
$path = $rcmail->config->get('password_plesk_rpc_path');
// create plesk-object
$plesk = new plesk_rpc;
$plesk->init($host, $port, $path, $user, $pass);
// try to change password and return the status
$result = $plesk->change_mailbox_password($username, $newpass);
//$plesk->destroy();
if ($result == "ok") {
return PASSWORD_SUCCESS;
} elseif (is_array($result)) {
return $result;
}
return PASSWORD_ERROR;
}
}
/**
* Plesk RPC-Class
*
* Striped down version of Plesk-RPC-Class
* Just functions for changing mail-passwords included
*
* Documentation of Plesk RPC-API: http://download1.parallels.com/Plesk/PP11/11.0/Doc/en-US/online/plesk-api-rpc/
*
* @author Cyrill von Wattenwyl <cyrill.vonwattenwyl@adfinis-sygroup.ch>
*/
class plesk_rpc
{
protected $old_version = false;
protected $curl;
/**
* init plesk-rpc via curl
*
* @param string $host plesk host
* @param string $port plesk rpc port
* @param string $path plesk rpc path
* @param string $user plesk user
* @param string $user plesk password
* @returns void
*/
function init($host, $port, $path, $user, $pass)
{
$headers = [
sprintf("HTTP_AUTH_LOGIN: %s", $user),
sprintf("HTTP_AUTH_PASSWD: %s", $pass),
"Content-Type: text/xml"
];
$url = sprintf("https://%s:%s/%s", $host, $port, $path);
$this->curl = curl_init();
curl_setopt($this->curl, CURLOPT_CONNECTTIMEOUT , 5);
curl_setopt($this->curl, CURLOPT_SSL_VERIFYHOST , 0);
curl_setopt($this->curl, CURLOPT_SSL_VERIFYPEER , false);
curl_setopt($this->curl, CURLOPT_HTTPHEADER , $headers);
curl_setopt($this->curl, CURLOPT_URL , $url);
}
/**
* send a request to the plesk
*
* @param string $packet XML-Packet to send to Plesk
*
* @returns string Response body
*/
function send_request($packet)
{
curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($this->curl, CURLOPT_POSTFIELDS, $packet);
$result = curl_exec($this->curl);
return $result && strpos($result, '<?xml') === 0 ? $result : null;
}
/**
* close curl
*/
function destroy()
{
curl_close($this->curl);
}
/**
* Get all hosting-information of a domain
*
* @param string $domain domain-name
* @returns object SimpleXML object
*/
function domain_info($domain)
{
// build xml
$request = new SimpleXMLElement("<packet></packet>");
$site = $request->addChild("site");
$get = $site->addChild("get");
$filter = $get->addChild("filter");
$filter->addChild("name", utf8_encode($domain));
$dataset = $get->addChild("dataset");
$dataset->addChild("hosting");
$packet = $request->asXML();
$xml = null;
// send the request and make it to simple-xml-object
if ($res = $this->send_request($packet)) {
$xml = new SimpleXMLElement($res);
}
// Old Plesk versions require version attribute, add it and try again
if ($xml && $xml->site->get->result->status == 'error' && $xml->site->get->result->errcode == 1017) {
$request->addAttribute("version", "1.6.3.0");
$packet = $request->asXML();
$this->old_version = true;
// send the request and make it to simple-xml-object
if ($res = $this->send_request($packet)) {
$xml = new SimpleXMLElement($res);
}
}
return $xml;
}
/**
* Get psa-id of a domain
*
* @param string $domain domain-name
*
* @returns int Domain ID
*/
function get_domain_id($domain)
{
if ($xml = $this->domain_info($domain)) {
return intval($xml->site->get->result->id);
}
}
/**
* Change Password of a mailbox
*
* @param string $mailbox full email-address (user@domain.tld)
* @param string $newpass new password of mailbox
*
* @returns bool
*/
function change_mailbox_password($mailbox, $newpass)
{
list($user, $domain) = explode("@", $mailbox);
$domain_id = $this->get_domain_id($domain);
// if domain cannot be resolved to an id, do not continue
if (!$domain_id) {
return false;
}
// build xml-packet
$request = new SimpleXMLElement("<packet></packet>");
$mail = $request->addChild("mail");
$update = $mail->addChild("update");
$add = $update->addChild("set");
$filter = $add->addChild("filter");
$filter->addChild("site-id", $domain_id);
$mailname = $filter->addChild("mailname");
$mailname->addChild("name", $user);
$password = $mailname->addChild("password");
$password->addChild("value", $newpass);
$password->addChild("type", "plain");
if ($this->old_version) {
$request->addAttribute("version", "1.6.3.0");
}
$packet = $request->asXML();
// send the request to plesk
if ($res = $this->send_request($packet)) {
$xml = new SimpleXMLElement($res);
$res = strval($xml->mail->update->set->result->status);
if ($res != "ok") {
$res = [
'code' => PASSWORD_ERROR,
'message' => strval($xml->mail->update->set->result->errtext)
];
}
return $res;
}
return false;
}
}
@@ -0,0 +1,86 @@
<?php
/**
* Poppassd Password Driver
*
* Driver to change passwords via Poppassd/Courierpassd
*
* @version 2.0
* @author Philip Weir
*
* Copyright (C) The Roundcube Dev Team
*
* This program 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 program 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/.
*/
class rcube_poppassd_password
{
function format_error_result($code, $line)
{
if (preg_match('/^\d\d\d\s+(\S.*)\s*$/', $line, $matches)) {
return ['code' => $code, 'message' => $matches[1]];
}
return $code;
}
function save($curpass, $passwd, $username)
{
$rcmail = rcmail::get_instance();
$poppassd = new Net_Socket();
$port = $rcmail->config->get('password_pop_port', 106);
$host = $rcmail->config->get('password_pop_host', 'localhost');
$host = rcube_utils::parse_host($host);
$result = $poppassd->connect($host, $port, null);
if (is_a($result, 'PEAR_Error')) {
return $this->format_error_result(PASSWORD_CONNECT_ERROR, $result->getMessage());
}
$result = $poppassd->readLine();
if (!preg_match('/^2\d\d/', $result)) {
$poppassd->disconnect();
return $this->format_error_result(PASSWORD_ERROR, $result);
}
$poppassd->writeLine("user ". $username);
$result = $poppassd->readLine();
if (!preg_match('/^[23]\d\d/', $result)) {
$poppassd->disconnect();
return $this->format_error_result(PASSWORD_CONNECT_ERROR, $result);
}
$poppassd->writeLine("pass ". $curpass);
$result = $poppassd->readLine();
if (!preg_match('/^[23]\d\d/', $result)) {
$poppassd->disconnect();
return $this->format_error_result(PASSWORD_ERROR, $result);
}
$poppassd->writeLine("newpass ". $passwd);
$result = $poppassd->readLine();
$poppassd->disconnect();
if (!preg_match('/^2\d\d/', $result)) {
return $this->format_error_result(PASSWORD_ERROR, $result);
}
return PASSWORD_SUCCESS;
}
}
@@ -0,0 +1,55 @@
<?php
/**
* pw_usermod Driver
*
* Driver that adds functionality to change the systems user password via
* the 'pw usermod' command.
*
* For installation instructions please read the README file.
*
* @version 2.0
* @author Alex Cartwright <acartwright@mutinydesign.co.uk>
* @author Adamson Huang <adomputer@gmail.com>
*
* Copyright (C) The Roundcube Dev Team
*
* This program 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 program 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/.
*/
class rcube_pw_usermod_password
{
public function save($currpass, $newpass, $username)
{
$cmd = rcmail::get_instance()->config->get('password_pw_usermod_cmd', 'sudo /usr/sbin/pw usermod -h 0 -n');
$cmd .= ' ' . escapeshellarg($username) . ' > /dev/null';
$handle = popen($cmd, 'w');
fwrite($handle, "$newpass\n");
if (pclose($handle) == 0) {
return PASSWORD_SUCCESS;
}
rcube::raise_error([
'code' => 600,
'file' => __FILE__,
'line' => __LINE__,
'message' => "Password plugin: Unable to execute $cmd"
], true, false
);
return PASSWORD_ERROR;
}
}
@@ -0,0 +1,226 @@
<?php
/**
* Have I Been Pwned Password Strength Driver
*
* Driver to check passwords using HIBP:
* https://haveibeenpwned.com/Passwords
*
* This driver will return a strength of:
* 3: if the password WAS NOT found in HIBP
* 1: if the password WAS found in HIBP
* 2: if there was an ERROR retrieving data.
*
* To use this driver, configure (in ../config.inc.php):
*
* $config['password_strength_driver'] = 'pwned';
* $config['password_minimum_score'] = 3;
*
* Set the minimum score to 3 if you want to make sure that all
* passwords are successfully checked against HIBP (recommended).
*
* Set it to 2 if you still want to accept passwords in case a
* HIBP check fails for some (technical) reason.
*
* Setting the minimum score to 1 or less effectively renders
* the checks useless, as all passwords would be accepted.
* Setting it to 4 or more will effectively reject all passwords.
*
* This driver will only return a maximum score of 3 because not
* being listed in HIBP does not necessarily mean that the
* password is a good one. It is therefore recommended to also
* configure a minimum length for the password.
*
* Background reading (don't worry, your passwords are not sent anywhere):
* https://www.troyhunt.com/ive-just-launched-pwned-passwords-version-2/#cloudflareprivacyandkanonymity
*
* @version 1.0
* @author Christoph Langguth
*
* Copyright (C) The Roundcube Dev Team
*
* This program 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 program 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/.
*/
class rcube_pwned_password
{
// API URL. Note: the trailing slash is mandatory.
const API_URL = 'https://api.pwnedpasswords.com/range/';
// See https://www.troyhunt.com/enhancing-pwned-passwords-privacy-with-padding/
const ENHANCED_PRIVACY_CURL = 1;
// Score constants, these directly correspond to the score that is returned.
const SCORE_LISTED = 1;
const SCORE_ERROR = 2;
const SCORE_NOT_LISTED = 3;
/**
* Rule description.
*
* @return array human-readable description of the check rule.
*/
function strength_rules()
{
$rc = rcmail::get_instance();
$href = 'https://haveibeenpwned.com/Passwords';
return [$rc->gettext(['name' => 'password.pwned_mustnotbedisclosed', 'vars' => ['href' => $href]])];
}
/**
* Password strength check.
* Return values:
* 1 - if password is definitely compromised.
* 2 - if status for password can't be determined (network failures etc.)
* 3 - if password is not publicly known to be compromised.
*
* @param string $passwd Password
*
* @return array password score (1 to 3) and (optional) reason message
*/
function check_strength($passwd)
{
$score = $this->check_pwned($passwd);
$message = null;
if ($score !== self::SCORE_NOT_LISTED) {
$rc = rcmail::get_instance();
if ($score === self::SCORE_LISTED) {
$message = $rc->gettext('password.pwned_isdisclosed');
}
else {
$message = $rc->gettext('password.pwned_fetcherror');
}
}
return [$score, $message];
}
/**
* Check password using HIBP.
*
* @param string $passwd
*
* @return int score, one of the SCORE_* constants (between 1 and 3).
*/
function check_pwned($passwd)
{
// initialize with error score
$result = self::SCORE_ERROR;
if (!$this->can_retrieve()) {
// Log the fact that we cannot check because of configuration error.
rcube::raise_error("Need curl or allow_url_fopen to check password strength with 'pwned'", true, true);
}
else {
list($prefix, $suffix) = $this->hash_split($passwd);
$suffixes = $this->retrieve_suffixes(self::API_URL . $prefix);
if ($suffixes) {
$result = $this->check_suffix_in_list($suffix, $suffixes);
}
}
return $result;
}
function hash_split($passwd)
{
$hash = strtolower(sha1($passwd));
$prefix = substr($hash, 0, 5);
$suffix = substr($hash, 5);
return [$prefix, $suffix];
}
function can_retrieve()
{
return $this->can_curl() || $this->can_fopen();
}
function can_curl()
{
return function_exists('curl_init');
}
function can_fopen()
{
return ini_get('allow_url_fopen');
}
function retrieve_suffixes($url)
{
if ($this->can_curl()) {
return $this->retrieve_curl($url);
}
else {
return $this->retrieve_fopen($url);
}
}
function retrieve_curl($url)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
if (self::ENHANCED_PRIVACY_CURL == 1) {
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Add-Padding: true']);
}
$output = curl_exec($ch);
curl_close($ch);
return $output;
}
function retrieve_fopen($url)
{
$output = '';
$ch = fopen($url, 'r');
while (!feof($ch)) {
$output .= fgets($ch);
}
fclose($ch);
return $output;
}
function check_suffix_in_list($candidate, $list)
{
// initialize to error in case there are no lines at all
$result = self::SCORE_ERROR;
foreach (preg_split('/[\r\n]+/', $list) as $line) {
$line = strtolower($line);
if (preg_match('/^([0-9a-f]{35}):(\d+)$/', $line, $matches)) {
if ($matches[2] > 0 && $matches[1] === $candidate) {
// more than 0 occurrences, and suffix matches
// -> password is compromised
return self::SCORE_LISTED;
}
// valid line, not matching the current password
$result = self::SCORE_NOT_LISTED;
}
else {
// invalid line
return self::SCORE_ERROR;
}
}
return $result;
}
}
@@ -0,0 +1,61 @@
<?php
/**
* SASL Password Driver
*
* Driver that adds functionality to change the users Cyrus/SASL password.
* The code is derived from the Squirrelmail "Change SASL Password" Plugin
* by Galen Johnson.
*
* It only works with saslpasswd2 on the same host where Roundcube runs
* and requires shell access and gcc in order to compile the binary.
*
* For installation instructions please read the README file.
*
* @version 2.0
* @author Thomas Bruederli
*
* Copyright (C) The Roundcube Dev Team
*
* This program 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 program 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/.
*/
class rcube_sasl_password
{
function save($currpass, $newpass, $username)
{
$curdir = RCUBE_PLUGINS_DIR . 'password/helpers';
$username = escapeshellarg($username);
$args = rcmail::get_instance()->config->get('password_saslpasswd_args', '');
if ($fh = popen("$curdir/chgsaslpasswd -p $args $username", 'w')) {
fwrite($fh, $newpass."\n");
$code = pclose($fh);
if ($code == 0) {
return PASSWORD_SUCCESS;
}
}
rcube::raise_error([
'code' => 600,
'file' => __FILE__,
'line' => __LINE__,
'message' => "Password plugin: Unable to execute $curdir/chgsaslpasswd"
], true, false
);
return PASSWORD_ERROR;
}
}
@@ -0,0 +1,72 @@
<?php
/**
* smb Driver
*
* Driver that adds functionality to change the systems user password via
* the 'smbpasswd' command.
*
* For installation instructions please read the README file.
*
* @version 2.0
* @author Andy Theuninck <gohanman@gmail.com)
*
* Based on chpasswd roundcubemail password driver by
* @author Alex Cartwright <acartwright@mutinydesign.co.uk)
* and smbpasswd horde passwd driver by
* @author Rene Lund Jensen <Rene@lundjensen.net>
*
* Configuration settings:
* password_smb_host => samba host (default: localhost)
* password_smb_cmd => smbpasswd binary (default: /usr/bin/smbpasswd)
*
* Copyright (C) The Roundcube Dev Team
*
* This program 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 program 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/.
*/
class rcube_smb_password
{
public function save($currpass, $newpass, $username)
{
$host = rcmail::get_instance()->config->get('password_smb_host', 'localhost');
$bin = rcmail::get_instance()->config->get('password_smb_cmd', '/usr/bin/smbpasswd');
$host = rcube_utils::parse_host($host);
$tmpfile = tempnam(sys_get_temp_dir(), 'smb');
$cmd = $bin . ' -r ' . escapeshellarg($host) . ' -s -U ' . escapeshellarg($username) . ' > ' . $tmpfile . ' 2>&1';
$handle = @popen($cmd, 'w');
fwrite($handle, $currpass."\n");
fwrite($handle, $newpass."\n");
fwrite($handle, $newpass."\n");
@pclose($handle);
$res = file($tmpfile);
unlink($tmpfile);
if (strstr($res[count($res) - 1], 'Password changed for user') !== false) {
return PASSWORD_SUCCESS;
}
rcube::raise_error([
'code' => 600,
'file' => __FILE__,
'line' => __LINE__,
'message' => "Password plugin: Unable to execute $cmd"
], true, false
);
return PASSWORD_ERROR;
}
}
@@ -0,0 +1,158 @@
<?php
/**
* SQL Password Driver
*
* Driver for passwords stored in SQL database
*
* @version 2.1
* @author Aleksander Machniak <alec@alec.pl>
*
* Copyright (C) The Roundcube Dev Team
*
* This program 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 program 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/.
*/
class rcube_sql_password
{
/**
* Update current user password
*
* @param string $curpass Current password
* @param string $passwd New password
*
* @return int Result
*/
function save($curpass, $passwd)
{
$rcmail = rcmail::get_instance();
if (!($sql = $rcmail->config->get('password_query'))) {
$sql = 'SELECT update_passwd(%P, %u)';
}
if ($dsn = $rcmail->config->get('password_db_dsn')) {
$db = rcube_db::factory(self::parse_dsn($dsn), '', false);
$db->set_debug((bool)$rcmail->config->get('sql_debug'));
}
else {
$db = $rcmail->get_dbh();
}
if ($db->is_error()) {
return PASSWORD_ERROR;
}
// new password - default hash method
if (strpos($sql, '%P') !== false) {
$password = password::hash_password($passwd);
if ($password === false) {
return PASSWORD_CRYPT_ERROR;
}
$sql = str_replace('%P', $db->quote($password), $sql);
}
// old password - default hash method
if (strpos($sql, '%O') !== false) {
$password = password::hash_password($curpass);
if ($password === false) {
return PASSWORD_CRYPT_ERROR;
}
$sql = str_replace('%O', $db->quote($password), $sql);
}
// Handle clear text passwords securely (#1487034)
$sql_vars = [];
if (preg_match_all('/%[p|o]/', $sql, $m)) {
foreach ($m[0] as $var) {
if ($var == '%p') {
$sql = preg_replace('/%p/', '?', $sql, 1);
$sql_vars[] = (string) $passwd;
}
else { // %o
$sql = preg_replace('/%o/', '?', $sql, 1);
$sql_vars[] = (string) $curpass;
}
}
}
$local_part = $rcmail->user->get_username('local');
$domain_part = $rcmail->user->get_username('domain');
$username = $_SESSION['username'];
$host = $_SESSION['imap_host'];
// convert domains to/from punycode
if ($rcmail->config->get('password_idn_ascii')) {
$domain_part = rcube_utils::idn_to_ascii($domain_part);
$username = rcube_utils::idn_to_ascii($username);
$host = rcube_utils::idn_to_ascii($host);
}
else {
$domain_part = rcube_utils::idn_to_utf8($domain_part);
$username = rcube_utils::idn_to_utf8($username);
$host = rcube_utils::idn_to_utf8($host);
}
// at least we should always have the local part
$sql = str_replace('%l', $db->quote($local_part, 'text'), $sql);
$sql = str_replace('%d', $db->quote($domain_part, 'text'), $sql);
$sql = str_replace('%u', $db->quote($username, 'text'), $sql);
$sql = str_replace('%h', $db->quote($host, 'text'), $sql);
$res = $db->query($sql, $sql_vars);
if (!$db->is_error()) {
if (strtolower(substr(trim($sql),0,6)) == 'select') {
if ($db->fetch_array($res)) {
return PASSWORD_SUCCESS;
}
}
else {
// Note: Don't be tempted to check affected_rows = 1. For some queries
// (e.g. INSERT ... ON DUPLICATE KEY UPDATE) the result can be 2.
if ($db->affected_rows($res) > 0) {
return PASSWORD_SUCCESS;
}
}
}
return PASSWORD_ERROR;
}
/**
* Parse DSN string and replace host variables
*
* @param string $dsn DSN string
*
* @return string DSN string
*/
protected static function parse_dsn($dsn)
{
if (strpos($dsn, '%')) {
// parse DSN and replace variables in hostname
$parsed = rcube_db::parse_dsn($dsn);
$host = rcube_utils::parse_host($parsed['hostspec']);
// build back the DSN string
if ($host != $parsed['hostspec']) {
$dsn = str_replace('@' . $parsed['hostspec'], '@' . $host, $dsn);
}
}
return $dsn;
}
}
@@ -0,0 +1,67 @@
<?php
/**
* TinyCP driver
*
* Enable the password driver in Roundcube (https://roundcube.net/) for the
* TinyCP Lightweight Linux Control Panel (https://tinycp.com/).
* See README for instructions, Connector Required.
*
* @version 1.2
* @author Ricky Mendoza (HelloWorld@rickymendoza.dev)
*
* Copyright (C) 2020 Ricky Mendoza
*
* This program 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 program 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 <https://www.gnu.org/licenses/>.
*/
class rcube_tinycp_password
{
public function save($currpass, $newpass, $username)
{
require_once 'TinyCPConnector.php';
$tinycp_host = rcmail::get_instance()->config->get('password_tinycp_host');
$tinycp_port = rcmail::get_instance()->config->get('password_tinycp_port');
$tinycp_user = rcmail::get_instance()->config->get('password_tinycp_user');
$tinycp_pass = rcmail::get_instance()->config->get('password_tinycp_pass');
$error_message = '';
if ($tinycp_host && $tinycp_port && $tinycp_user && $tinycp_pass) {
try {
$tcp = new TinyCPConnector($tinycp_host, $tinycp_port);
$tcp->Auth($tinycp_user, $tinycp_pass);
$tcp->mail___mailserver___email_pass_change2($username, $newpass);
}
catch (Exception $e) {
$error_message = $e->getMessage();
}
}
else {
$error_message = "Missing configuration value(s). ";
}
if ($error_message) {
rcube::raise_error([
'code' => 600, 'file' => __FILE__, 'line' => __LINE__,
'message' => "Password driver: $error_message",
],
true, false
);
return PASSWORD_ERROR;
}
return PASSWORD_SUCCESS;
}
}
@@ -0,0 +1,77 @@
<?php
/**
* Virtualmin Password Driver
*
* Driver that adds functionality to change the users Virtualmin password.
* The code is derived from the Squirrelmail "Change Cyrus/SASL Password" Plugin
* by Thomas Bruederli.
*
* It only works with virtualmin on the same host where Roundcube runs
* and requires shell access and gcc in order to compile the binary.
*
* @version 3.0
* @author Martijn de Munnik
*
* Copyright (C) The Roundcube Dev Team
*
* This program 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 program 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/.
*/
class rcube_virtualmin_password
{
function save($currpass, $newpass, $username)
{
$curdir = RCUBE_PLUGINS_DIR . 'password/helpers';
$username = escapeshellarg($username);
// Get the domain using virtualmin CLI:
exec("$curdir/chgvirtualminpasswd list-domains --mail-user $username --name-only", $output_domain, $returnvalue);
if ($returnvalue == 0 && count($output_domain) == 1) {
$domain = trim($output_domain[0]);
}
else {
rcube::raise_error([
'code' => 600,
'file' => __FILE__,
'line' => __LINE__,
'message' => "Password plugin: Unable to execute $curdir/chgvirtualminpasswd "
. "or domain for mail-user '$username' not known to Virtualmin"
], true, false
);
return PASSWORD_ERROR;
}
$domain = escapeshellarg($domain);
$newpass = escapeshellarg($newpass);
exec("$curdir/chgvirtualminpasswd modify-user --domain $domain --user $username --pass $newpass", $output, $returnvalue);
if ($returnvalue == 0) {
return PASSWORD_SUCCESS;
}
rcube::raise_error([
'code' => 600,
'file' => __FILE__,
'line' => __LINE__,
'message' => "Password plugin: Unable to execute $curdir/chgvirtualminpasswd"
], true, false
);
return PASSWORD_ERROR;
}
}
@@ -0,0 +1,71 @@
<?php
/**
* vpopmail Password Driver
*
* Driver to change passwords via vpopmaild
*
* @version 2.0
* @author Johannes Hessellund
*
* Copyright (C) The Roundcube Dev Team
*
* This program 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 program 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/.
*/
class rcube_vpopmaild_password
{
function save($curpass, $passwd, $username)
{
$rcmail = rcmail::get_instance();
$vpopmaild = new Net_Socket();
$host = $rcmail->config->get('password_vpopmaild_host');
$port = $rcmail->config->get('password_vpopmaild_port');
$result = $vpopmaild->connect($host, $port, null);
if (is_a($result, 'PEAR_Error')) {
return PASSWORD_CONNECT_ERROR;
}
$vpopmaild->setTimeout($rcmail->config->get('password_vpopmaild_timeout'),0);
$result = $vpopmaild->readLine();
if(!preg_match('/^\+OK/', $result)) {
$vpopmaild->disconnect();
return PASSWORD_CONNECT_ERROR;
}
$vpopmaild->writeLine("slogin ". $username . " " . $curpass);
$result = $vpopmaild->readLine();
if(!preg_match('/^\+OK/', $result) ) {
$vpopmaild->writeLine("quit");
$vpopmaild->disconnect();
return PASSWORD_ERROR;
}
$vpopmaild->writeLine("mod_user ". $username);
$vpopmaild->writeLine("clear_text_password ". $passwd);
$vpopmaild->writeLine(".");
$result = $vpopmaild->readLine();
$vpopmaild->writeLine("quit");
$vpopmaild->disconnect();
if (!preg_match('/^\+OK/', $result)) {
return PASSWORD_ERROR;
}
return PASSWORD_SUCCESS;
}
}
@@ -0,0 +1,90 @@
<?php
/**
* Communigate driver for the Password Plugin for Roundcube
*
* Tested with Communigate Pro 5.1.2
*
* Configuration options:
* password_ximss_host - Host name of Communigate server
* password_ximss_port - XIMSS port on Communigate server
*
* References:
* http://www.communigate.com/WebGuide/XMLAPI.html
*
* @version 2.0
* @author Erik Meitner <erik wanderings.us>
*
* Copyright (C) The Roundcube Dev Team
*
* This program 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 program 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/.
*/
class rcube_ximss_password
{
function save($pass, $newpass, $username)
{
$rcmail = rcmail::get_instance();
$host = $rcmail->config->get('password_ximss_host');
$port = $rcmail->config->get('password_ximss_port');
$sock = stream_socket_client("tcp://$host:$port", $errno, $errstr, 30);
if ($sock === false) {
return PASSWORD_CONNECT_ERROR;
}
// send all requests at once(pipelined)
fwrite($sock, '<login id="A001" authData="'.$username.'" password="'.$pass.'" />'."\0");
fwrite($sock, '<passwordModify id="A002" oldPassword="'.$pass.'" newPassword="'.$newpass.'" />'."\0");
fwrite($sock, '<bye id="A003" />'."\0");
//example responses
// <session id="A001" urlID="4815-vN2Txjkggy7gjHRD10jw" userName="user@example.com"/>\0
// <response id="A001"/>\0
// <response id="A002"/>\0
// <response id="A003"/>\0
// or an error:
// <response id="A001" errorText="incorrect password or account name" errorNum="515"/>\0
$responseblob = '';
while (!feof($sock)) {
$responseblob .= fgets($sock, 1024);
}
fclose($sock);
foreach (explode( "\0", $responseblob) as $response) {
$resp = simplexml_load_string("<xml>".$response."</xml>");
$id = $resp && !empty($resp->response[0]['id']) ? $resp->response[0]['id'] : null;
if ($id == 'A001') {
if (isset($resp->response[0]['errorNum'])) {
return PASSWORD_CONNECT_ERROR;
}
}
else if ($id == 'A002') {
if (isset($resp->response[0]['errorNum'])) {
return PASSWORD_ERROR;
}
}
else if ($id == 'A003') {
if (isset($resp->response[0]['errorNum'])) {
// There was a problem during logout (This is probably harmless)
}
}
}
return PASSWORD_SUCCESS;
}
}
@@ -0,0 +1,123 @@
<?php
/**
* XMail Password Driver
*
* Driver for XMail password
*
* @version 2.0
* @author Helio Cavichiolo Jr <helio@hcsistemas.com.br>
*
* Setup xmail_host, xmail_user, xmail_pass and xmail_port into
* config.inc.php of password plugin as follows:
*
* $config['xmail_host'] = 'localhost';
* $config['xmail_user'] = 'YourXmailControlUser';
* $config['xmail_pass'] = 'YourXmailControlPass';
* $config['xmail_port'] = 6017;
*
* Copyright (C) The Roundcube Dev Team
*
* This program 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 program 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/.
*/
class rcube_xmail_password
{
function save($currpass, $newpass)
{
$rcmail = rcmail::get_instance();
list($user, $domain) = explode('@', $_SESSION['username']);
$xmail = new XMail;
$xmail->hostname = $rcmail->config->get('xmail_host');
$xmail->username = $rcmail->config->get('xmail_user');
$xmail->password = $rcmail->config->get('xmail_pass');
$xmail->port = $rcmail->config->get('xmail_port');
if (!$xmail->connect()) {
rcube::raise_error([
'code' => 600,
'file' => __FILE__,
'line' => __LINE__,
'message' => "Password plugin: Unable to connect to mail server"
], true, false
);
return PASSWORD_CONNECT_ERROR;
}
if (!$xmail->send("userpasswd\t".$domain."\t".$user."\t".$newpass."\n")) {
$xmail->close();
rcube::raise_error([
'code' => 600,
'file' => __FILE__,
'line' => __LINE__,
'message' => "Password plugin: Unable to change password"
], true, false
);
return PASSWORD_ERROR;
}
$xmail->close();
return PASSWORD_SUCCESS;
}
}
class XMail {
var $socket;
var $hostname = 'localhost';
var $username = 'xmail';
var $password = '';
var $port = 6017;
function send($msg)
{
socket_write($this->socket,$msg);
if (substr(socket_read($this->socket, 512, PHP_BINARY_READ),0,1) != "+") {
return false;
}
return true;
}
function connect()
{
$this->socket = socket_create(AF_INET, SOCK_STREAM, 0);
if ($this->socket < 0)
return false;
$result = socket_connect($this->socket, $this->hostname, $this->port);
if ($result < 0) {
socket_close($this->socket);
return false;
}
if (substr(socket_read($this->socket, 512, PHP_BINARY_READ),0,1) != "+") {
socket_close($this->socket);
return false;
}
if (!$this->send("$this->username\t$this->password\n")) {
socket_close($this->socket);
return false;
}
return true;
}
function close()
{
$this->send("quit\n");
socket_close($this->socket);
}
}
@@ -0,0 +1,66 @@
<?php
/**
* Zxcvb Password Strength Driver
*
* Driver to check password strength using Zxcvbn-PHP
*
* @version 0.1
* @author Philip Weir
*
* Copyright (C) Philip Weir
*
* This program 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 program 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/.
*/
class rcube_zxcvbn_password
{
function strength_rules()
{
$rcmail = rcmail::get_instance();
$rules = [
$rcmail->gettext('password.passwordnoseq'),
$rcmail->gettext('password.passwordnocommon'),
];
return $rules;
}
/**
* Password strength check
*
* @param string $passwd Password
*
* @return array Score (1 to 5) and Reason
*/
function check_strength($passwd)
{
if (!class_exists('ZxcvbnPhp\Zxcvbn')) {
rcube::raise_error([
'code' => 600,
'file' => __FILE__,
'line' => __LINE__,
'message' => "Password plugin: Zxcvbn library not found."
], true, false
);
return;
}
$zxcvbn = new ZxcvbnPhp\Zxcvbn();
$strength = $zxcvbn->passwordStrength($passwd);
return [$strength['score'] + 1, $strength['feedback']['warning']];
}
}