- fixed crash when public store does not exist - check if temporary directory is writeable - disabled display_error with ini_set - fixed exporter: now really exporting the chosen calendar - improved parser (timezone detection)
		
			
				
	
	
		
			575 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			575 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?php	
 | 
						|
/**
 | 
						|
 * class.calendar.php, zarafa calender to ics im/exporter
 | 
						|
 *
 | 
						|
 * Author: Christoph Haas <christoph.h@sprinternet.at>
 | 
						|
 * Copyright (C) 2012-2013 Christoph Haas
 | 
						|
 *
 | 
						|
 * This library is free software; you can redistribute it and/or
 | 
						|
 * modify it under the terms of the GNU Lesser General Public
 | 
						|
 * License as published by the Free Software Foundation; either
 | 
						|
 * version 2.1 of the License, or (at your option) any later version.
 | 
						|
 *
 | 
						|
 * This library is distributed in the hope that it will be useful,
 | 
						|
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
						|
 * Lesser General Public License for more details.
 | 
						|
 *
 | 
						|
 * You should have received a copy of the GNU Lesser General Public
 | 
						|
 * License along with this library; if not, write to the Free Software
 | 
						|
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
						|
 *
 | 
						|
 */
 | 
						|
 
 | 
						|
include_once('mapi/class.recurrence.php');
 | 
						|
include_once('plugins/calendarimporter/php/ical/class.icalcreator.php');
 | 
						|
include_once('plugins/calendarimporter/php/ical/class.icalparser.php');
 | 
						|
 
 | 
						|
