<?php

// required to handle php errors
require_once __DIR__ . '/exceptions/class.ZarafaErrorException.php';
require_once __DIR__ . '/download_base.php';

/**
 * DownloadMessage.
 *
 * A class to manage downloading of message as a file,
 * it will generate the message as RFC822-formatted e-mail stream.
 * It extends the DownloadBase class.
 */
class DownloadMessage extends DownloadBase {
	/**
	 * Function get message-stream using respective mapi function.
	 * It also sends the eml file to the client.
	 */
	public function downloadMessageAsFile() {
		if (!$this->message || !$this->store) {
			return;
		}
		// get message properties.
		$messageProps = mapi_getprops($this->message, [PR_SUBJECT, PR_MESSAGE_CLASS]);

		$stream = $this->getEmlStream($messageProps);

		$filename = (!empty($messageProps[PR_SUBJECT])) ? $messageProps[PR_SUBJECT] : _('Untitled');
		$filename .= '.eml';

		// Set the file length
		$stat = mapi_stream_stat($stream);

		$this->setNecessaryHeaders($filename, $stat['cb']);

		// Read whole message and echo it.
		for ($i = 0; $i < $stat['cb']; $i += BLOCK_SIZE) {
			// Print stream
			echo mapi_stream_read($stream, BLOCK_SIZE);

			// Need to discard the buffer contents to prevent memory
			// exhaustion while echoing large content.
			ob_flush();
		}
	}

	/**
	 * Function will create a ZIP archive and add eml files into the same.
	 * It also configures necessary header information which required to send the ZIP file to client.
	 * Send ZIP to the client if all the requested eml files included successfully into the same.
	 */
	public function downloadMessageAsZipFile() {
		if (!$this->store) {
			return;
		}
		// Generate random ZIP file name at default temporary path of PHP
		$randomZipName = tempnam(sys_get_temp_dir(), 'zip');

		// Create an open zip archive.
		$zip = new ZipArchive();
		$result = $zip->open($randomZipName, ZipArchive::CREATE);

		if ($result === true) {
			for ($index = 0, $count = count($this->entryIds); $index < $count; ++$index) {
				$this->message = mapi_msgstore_openentry($this->store, hex2bin((string) $this->entryIds[$index]));

				// get message properties.
				$messageProps = mapi_getprops($this->message, [PR_SUBJECT, PR_MESSAGE_CLASS]);

				$stream = $this->getEmlStream($messageProps);
				$stat = mapi_stream_stat($stream);

				// Get the stream
				$datastring = '';
				for ($i = 0; $i < $stat['cb']; $i += BLOCK_SIZE) {
					$datastring .= mapi_stream_read($stream, BLOCK_SIZE);
					// Need to discard the buffer contents to prevent memory
					// exhaustion.
					ob_flush();
				}

				$filename = (!empty($messageProps[PR_SUBJECT])) ? $messageProps[PR_SUBJECT] : _('Untitled');
				$filename .= '.eml';

				$filename = $this->handleDuplicateFileNames($filename);
				// Remove slashes to prevent unwanted directories to be created in the zip file.
				$filename = str_replace('\\', '_', $filename);
				$filename = str_replace('/', '_', $filename);

				// Add file into zip by stream
				$zip->addFromString($filename, $datastring);
			}
		}
		else {
			$zip->close();
			// Remove the zip file to avoid unnecessary disk-space consumption
			unlink($randomZipName);

			// Throw exception if ZIP is not created successfully
			throw new ZarafaException(_("ZIP is not created successfully"));
		}

		$zip->close();

		// Set the headers
		header('Pragma: public');
		header('Expires: 0'); // set expiration time
		header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
		header('Content-Disposition: attachment; filename="' . addslashes(browserDependingHTTPHeaderEncode(_("Messages") . date(" d-m-Y") . ".zip")) . '"');
		header('Content-Transfer-Encoding: binary');
		header('Content-Type:  application/zip');
		header('Content-Length: ' . filesize($randomZipName));

		// Send the actual response as ZIP file
		readfile($randomZipName);

		// Remove the zip file to avoid unnecessary disk-space consumption
		unlink($randomZipName);
	}

	/**
	 * Function will obtain stream from the message, For email messages it will open email as
	 * inet object and get the stream content as eml format, when user has IMAP enabled.
	 *
	 * @param array $messageProps properties of this particular message
	 *
	 * @return Stream $stream the eml stream obtained from message
	 */
	public function getEmlStream($messageProps) {
		// Get addressbook for current session
		$addrBook = $GLOBALS['mapisession']->getAddressbook();

		// Read the message as RFC822-formatted e-mail stream.
		$stream = mapi_inetmapi_imtoinet($GLOBALS['mapisession']->getSession(), $addrBook, $this->message, []);

		return $stream;
	}

	/**
	 * Check received data and decide either the eml file or
	 * ZIP file is requested to be downloaded.
	 */
	public function download() {
		if ($this->allAsZip) {
			// download multiple eml messages in a ZIP file
			$this->downloadMessageAsZipFile();
		}
		else {
			// download message as file
			$this->downloadMessageAsFile();
		}
	}
}

// create instance of class to download message as file
$messageInstance = new DownloadMessage();

try {
	// initialize variables
	$messageInstance->init($_GET);

	// download message
	$messageInstance->download();
}
catch (Exception $e) {
	$messageInstance->handleSaveMessageException($e);
}
