284 lines
8.2 KiB
PHP
284 lines
8.2 KiB
PHP
<?php
|
|
/**
|
|
* Copyright 2003-2017 Horde LLC (http://www.horde.org/)
|
|
*
|
|
* See the enclosed file COPYING for license information (GPL). If you
|
|
* did not receive this file, see http://www.horde.org/licenses/gpl.
|
|
*
|
|
* @category Horde
|
|
* @copyright 2003-2017 Horde LLC
|
|
* @license http://www.horde.org/licenses/gpl GPL
|
|
* @package Passwd
|
|
*/
|
|
|
|
/**
|
|
* Changes a user's password in a Pine password file.
|
|
*
|
|
* @author Max Kalika <max@horde.org>
|
|
* @category Horde
|
|
* @copyright 2003-2017 Horde LLC
|
|
* @license http://www.horde.org/licenses/gpl GPL
|
|
* @package Passwd
|
|
*/
|
|
class Passwd_Driver_Pine extends Passwd_Driver
|
|
{
|
|
/** Lower boundary character. */
|
|
const FIRSTCH = 0x20;
|
|
|
|
/** Upper boundary character. */
|
|
const LASTCH = 0x7e;
|
|
|
|
/** Median character. */
|
|
const TABSZ = 0x5f;
|
|
|
|
/**
|
|
* Boolean which contains state of the ftp connection.
|
|
*
|
|
* @var boolean
|
|
*/
|
|
protected $_connected = false;
|
|
|
|
/**
|
|
* Contents array of the pine password file.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $_contents = array();
|
|
|
|
/**
|
|
* Horde_Vfs instance.
|
|
*
|
|
* @var VFS
|
|
*/
|
|
protected $_ftp;
|
|
|
|
/**
|
|
*/
|
|
public function __construct(array $params = array())
|
|
{
|
|
self::__construct(array_merge(array(
|
|
/* We self-encrypt here, so plaintext is needed. */
|
|
'encryption' => 'plain',
|
|
'show_encryption' => false,
|
|
|
|
/* Sensible FTP server parameters. */
|
|
'host' => 'localhost',
|
|
'port' => 21,
|
|
'path' => '',
|
|
'file' => '.pinepw',
|
|
|
|
/* Connect to FTP server using just-passed-in credentials?
|
|
* Only useful if using the composite driver and changing
|
|
* system (FTP) password prior to this one. */
|
|
'use_new_passwd' => false,
|
|
|
|
/* What host to look for on each line? */
|
|
'imaphost' => 'localhost'
|
|
), $params));
|
|
}
|
|
|
|
/**
|
|
* Connects to the FTP server.
|
|
*
|
|
* @throws Passwd_Exception
|
|
*/
|
|
protected function _connect($user, $password)
|
|
{
|
|
if ($this->_connected) {
|
|
return;
|
|
}
|
|
|
|
$params = array(
|
|
'username' => $user,
|
|
'password' => $password,
|
|
'hostspec' => $this->_params['host'],
|
|
'port' => $this->_params['port'],
|
|
);
|
|
|
|
try {
|
|
$this->_ftp = Horde_Vfs::factory('ftp', $params);
|
|
$this->_ftp->checkCredentials();
|
|
} catch (Horde_Vfs_Exception $e) {
|
|
throw new Passwd_Exception($e);
|
|
}
|
|
|
|
$this->_connected = true;
|
|
}
|
|
|
|
/**
|
|
* Disconnect from the FTP server.
|
|
*
|
|
* @throws Passwd_Exception
|
|
*/
|
|
protected function _disconnect()
|
|
{
|
|
if ($this->_connected) {
|
|
try {
|
|
$this->_ftp->disconnect();
|
|
} catch (Horde_Vfs_Exception $e) {
|
|
throw new Passwd_Exception($e);
|
|
}
|
|
$this->_connected = false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Decodes a Pine-encoded password string.
|
|
*
|
|
* The algorithm is borrowed from read_passfile() and xlate_out()
|
|
* functions in pine/imap.c file distributed in the Pine source archive.
|
|
*
|
|
* @param string $string The contents of a pine-encoded password file.
|
|
*
|
|
* @return array List of lines of decoded elements.
|
|
*/
|
|
protected function _decode($string)
|
|
{
|
|
$list = array();
|
|
|
|
$lines = explode("\n", $string);
|
|
for ($n = 0; $n < sizeof($lines); $n++) {
|
|
$key = $n;
|
|
$tmp = $lines[$n];
|
|
for ($i = 0; $i < strlen($tmp); $i++) {
|
|
if ((ord($tmp[$i]) >= self::FIRSTCH) &&
|
|
(ord($tmp[$i]) <= self::LASTCH)) {
|
|
$xch = ord($tmp[$i]) - ($dti = $key);
|
|
$xch += ($xch < self::FIRSTCH - self::TABSZ)
|
|
? 2 * self::TABSZ
|
|
: ($xch < self::FIRSTCH) ? self::TABSZ : 0;
|
|
$dti = ($xch - self::FIRSTCH) + $dti;
|
|
$dti -= ($dti >= 2 * self::TABSZ)
|
|
? 2 * self::TABSZ
|
|
: ($dti >= self::TABSZ) ? self::TABSZ : 0;
|
|
$key = $dti;
|
|
$tmp[$i] = chr($xch);
|
|
}
|
|
}
|
|
|
|
if ($i && $tmp[$i - 1] == "\n") {
|
|
$tmp = substr($tmp, 0, -1);
|
|
}
|
|
|
|
$parts = explode("\t", $tmp);
|
|
if (count($parts) >= 4) {
|
|
$list[] = $parts;
|
|
}
|
|
}
|
|
|
|
return $list;
|
|
}
|
|
|
|
/**
|
|
* Encodes an array of elements into a Pine-readable password string.
|
|
*
|
|
* The algorithm is borrowed from write_passfile() and xlate_in()
|
|
* functions in pine/imap.c file distributed in the Pine source archive.
|
|
*
|
|
* @param array $lines List of lines of decoded elements.
|
|
*
|
|
* @return array Contents of a pine-readable password file.
|
|
*/
|
|
protected function _encode($lines)
|
|
{
|
|
$string = '';
|
|
for ($n = 0; $n < sizeof($lines); $n++) {
|
|
if (isset($lines[$n][4])) {
|
|
$lines[$n][4] = "\t" . $lines[$n][4];
|
|
} else {
|
|
$lines[$n][4] = '';
|
|
}
|
|
|
|
$key = $n;
|
|
$tmp = vsprintf("%.100s\t%.100s\t%.100s\t%d%s\n", $lines[$n]);
|
|
for ($i = 0; $i < strlen($tmp); $i++) {
|
|
$eti = $key;
|
|
if ((ord($tmp[$i]) >= self::FIRSTCH) &&
|
|
(ord($tmp[$i]) <= self::LASTCH)) {
|
|
$eti += ord($tmp[$i]) - self::FIRSTCH;
|
|
$eti -= ($eti >= 2 * self::TABSZ)
|
|
? 2 * self::TABSZ
|
|
: ($eti >= self::TABSZ) ? self::TABSZ : 0;
|
|
$key = $eti;
|
|
$tmp[$i] = chr($eti + self::FIRSTCH);
|
|
}
|
|
}
|
|
|
|
$string .= $tmp;
|
|
}
|
|
|
|
return $string;
|
|
}
|
|
|
|
/**
|
|
* Finds out if a username and password is valid.
|
|
*
|
|
* @param string $user The userID to check.
|
|
* @param string $oldPassword An old password to check.
|
|
*
|
|
* @throws Passwd_Exception
|
|
*/
|
|
protected function _lookup($user, $oldPassword)
|
|
{
|
|
try {
|
|
$contents = $this->_ftp->read($this->_params['path'],
|
|
$this->_params['file']);
|
|
} catch (Horde_Vfs_Exception $e) {
|
|
throw new Passwd_Exception($e);
|
|
}
|
|
|
|
$this->_contents = $this->_decode($contents);
|
|
foreach ($this->_contents as $line) {
|
|
if ($line[1] == $user &&
|
|
(($line[2] == $this->_params['imaphost']) ||
|
|
(!empty($line[4]) && $line[4] == $this->_params['imaphost']))) {
|
|
$this->_comparePasswords($line[0], $oldPassword);
|
|
return;
|
|
}
|
|
}
|
|
|
|
throw new Passwd_Exception(_("User not found."));
|
|
}
|
|
|
|
/**
|
|
* Modifies a pine password record for a user.
|
|
*
|
|
* @param string $user The user whose record we will udpate.
|
|
* @param string $newPassword The new password value to set.
|
|
*
|
|
* @throws Passwd_Exception
|
|
*/
|
|
protected function _modify($user, $newPassword)
|
|
{
|
|
for ($i = 0; $i < sizeof($this->_contents); $i++) {
|
|
if ($this->_contents[$i][1] == $user &&
|
|
(($this->_contents[$i][2] == $this->_params['imaphost']) ||
|
|
(!empty($this->_contents[$i][4]) &&
|
|
$this->_contents[$i][4] == $this->_params['imaphost']))) {
|
|
$this->_contents[$i][0] = $newPassword;
|
|
}
|
|
}
|
|
|
|
$string = $this->_encode($this->_contents);
|
|
try {
|
|
$this->_ftp->writeData($this->_params['path'],
|
|
$this->_params['file'],
|
|
$string);
|
|
} catch (Horde_Vfs_Exception $e) {
|
|
throw new Passwd_Exception($e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
*/
|
|
protected function _changePassword($user, $oldpass, $newpass)
|
|
{
|
|
/* Connect to the ftp server. */
|
|
$this->_connect($user, $this->_params['use_new_passwd'] ? $newpass : $oldpass);
|
|
$this->_lookup($user, $oldpass);
|
|
$this->_modify($user, $newpass);
|
|
$this->_disconnect();
|
|
}
|
|
|
|
}
|