class CalendarModule extends Module {
 | 
						|
 | 
						|
	private $DEBUG = false; 	// enable error_log debugging
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @constructor
 | 
						|
	 * @param $id
 | 
						|
	 * @param $data
 | 
						|
	 */
 | 
						|
	public function __construct($id, $data) {
 | 
						|
			parent::Module($id, $data);	
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Executes all the actions in the $data variable.
 | 
						|
	 * Exception part is used for authentication errors also
 | 
						|
	 * @return boolean true on success or false on failure.
 | 
						|
	 */
 | 
						|
	public function execute() {
 | 
						|
		$result = false;
 | 
						|
		
 | 
						|
		if(!$this->DEBUG) {
 | 
						|
			/* disable error printing - otherwise json communication might break... */
 | 
						|
			ini_set('display_errors', '0');
 | 
						|
		}
 | 
						|
		
 | 
						|
		foreach($this->data as $actionType => $actionData) {
 | 
						|
			if(isset($actionType)) {
 | 
						|
				try {
 | 
						|
					if($this->DEBUG) {
 | 
						|
						error_log("exec: " . $actionType);
 | 
						|
					}
 | 
						|
					switch($actionType) {
 | 
						|
						case "export":
 | 
						|
							$result = $this->exportCalendar($actionType, $actionData);
 | 
						|
							break;
 | 
						|
						case "import":
 | 
						|
							$result = $this->importCalendar($actionType, $actionData);
 | 
						|
							break;
 | 
						|
						case "attachmentpath":
 | 
						|
							$result = $this->getAttachmentPath($actionType, $actionData);
 | 
						|
							break;
 | 
						|
						default:
 | 
						|
							$this->handleUnknownActionType($actionType);
 | 
						|
					}
 | 
						|
 | 
						|
				} catch (MAPIException $e) {
 | 
						|
					if($this->DEBUG) {
 | 
						|
						error_log("mapi exception: " . $e->getMessage());
 | 
						|
					}
 | 
						|
				} catch (Exception $e) {
 | 
						|
					if($this->DEBUG) {
 | 
						|
						error_log("exception: " . $e->getMessage());
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
		
 | 
						|
		return $result;
 | 
						|
	}
 | 
						|
	
 | 
						|
	/**
 | 
						|
	 * Generates a random string with variable length.
 | 
						|
	 * @param $length the lenght of the generated string
 | 
						|
	 * @return string a random string
 | 
						|
	 */
 | 
						|
	private function randomstring($length = 6) {
 | 
						|
		// $chars - all allowed charakters
 | 
						|
		$chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
 | 
						|
 | 
						|
		srand((double)microtime()*1000000);
 | 
						|
		$i = 0;
 | 
						|
		$pass = "";
 | 
						|
		while ($i < $length) {
 | 
						|
			$num = rand() % strlen($chars);
 | 
						|
			$tmp = substr($chars, $num, 1);
 | 
						|
			$pass = $pass . $tmp;
 | 
						|
			$i++;
 | 
						|
		}
 | 
						|
		return $pass;
 | 
						|
	}
 | 
						|
	
 | 
						|
	/**
 | 
						|
	 * Generates the secid file (used to verify the download path)
 | 
						|
	 * @param $secid the secid, a random security token
 | 
						|
	 */
 | 
						|
	private function createSecIDFile($secid) {
 | 
						|
		$lockFile = TMP_PATH . "/secid." . $secid;
 | 
						|
		$fh = fopen($lockFile, 'w') or die("can't open secid file");
 | 
						|
		$stringData = date(DATE_RFC822);
 | 
						|
		fwrite($fh, $stringData);
 | 
						|
		fclose($fh);
 | 
						|
	}
 | 
						|
	
 | 
						|
	/**
 | 
						|
	 * Generates the secid file (used to verify the download path)
 | 
						|
	 * @param $time a timestamp
 | 
						|
	 * @param $incl_time true if date should include time
 | 
						|
	 * @ return date object
 | 
						|
	 */
 | 
						|
	private function getIcalDate($time, $incl_time = true) {
 | 
						|
		return $incl_time ? date('Ymd\THis', $time) : date('Ymd', $time);
 | 
						|
	}
 | 
						|
	
 | 
						|
	/**
 | 
						|
	 * adds an event to the exported calendar)
 | 
						|
	 * @param $vevent pointer to the eventstore
 | 
						|
	 * @param $event the event to add
 | 
						|
	 */
 | 
						|
	private function addEvent(&$vevent, $event) {
 | 
						|
		
 | 
						|
		$busystate = array("FREE", "TENTATIVE", "BUSY", "OOF");
 | 
						|
		$zlabel = array("NONE", "IMPORTANT", "WORK", "PERSONAL", "HOLIDAY", "REQUIRED", "TRAVEL REQUIRED", "PREPARATION REQUIERED", "BIRTHDAY", "SPECIAL DATE", "PHONE INTERVIEW");
 | 
						|
		
 | 
						|
		$vevent->setProperty("LOCATION", $event["location"]);       // property name - case independent
 | 
						|
		$vevent->setProperty("SUMMARY", $event["subject"]);
 | 
						|
		$vevent->setProperty("DESCRIPTION", str_replace("\n", "\\n",$event["description"]));
 | 
						|
		$vevent->setProperty("COMMENT", "Exported from Zarafa" );
 | 
						|
		$vevent->setProperty("ORGANIZER", $event["sent_representing_email_address"]);
 | 
						|
		$vevent->setProperty("DTSTART", $this->getIcalDate($event["commonstart"]) . "Z"); 
 | 
						|
		$vevent->setProperty("DTEND", $this->getIcalDate($event["commonend"]) . "Z");
 | 
						|
		$vevent->setProperty("DTSTAMP", $this->getIcalDate($event["creation_time"]) . "Z");
 | 
						|
		$vevent->setProperty("CREATED", $this->getIcalDate($event["creation_time"]) . "Z");
 | 
						|
		$vevent->setProperty("LAST-MODIFIED", $this->getIcalDate($event["last_modification_time"]) . "Z");
 | 
						|
		$vevent->setProperty("X-MICROSOFT-CDO-BUSYSTATUS", $busystate[$event["busystatus"]]);
 | 
						|
		$vevent->setProperty("X-ZARAFA-LABEL", $zlabel[$event["label"]]);
 | 
						|
		$vevent->setProperty("PRIORITY", $event["importance"]);
 | 
						|
		$vevent->setProperty("CLASS", $event["private"] ? "PRIVATE" : "PUBLIC");
 | 
						|
		
 | 
						|
		// ATTENDEES
 | 
						|
		if(count($event["attendees"]) > 0) {
 | 
						|
			foreach($event["attendees"] as $attendee) {
 | 
						|
				$vevent->setProperty("ATTENDEE", $attendee["props"]["smtp_address"]);
 | 
						|
			}
 | 
						|
		}		
 | 
						|
		
 | 
						|
		// REMINDERS
 | 
						|
		if($event["reminder"]) {
 | 
						|
			$valarm = & $vevent->newComponent("valarm");	// create an event alarm
 | 
						|
			$valarm->setProperty("action", "DISPLAY" );
 | 
						|
			$valarm->setProperty("description", $vevent->getProperty("SUMMARY"));	// reuse the event summary
 | 
						|
			$valarm->setProperty("trigger", $this->getIcalDate($event["reminder_time"]) . "Z");	// create alarm trigger (in UTC datetime)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	
 | 
						|
	/**
 | 
						|
	 * Loads the descriptiontext of an event
 | 
						|
	 * @param $event
 | 
						|
	 * @return array with event description/body
 | 
						|
	 */
 | 
						|
	private function loadEventDescription($event) {
 | 
						|
		$entryid = $this->getActionEntryID($event);
 | 
						|
		$store = $this->getActionStore($event);
 | 
						|
		
 | 
						|
		$basedate = null;
 | 
						|
		
 | 
						|
		$properties = $GLOBALS['properties']->getAppointmentProperties();
 | 
						|
		$plaintext = true;
 | 
						|
		
 | 
						|
		$data = array();
 | 
						|
		
 | 
						|
		if($store && $entryid) {			
 | 
						|
			$message = $GLOBALS['operations']->openMessage($store, $entryid);
 | 
						|
			
 | 
						|
			
 | 
						|
			// add all standard properties from the series/normal message 
 | 
						|
			$data['item'] = $GLOBALS['operations']->getMessageProps($store, $message, $properties, (isset($plaintext) && $plaintext));
 | 
						|
			
 | 
						|
			// if appointment is recurring then only we should get properties of occurence if basedate is supplied
 | 
						|
			if($data['item']['props']['recurring'] === true) {
 | 
						|
				if(isset($basedate) && $basedate) {
 | 
						|
					$recur = new Recurrence($store, $message);
 | 
						|
 | 
						|
					$exceptionatt = $recur->getExceptionAttachment($basedate);
 | 
						|
 | 
						|
					// Single occurences are never recurring
 | 
						|
					$data['item']['props']['recurring'] = false;
 | 
						|
 | 
						|
					if($exceptionatt) {
 | 
						|
						// Existing exception (open existing item, which includes basedate)
 | 
						|
						$exceptionattProps = mapi_getprops($exceptionatt, array(PR_ATTACH_NUM));
 | 
						|
						$exception = mapi_attach_openobj($exceptionatt, 0);
 | 
						|
 | 
						|
						// overwrite properties with the ones from the exception
 | 
						|
						$exceptionProps = $GLOBALS['operations']->getMessageProps($store, $exception, $properties, (isset($plaintext) && $plaintext));
 | 
						|
 | 
						|
						/**
 | 
						|
						 * If recurring item has set reminder to true then
 | 
						|
						 * all occurrences before the 'flagdueby' value(of recurring item)
 | 
						|
						 * should not show that reminder is set.
 | 
						|
						 */
 | 
						|
						if (isset($exceptionProps['props']['reminder']) && $data['item']['props']['reminder'] == true) {
 | 
						|
							$flagDueByDay = $recur->dayStartOf($data['item']['props']['flagdueby']);
 | 
						|
 | 
						|
							if ($flagDueByDay > $basedate) {
 | 
						|
								$exceptionProps['props']['reminder'] = false;
 | 
						|
							}
 | 
						|
						}
 | 
						|
 | 
						|
						// The properties must be merged, if the recipients or attachments are present in the exception
 | 
						|
						// then that list should be used. Otherwise the list from the series must be applied (this
 | 
						|
						// corresponds with OL2007).
 | 
						|
						// @FIXME getMessageProps should not return empty string if exception doesn't contain body
 | 
						|
						// by this change we can handle a situation where user has set empty string in the body explicitly
 | 
						|
						if (!empty($exceptionProps['props']['body']) || !empty($exceptionProps['props']['html_body'])) {
 | 
						|
							if(!empty($exceptionProps['props']['body'])) {
 | 
						|
								$data['item']['props']['body'] = $exceptionProps['props']['body'];
 | 
						|
							}
 | 
						|
 | 
						|
							if(!empty($exceptionProps['props']['html_body'])) {
 | 
						|
								$data['item']['props']['html_body'] = $exceptionProps['props']['html_body'];
 | 
						|
							}
 | 
						|
 | 
						|
							$data['item']['props']['isHTML'] = $exceptionProps['props']['isHTML'];
 | 
						|
						}
 | 
						|
						// remove properties from $exceptionProps so array_merge will not overwrite it
 | 
						|
						unset($exceptionProps['props']['html_body']);
 | 
						|
						unset($exceptionProps['props']['body']);
 | 
						|
						unset($exceptionProps['props']['isHTML']);
 | 
						|
 | 
						|
						$data['item']['props'] = array_merge($data['item']['props'], $exceptionProps['props']);
 | 
						|
						if (isset($exceptionProps['recipients'])) {
 | 
						|
							$data['item']['recipients'] = $exceptionProps['recipients'];
 | 
						|
						}
 | 
						|
 | 
						|
						if (isset($exceptionProps['attachments'])) {
 | 
						|
							$data['item']['attachments'] = $exceptionProps['attachments'];
 | 
						|
						}
 | 
						|
 | 
						|
						// Make sure we are using the passed basedate and not something wrong in the opened item
 | 
						|
						$data['item']['props']['basedate'] = $basedate;
 | 
						|
					} else {
 | 
						|
						// opening an occurence of a recurring series (same as normal open, but add basedate, startdate and enddate)
 | 
						|
						$data['item']['props']['basedate'] = $basedate;
 | 
						|
						$data['item']['props']['startdate'] = $recur->getOccurrenceStart($basedate);
 | 
						|
						$data['item']['props']['duedate'] = $recur->getOccurrenceEnd($basedate);
 | 
						|
						$data['item']['props']['commonstart'] = $data['item']['props']['startdate'];
 | 
						|
						$data['item']['props']['commonend'] = $data['item']['props']['duedate'];
 | 
						|
						unset($data['item']['props']['reminder_time']);
 | 
						|
 | 
						|
						/**
 | 
						|
						 * If recurring item has set reminder to true then
 | 
						|
						 * all occurrences before the 'flagdueby' value(of recurring item)
 | 
						|
						 * should not show that reminder is set.
 | 
						|
						 */
 | 
						|
						if (isset($exceptionProps['props']['reminder']) && $data['item']['props']['reminder'] == true) {
 | 
						|
							$flagDueByDay = $recur->dayStartOf($data['item']['props']['flagdueby']);
 | 
						|
 | 
						|
							if ($flagDueByDay > $basedate) {
 | 
						|
								$exceptionProps['props']['reminder'] = false;
 | 
						|
							}
 | 
						|
						}
 | 
						|
					}
 | 
						|
				} else {
 | 
						|
					// Opening a recurring series, get the recurrence information
 | 
						|
					$recur = new Recurrence($store, $message);
 | 
						|
					$recurpattern = $recur->getRecurrence();
 | 
						|
					$tz = $recur->tz; // no function to do this at the moment
 | 
						|
 | 
						|
					// Add the recurrence pattern to the data
 | 
						|
					if(isset($recurpattern) && is_array($recurpattern)) {
 | 
						|
						$data['item']['props'] += $recurpattern;
 | 
						|
					}
 | 
						|
 | 
						|
					// Add the timezone information to the data
 | 
						|
					if(isset($tz) && is_array($tz)) {
 | 
						|
						$data['item']['props'] += $tz;
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		return $data['item']['props']['body'];
 | 
						|
	}
 | 
						|
	
 | 
						|
	/**
 | 
						|
	 * Loads the attendees of an event
 | 
						|
	 * @param $event
 | 
						|
	 * @return array with event attendees
 | 
						|
	 */
 | 
						|
	private function loadAttendees($event) {
 | 
						|
		$entryid = $this->getActionEntryID($event);
 | 
						|
		$store = $this->getActionStore($event);
 | 
						|
		
 | 
						|
		$basedate = null;
 | 
						|
		
 | 
						|
		$properties = $GLOBALS['properties']->getAppointmentProperties();
 | 
						|
		$plaintext = true;
 | 
						|
		
 | 
						|
		$data = array();
 | 
						|
		
 | 
						|
		if($store && $entryid) {
 | 
						|
			$message = $GLOBALS['operations']->openMessage($store, $entryid);
 | 
						|
			
 | 
						|
			
 | 
						|
			// add all standard properties from the series/normal message 
 | 
						|
			$data['item'] = $GLOBALS['operations']->getMessageProps($store, $message, $properties, (isset($plaintext) && $plaintext));
 | 
						|
			
 | 
						|
		}
 | 
						|
 | 
						|
		return $data['item']['recipients']['item'];
 | 
						|
	}
 | 
						|
	
 | 
						|
	/**
 | 
						|
	 * The main export function, creates the ics file for download
 | 
						|
	 * @param $actionType
 | 
						|
	 * @param $actionData
 | 
						|
	 */
 | 
						|
	private function exportCalendar($actionType, $actionData) {
 | 
						|
		$secid = $this->randomstring();	
 | 
						|
		$this->createSecIDFile($secid);
 | 
						|
		$tmpname = stripslashes($actionData["calendar"] . ".ics." . $this->randomstring(8));
 | 
						|
		$filename = TMP_PATH . "/" . $tmpname . "." . $secid;
 | 
						|
		
 | 
						|
		if(!is_writable(TMP_PATH . "/")) {
 | 
						|
			error_log("could not write to export tmp directory!");
 | 
						|
		}
 | 
						|
		
 | 
						|
		$tz = date("e");	// use php timezone (maybe set up in php.ini, date.timezone)
 | 
						|
		
 | 
						|
		if($this->DEBUG) {
 | 
						|
			error_log("PHP Timezone: " . $tz);
 | 
						|
		}		
 | 
						|
		
 | 
						|
		$config = array(
 | 
						|
						"language" => substr($GLOBALS["settings"]->get("zarafa/v1/main/language"),0,2),
 | 
						|
						"directory" => TMP_PATH . "/", 
 | 
						|
						"filename" => $tmpname . "." . $secid,
 | 
						|
						"unique_id" => "zarafa-export-plugin", 
 | 
						|
						"TZID" => $tz
 | 
						|
						);
 | 
						|
		
 | 
						|
		$v = new vcalendar($config); 
 | 
						|
		$v->setProperty("method", "PUBLISH");							// required of some calendar software
 | 
						|
		$v->setProperty("x-wr-calname", $actionData["calendar"]);		// required of some calendar software
 | 
						|
		$v->setProperty("X-WR-CALDESC", "Exported Zarafa Calendar");	// required of some calendar software
 | 
						|
		$v->setProperty("X-WR-TIMEZONE", $tz); 
 | 
						|
 | 
						|
		$xprops = array("X-LIC-LOCATION" => $tz);					// required of some calendar software
 | 
						|
		iCalUtilityFunctions::createTimezone($v, $tz, $xprops);		// create timezone object in calendar
 | 
						|
				
 | 
						|
		
 | 
						|
		foreach($actionData["data"] as $event) {
 | 
						|
			$event["props"]["description"] = $this->loadEventDescription($event);
 | 
						|
			$event["props"]["attendees"] = $this->loadAttendees($event);
 | 
						|
			
 | 
						|
			$vevent = & $v->newComponent("vevent");	// create a new event object
 | 
						|
			$this->addEvent($vevent, $event["props"]);
 | 
						|
		}
 | 
						|
		
 | 
						|
		$v->saveCalendar();
 | 
						|
		
 | 
						|
		$response['status']		= true;
 | 
						|
		$response['fileid']		= $tmpname;	// number of entries that will be exported
 | 
						|
		$response['basedir']	= TMP_PATH;
 | 
						|
		$response['secid']		= $secid;
 | 
						|
		$response['realname']	= $actionData["calendar"];
 | 
						|
		$this->addActionData($actionType, $response);
 | 
						|
		$GLOBALS["bus"]->addData($this->getResponseData());
 | 
						|
		
 | 
						|
		if($this->DEBUG) {
 | 
						|
			error_log("export done, bus data written!");
 | 
						|
		}
 | 
						|
	}
 | 
						|
	
 | 
						|
	/**
 | 
						|
	 * The main import function, parses the uploaded ics file
 | 
						|
	 * @param $actionType
 | 
						|
	 * @param $actionData
 | 
						|
	 */
 | 
						|
	private function importCalendar($actionType, $actionData) {
 | 
						|
		if($this->DEBUG) {
 | 
						|
			error_log("PHP Timezone: " . $tz);
 | 
						|
		}
 | 
						|
		
 | 
						|
		if(is_readable ($actionData["ics_filepath"])) {
 | 
						|
			$ical = new ICal($actionData["ics_filepath"], $GLOBALS["settings"]->get("zarafa/v1/plugins/calendarimporter/default_timezone"), $actionData["timezone"], $actionData["ignore_dst"]); // Parse it!
 | 
						|
			
 | 
						|
			if(isset($ical->errors)) {
 | 
						|
				$response['status']	= false;
 | 
						|
				$response['message']= $ical->errors;
 | 
						|
			} else if(!$ical->hasEvents()) {
 | 
						|
				$response['status']	= false;
 | 
						|
				$response['message']= "No events in ics file";
 | 
						|
			} else {
 | 
						|
				$response['status']		= true;
 | 
						|
				$response['parsed_file']= $actionData["ics_filepath"];
 | 
						|
				$response['parsed']		= array (
 | 
						|
					'timezone'  =>  $ical->timezone(),
 | 
						|
					'calendar'	=>	$ical->calendar(),
 | 
						|
					'events'	=>	$ical->events()
 | 
						|
				);
 | 
						|
			}
 | 
						|
		} else {
 | 
						|
			$response['status']	= false;
 | 
						|
			$response['message']= "File could not be read by server";
 | 
						|
		}
 | 
						|
		
 | 
						|
		$this->addActionData($actionType, $response);
 | 
						|
		$GLOBALS["bus"]->addData($this->getResponseData());
 | 
						|
		
 | 
						|
		if($this->DEBUG) {
 | 
						|
			error_log("parsing done, bus data written!");
 | 
						|
		}
 | 
						|
	}
 | 
						|
	
 | 
						|
	/**
 | 
						|
	 * Store the file to a temporary directory, prepare it for oc upload
 | 
						|
	 * @param $actionType
 | 
						|
	 * @param $actionData
 | 
						|
	 * @private
 | 
						|
	 */
 | 
						|
	private function getAttachmentPath($actionType, $actionData) {
 | 
						|
		// Get store id
 | 
						|
		$storeid = false;
 | 
						|
		if(isset($actionData["store"])) {
 | 
						|
			$storeid = $actionData["store"];
 | 
						|
		}
 | 
						|
 | 
						|
		// Get message entryid
 | 
						|
		$entryid = false;
 | 
						|
		if(isset($actionData["entryid"])) {
 | 
						|
			$entryid = $actionData["entryid"];
 | 
						|
		}
 | 
						|
 | 
						|
		// Check which type isset
 | 
						|
		$openType = "attachment";
 | 
						|
 | 
						|
		// Get number of attachment which should be opened.
 | 
						|
		$attachNum = false;
 | 
						|
		if(isset($actionData["attachNum"])) {
 | 
						|
			$attachNum = $actionData["attachNum"];
 | 
						|
		}
 | 
						|
 | 
						|
		// Check if storeid and entryid isset
 | 
						|
		if($storeid && $entryid) {
 | 
						|
			// Open the store
 | 
						|
			$store = $GLOBALS["mapisession"]->openMessageStore(hex2bin($storeid));
 | 
						|
			
 | 
						|
			if($store) {
 | 
						|
				// Open the message
 | 
						|
				$message = mapi_msgstore_openentry($store, hex2bin($entryid));
 | 
						|
				
 | 
						|
				if($message) {
 | 
						|
					$attachment = false;
 | 
						|
 | 
						|
					// Check if attachNum isset
 | 
						|
					if($attachNum) {
 | 
						|
						// Loop through the attachNums, message in message in message ...
 | 
						|
						for($i = 0; $i < (count($attachNum) - 1); $i++)
 | 
						|
						{
 | 
						|
							// Open the attachment
 | 
						|
							$tempattach = mapi_message_openattach($message, (int) $attachNum[$i]);
 | 
						|
							if($tempattach) {
 | 
						|
								// Open the object in the attachment
 | 
						|
								$message = mapi_attach_openobj($tempattach);
 | 
						|
							}
 | 
						|
						}
 | 
						|
 | 
						|
						// Open the attachment
 | 
						|
						$attachment = mapi_message_openattach($message, (int) $attachNum[(count($attachNum) - 1)]);
 | 
						|
					}
 | 
						|
 | 
						|
					// Check if the attachment is opened
 | 
						|
					if($attachment) {
 | 
						|
						
 | 
						|
						// Get the props of the attachment
 | 
						|
						$props = mapi_attach_getprops($attachment, array(PR_ATTACH_LONG_FILENAME, PR_ATTACH_MIME_TAG, PR_DISPLAY_NAME, PR_ATTACH_METHOD));
 | 
						|
						// Content Type
 | 
						|
						$contentType = "application/octet-stream";
 | 
						|
						// Filename
 | 
						|
						$filename = "ERROR";
 | 
						|
 | 
						|
						// Set filename
 | 
						|
						if(isset($props[PR_ATTACH_LONG_FILENAME])) {
 | 
						|
							$filename = $props[PR_ATTACH_LONG_FILENAME];
 | 
						|
						} else if(isset($props[PR_ATTACH_FILENAME])) {
 | 
						|
							$filename = $props[PR_ATTACH_FILENAME];
 | 
						|
						} else if(isset($props[PR_DISPLAY_NAME])) {
 | 
						|
							$filename = $props[PR_DISPLAY_NAME];
 | 
						|
						} 
 | 
						|
				
 | 
						|
						// Set content type
 | 
						|
						if(isset($props[PR_ATTACH_MIME_TAG])) {
 | 
						|
							$contentType = $props[PR_ATTACH_MIME_TAG];
 | 
						|
						} else {
 | 
						|
							// Parse the extension of the filename to get the content type
 | 
						|
							if(strrpos($filename, ".") !== false) {
 | 
						|
								$extension = strtolower(substr($filename, strrpos($filename, ".")));
 | 
						|
								$contentType = "application/octet-stream";
 | 
						|
								if (is_readable("mimetypes.dat")){
 | 
						|
									$fh = fopen("mimetypes.dat","r");
 | 
						|
									$ext_found = false;
 | 
						|
									while (!feof($fh) && !$ext_found){
 | 
						|
										$line = fgets($fh);
 | 
						|
										preg_match("/(\.[a-z0-9]+)[ \t]+([^ \t\n\r]*)/i", $line, $result);
 | 
						|
										if ($extension == $result[1]){
 | 
						|
											$ext_found = true;
 | 
						|
											$contentType = $result[2];
 | 
						|
										}
 | 
						|
									}
 | 
						|
									fclose($fh);
 | 
						|
								}
 | 
						|
							}
 | 
						|
						}
 | 
						|
						
 | 
						|
						
 | 
						|
						$tmpname = tempnam(TMP_PATH, stripslashes($filename));
 | 
						|
 | 
						|
						// Open a stream to get the attachment data
 | 
						|
						$stream = mapi_openpropertytostream($attachment, PR_ATTACH_DATA_BIN);
 | 
						|
						$stat = mapi_stream_stat($stream);
 | 
						|
						// File length =  $stat["cb"]
 | 
						|
						
 | 
						|
						$fhandle = fopen($tmpname,'w');
 | 
						|
						$buffer = null;
 | 
						|
						for($i = 0; $i < $stat["cb"]; $i += BLOCK_SIZE) {
 | 
						|
							// Write stream
 | 
						|
							$buffer = mapi_stream_read($stream, BLOCK_SIZE);
 | 
						|
							fwrite($fhandle,$buffer,strlen($buffer));
 | 
						|
						}
 | 
						|
						fclose($fhandle);
 | 
						|
						
 | 
						|
						$response = array();
 | 
						|
						$response['tmpname'] = $tmpname;
 | 
						|
						$response['filename'] = $filename;
 | 
						|
						$response['status'] = true;
 | 
						|
						$this->addActionData($actionType, $response);
 | 
						|
						$GLOBALS["bus"]->addData($this->getResponseData());
 | 
						|
					}
 | 
						|
				}
 | 
						|
			} else {
 | 
						|
				$response['status'] = false;
 | 
						|
				$response['message'] = "Store could not be opened!";
 | 
						|
				$this->addActionData($actionType, $response);
 | 
						|
				$GLOBALS["bus"]->addData($this->getResponseData());
 | 
						|
			}
 | 
						|
		} else {
 | 
						|
			$response['status'] = false;
 | 
						|
			$response['message'] = "Wrong call, store and entryid have to be set!";
 | 
						|
			$this->addActionData($actionType, $response);
 | 
						|
			$GLOBALS["bus"]->addData($this->getResponseData());
 | 
						|
		}
 | 
						|
	}
 | 
						|
};
 | 
						|
 | 
						|
?>
 |