<?php 
/*
* Copyright (c) 2009-2010 ATMAIL. All rights reserved
* See http://atmail.com/license.php for license agreement
*/

class Atmail_Mail_Storage_Pop3LocalMaildir extends Zend_Mail_Storage
{	
	public $mailStorageType = 'POP3';
	public $dataSource = 'new';
	public $updateCacheStamp = 0;
	private $_defaultNamespaceName = 'Atmail_Mail_Storage_Pop3LocalMaildir';
	private $_namespaceName = null;
	private $_config;
	public $_session;
	private $_pop3StorageClassName = 'Atmail_Mail_Storage_Pop3';
	public $_pop3Storage = null;
	private $_maildirStorageClassName = 'Atmail_Mail_Storage_Writable_Maildir';  
	public $_maildirStorage = null;  
	private $_checkLocalFoldersExistDone = false;
	private $_connected = false;
	
	public function __construct(&$config)
	{
		if( !isset($config['dirname']) )
			throw new Atmail_Mail_Exception('Compulsory dirname not found in args');
		$this->_config = $config;
		if(isset($this->_config['namespaceName']))
			$this->_namespaceName = $this->_config['namespaceName'];
		else
			$this->_namespaceName = $_defaultNamespace;
		$this->_session = new Zend_Session_Namespace($this->_namespaceName); // session used as seperate persistent store for if caching disabled
		$pop3StorageConfig = array(	'namespaceName' => $this->_config['namespaceName'] . 'Pop3', 
									'host' => $this->_config['host'], 
									'port' => $this->_config['port'], 
									'ssl' => $this->_config['ssl'], 
									'user' => $this->_config['user'], 
									'password' => $this->_config['password']); //this is just here for neatness, only pass required vars in, else bloats cache
		$this->_pop3Storage = new $this->_pop3StorageClassName($pop3StorageConfig); //leaving cachine of this sub class to this
		$maildirStorageConfig = array(	'namespaceName' => $this->_config['namespaceName'] . 'Local', 
										'dirname' => $this->_config['dirname'], 
										'folder' => (isset($this->_config['folder'])?$this->_config['folder']:null) ); //this is just here for neatness, only pass required vars in, else bloats cache
		$this->_maildirStorage = new $this->_maildirStorageClassName($maildirStorageConfig); //leaving cachine of this sub class to this
		$this->updateCacheStamp = time(); //if constructor gets called then new class starting up
		Zend_Registry::get('log')->info('new ' . $this->_namespaceName . ' mail store object instance created');
	}
	
	public function __call($name, $args)
	{
		//certain methods directed always to to Maildir class
		if( in_array($name, array('createFolder', 'renameFolder', 'removeFolder', 'selectFolder', 'getCurrentFolder', 'getFolders', 'fetchFolders')) ) { 
			if(method_exists($this->_maildirStorage, $name)) {
				Zend_Registry::get('log')->info(get_class($this->_maildirStorage) . '::' . $name . ' called');
				$result = $this->_maildirStorage->$name((isset($args[0])?$args[0]:null), (isset($args[1])?$args[1]:null), (isset($args[2])?$args[2]:null), (isset($args[3])?$args[3]:null), (isset($args[4])?$args[4]:null) );
				Zend_Registry::get('log')->info(get_class($this->_maildirStorage) . '::' . $name . ' finished');
				return $result;
				
			}
		} elseif( $this->getCurrentFolder() == 'INBOX' ) {
			if(method_exists($this->_pop3Storage, $name)) {
				Zend_Registry::get('log')->info(get_class($this->_pop3Storage) . '::' . $name . ' called');
				$result = $this->_pop3Storage->$name((isset($args[0])?$args[0]:null), (isset($args[1])?$args[1]:null), (isset($args[2])?$args[2]:null), (isset($args[3])?$args[3]:null), (isset($args[4])?$args[4]:null) );
	    		Zend_Registry::get('log')->info(get_class($this->_pop3Storage) . '::' . $name . ' finished');
				return $result;
			}
		} else {
			if(method_exists($this->_maildirStorage, $name)) {
				Zend_Registry::get('log')->info(get_class($this->_maildirStorage) . '::' . $name . ' called');
				$result = $this->_maildirStorage->$name((isset($args[0])?$args[0]:null), (isset($args[1])?$args[1]:null), (isset($args[2])?$args[2]:null), (isset($args[3])?$args[3]:null), (isset($args[4])?$args[4]:null) );
	    		Zend_Registry::get('log')->info(get_class($this->_maildirStorage) . '::' . $name . ' finished');
				return $result;
			}
		}
		throw new Atmail_Mail_Exception(__CLASS__ . ': method not found: ' . $name);
	}
	
