1: <?php
2: /**
3: * PHPMailer POP-Before-SMTP Authentication Class.
4: * PHP Version 5
5: * @package PHPMailer
6: * @link https://github.com/PHPMailer/PHPMailer/
7: * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
8: * @author Jim Jagielski (jimjag) <jimjag@gmail.com>
9: * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
10: * @author Brent R. Matzelle (original founder)
11: * @copyright 2012 - 2014 Marcus Bointon
12: * @copyright 2010 - 2012 Jim Jagielski
13: * @copyright 2004 - 2009 Andy Prevost
14: * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
15: * @note This program is distributed in the hope that it will be useful - WITHOUT
16: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17: * FITNESS FOR A PARTICULAR PURPOSE.
18: */
19:
20: /**
21: * PHPMailer POP-Before-SMTP Authentication Class.
22: * Specifically for PHPMailer to use for RFC1939 POP-before-SMTP authentication.
23: * Does not support APOP.
24: * @package PHPMailer
25: * @author Richard Davey (original author) <rich@corephp.co.uk>
26: * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
27: * @author Jim Jagielski (jimjag) <jimjag@gmail.com>
28: * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
29: */
30: class POP3
31: {
32: /**
33: * The POP3 PHPMailer Version number.
34: * @var string
35: * @access public
36: */
37: public $Version = '5.2.28';
38:
39: /**
40: * Default POP3 port number.
41: * @var integer
42: * @access public
43: */
44: public $POP3_PORT = 110;
45:
46: /**
47: * Default timeout in seconds.
48: * @var integer
49: * @access public
50: */
51: public $POP3_TIMEOUT = 30;
52:
53: /**
54: * POP3 Carriage Return + Line Feed.
55: * @var string
56: * @access public
57: * @deprecated Use the constant instead
58: */
59: public $CRLF = "\r\n";
60:
61: /**
62: * Debug display level.
63: * Options: 0 = no, 1+ = yes
64: * @var integer
65: * @access public
66: */
67: public $do_debug = 0;
68:
69: /**
70: * POP3 mail server hostname.
71: * @var string
72: * @access public
73: */
74: public $host;
75:
76: /**
77: * POP3 port number.
78: * @var integer
79: * @access public
80: */
81: public $port;
82:
83: /**
84: * POP3 Timeout Value in seconds.
85: * @var integer
86: * @access public
87: */
88: public $tval;
89:
90: /**
91: * POP3 username
92: * @var string
93: * @access public
94: */
95: public $username;
96:
97: /**
98: * POP3 password.
99: * @var string
100: * @access public
101: */
102: public $password;
103:
104: /**
105: * Resource handle for the POP3 connection socket.
106: * @var resource
107: * @access protected
108: */
109: protected $pop_conn;
110:
111: /**
112: * Are we connected?
113: * @var boolean
114: * @access protected
115: */
116: protected $connected = false;
117:
118: /**
119: * Error container.
120: * @var array
121: * @access protected
122: */
123: protected $errors = array();
124:
125: /**
126: * Line break constant
127: */
128: const CRLF = "\r\n";
129:
130: /**
131: * Simple static wrapper for all-in-one POP before SMTP
132: * @param $host
133: * @param integer|boolean $port The port number to connect to
134: * @param integer|boolean $timeout The timeout value
135: * @param string $username
136: * @param string $password
137: * @param integer $debug_level
138: * @return boolean
139: */
140: public static function popBeforeSmtp(
141: $host,
142: $port = false,
143: $timeout = false,
144: $username = '',
145: $password = '',
146: $debug_level = 0
147: ) {
148: $pop = new POP3;
149: return $pop->authorise($host, $port, $timeout, $username, $password, $debug_level);
150: }
151:
152: /**
153: * Authenticate with a POP3 server.
154: * A connect, login, disconnect sequence
155: * appropriate for POP-before SMTP authorisation.
156: * @access public
157: * @param string $host The hostname to connect to
158: * @param integer|boolean $port The port number to connect to
159: * @param integer|boolean $timeout The timeout value
160: * @param string $username
161: * @param string $password
162: * @param integer $debug_level
163: * @return boolean
164: */
165: public function authorise($host, $port = false, $timeout = false, $username = '', $password = '', $debug_level = 0)
166: {
167: $this->host = $host;
168: // If no port value provided, use default
169: if (false === $port) {
170: $this->port = $this->POP3_PORT;
171: } else {
172: $this->port = (integer)$port;
173: }
174: // If no timeout value provided, use default
175: if (false === $timeout) {
176: $this->tval = $this->POP3_TIMEOUT;
177: } else {
178: $this->tval = (integer)$timeout;
179: }
180: $this->do_debug = $debug_level;
181: $this->username = $username;
182: $this->password = $password;
183: // Reset the error log
184: $this->errors = array();
185: // connect
186: $result = $this->connect($this->host, $this->port, $this->tval);
187: if ($result) {
188: $login_result = $this->login($this->username, $this->password);
189: if ($login_result) {
190: $this->disconnect();
191: return true;
192: }
193: }
194: // We need to disconnect regardless of whether the login succeeded
195: $this->disconnect();
196: return false;
197: }
198:
199: /**
200: * Connect to a POP3 server.
201: * @access public
202: * @param string $host
203: * @param integer|boolean $port
204: * @param integer $tval
205: * @return boolean
206: */
207: public function connect($host, $port = false, $tval = 30)
208: {
209: // Are we already connected?
210: if ($this->connected) {
211: return true;
212: }
213:
214: //On Windows this will raise a PHP Warning error if the hostname doesn't exist.
215: //Rather than suppress it with @fsockopen, capture it cleanly instead
216: set_error_handler(array($this, 'catchWarning'));
217:
218: if (false === $port) {
219: $port = $this->POP3_PORT;
220: }
221:
222: // connect to the POP3 server
223: $this->pop_conn = fsockopen(
224: $host, // POP3 Host
225: $port, // Port #
226: $errno, // Error Number
227: $errstr, // Error Message
228: $tval
229: ); // Timeout (seconds)
230: // Restore the error handler
231: restore_error_handler();
232:
233: // Did we connect?
234: if (false === $this->pop_conn) {
235: // It would appear not...
236: $this->setError(array(
237: 'error' => "Failed to connect to server $host on port $port",
238: 'errno' => $errno,
239: 'errstr' => $errstr
240: ));
241: return false;
242: }
243:
244: // Increase the stream time-out
245: stream_set_timeout($this->pop_conn, $tval, 0);
246:
247: // Get the POP3 server response
248: $pop3_response = $this->getResponse();
249: // Check for the +OK
250: if ($this->checkResponse($pop3_response)) {
251: // The connection is established and the POP3 server is talking
252: $this->connected = true;
253: return true;
254: }
255: return false;
256: }
257:
258: /**
259: * Log in to the POP3 server.
260: * Does not support APOP (RFC 2828, 4949).
261: * @access public
262: * @param string $username
263: * @param string $password
264: * @return boolean
265: */
266: public function login($username = '', $password = '')
267: {
268: if (!$this->connected) {
269: $this->setError('Not connected to POP3 server');
270: }
271: if (empty($username)) {
272: $username = $this->username;
273: }
274: if (empty($password)) {
275: $password = $this->password;
276: }
277:
278: // Send the Username
279: $this->sendString("USER $username" . self::CRLF);
280: $pop3_response = $this->getResponse();
281: if ($this->checkResponse($pop3_response)) {
282: // Send the Password
283: $this->sendString("PASS $password" . self::CRLF);
284: $pop3_response = $this->getResponse();
285: if ($this->checkResponse($pop3_response)) {
286: return true;
287: }
288: }
289: return false;
290: }
291:
292: /**
293: * Disconnect from the POP3 server.
294: * @access public
295: */
296: public function disconnect()
297: {
298: $this->sendString('QUIT');
299: //The QUIT command may cause the daemon to exit, which will kill our connection
300: //So ignore errors here
301: try {
302: @fclose($this->pop_conn);
303: } catch (Exception $e) {
304: //Do nothing
305: };
306: }
307:
308: /**
309: * Get a response from the POP3 server.
310: * $size is the maximum number of bytes to retrieve
311: * @param integer $size
312: * @return string
313: * @access protected
314: */
315: protected function getResponse($size = 128)
316: {
317: $response = fgets($this->pop_conn, $size);
318: if ($this->do_debug >= 1) {
319: echo "Server -> Client: $response";
320: }
321: return $response;
322: }
323:
324: /**
325: * Send raw data to the POP3 server.
326: * @param string $string
327: * @return integer
328: * @access protected
329: */
330: protected function sendString($string)
331: {
332: if ($this->pop_conn) {
333: if ($this->do_debug >= 2) { //Show client messages when debug >= 2
334: echo "Client -> Server: $string";
335: }
336: return fwrite($this->pop_conn, $string, strlen($string));
337: }
338: return 0;
339: }
340:
341: /**
342: * Checks the POP3 server response.
343: * Looks for for +OK or -ERR.
344: * @param string $string
345: * @return boolean
346: * @access protected
347: */
348: protected function checkResponse($string)
349: {
350: if (substr($string, 0, 3) !== '+OK') {
351: $this->setError(array(
352: 'error' => "Server reported an error: $string",
353: 'errno' => 0,
354: 'errstr' => ''
355: ));
356: return false;
357: } else {
358: return true;
359: }
360: }
361:
362: /**
363: * Add an error to the internal error store.
364: * Also display debug output if it's enabled.
365: * @param $error
366: * @access protected
367: */
368: protected function setError($error)
369: {
370: $this->errors[] = $error;
371: if ($this->do_debug >= 1) {
372: echo '<pre>';
373: foreach ($this->errors as $error) {
374: print_r($error);
375: }
376: echo '</pre>';
377: }
378: }
379:
380: /**
381: * Get an array of error messages, if any.
382: * @return array
383: */
384: public function getErrors()
385: {
386: return $this->errors;
387: }
388:
389: /**
390: * POP3 connection error handler.
391: * @param integer $errno
392: * @param string $errstr
393: * @param string $errfile
394: * @param integer $errline
395: * @access protected
396: */
397: protected function catchWarning($errno, $errstr, $errfile, $errline)
398: {
399: $this->setError(array(
400: 'error' => "Connecting to the POP3 server raised a PHP warning: ",
401: 'errno' => $errno,
402: 'errstr' => $errstr,
403: 'errfile' => $errfile,
404: 'errline' => $errline
405: ));
406: }
407: }
408: