<?php
/**
 * Provides mail functionality.
*/
class MAIL {
	protected $imap = null;
	/**
	 * Return the name of the FOLDER_TYPE_TRASH folder
	 * @return
	 *	String folder name or empty if not found
	*/
	private function getTrashFolder() { // TODO: These functions are reduntant
		global $folderMapping;
		foreach ( $folderMapping as $key => $value ) {
			if ( $value['type'] == FOLDER_TYPE_TRASH ) {
				return $key;
			}
		}
		return "";
	}

	/**
	 * Return the name of the FOLDER_TYPE_SENT folder
	 * @return
	 *	String folder name or empty if not found
	*/
	private function getSentFolder() {
		global $folderMapping;
		foreach ( $folderMapping as $key => $value ) {
			if ( $value['type'] == FOLDER_TYPE_SENT ) {
				return $key;
			}
		}
		return "";
	}
	
	/**
	 * Return the name of the FOLDER_TYPE_SENT folder
	 * @return
	 *	String folder name or empty if not found
	*/
	private function getDraftFolder() {
		global $folderMapping;
		foreach ( $folderMapping as $key => $value ) {
			if ( $value['type'] == FOLDER_TYPE_DRAFTS ) {
				return $key;
			}
		}
		return "";
	}

	/**
	 * Create Extjs grid paging.
	*/
	private function makePaging() {
		$limit = "";
		if ( isset( $_POST['start'] ) && isset( $_POST['limit'] ) ) {
			$limit = (int)$_POST['start'].",".(int)$_POST['limit'];
		} else {
			$limit = "0,50"; // Load default limit.
		}
		return "LIMIT ".$limit;
	}

	public function buildSort() {
		if ( !isset( $_POST['sort'] ) || !isset( $_POST['dir'] ) ) {
			return "";
		} else {
			switch ( $_POST['sort'] ) {
				case "subject":
					return " subject ".( $_POST['dir'] == "DESC" ? "DESC" : "ASC" );
					break;
				case "sender":
					return "sender ".( $_POST['dir'] == "DESC" ? "DESC" : "ASC" );
					break;
				case "date":
					return "date ".( $_POST['dir'] == "DESC" ? "DESC" : "ASC" );
					break;
				case "size":
					return "size ".( $_POST['dir'] == "DESC" ? "DESC" : "ASC" );
					break;
				case "attachments":
					return "attachments ".( $_POST['dir'] == "DESC" ? "DESC" : "ASC" );
					break;
				default:
					return "";
					break;
			}
		}
	}

	public function list_messages() {
		global $format;
		$folder_id = $_GET['folder_id'];
		$sort = $this->buildSort();
		$limit = $this->makePaging();
		$filter = ""; // See if we need to build a filter
		$includeBody = false;
		$show = "";
		if ( isset( $_POST['show'] ) ) {
			if ( $_POST['show'] == 2 ) {
				$show = " AND seen = 0 ";
			}
			if ( $_POST['show'] == 3 ) {
				$show = " AND seen = 1 ";
			}
		}
		if ( isset( $_POST['value'] ) && isset( $_POST['type'] ) && $_POST['value'] != "" ) {
			$type = 1;
			if ( $_POST['type'] != "" ) {
				$type = (int)$_POST['type'];
			}
			$filter = mysql_real_escape_string( $_POST['value'] );
			switch ( $type ) {
				case 1:
					$filter = " AND ( subject LIKE '%".$filter."%' OR sender LIKE '%".$filter."%' OR content LIKE '%".$filter."%' )";
					$includeBody = true;
					break;
				case 2:
					$filter = " AND subject like '%".$filter."%'";
					break;
				case 3:
					$filter = " AND sender like '%".$filter."%'";
					break;
				case 4:
					$filter = " AND content like '%".$filter."%'";
					$includeBody = true;
					break;
				default:
					$filter = "";
					$includeBody = false;
					break;
			}
		}
		// Count total messages, before paging
		$result = mysql_fetch_assoc( mysql_query("SELECT COUNT(*) AS total FROM folder_messages".( $includeBody == true ? ",message_body" : "" )." WHERE folder_messages.account_id = '".(int)$_SESSION['account_id']."' ".( $includeBody == true ? " AND message_body.message_id = folder_messages.id" : "" )." AND folder_messages.folder_id = '".(int)$folder_id."' ".$filter.$show ) );
		$total = $result['total'];
		$result = mysql_query("SELECT folder_messages.uid AS id, subject, sender, ccaddress, bccaddress, reply_toaddress, date, size, seen, answered, recipient, attachments FROM folder_messages".( $includeBody == true ? ",message_body " : "" )." WHERE folder_messages.account_id = '".(int)$_SESSION['account_id']."' ".( $includeBody == true ? " AND message_body.message_id = folder_messages.id" : "" )." AND folder_messages.folder_id = '".(int)$folder_id."' ".$filter.$show." ORDER BY ".( $sort != "" ? $sort." " : " folder_messages.date DESC " ).$limit );
		$response['total'] = $total;
		$mailList = array();
		for ( $i = 0; $i < mysql_num_rows( $result ); $i++ ) {
			$mailList[$i] = mysql_fetch_assoc( $result );
			$mailList[$i]['subject'] = $format->decodeSubject( $mailList[$i]['subject'] );
			$mailList[$i]['date'] = @strtotime( $mailList[$i]['date'] );
		}
		$response['mailList'] = $mailList;
		echo $format->jsonResponse( $response );
	}