	public function connect()
	{
		Zend_Registry::get('log')->info(__METHOD__ . ' called while ' . ($this->_connected?'connected':'not connected') . ' at the time');
		if($this->_connected)
			return;
		$this->_pop3Storage->connect(); //N.B. During first logon to pop3 try logon to service first and only create folders if logon succeeds to prevent attempted abuse from creating lots of dead folders.
		$this->checkLocalFoldersExist();
		$this->_maildirStorage->connect();
		$this->_connected = true;
 	}

	public function close()
	{
		$this->_pop3Storage->close();
		$this->_maildirStorage->close();
		$this->_connected = false; 
	}
	
	public function checkLocalFoldersExist()
	{
		if($this->_checkLocalFoldersExistDone)
			return;
		if( !isset($this->_config['dirname']) )
			throw new Atmail_Mail_Exception('Compulsory dirname not found in args');
		$users_dir = $this->_config['dirname'];
		if(!is_dir($users_dir))
			throw new Atmail_Mail_Exception('Compulsory users folder not found on file system');
		Zend_Registry::get('log')->info('Checking local user directories for use with pop3'); 
		if(!is_dir($users_dir . DIRECTORY_SEPARATOR . 'new'))
			 mkdir($users_dir . DIRECTORY_SEPARATOR . 'new');
		if(!is_dir($users_dir . DIRECTORY_SEPARATOR . 'cur'))
			 mkdir($users_dir . DIRECTORY_SEPARATOR . 'cur');
	    if(!is_dir($users_dir . DIRECTORY_SEPARATOR . 'tmp'))
			 mkdir($users_dir . DIRECTORY_SEPARATOR . 'tmp');
		if(!is_dir($users_dir . DIRECTORY_SEPARATOR . '.Drafts'))
			 mkdir($users_dir . DIRECTORY_SEPARATOR . '.Drafts');
		if(!is_dir($users_dir . DIRECTORY_SEPARATOR . '.Drafts' . DIRECTORY_SEPARATOR . 'cur'))
			 mkdir($users_dir . DIRECTORY_SEPARATOR . '.Drafts' . DIRECTORY_SEPARATOR . 'cur');
		if(!is_dir($users_dir . DIRECTORY_SEPARATOR . '.Drafts' . DIRECTORY_SEPARATOR . 'new'))
			 mkdir($users_dir . DIRECTORY_SEPARATOR . '.Drafts' . DIRECTORY_SEPARATOR . 'new');
		if(!is_dir($users_dir . DIRECTORY_SEPARATOR . '.Drafts' . DIRECTORY_SEPARATOR . 'tmp'))
			 mkdir($users_dir . DIRECTORY_SEPARATOR . '.Drafts' . DIRECTORY_SEPARATOR . 'tmp');
		if(!is_dir($users_dir . DIRECTORY_SEPARATOR . '.Sent'))
			 mkdir($users_dir . DIRECTORY_SEPARATOR . '.Sent');
		if(!is_dir($users_dir . DIRECTORY_SEPARATOR . '.Sent' . DIRECTORY_SEPARATOR . 'cur'))
			 mkdir($users_dir . DIRECTORY_SEPARATOR . '.Sent' . DIRECTORY_SEPARATOR . 'cur');
		if(!is_dir($users_dir . DIRECTORY_SEPARATOR . '.Sent' . DIRECTORY_SEPARATOR . 'new'))
			 mkdir($users_dir . DIRECTORY_SEPARATOR . '.Sent' . DIRECTORY_SEPARATOR . 'new');
		if(!is_dir($users_dir . DIRECTORY_SEPARATOR . '.Sent' . DIRECTORY_SEPARATOR . 'tmp'))
			 mkdir($users_dir . DIRECTORY_SEPARATOR . '.Sent' . DIRECTORY_SEPARATOR . 'tmp');
		if(!is_dir($users_dir . DIRECTORY_SEPARATOR . '.Spam'))
			 mkdir($users_dir . DIRECTORY_SEPARATOR . '.Spam');
		if(!is_dir($users_dir . DIRECTORY_SEPARATOR . '.Spam' . DIRECTORY_SEPARATOR . 'cur'))
			 mkdir($users_dir . DIRECTORY_SEPARATOR . '.Spam' . DIRECTORY_SEPARATOR . 'cur');
		if(!is_dir($users_dir . DIRECTORY_SEPARATOR . '.Spam' . DIRECTORY_SEPARATOR . 'new'))
			 mkdir($users_dir . DIRECTORY_SEPARATOR . '.Spam' . DIRECTORY_SEPARATOR . 'new');
		if(!is_dir($users_dir . DIRECTORY_SEPARATOR . '.Spam' . DIRECTORY_SEPARATOR . 'tmp'))
			 mkdir($users_dir . DIRECTORY_SEPARATOR . '.Spam' . DIRECTORY_SEPARATOR . 'tmp');
		if(!is_dir($users_dir . DIRECTORY_SEPARATOR . '.Trash'))
			 mkdir($users_dir . DIRECTORY_SEPARATOR . '.Trash');
		if(!is_dir($users_dir . DIRECTORY_SEPARATOR . '.Trash' . DIRECTORY_SEPARATOR . 'cur'))
			 mkdir($users_dir . DIRECTORY_SEPARATOR . '.Trash' . DIRECTORY_SEPARATOR . 'cur');
		if(!is_dir($users_dir . DIRECTORY_SEPARATOR . '.Trash' . DIRECTORY_SEPARATOR . 'new'))
			 mkdir($users_dir . DIRECTORY_SEPARATOR . '.Trash' . DIRECTORY_SEPARATOR . 'new');
		if(!is_dir($users_dir . DIRECTORY_SEPARATOR . '.Trash' . DIRECTORY_SEPARATOR . 'tmp'))
			 mkdir($users_dir . DIRECTORY_SEPARATOR . '.Trash' . DIRECTORY_SEPARATOR . 'tmp');
		$this->_checkLocalFoldersExistDone = true;
	}
	
