<?php
/**
* @version	$Id: pop3_helper.php 14241 2011-03-16 20:24:35Z alex $
* @package	In-Portal
* @copyright	Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license      GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/

	defined('FULL_PATH') or die('restricted access!');

	/**
	 * POP3 connection class, that uses sockets
	 *
	 */
	class POP3Helper extends kHelper {

		/**
		 * Pointer to socket to POP3 server
		 *
		 * @var resource
		 */
		var $fp = null;

		/**
		 * Connection to POP3 server established
		 *
		 * @var bool
		 */
		var $connected = false;

		/**
		 * Connection timeout
		 *
		 * @var int
		 */
		var $connectionTimeout = 60;

		/**
		 * Server connection information
		 *
		 * @var Array
		 */
		var $serverInfo = Array ();

		/**
		 * Size of each message in mailbox
		 *
		 * @var Array
		 */
		var $messageSizes = Array ();

		/**
		 * Sets up the server information array
		 *
		 * @param  Array $server_info
		 * @return POP3Helper
		 */
		function POP3Helper($server_info, $connection_timeout = null)
		{
			parent::kHelper();

			$this->serverInfo = $server_info;

			if (isset($connection_timeout)) {
				$this->connectionTimeout = $connection_timeout;
			}
		}

		/**
		 * Check if we can connect and log into the mailbox
		 *
		 * @return string
		 */
		function initMailbox($dry_run = false)
		{
			if ($this->connect() === false) {
				return 'socket';
			}
			elseif ($this->auth() === false) {
				$this->close();
				return 'login';
			}
			elseif ($this->getMessageList() === false) {
				$this->close();
				return 'list';
			}
			elseif (empty($this->messageSizes)) {
				$this->close();
				return 'empty'; // Still 'good'
			}
			elseif ($dry_run) {
				$this->close();
				return 'success';
			}

			return true;
		}

		/**
		 * Logs failed logins to our server
		 *
		 * @param int $error error code
		 * @param Array $error_array params of error
		 */
		function error($error, $error_array)
		{
			trigger_error('POP3 Error. Code: ' . $error . '; Params: ' . print_r($error_array, true), E_USER_WARNING);
		}

		/**
		 * Connect to the server
		 *
		 * @return bool
		 */
		function connect()
		{
			// Apparently fsockopen() doesn't return false in these cases:
			if (empty($this->serverInfo['server'])) {
				return false;
			}

			if ($this->connected == true) {
				return true;
			}

			$this->fp = @fsockopen($this->serverInfo['server'], intval($this->serverInfo['port']), $error_no, $error_str, $this->connectionTimeout);

			if (!is_resource($this->fp)) {
				$this->error(101, $this->serverInfo);
				return false;
			}

			$this->connected = true;
			$buffer = fgets($this->fp, 4096);

			if (substr($buffer, 0, 3) != '+OK') {
				$this->error(101, $this->serverInfo);
				$this->close();
				return false;
			}
			else {
				return true;
			}
		}

		/**
		 * Close connection to the server
		 *
		 */
		function close()
		{
			if ($this->connected == true) { // and is_resource($this->fp)) {
				$this->connected = false;
				@fputs($this->fp, "QUIT\r\n");
				@fclose($this->fp);
			}
		}

		/**
		 * Login to the server
		 *
		 * @param bool $dry_run
		 * @return bool
		 */
		function auth($dry_run = false)
		{
			if (!is_resource($this->fp) && ($this->connect() === false)) {
				return false;
			}

			fputs($this->fp, 'USER ' . $this->serverInfo['username'] . "\r\n");
			$buffer = fgets($this->fp, 4096);

			if (substr($buffer, 0, 3) != '+OK') {
				$this->close();
				return false;
			}

			fputs($this->fp, 'PASS ' . $this->serverInfo['password'] . "\r\n");
			$buffer = fgets($this->fp, 4096);

			if (substr($buffer, 0, 3) != '+OK') {
				$this->error(102, $this->serverInfo);
				$this->close();

				return false;
			}
			else {
				if ($dry_run) {
					$this->close();
				}

				return true;
			}
		}

		/**
		 * Get the list of messages and their ID's
		 *
		 * @return bool
		 */
		function getMessageList()
		{
			fputs($this->fp, "LIST\r\n");

			if (substr(fgets($this->fp, 4096), 0, 3) != '+OK') {
				$this->close();
				return false;
			}

			// Store the message numbers and sizes
			$buffer = fgets($this->fp, 4096);

			while ($buffer != ".\r\n") {
				$msginfo = explode(' ', $buffer);
				$this->messageSizes[ trim($msginfo[0]) ] = trim($msginfo[1]);
				$buffer = fgets($this->fp, 4096);
			}

			return true;
		}

		/**
		 * Gets the size of a message
		 *
		 * @param int $message_number
		 * @return int
		 */
		function getSize($message_number)
		{
			return $this->messageSizes["$message_number"];
		}

		/**
		 * Gets email number $message_number from the server
		 *
		 * @param int $message_number
		 * @param string $source
		 * @return string
		 */
		function getEmail($message_number, &$source)
		{
			return $this->getData("RETR $message_number\r\n", $source);
		}

		/**
		 * Gets the top $lines from the message
		 *
		 * @param int $message_number
		 * @param string $source
		 * @param int $lines
		 * @param string $stopat
		 * @param bool $onelineonly
		 * @return string
		 */
		function getTop($message_number, &$source, $lines = 0, $stopat = '', $onelineonly = false)
		{
			return $this->getData("TOP $message_number $lines\r\n", $source, $stopat, $onelineonly);
		}

		/**
		 * Issues $command and returns the output
		 *
		 * @param string $command
		 * @param string $source
		 * @param string $stopat
		 * @param bool $onelineonly
		 * @return string
		 */
		function getData($command, &$source, $stopat = '', $onelineonly = false)
		{
			fputs($this->fp, $command);

			if (substr(fgets($this->fp, 4096), 0, 3) != '+OK') {
				return false;
			}

			$source = '';
			$buffer = fgets($this->fp, 4096);

			while ($buffer != ".\r\n") {
				if (!$onelineonly) {
					$source .= $buffer;
				}

				if (!empty($stopat)) {
					if (strtolower(substr(trim($buffer), 0, strlen($stopat))) == strtolower($stopat)) {
						if ($onelineonly) {
							$source = $buffer;
						}
						else {
							$onelineonly = true;
						}

						$stopat = '';
					}
				}

				$buffer = fgets($this->fp, 4096);
			}

			return true;
		}

		/**
		 * Sends the given command to the server and returns true or false on success
		 *
		 * @param string $command
		 * @return bool
		 */
		function sendCommand($command)
		{
			fputs($this->fp, $command . "\r\n");
			$buffer = trim(fgets($this->fp, 4096));

			if (substr($buffer, 0, 3) != '+OK') {
				$this->error(103, Array ('cmd' => $command, 'resp' => $buffer));
				return false;
			}
			else {
				return true;
			}
		}

		/**
		 * Delete message number $message_number from the server
		 *
		 * @param int $message_number
		 * @return bool
		 */
		function deleteEmail($message_number)
		{
			return $this->sendCommand("DELE $message_number");
		}

		/**
		 * Create new instance of object
		 *
		 * @return kBase
		 */
		function &makeClass($server_info, $connection_timeout = null)
		{
			$object = new POP3Helper($server_info, $connection_timeout);

			return $object;
		}
	}