1<?php
2
3/**
4 * Licensed to Jasig under one or more contributor license
5 * agreements. See the NOTICE file distributed with this work for
6 * additional information regarding copyright ownership.
7 *
8 * Jasig licenses this file to you under the Apache License,
9 * Version 2.0 (the "License"); you may not use this file except in
10 * compliance with the License. You may obtain a copy of the License at:
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 *
20 * PHP Version 7
21 *
22 * @file     CAS/ProxiedService/Imap.php
23 * @category Authentication
24 * @package  PhpCAS
25 * @author   Adam Franco <afranco@middlebury.edu>
26 * @license  http://www.apache.org/licenses/LICENSE-2.0  Apache License 2.0
27 * @link     https://wiki.jasig.org/display/CASC/phpCAS
28 */
29
30/**
31 * Provides access to a proxy-authenticated IMAP stream
32 *
33 * @class    CAS_ProxiedService_Imap
34 * @category Authentication
35 * @package  PhpCAS
36 * @author   Adam Franco <afranco@middlebury.edu>
37 * @license  http://www.apache.org/licenses/LICENSE-2.0  Apache License 2.0
38 * @link     https://wiki.jasig.org/display/CASC/phpCAS
39 */
40class CAS_ProxiedService_Imap
41extends CAS_ProxiedService_Abstract
42{
43
44    /**
45     * The username to send via imap_open.
46     *
47     * @var string $_username;
48     */
49    private $_username;
50
51    /**
52     * Constructor.
53     *
54     * @param string $username Username
55     *
56     * @return void
57     */
58    public function __construct ($username)
59    {
60        if (!is_string($username) || !strlen($username)) {
61            throw new CAS_InvalidArgumentException('Invalid username.');
62        }
63
64        $this->_username = $username;
65    }
66
67    /**
68     * The target service url.
69     * @var string $_url;
70     */
71    private $_url;
72
73    /**
74     * Answer a service identifier (URL) for whom we should fetch a proxy ticket.
75     *
76     * @return string
77     * @throws Exception If no service url is available.
78     */
79    public function getServiceUrl ()
80    {
81        if (empty($this->_url)) {
82            throw new CAS_ProxiedService_Exception(
83                'No URL set via '.get_class($this).'->getServiceUrl($url).'
84            );
85        }
86
87        return $this->_url;
88    }
89
90    /*********************************************************
91     * Configure the Stream
92    *********************************************************/
93
94    /**
95     * Set the URL of the service to pass to CAS for proxy-ticket retrieval.
96     *
97     * @param string $url Url to set
98     *
99     * @return void
100     * @throws CAS_OutOfSequenceException If called after the stream has been opened.
101     */
102    public function setServiceUrl ($url)
103    {
104        if ($this->hasBeenOpened()) {
105            throw new CAS_OutOfSequenceException(
106                'Cannot set the URL, stream already opened.'
107            );
108        }
109        if (!is_string($url) || !strlen($url)) {
110            throw new CAS_InvalidArgumentException('Invalid url.');
111        }
112
113        $this->_url = $url;
114    }
115
116    /**
117     * The mailbox to open. See the $mailbox parameter of imap_open().
118     *
119     * @var string $_mailbox
120     */
121    private $_mailbox;
122
123    /**
124     * Set the mailbox to open. See the $mailbox parameter of imap_open().
125     *
126     * @param string $mailbox Mailbox to set
127     *
128     * @return void
129     * @throws CAS_OutOfSequenceException If called after the stream has been opened.
130     */
131    public function setMailbox ($mailbox)
132    {
133        if ($this->hasBeenOpened()) {
134            throw new CAS_OutOfSequenceException(
135                'Cannot set the mailbox, stream already opened.'
136            );
137        }
138        if (!is_string($mailbox) || !strlen($mailbox)) {
139            throw new CAS_InvalidArgumentException('Invalid mailbox.');
140        }
141
142        $this->_mailbox = $mailbox;
143    }
144
145    /**
146     * A bit mask of options to pass to imap_open() as the $options parameter.
147     *
148     * @var int $_options
149     */
150    private $_options = null;
151
152    /**
153     * Set the options for opening the stream. See the $options parameter of
154     * imap_open().
155     *
156     * @param int $options Options for the stream
157     *
158     * @return void
159     * @throws CAS_OutOfSequenceException If called after the stream has been opened.
160     */
161    public function setOptions ($options)
162    {
163        if ($this->hasBeenOpened()) {
164            throw new CAS_OutOfSequenceException(
165                'Cannot set options, stream already opened.'
166            );
167        }
168        if (!is_int($options)) {
169            throw new CAS_InvalidArgumentException('Invalid options.');
170        }
171
172        $this->_options = $options;
173    }
174
175    /*********************************************************
176     * 2. Open the stream
177    *********************************************************/
178
179    /**
180     * Open the IMAP stream (similar to imap_open()).
181     *
182     * @return resource Returns an IMAP stream on success
183     * @throws CAS_OutOfSequenceException If called multiple times.
184     * @throws CAS_ProxyTicketException If there is a proxy-ticket failure.
185     *		The code of the Exception will be one of:
186     *			PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE
187     *			PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE
188     *			PHPCAS_SERVICE_PT_FAILURE
189     * @throws CAS_ProxiedService_Exception If there is a failure sending the
190     *         request to the target service.
191     */
192    public function open ()
193    {
194        if ($this->hasBeenOpened()) {
195            throw new CAS_OutOfSequenceException('Stream already opened.');
196        }
197        if (empty($this->_mailbox)) {
198            throw new CAS_ProxiedService_Exception(
199                'You must specify a mailbox via '.get_class($this)
200                .'->setMailbox($mailbox)'
201            );
202        }
203
204        phpCAS::traceBegin();
205
206        // Get our proxy ticket and append it to our URL.
207        $this->initializeProxyTicket();
208        phpCAS::trace('opening IMAP mailbox `'.$this->_mailbox.'\'...');
209        $this->_stream = @imap_open(
210            $this->_mailbox, $this->_username, $this->getProxyTicket(),
211            $this->_options
212        );
213        if ($this->_stream) {
214            phpCAS::trace('ok');
215        } else {
216            phpCAS::trace('could not open mailbox');
217            // @todo add localization integration.
218            $message = 'IMAP Error: '.$this->_url.' '. var_export(imap_errors(), true);
219            phpCAS::trace($message);
220            throw new CAS_ProxiedService_Exception($message);
221        }
222
223        phpCAS::traceEnd();
224        return $this->_stream;
225    }
226
227    /**
228     * Answer true if our request has been sent yet.
229     *
230     * @return bool
231     */
232    protected function hasBeenOpened ()
233    {
234        return !empty($this->_stream);
235    }
236
237    /*********************************************************
238     * 3. Access the result
239    *********************************************************/
240    /**
241     * The IMAP stream
242     *
243     * @var resource $_stream
244     */
245    private $_stream;
246
247    /**
248     * Answer the IMAP stream
249     *
250     * @return resource
251     * @throws CAS_OutOfSequenceException if stream is not opened yet
252     */
253    public function getStream ()
254    {
255        if (!$this->hasBeenOpened()) {
256            throw new CAS_OutOfSequenceException(
257                'Cannot access stream, not opened yet.'
258            );
259        }
260        return $this->_stream;
261    }
262
263    /**
264     * CAS_Client::serviceMail() needs to return the proxy ticket for some reason,
265     * so this method provides access to it.
266     *
267     * @return string
268     * @throws CAS_OutOfSequenceException If called before the stream has been
269     * opened.
270     */
271    public function getImapProxyTicket ()
272    {
273        if (!$this->hasBeenOpened()) {
274            throw new CAS_OutOfSequenceException(
275                'Cannot access errors, stream not opened yet.'
276            );
277        }
278        return $this->getProxyTicket();
279    }
280}
281?>
282