	public function moveMessage($uniqueId, $toFolder, $fromFolder = null)
	{		
		$this->copyMessage($uniqueId, $toFolder, $fromFolder);
		$success = true;
		try {
			$this->removeMessage($uniqueId);
		} catch ( Atmail_Mail_Exception $e ) {
			$success = false;
			dump($e);
		}
		if( !$success )
		   throw new Atmail_Exception('Pop3: Failed moving message');
	}

	public function copyMessage($uniqueId, $toFolder, $fromFolder = null)
	{
		if( $fromFolder == null )
			$fromFolder = $this->getCurrentFolder();
		if($fromFolder == $toFolder) {
			throw new Atmail_Exception('Copying message into same folder not implemented');
		} elseif($fromFolder != 'INBOX' && $toFolder != 'INBOX') {
			//Moving around in Maildir
			if($fromFolder != $this->getCurrentFolder())
				$this->selectFolder($fromFolder);
			$this->_maildirStorage->copyMessage($uniqueId, $toFolder);
		} elseif($fromFolder == 'INBOX') {
			//moving from Pop3 INBOX to Maildir
		    $messageHeader = $this->_pop3Storage->getRawHeader($uniqueId);
			$messageContent = $this->_pop3Storage->getRawContent($uniqueId);
			$messageRaw = $messageHeader . $messageContent; 
			$this->_maildirStorage->appendMessage($messageRaw, $toFolder);
		} elseif($toFolder == 'INBOX') {
			throw new Atmail_Exception('Copying message back into the Inbox is not implemented');
		}
	}
	
	// CACHE FUNCTIONS
	////////////////////////////////////////////////////////////////////////////////
	