	public function view_message( $uid, $folderId ) {
		$result = mysql_query("SELECT name FROM mail_folders WHERE id = '".(int)$folderId."' AND account_id = '".(int)$_SESSION['account_id']."' LIMIT 1");
		$result = mysql_fetch_assoc( $result );
		$folderName = $result['name'];
		/* Check if chached */
		$result = mysql_query("SELECT * FROM message_body WHERE folder_id = '".(int)$folderId."' AND account_id = '".(int)$_SESSION['account_id']."' AND uid = '".(int)$uid."' LIMIT 1");

		/* Mark as read */
		mysql_query("UPDATE folder_messages SET seen = 1 WHERE folder_id = '".(int)$folderId."' AND account_id = '".(int)$_SESSION['account_id']."' AND uid = '".(int)$uid."' LIMIT 1");
		if ( mysql_num_rows( $result ) == 1 ) {
			/* Return cached message */
			$content = mysql_fetch_assoc( $result );
		} else {
			$this->imap = new IMAP( IMAP_SERVER_HOST, IMAP_SERVER_PORT, $_SESSION['username'], $_SESSION['password'], $folderName, IMAP_SERVER_VALIDATE_CERTIFICATE );
			$content = $this->imap->get_body( $this->imap->stream, $uid );
			/* Cache */
			$result = mysql_query("SELECT id FROM folder_messages WHERE folder_id = '".(int)$folderId."' AND account_id = '".(int)$_SESSION['account_id']."' AND uid = '".(int)$uid."' LIMIT 1");
			$result = mysql_fetch_assoc( $result );
			mysql_query("INSERT INTO message_body SET account_id = '".(int)$_SESSION['account_id']."', folder_id = '".(int)$folderId."', mime_type = '".mysql_real_escape_string( $content['mime_type'] )."', content = '".mysql_real_escape_string( $content['content'] )."', uid = '".(int)$uid."', message_id = '".(int)$result['id']."'");
		}
		switch ( $content['mime_type'] ) {
			case "text":
				header('Content-Type: text/html; charset=utf-8');
				echo $GLOBALS['format']->formatTextMessage( $content['content'] );
				break;
			case "html":
				header('Content-Type: text/html; charset=UTF-8');
				echo $GLOBALS['format']->formatHtmlMessage( $content['content'] );
				break;
		}
	}

	public function list_folders() {
		global $folderMapping;
		global $format;
		$result = mysql_query("SELECT id, name, ( SELECT COUNT(*) FROM folder_messages WHERE folder_id = mail_folders.id AND seen = 0 ) AS `unread`, ( SELECT COUNT(*) FROM folder_messages WHERE folder_id = mail_folders.id AND seen = 1 ) AS `read` FROM mail_folders WHERE account_id = '".(int)$_SESSION['account_id']."' ORDER BY name ASC");
		$response = array();
		for ( $i = 0; $i < mysql_num_rows( $result ); $i++ ) {
			$row = mysql_fetch_row( $result );
			$response[$i]['text'] = $folderMapping[$row[1]]['text'] .' ('.(int)$row[2].'/'.( (int)$row[3] + (int)$row[2] ).')';
			$response[$i]['name'] = $folderMapping[$row[1]]['text'];
			$response[$i]['folder_type'] = $folderMapping[$row[1]]['type']; // NOTE: This is the _folder_ type.
			$response[$i]['folder_id'] = (int) $row[0];
			$response[$i]['leaf'] = true;
			$response[$i]['type'] = "clickable"; // NOTE: This is the _node_ type.
		}

		echo $format->jsonResponse( $response );
	}

