1<?php
2/**
3 * Copyright 2006-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 2006-2017 Horde LLC
10 * @license   http://www.horde.org/licenses/gpl GPL
11 * @package   Passwd
12 */
13
14/**
15 * A PECL expect implementation of the Passwd system.
16 *
17 * Copyright 2006-2017 Horde LLC (http://www.horde.org/)
18 *
19 * See the enclosed file COPYING for license information (GPL). If you
20 * did not receive this file, see http://www.horde.org/licenses/gpl.
21 *
22 * @author    Duck <duck@obala.net>
23 * @category  Horde
24 * @copyright 2006-2017 Horde LLC
25 * @license   http://www.horde.org/licenses/gpl GPL
26 * @package   Passwd
27 */
28class Passwd_Driver_Expectpecl extends Passwd_Driver
29{
30    /**
31     * Expect connection handle.
32     *
33     * @var resource
34     */
35    protected $_stream;
36
37    /**
38     * Handles expect communication.
39     *
40     * @param string $expect  String to expect.
41     * @param string $error   Error message.
42     *
43     * @throws Passwd_Exception
44     */
45    protected function _ctl($expect, $error)
46    {
47        $result = expect_expectl($this->_stream, array(
48            array(
49                0 => $expect,
50                1 => 'ok',
51                2 => EXP_REGEXP
52            )
53        ));
54
55        switch ($result) {
56        case EXP_EOF:
57            throw new Passwd_Exception(_("End of file."));
58
59        case EXP_TIMEOUT:
60            throw new Passwd_Exception(_("Time out."));
61
62        case EXP_FULLBUFFER:
63            throw new Passwd_Exception(_("Full buffer."));
64
65        case 'ok':
66            return;
67
68        default:
69            throw new Passwd_Exception($error);
70        }
71    }
72
73    /**
74     */
75    protected function _changePassword($user, $oldpass, $newpass)
76    {
77        if (!Horde_Util::loadExtension('expect')) {
78            throw new Passwd_Exception(_("expect extension cannot be loaded"));
79        }
80
81        // Set up parameters
82        foreach (array('logfile', 'loguser', 'timeout') as $val) {
83            if (isset($this->_params[$val])) {
84                ini_set('expect.' . $val, $this->_params[$val]);
85            }
86        }
87
88        // Open connection
89        $call = sprintf(
90            'ssh %s@%s %s',
91            $user,
92            $this->_params['host'],
93            $this->_params['program']
94        );
95        if (!($this->_stream = expect_popen($call))) {
96            throw new Passwd_Exception(_("Unable to open expect stream"));
97        }
98
99        // Log in
100        $this->_ctl(
101            '(P|p)assword.*',
102            _("Could not login to system (no password prompt)")
103        );
104
105        // Send login password
106        fwrite($this->_stream, $oldpass . "\n");
107
108        // Expect old password prompt
109        $this->_ctl(
110            '((O|o)ld|login|current).* (P|p)assword.*',
111            _("Could not start passwd program (no old password prompt)")
112        );
113
114        // Send old password
115        fwrite($this->_stream, $oldpass . "\n");
116
117        // Expect new password prompt
118        $this->_ctl(
119            '(N|n)ew.* (P|p)assword.*',
120            _("Could not change password (bad old password?)")
121        );
122
123        // Send new password
124        fwrite($this->_stream, $newpass . "\n");
125
126        // Expect reenter password prompt
127        $this->_ctl(
128            "((R|r)e-*enter.*(P|p)assword|Retype new( UNIX)? password|(V|v)erification|(V|v)erify|(A|a)gain).*",
129            _("New password not valid (too short, bad password, too similar, ...)")
130        );
131
132        // Send new password
133        fwrite($this->_stream, $newpass . "\n");
134
135        // Expect successfully message
136        $this->_ctl(
137            '((P|p)assword.* changed|successfully)',
138            _("Could not change password.")
139        );
140    }
141
142}
143