	public static function createCache($config)
	{
		$namespaceName = isset($config['namespaceName'])?$config['namespaceName']:self::_defaultNamespaceName;
		$session = new Zend_Session_Namespace($namespaceName);
		if(!isset($config['folder']))
		{
			if( isset($session->folder) )
				$config['folder'] = $session->folder;
			elseif( isset($config['defaultFolder']) )
				$config['folder'] = $config['defaultFolder']; //_config not persistent but useful further on
			else
				$config['folder'] = 'default';
		}
		if( !isset($config['tmpFolderBaseName']) )
			throw new Atmail_Mail_Exception('Compulsory tmpFolderBaseName not found in args');
		$cache_dir = $config['tmpFolderBaseName'];
		if(!is_dir($cache_dir))
			mkdir($cache_dir);
		$safeAccount = simplifyString($config['Account']);
		$accountFirstLetter = substr($safeAccount,0, 1);
		$accountSecondLetter = substr($safeAccount,1, 1);
		$cache_dir .= $accountFirstLetter;
		if(!is_dir($cache_dir))
			mkdir($cache_dir);
		$cache_dir .= DIRECTORY_SEPARATOR . $accountSecondLetter;
		if(!is_dir($cache_dir))
			mkdir($cache_dir);
		$cache_dir .= DIRECTORY_SEPARATOR . $safeAccount;
		if(!is_dir($cache_dir))
			mkdir($cache_dir);
		$cache_dir .= DIRECTORY_SEPARATOR . $namespaceName;
		if(!is_dir($cache_dir))
			mkdir($cache_dir);
		return $config;
	}
	
	public static function &tryLoadFromCache($config)
	{
		$config = self::createCache($config);
		$namespaceName = isset($config['namespaceName'])?$config['namespaceName']:self::_defaultNamespaceName; //need to do this again cos static
		$session = new Zend_Session_Namespace($config['namespaceName']);
		if( !isset($session->folder) )
			return false;// no session existed so therefore new session, will also cause new sessions to force not using old cache
		$session->folder = isset($config['folder'])?$config['folder']:$session->folder;
		Zend_Registry::get('log')->info(__METHOD__ . ' ' . $session->folder . ' called');
		
		$cached =& Atmail_Cache::fetch(Atmail_Cache::TYPE_MAIL, simplifyString($session->folder) );
		dump($cached->_maildirStorage, __METHOD__ . '#' . __LINE__ . ': _maildirStorage just loaded from cache');
		Zend_Registry::get('log')->info(__METHOD__ . ' ' . $session->folder . ' finished (' . ($cached !== false?'found':'not found') . ')');
		return $cached;
	}
	
	public function updateCache()
	{
		//only update current folder and folderlist
		$this->dataSource = 'fresh';
		if( $this->getCurrentFolder() == 'INBOX' )
			$this->_pop3Storage->updateCache();
	    $this->_maildirStorage->updateCache(); //always update maildir with fresh folder list and fresh current folder (Maildir INBOX should be empty)
		$this->updateCacheStamp = time();
	}   
	
	
	public function saveCache()
	{
		dump($this->_maildirStorage, __METHOD__ . '#' . __LINE__ . ': _maildirStorage 1 saving to cache');
		Zend_Registry::get('log')->info('saving ' . $this->getCurrentFolder() . ' to cache'); 
		$this->dataSource = 'cached'; 
		dump($this->_maildirStorage, __METHOD__ . '#' . __LINE__ . ': _maildirStorage 2 saving to cache');
		$this->close();
		
		return Atmail_Cache::store(Atmail_Cache::TYPE_MAIL, simplifyString($this->getCurrentFolder()), $this );
	}
	
	// SEARCH FUNCTIONS
	////////////////////////////////////////////////////////////////////////////////

	public function reIndex($folder = null)
	{
		// Keep in mind that we are using the same Maildir class as for normal maildir access with the exception that for Pop3Maildir we dont index INBOX so we must call one at a time without the inbox specified.
		$previouslySelectedFolder = $this->getCurrentFolder();
		if($folder == null) {
			$folders = $this->getFolders();
			foreach($folders AS $key => $folder) {
				if($key != 'INBOX') {
				 	$this->selectFolder($key);
					$this->_maildirStorage->indexCurrentFolder();
				}
				else
					Zend_Registry::get('log')->info('Not indexing INBOX');
					
			}
		} elseif( $folder != 'INBOX' ) {
		   	$this->selectFolder($folder);
			$this->_maildirStorage->indexCurrentFolder();
		} else
			Zend_Registry::get('log')->info('Not indexing INBOX');
		
		$this->selectFolder($previouslySelectedFolder);		
	}
	
	public function search($args)
	{
		if( $this->getCurrentFolder() == 'INBOX' )
			return array(); //no inbox search
		else
			return $this->_maildirStorage->search($args);
	} 
	
}