	public function mark_message( $as, $ids, $folderId ) {
		switch ( $as ) {
			case 2:
				$as = 0; // Unread
				break;
			case 1:
				$as = 1; // Read
				break;
		}
		/* Build message SQL in */
		$idArray = explode( ",", $ids );
		$sqlIn = "";
		for ( $i = 0; $i < count( $idArray ); $i++ ) {
			if ( $i != 0 ) {
				$sqlIn .= ",";
			}
			$sqlIn .= (int)$idArray[$i];
		}
		/* Update messages */
		mysql_query("UPDATE folder_messages SET seen = '".(int)$as."' WHERE account_id = '".(int)$_SESSION['account_id']."' AND folder_id = '".(int)$folderId."' AND uid IN (".$sqlIn.")");
	}

	private function makeAttachments() {
		$attachments = array(); // Inneficient function, for fetching uploaded files
		if ( isset( $_FILES['attachment_1'] ) ) {
			if ( is_uploaded_file( $_FILES['attachment_1']['tmp_name'] ) ) {
				array_push( $attachments, array(
						"content" => file_get_contents( $_FILES['attachment_1']['tmp_name'] )
						,"filename" => $_FILES['attachment_1']['name']
					)
				);
			}
		}
		if ( isset( $_FILES['attachment_2'] ) ) {
			if ( is_uploaded_file( $_FILES['attachment_2']['tmp_name'] ) ) {
				array_push( $attachments, array(
						"content" => file_get_contents( $_FILES['attachment_2']['tmp_name'] )
						,"filename" => $_FILES['attachment_2']['name']
					)
				);
			}
		}
		if ( isset( $_FILES['attachment_3'] ) ) {
			if ( is_uploaded_file( $_FILES['attachment_3']['tmp_name'] ) ) {
				array_push( $attachments, array(
						"content" => file_get_contents( $_FILES['attachment_3']['tmp_name'] )
						,"filename" => $_FILES['attachment_3']['name']
					)
				);
			}
		}
		return $attachments;
	}

	private function makeBody( $to, $subject, $content, $cc, $bcc, $attachments ) {
		$envelope["from"] = $_SESSION['username'];
		$envelope["reply_to"] = $_SESSION['username'];
		$envelope["cc"] = $cc;
		$envelope["bcc"] = $bcc;
		$body[0]["type"] = count( $attachments ) == 0 ? TYPETEXT : TYPEMULTIPART;
		$body[0]["subtype"] = count( $attachments ) == 0 ? "html" : "mixed";
		if ( count( $attachments ) != 0 ) {
			array_push( $body, array (
					"type" => TYPETEXT
					,"subtype" => "html"
					,"contents.data" => $content
				)
			);
		}
		for ( $i = 0; $i < count( $attachments ); $i++ ) {
			array_push( $body, array(
					"type" => TYPEAPPLICATION
					,"type.parameters" => array( "name" => $attachments[$i]['filename'] )
					,"encoding" => ENCBASE64
					,"subtype" => "octet-stream"
					,"description" => $attachments[$i]['filename']
					,"disposition.type" => 'attachment'
					,"parameters.name" => $attachments[$i]['filename']
					,"dparameters.filename" => $attachments[$i]['filename']
					,"contents.data" => base64_encode( $attachments[$i]['content'] )
				) 
			);
		}
		return array( "body" => $body, "envelope" => $envelope );
	}

	public function compose( $draft, $to, $subject, $content, $cc, $bcc ) {
		global $format;
		$username = $_SESSION['username'];
		$password = $_SESSION['password'];
		if ( $draft == 0 ) {
			$folder = $this->getDraftFolder();
		} else {
			$folder = $this->getSentFolder();
		}
		$attachments = $this->makeAttachments();
		$temp = $this->makeBody( $to, $subject, $content, $cc, $bcc, $attachments );
		$body = $temp["body"];
		$envelope = $temp["envelope"];
		if ( $draft == 0 ) {
			// Nothing, for now.
		} else {
			$mail = imap_mail_compose( $envelope, $body );
			imap_mail( $to, $subject, $content, $mail );
		}
		unset( $body );
		unset( $envelope );
		unset( $mail );
		$temp = $this->makeBody( $to, $subject, $content, $cc, $bcc, $attachments );
		$body = $temp["body"];
		$envelope = $temp["envelope"];
		$this->imap = new IMAP( IMAP_SERVER_HOST, IMAP_SERVER_PORT, $username, $password, $folder, IMAP_SERVER_VALIDATE_CERTIFICATE );
		// Rebuild MIME message, for storing in the 'sent' folder
		$envelope["to"] = $to;
		$envelope["cc"] = $cc;
		$envelope["bcc"] = $bcc;
		$envelope["subject"] = $subject;
		$body[0]["contents.data"] = $content;
		$mail = imap_mail_compose( $envelope, $body );
		$this->imap->imap_append( $mail );
		echo $format->jsonResponse( array( "success" => true ) );
		die();
	}

	private function checkOwner( $message_id ) {
		$account_id = (int)$_SESSION['account_id'];
		$result = mysql_query("SELECT id FROM folder_messages WHERE uid = '".(int)$message_id."' AND account_id = '".(int)$account_id."' LIMIT 1");
		if ( mysql_num_rows( $result ) != 1 ) {
			return false;
		} else {
			return true;
		}
	}
	
	private function getFolderName( $folder_id ) { 
		$account_id = (int)$_SESSION['account_id'];
		$result = mysql_query("SELECT name FROM mail_folders WHERE id = '".(int)$folder_id."' AND account_id = '".(int)$account_id."' LIMIT 1");
		if ( mysql_num_rows( $result ) != 1 ) {
			return false;
		} else {
			$result = mysql_fetch_assoc( $result );
			return $result['name'];
		}
	}

	public function delete( $ids, $folder_id, $to_folder_id = "", $move = false ) {
		global $synch;
		$username = $_SESSION['username'];
		$password = $_SESSION['password'];
		$account_id = (int)$_SESSION['account_id'];
		/* Build message SQL IN and check owner */
		$idArray = explode( ",", $ids );
		$messageSequence = "";
		for ( $i = 0; $i < count( $idArray ); $i++ ) {
			if ( $i != 0 ) {
				$messageSequence .= ",";
			}
			if ( !$this->checkOwner( $idArray[$i] ) ) {
				die(); // Ignore the request.
			}
			$messageSequence .= (int)$idArray[$i];
		}

		$folderName = $this->getFolderName( $folder_id );
		if ( $move == false ) {
			$targetFolder = $this->getTrashFolder();
		} else {
			$targetFolder = $this->getFolderName( $to_folder_id );
		}

		if ( $folderName === false || $targetFolder === false ) {
			die(); // Ignore the request, if the folder does not exist, or does not belong to current account.
		}

		// Move to trash folder OR if in trash folder -> delete
		$this->imap = new IMAP( IMAP_SERVER_HOST, IMAP_SERVER_PORT, $username, $password, $folderName, IMAP_SERVER_VALIDATE_CERTIFICATE );

		if ( $move == false ) {
			if ( $folderName != $targetFolder ) {
				$this->imap->imap_mail_move( $messageSequence, $targetFolder );
			} else {
				for ( $i = 0; $i < count( $idArray ); $i++ ) {
					$this->imap->imap_delete( $idArray[$i] );
				}
				$this->imap->imap_expunge();
			}
			mysql_query("DELETE FROM folder_messages WHERE account_id = '".(int)$account_id."' AND uid IN (" . $messageSequence . ") AND folder_id = '".(int)$folder_id."'" );
			mysql_query("DELETE FROM message_body WHERE account_id = '".(int)$account_id."' AND uid IN (" . $messageSequence . ") AND folder_id = '".(int)$folder_id."'" );
			mysql_query("DELETE FROM attachments WHERE account_id = '".(int)$account_id."' AND uid IN (" . $messageSequence . ") AND folder_id = '".(int)$folder_id."'" );
		} else {
			$this->imap->imap_mail_move( $messageSequence, $targetFolder );
			$this->imap->close();
			mysql_query("DELETE FROM folder_messages WHERE account_id = '".(int)$account_id."' AND uid IN (" . $messageSequence . ") AND folder_id = '".(int)$folder_id."'" );
			$synch->connect( $targetFolder );
			$synch->import_messages( $to_folder_id, $account_id );
		}
	}

	public function move( $ids, $folder_id, $to_folder_id ) {
		$this->delete( $ids, $folder_id, $to_folder_id, true );
	}
	
	public function fetchAttachment( $folder_id, $message_id, $filename ) {
		$filename = urldecode( $filename );
		$folderName = $this->getFolderName( $folder_id );
		if ( $folderName === false || $this->checkOwner( $message_id ) === false ) {
			die(); // Folder or message does not belong to current user.
		}
		global $synch;
		$synch->connect( $folderName );
		$result = $synch->get_attachments( $message_id, $filename );
		if ( $result === false || $filename == "" ) {
			header("HTTP/1.0 404 Not Found");
			die("File not found.");
		} else {
			header('Content-Description: File Transfer');
			header('Content-Type: application/octet-stream');
			header('Content-Disposition: attachment; filename='.urlencode( basename( $filename ) ) );
			header('Content-Transfer-Encoding: binary');
			header('Expires: 0');
			header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
			header('Pragma: public');
			header('Content-Length: ' . strlen( $result ) );
			echo $result;
		}
	}
}
?>