1<?php
2/**
3 * Copyright 2000-2017 Horde LLC (http://www.horde.org/)
4 *
5 * See the enclosed file COPYING for license information (GPL). If you
6 * did not receive this file, see http://www.horde.org/licenses/gpl.
7 *
8 * @category  Horde
9 * @copyright 2000-2017 Horde LLC
10 * @license   http://www.horde.org/licenses/gpl GPL
11 * @package   Passwd
12 */
13
14/**
15 * Changes a password via the SITE PSWD command used by Serv-u ftpd for
16 * Windows.
17 *
18 * @author    Lucas Nelan (screen@brainkrash.com)
19 * @category  Horde
20 * @copyright 2000-2017 Horde LLC
21 * @license   http://www.horde.org/licenses/gpl GPL
22 * @package   Passwd
23 */
24class Passwd_Driver_Servuftp extends Passwd_Driver
25{
26    const CONNECTED   = '220';
27    const GOODBYE     = '221';
28    const PASSWORDOK  = '230';
29    const USERNAMEOK  = '331';
30    const PASSWORDBAD = '530';
31
32    /**
33     */
34    protected $_fp;
35
36    /**
37     */
38    public function __construct(array $params = array())
39    {
40        if (empty($params['host']) || empty($params['port'])) {
41            throw new Passwd_Exception(_("Password module is missing required parameters."));
42        }
43
44        parent::__construct(array_merge(array(
45            'timeout' => 30
46        ), $params));
47    }
48
49    /**
50     */
51    protected function _changePassword($user, $oldpass, $newpass)
52    {
53        if ($this->_connect() != self::CONNECTED) {
54            throw new Passwd_Exception(_("Connection failed"));
55        }
56
57        if ($this->_sendCommand('user', $user) != self::USERNAMEOK) {
58            $this->_disconnect();
59            throw new Passwd_Exception(_("Unknown user"));
60        }
61
62        if ($this->_sendCommand('pass', $oldpass) != self::PASSWORDOK) {
63            $this->_disconnect();
64            throw new Passwd_Exception(_("Incorrect password"));
65        }
66
67        if ($this->_sendCommand('site pswd', '"' . $oldpass. '" "' . $newpass. '"') != self::PASSWORDOK) {
68            $this->_disconnect();
69            throw new Passwd_Exception(_("Cannot change password"));
70        }
71
72        $this->_disconnect();
73    }
74
75    /**
76     * @throws Passwd_Exception
77     */
78    protected function _connect()
79    {
80        $this->_fp = fsockopen(
81            $this->_params['host'],
82            $this->_params['port'],
83            $errno,
84            $errstr,
85            $this->_params['timeout']
86        );
87        if (!$this->_fp) {
88            throw new Passwd_Exception($errstr);
89        }
90        return $this->_getPrompt();
91    }
92
93    /**
94     * @throws Passwd_Exception
95     */
96    protected function _disconnect()
97    {
98        if ($this->_fp) {
99            fputs($this->_fp, "quit\n");
100            fclose($this->_fp);
101        }
102    }
103
104    /**
105     * @throws Passwd_Exception
106     */
107    protected function _getPrompt()
108    {
109        $prompt = fgets($this->_fp, 4096);
110
111        if (preg_match('/^[1-5][0-9][0-9]/', $prompt, $res)) {
112            return $res[1];
113        }
114    }
115
116    /**
117     * @throws Passwd_Exception
118     */
119    protected function _sendCommand($cmd, $arg)
120    {
121        $line = $cmd . ' ' . $arg . "\r\n";
122        fputs($this->_fp, $line);
123        return $this->_getPrompt();
124    }
125
126}
127