Back to index

d-push  2.0
mapiprovider.php
Go to the documentation of this file.
00001 <?php
00002 /***********************************************
00003 * File      :   mapiprovider.php
00004 * Project   :   Z-Push
00005 * Descr     :
00006 *
00007 * Created   :   14.02.2011
00008 *
00009 * Copyright 2007 - 2011 Zarafa Deutschland GmbH
00010 *
00011 * This program is free software: you can redistribute it and/or modify
00012 * it under the terms of the GNU Affero General Public License, version 3,
00013 * as published by the Free Software Foundation with the following additional
00014 * term according to sec. 7:
00015 *
00016 * According to sec. 7 of the GNU Affero General Public License, version 3,
00017 * the terms of the AGPL are supplemented with the following terms:
00018 *
00019 * "Zarafa" is a registered trademark of Zarafa B.V.
00020 * "Z-Push" is a registered trademark of Zarafa Deutschland GmbH
00021 * The licensing of the Program under the AGPL does not imply a trademark license.
00022 * Therefore any rights, title and interest in our trademarks remain entirely with us.
00023 *
00024 * However, if you propagate an unmodified version of the Program you are
00025 * allowed to use the term "Z-Push" to indicate that you distribute the Program.
00026 * Furthermore you may use our trademarks where it is necessary to indicate
00027 * the intended purpose of a product or service provided you use it in accordance
00028 * with honest practices in industrial or commercial matters.
00029 * If you want to propagate modified versions of the Program under the name "Z-Push",
00030 * you may only do so if you have a written permission by Zarafa Deutschland GmbH
00031 * (to acquire a permission please contact Zarafa at trademark@zarafa.com).
00032 *
00033 * This program is distributed in the hope that it will be useful,
00034 * but WITHOUT ANY WARRANTY; without even the implied warranty of
00035 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00036 * GNU Affero General Public License for more details.
00037 *
00038 * You should have received a copy of the GNU Affero General Public License
00039 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00040 *
00041 * Consult LICENSE file for details
00042 ************************************************/
00043 
00044 class MAPIProvider {
00045     private $session;
00046     private $store;
00047     private $zRFC822;
00048 
00058     function MAPIProvider($session, $store) {
00059         $this->session = $session;
00060         $this->store = $store;
00061     }
00062 
00063 
00078     public function GetMessage($mapimessage, $contentparameters) {
00079         // Gets the Sync object from a MAPI object according to its message class
00080 
00081         $props = mapi_getprops($mapimessage, array(PR_MESSAGE_CLASS));
00082         if(isset($props[PR_MESSAGE_CLASS]))
00083             $messageclass = $props[PR_MESSAGE_CLASS];
00084         else
00085             $messageclass = "IPM";
00086 
00087         if(strpos($messageclass,"IPM.Contact") === 0)
00088             return $this->getContact($mapimessage, $contentparameters);
00089         else if(strpos($messageclass,"IPM.Appointment") === 0)
00090             return $this->getAppointment($mapimessage, $contentparameters);
00091         else if(strpos($messageclass,"IPM.Task") === 0)
00092             return $this->getTask($mapimessage, $contentparameters);
00093         else if(strpos($messageclass,"IPM.StickyNote") === 0)
00094             return $this->getNote($mapimessage, $contentparameters);
00095         else
00096             return $this->getEmail($mapimessage, $contentparameters);
00097     }
00098 
00108     private function getContact($mapimessage, $contentparameters) {
00109         $message = new SyncContact();
00110 
00111         // Standard one-to-one mappings first
00112         $this->getPropsFromMAPI($message, $mapimessage, MAPIMapping::GetContactMapping());
00113 
00114         // Contact specific props
00115         $contactproperties = MAPIMapping::GetContactProperties();
00116         $messageprops = $this->getProps($mapimessage, $contactproperties);
00117 
00118         //set the body according to contentparameters and supported AS version
00119         $this->setMessageBody($mapimessage, $contentparameters, $message);
00120 
00121         //check the picture
00122         if (isset($messageprops[$contactproperties["haspic"]]) && $messageprops[$contactproperties["haspic"]]) {
00123             // Add attachments
00124             $attachtable = mapi_message_getattachmenttable($mapimessage);
00125             mapi_table_restrict($attachtable, MAPIUtils::GetContactPicRestriction());
00126             $rows = mapi_table_queryallrows($attachtable, array(PR_ATTACH_NUM, PR_ATTACH_SIZE));
00127 
00128             foreach($rows as $row) {
00129                 if(isset($row[PR_ATTACH_NUM])) {
00130                     if (isset($row[PR_ATTACH_SIZE]) && $row[PR_ATTACH_SIZE] < MAX_EMBEDDED_SIZE) {
00131                         $mapiattach = mapi_message_openattach($mapimessage, $row[PR_ATTACH_NUM]);
00132                         $message->picture = base64_encode(mapi_attach_openbin($mapiattach, PR_ATTACH_DATA_BIN));
00133                     }
00134                 }
00135             }
00136         }
00137 
00138         return $message;
00139     }
00140 
00150     private function getTask($mapimessage, $contentparameters) {
00151         $message = new SyncTask();
00152 
00153         // Standard one-to-one mappings first
00154         $this->getPropsFromMAPI($message, $mapimessage, MAPIMapping::GetTaskMapping());
00155 
00156         // Task specific props
00157         $taskproperties = MAPIMapping::GetTaskProperties();
00158         $messageprops = $this->getProps($mapimessage, $taskproperties);
00159 
00160         //set the body according to contentparameters and supported AS version
00161         $this->setMessageBody($mapimessage, $contentparameters, $message);
00162 
00163         //task with deadoccur is an occurrence of a recurring task and does not need to be handled as recurring
00164         //webaccess does not set deadoccur for the initial recurring task
00165         if(isset($messageprops[$taskproperties["isrecurringtag"]]) &&
00166             $messageprops[$taskproperties["isrecurringtag"]] &&
00167             (!isset($messageprops[$taskproperties["deadoccur"]]) ||
00168             (isset($messageprops[$taskproperties["deadoccur"]]) &&
00169             !$messageprops[$taskproperties["deadoccur"]]))) {
00170             // Process recurrence
00171             $message->recurrence = new SyncTaskRecurrence();
00172             $this->getRecurrence($mapimessage, $messageprops, $message, $message->recurrence, false);
00173         }
00174 
00175         // when set the task to complete using the WebAccess, the dateComplete property is not set correctly
00176         if ($message->complete == 1 && !isset($message->datecompleted))
00177             $message->datecompleted = time();
00178 
00179         // if no reminder is set, announce that to the mobile
00180         if (!isset($message->reminderset))
00181             $message->reminderset = 0;
00182 
00183         return $message;
00184     }
00185 
00195     private function getAppointment($mapimessage, $contentparameters) {
00196         $message = new SyncAppointment();
00197 
00198         // Standard one-to-one mappings first
00199         $this->getPropsFromMAPI($message, $mapimessage, MAPIMapping::GetAppointmentMapping());
00200 
00201         // Appointment specific props
00202         $appointmentprops = MAPIMapping::GetAppointmentProperties();
00203         $messageprops = $this->getProps($mapimessage, $appointmentprops);
00204 
00205         //set the body according to contentparameters and supported AS version
00206         $this->setMessageBody($mapimessage, $contentparameters, $message);
00207 
00208         // Set reminder time if reminderset is true
00209         if(isset($messageprops[$appointmentprops["reminderset"]]) && $messageprops[$appointmentprops["reminderset"]] == true) {
00210             if ($messageprops[$appointmentprops["remindertime"]] == 0x5AE980E1)
00211                 $message->reminder = 15;
00212             else
00213                 $message->reminder = $messageprops[$appointmentprops["remindertime"]];
00214         }
00215 
00216         if(!isset($message->uid))
00217             $message->uid = bin2hex($messageprops[$appointmentprops["sourcekey"]]);
00218         else
00219             $message->uid = Utils::GetICalUidFromOLUid($message->uid);
00220 
00221         // Get organizer information if it is a meetingrequest
00222         if(isset($messageprops[$appointmentprops["meetingstatus"]]) &&
00223             $messageprops[$appointmentprops["meetingstatus"]] > 0 &&
00224             isset($messageprops[$appointmentprops["representingentryid"]]) &&
00225             isset($messageprops[$appointmentprops["representingname"]])) {
00226 
00227             $message->organizeremail = w2u($this->getSMTPAddressFromEntryID($messageprops[$appointmentprops["representingentryid"]]));
00228             $message->organizername = w2u($messageprops[$appointmentprops["representingname"]]);
00229         }
00230 
00231         if(isset($messageprops[$appointmentprops["timezonetag"]]))
00232             $tz = $this->getTZFromMAPIBlob($messageprops[$appointmentprops["timezonetag"]]);
00233         else {
00234             // set server default timezone (correct timezone should be configured!)
00235             $tz = TimezoneUtil::GetFullTZ();
00236         }
00237         $message->timezone = base64_encode($this->getSyncBlobFromTZ($tz));
00238 
00239         if(isset($messageprops[$appointmentprops["isrecurring"]]) && $messageprops[$appointmentprops["isrecurring"]]) {
00240             // Process recurrence
00241             $message->recurrence = new SyncRecurrence();
00242             $this->getRecurrence($mapimessage, $messageprops, $message, $message->recurrence, $tz);
00243         }
00244 
00245         // Do attendees
00246         $reciptable = mapi_message_getrecipienttable($mapimessage);
00247         // Only get first 256 recipients, to prevent possible load issues.
00248         $rows = mapi_table_queryrows($reciptable, array(PR_DISPLAY_NAME, PR_EMAIL_ADDRESS, PR_SMTP_ADDRESS, PR_ADDRTYPE, PR_RECIPIENT_TRACKSTATUS, PR_RECIPIENT_TYPE), 0, 256);
00249 
00250         // Exception: we do not synchronize appointments with more than 250 attendees
00251         if (count($rows) > 250) {
00252             $message->id = bin2hex($messageprops[$appointmentprops["sourcekey"]]);
00253             $mbe = new SyncObjectBrokenException("Appointment has too many attendees");
00254             $mbe->SetSyncObject($message);
00255             throw $mbe;
00256         }
00257 
00258         if(count($rows) > 0)
00259             $message->attendees = array();
00260 
00261         foreach($rows as $row) {
00262             $attendee = new SyncAttendee();
00263 
00264             $attendee->name = w2u($row[PR_DISPLAY_NAME]);
00265             //smtp address is always a proper email address
00266             if(isset($row[PR_SMTP_ADDRESS]))
00267                 $attendee->email = w2u($row[PR_SMTP_ADDRESS]);
00268             elseif (isset($row[PR_ADDRTYPE]) && isset($row[PR_EMAIL_ADDRESS])) {
00269                 //if address type is SMTP, it's also a proper email address
00270                 if ($row[PR_ADDRTYPE] == "SMTP")
00271                     $attendee->email = w2u($row[PR_EMAIL_ADDRESS]);
00272                 //if address type is ZARAFA, the PR_EMAIL_ADDRESS contains username
00273                 elseif ($row[PR_ADDRTYPE] == "ZARAFA") {
00274                     $userinfo = mapi_zarafa_getuser_by_name($this->store, $row[PR_EMAIL_ADDRESS]);
00275                     if (is_array($userinfo) && isset($userinfo["emailaddress"]))
00276                         $attendee->email = w2u($userinfo["emailaddress"]);
00277                 }
00278             }
00279 
00280             //set attendee's status and type if they're available
00281             if (isset($row[PR_RECIPIENT_TRACKSTATUS]))
00282                 $attendee->attendeestatus = $row[PR_RECIPIENT_TRACKSTATUS];
00283             if (isset($row[PR_RECIPIENT_TYPE]))
00284                 $attendee->attendeetype = $row[PR_RECIPIENT_TYPE];
00285             // Some attendees have no email or name (eg resources), and if you
00286             // don't send one of those fields, the phone will give an error ... so
00287             // we don't send it in that case.
00288             // also ignore the "attendee" if the email is equal to the organizers' email
00289             if(isset($attendee->name) && isset($attendee->email) && $attendee->email != "" && (!isset($message->organizeremail) || (isset($message->organizeremail) && $attendee->email != $message->organizeremail)))
00290                 array_push($message->attendees, $attendee);
00291         }
00292 
00293         if (!isset($message->nativebodytype)) $message->nativebodytype = $this->getNativeBodyType($messageprops);
00294 
00295         return $message;
00296     }
00297 
00310     private function getRecurrence($mapimessage, $recurprops, &$syncMessage, &$syncRecurrence, $tz) {
00311         if ($syncRecurrence instanceof SyncTaskRecurrence)
00312             $recurrence = new TaskRecurrence($this->store, $mapimessage);
00313         else
00314             $recurrence = new Recurrence($this->store, $mapimessage);
00315 
00316         switch($recurrence->recur["type"]) {
00317             case 10: // daily
00318                 switch($recurrence->recur["subtype"]) {
00319                     default:
00320                         $syncRecurrence->type = 0;
00321                         break;
00322                     case 1:
00323                         $syncRecurrence->type = 0;
00324                         $syncRecurrence->dayofweek = 62; // mon-fri
00325                         $syncRecurrence->interval = 1;
00326                         break;
00327                 }
00328                 break;
00329             case 11: // weekly
00330                     $syncRecurrence->type = 1;
00331                 break;
00332             case 12: // monthly
00333                 switch($recurrence->recur["subtype"]) {
00334                     default:
00335                         $syncRecurrence->type = 2;
00336                         break;
00337                     case 3:
00338                         $syncRecurrence->type = 3;
00339                         break;
00340                 }
00341                 break;
00342             case 13: // yearly
00343                 switch($recurrence->recur["subtype"]) {
00344                     default:
00345                         $syncRecurrence->type = 4;
00346                         break;
00347                     case 2:
00348                         $syncRecurrence->type = 5;
00349                         break;
00350                     case 3:
00351                         $syncRecurrence->type = 6;
00352                 }
00353         }
00354         // Termination
00355         switch($recurrence->recur["term"]) {
00356             case 0x21:
00357                 $syncRecurrence->until = $recurrence->recur["end"];
00358                 // fixes Mantis #350 : recur-end does not consider timezones - use ClipEnd if available
00359                 if (isset($recurprops[$recurrence->proptags["enddate_recurring"]]))
00360                     $syncRecurrence->until = $recurprops[$recurrence->proptags["enddate_recurring"]];
00361                 // add one day (minus 1 sec) to the end time to make sure the last occurrence is covered
00362                 $syncRecurrence->until += 86399;
00363                 break;
00364             case 0x22:
00365                 $syncRecurrence->occurrences = $recurrence->recur["numoccur"]; break;
00366             case 0x23:
00367                 // never ends
00368                 break;
00369         }
00370 
00371         // Correct 'alldayevent' because outlook fails to set it on recurring items of 24 hours or longer
00372         if(isset($recurrence->recur["endocc"], $recurrence->recur["startocc"]) && ($recurrence->recur["endocc"] - $recurrence->recur["startocc"] >= 1440))
00373             $syncMessage->alldayevent = true;
00374 
00375         // Interval is different according to the type/subtype
00376         switch($recurrence->recur["type"]) {
00377             case 10:
00378                 if($recurrence->recur["subtype"] == 0)
00379                     $syncRecurrence->interval = (int)($recurrence->recur["everyn"] / 1440);  // minutes
00380                 break;
00381             case 11:
00382             case 12:
00383                 $syncRecurrence->interval = $recurrence->recur["everyn"];
00384                 break; // months / weeks
00385             case 13:
00386                 $syncRecurrence->interval = (int)($recurrence->recur["everyn"] / 12);
00387                 break; // months
00388         }
00389 
00390         if(isset($recurrence->recur["weekdays"]))
00391             $syncRecurrence->dayofweek = $recurrence->recur["weekdays"]; // bitmask of days (1 == sunday, 128 == saturday
00392         if(isset($recurrence->recur["nday"]))
00393             $syncRecurrence->weekofmonth = $recurrence->recur["nday"]; // N'th {DAY} of {X} (0-5)
00394         if(isset($recurrence->recur["month"]))
00395             $syncRecurrence->monthofyear = (int)($recurrence->recur["month"] / (60 * 24 * 29)) + 1; // works ok due to rounding. see also $monthminutes below (1-12)
00396         if(isset($recurrence->recur["monthday"]))
00397             $syncRecurrence->dayofmonth = $recurrence->recur["monthday"]; // day of month (1-31)
00398 
00399         // All changed exceptions are appointments within the 'exceptions' array. They contain the same items as a normal appointment
00400         foreach($recurrence->recur["changed_occurences"] as $change) {
00401             $exception = new SyncAppointmentException();
00402 
00403             // start, end, basedate, subject, remind_before, reminderset, location, busystatus, alldayevent, label
00404             if(isset($change["start"]))
00405                 $exception->starttime = $this->getGMTTimeByTZ($change["start"], $tz);
00406             if(isset($change["end"]))
00407                 $exception->endtime = $this->getGMTTimeByTZ($change["end"], $tz);
00408             if(isset($change["basedate"])) {
00409                 $exception->exceptionstarttime = $this->getGMTTimeByTZ($this->getDayStartOfTimestamp($change["basedate"]) + $recurrence->recur["startocc"] * 60, $tz);
00410 
00411                 //open body because getting only property might not work because of memory limit
00412                 $exceptionatt = $recurrence->getExceptionAttachment($change["basedate"]);
00413                 if($exceptionatt) {
00414                     $exceptionobj = mapi_attach_openobj($exceptionatt, 0);
00415                     $exception->body = mapi_openproperty($exceptionobj, PR_BODY);
00416                 }
00417             }
00418             if(isset($change["subject"]))
00419                 $exception->subject = w2u($change["subject"]);
00420             if(isset($change["reminder_before"]) && $change["reminder_before"])
00421                 $exception->reminder = $change["remind_before"];
00422             if(isset($change["location"]))
00423                 $exception->location = w2u($change["location"]);
00424             if(isset($change["busystatus"]))
00425                 $exception->busystatus = $change["busystatus"];
00426             if(isset($change["alldayevent"]))
00427                 $exception->alldayevent = $change["alldayevent"];
00428 
00429             // set some data from the original appointment
00430             if (isset($syncMessage->uid))
00431                 $exception->uid = $syncMessage->uid;
00432             if (isset($syncMessage->organizername))
00433                 $exception->organizername = $syncMessage->organizername;
00434             if (isset($syncMessage->organizeremail))
00435                 $exception->organizeremail = $syncMessage->organizeremail;
00436 
00437             if(!isset($syncMessage->exceptions))
00438                 $syncMessage->exceptions = array();
00439 
00440             array_push($syncMessage->exceptions, $exception);
00441         }
00442 
00443         // Deleted appointments contain only the original date (basedate) and a 'deleted' tag
00444         foreach($recurrence->recur["deleted_occurences"] as $deleted) {
00445             $exception = new SyncAppointmentException();
00446 
00447             $exception->exceptionstarttime = $this->getGMTTimeByTZ($this->getDayStartOfTimestamp($deleted) + $recurrence->recur["startocc"] * 60, $tz);
00448             $exception->deleted = "1";
00449 
00450             if(!isset($syncMessage->exceptions))
00451                 $syncMessage->exceptions = array();
00452 
00453             array_push($syncMessage->exceptions, $exception);
00454         }
00455 
00456         if (isset($syncMessage->complete) && $syncMessage->complete) {
00457             $syncRecurrence->complete = $syncMessage->complete;
00458         }
00459     }
00460 
00470     private function getEmail($mapimessage, $contentparameters) {
00471         $message = new SyncMail();
00472 
00473         $this->getPropsFromMAPI($message, $mapimessage, MAPIMapping::GetEmailMapping());
00474 
00475         $emailproperties = MAPIMapping::GetEmailProperties();
00476         $messageprops = $this->getProps($mapimessage, $emailproperties);
00477 
00478         if(isset($messageprops[PR_SOURCE_KEY]))
00479             $sourcekey = $messageprops[PR_SOURCE_KEY];
00480         else
00481             return false;
00482 
00483         //set the body according to contentparameters and supported AS version
00484         $this->setMessageBody($mapimessage, $contentparameters, $message);
00485 
00486         $fromname = $fromaddr = "";
00487 
00488         if(isset($messageprops[$emailproperties["representingname"]])) {
00489             // remove encapsulating double quotes from the representingname
00490             $fromname = preg_replace('/^\"(.*)\"$/',"\${1}", $messageprops[$emailproperties["representingname"]]);
00491         }
00492         if(isset($messageprops[$emailproperties["representingentryid"]]))
00493             $fromaddr = $this->getSMTPAddressFromEntryID($messageprops[$emailproperties["representingentryid"]]);
00494 
00495         if($fromname == $fromaddr)
00496             $fromname = "";
00497 
00498         if($fromname)
00499             $from = "\"" . w2u($fromname) . "\" <" . w2u($fromaddr) . ">";
00500         else
00501             //START CHANGED dw2412 HTC shows "error" if sender name is unknown
00502             $from = "\"" . w2u($fromaddr) . "\" <" . w2u($fromaddr) . ">";
00503             //END CHANGED dw2412 HTC shows "error" if sender name is unknown
00504 
00505         $message->from = $from;
00506 
00507         // process Meeting Requests
00508         if(isset($message->messageclass) && strpos($message->messageclass, "IPM.Schedule.Meeting") === 0) {
00509             $message->meetingrequest = new SyncMeetingRequest();
00510             $this->getPropsFromMAPI($message->meetingrequest, $mapimessage, MAPIMapping::GetMeetingRequestMapping());
00511 
00512             $meetingrequestproperties = MAPIMapping::GetMeetingRequestProperties();
00513             $props = $this->getProps($mapimessage, $meetingrequestproperties);
00514 
00515             // Get the GOID
00516             if(isset($props[$meetingrequestproperties["goidtag"]]))
00517                 $message->meetingrequest->globalobjid = base64_encode($props[$meetingrequestproperties["goidtag"]]);
00518 
00519             // Set Timezone
00520             if(isset($props[$meetingrequestproperties["timezonetag"]]))
00521                 $tz = $this->getTZFromMAPIBlob($props[$meetingrequestproperties["timezonetag"]]);
00522             else
00523                 $tz = $this->getGMTTZ();
00524 
00525             $message->meetingrequest->timezone = base64_encode($this->getSyncBlobFromTZ($tz));
00526 
00527             // send basedate if exception
00528             if(isset($props[$meetingrequestproperties["recReplTime"]]) ||
00529                 (isset($props[$meetingrequestproperties["lidIsException"]]) && $props[$meetingrequestproperties["lidIsException"]] == true)) {
00530                 if (isset($props[$meetingrequestproperties["recReplTime"]])){
00531                     $basedate = $props[$meetingrequestproperties["recReplTime"]];
00532                     $message->meetingrequest->recurrenceid = $this->getGMTTimeByTZ($basedate, $this->getGMTTZ());
00533                 }
00534                 else {
00535                     if (!isset($props[$meetingrequestproperties["goidtag"]]) || !isset($props[$meetingrequestproperties["recurStartTime"]]) || !isset($props[$meetingrequestproperties["timezonetag"]]))
00536                         ZLog::Write(LOGLEVEL_WARN, "Missing property to set correct basedate for exception");
00537                     else {
00538                         $basedate = Utils::ExtractBaseDate($props[$meetingrequestproperties["goidtag"]], $props[$meetingrequestproperties["recurStartTime"]]);
00539                         $message->meetingrequest->recurrenceid = $this->getGMTTimeByTZ($basedate, $tz);
00540                     }
00541                 }
00542             }
00543 
00544             // Organizer is the sender
00545             $message->meetingrequest->organizer = $message->from;
00546 
00547             // Process recurrence
00548             if(isset($props[$meetingrequestproperties["isrecurringtag"]]) && $props[$meetingrequestproperties["isrecurringtag"]]) {
00549                 $myrec = new SyncMeetingRequestRecurrence();
00550                 // get recurrence -> put $message->meetingrequest as message so the 'alldayevent' is set correctly
00551                 $this->getRecurrence($mapimessage, $props, $message->meetingrequest, $myrec, $tz);
00552                 $message->meetingrequest->recurrences = array($myrec);
00553             }
00554 
00555             // Force the 'alldayevent' in the object at all times. (non-existent == 0)
00556             if(!isset($message->meetingrequest->alldayevent) || $message->meetingrequest->alldayevent == "")
00557                 $message->meetingrequest->alldayevent = 0;
00558 
00559             // Instancetype
00560             // 0 = single appointment
00561             // 1 = master recurring appointment
00562             // 2 = single instance of recurring appointment
00563             // 3 = exception of recurring appointment
00564             $message->meetingrequest->instancetype = 0;
00565             if (isset($props[$meetingrequestproperties["isrecurringtag"]]) && $props[$meetingrequestproperties["isrecurringtag"]] == 1)
00566                 $message->meetingrequest->instancetype = 1;
00567             else if ((!isset($props[$meetingrequestproperties["isrecurringtag"]]) || $props[$meetingrequestproperties["isrecurringtag"]] == 0 )&& isset($message->meetingrequest->recurrenceid))
00568                 if (isset($props[$meetingrequestproperties["appSeqNr"]]) && $props[$meetingrequestproperties["appSeqNr"]] == 0 )
00569                     $message->meetingrequest->instancetype = 2;
00570                 else
00571                     $message->meetingrequest->instancetype = 3;
00572 
00573             // Disable reminder if it is off
00574             if(!isset($props[$meetingrequestproperties["reminderset"]]) || $props[$meetingrequestproperties["reminderset"]] == false)
00575                 $message->meetingrequest->reminder = "";
00576             //the property saves reminder in minutes, but we need it in secs
00577             else {
00579                 if ($props[$meetingrequestproperties["remindertime"]] == 0x5AE980E1)
00580                     $message->meetingrequest->reminder = 900;
00581                 else
00582                     $message->meetingrequest->reminder = $props[$meetingrequestproperties["remindertime"]] * 60;
00583             }
00584 
00585             // Set sensitivity to 0 if missing
00586             if(!isset($message->meetingrequest->sensitivity))
00587                 $message->meetingrequest->sensitivity = 0;
00588 
00589             // if a meeting request response hasn't been processed yet,
00590             // do it so that the attendee status is updated on the mobile
00591             if(!isset($messageprops[$emailproperties["processed"]])) {
00592                 $req = new Meetingrequest($this->store, $mapimessage, $this->session);
00593                 if ($req->isMeetingRequestResponse()) {
00594                     $req->processMeetingRequestResponse();
00595                 }
00596                 if ($req->isMeetingCancellation()) {
00597                     $req->processMeetingCancellation();
00598                 }
00599             }
00600         }
00601 
00602         // Add attachments
00603         $attachtable = mapi_message_getattachmenttable($mapimessage);
00604         $rows = mapi_table_queryallrows($attachtable, array(PR_ATTACH_NUM));
00605         $entryid = bin2hex($messageprops[$emailproperties["entryid"]]);
00606 
00607         foreach($rows as $row) {
00608             if(isset($row[PR_ATTACH_NUM])) {
00609                 $mapiattach = mapi_message_openattach($mapimessage, $row[PR_ATTACH_NUM]);
00610 
00611                 $attachprops = mapi_getprops($mapiattach, array(PR_ATTACH_LONG_FILENAME, PR_ATTACH_FILENAME, PR_ATTACHMENT_HIDDEN, PR_ATTACH_CONTENT_ID, PR_ATTACH_CONTENT_ID_W, PR_ATTACH_MIME_TAG, PR_ATTACH_MIME_TAG_W));
00612 
00613                 $stream = mapi_openpropertytostream($mapiattach, PR_ATTACH_DATA_BIN);
00614                 if($stream) {
00615                     $stat = mapi_stream_stat($stream);
00616 
00617                     if (Request::GetProtocolVersion() >= 12.0) {
00618                         $attach = new SyncBaseAttachment();
00619                     }
00620                     else {
00621                         $attach = new SyncAttachment();
00622                     }
00623 
00624                     // the displayname is handled equal for all AS versions
00625                     $attach->displayname = w2u((isset($attachprops[PR_ATTACH_LONG_FILENAME])) ? $attachprops[PR_ATTACH_LONG_FILENAME] : ((isset($attachprops[PR_ATTACH_FILENAME])) ? $attachprops[PR_ATTACH_FILENAME] : "attachment.bin"));
00626 
00627                     // fix attachment name in case of inline images
00628                     if ($attach->displayname == "inline.txt" && (isset($attachprops[PR_ATTACH_MIME_TAG]) || $attachprops[PR_ATTACH_MIME_TAG_W])) {
00629                         $mimetype = (isset($attachprops[PR_ATTACH_MIME_TAG]))?$attachprops[PR_ATTACH_MIME_TAG]:$attachprops[PR_ATTACH_MIME_TAG_W];
00630                         $mime = explode("/", $mimetype);
00631 
00632                         if (count($mime) == 2 && $mime[0] == "image") {
00633                             $attach->displayname = "inline." . $mime[1];
00634                         }
00635                     }
00636 
00637                     // set AS version specific parameters
00638                     if (Request::GetProtocolVersion() >= 12.0) {
00639                         $attach->filereference = $entryid.":".$row[PR_ATTACH_NUM];
00640                         $attach->method = 1;
00641                         $attach->estimatedDataSize = $stat["cb"];
00642 
00643                         if (isset($attachprops[PR_ATTACH_CONTENT_ID]) && $attachprops[PR_ATTACH_CONTENT_ID])
00644                             $attach->contentid = $attachprops[PR_ATTACH_CONTENT_ID];
00645 
00646                         if (!isset($attach->contentid) && isset($attachprops[PR_ATTACH_CONTENT_ID_W]) && $attachprops[PR_ATTACH_CONTENT_ID_W])
00647                             $attach->contentid = $attachprops[PR_ATTACH_CONTENT_ID_W];
00648 
00649                         if (isset($attachprops[PR_ATTACHMENT_HIDDEN]) && $attachprops[PR_ATTACHMENT_HIDDEN]) $attach->isinline = 1;
00650 
00651                         if(!isset($message->asattachments))
00652                             $message->asattachments = array();
00653 
00654                         array_push($message->asattachments, $attach);
00655                     }
00656                     else {
00657                         $attach->attsize = $stat["cb"];
00658                         $attach->attname = $entryid.":".$row[PR_ATTACH_NUM];
00659                         if(!isset($message->attachments))
00660                             $message->attachments = array();
00661 
00662                         array_push($message->attachments, $attach);
00663                     }
00664                 }
00665             }
00666         }
00667 
00668         // Get To/Cc as SMTP addresses (this is different from displayto and displaycc because we are putting
00669         // in the SMTP addresses as well, while displayto and displaycc could just contain the display names
00670         $message->to = array();
00671         $message->cc = array();
00672 
00673         $reciptable = mapi_message_getrecipienttable($mapimessage);
00674         $rows = mapi_table_queryallrows($reciptable, array(PR_RECIPIENT_TYPE, PR_DISPLAY_NAME, PR_ADDRTYPE, PR_EMAIL_ADDRESS, PR_SMTP_ADDRESS, PR_ENTRYID));
00675 
00676         foreach ($rows as $row) {
00677             $address = "";
00678             $fulladdr = "";
00679 
00680             $addrtype = isset($row[PR_ADDRTYPE]) ? $row[PR_ADDRTYPE] : "";
00681 
00682             if (isset($row[PR_SMTP_ADDRESS]))
00683                 $address = $row[PR_SMTP_ADDRESS];
00684             elseif ($addrtype == "SMTP" && isset($row[PR_EMAIL_ADDRESS]))
00685                 $address = $row[PR_EMAIL_ADDRESS];
00686             elseif ($addrtype == "ZARAFA" && isset($row[PR_ENTRYID]))
00687                 $address = $this->getSMTPAddressFromEntryID($row[PR_ENTRYID]);
00688 
00689             $name = isset($row[PR_DISPLAY_NAME]) ? $row[PR_DISPLAY_NAME] : "";
00690 
00691             if($name == "" || $name == $address)
00692                 $fulladdr = w2u($address);
00693             else {
00694                 if (substr($name, 0, 1) != '"' && substr($name, -1) != '"') {
00695                     $fulladdr = "\"" . w2u($name) ."\" <" . w2u($address) . ">";
00696                 }
00697                 else {
00698                     $fulladdr = w2u($name) ."<" . w2u($address) . ">";
00699                 }
00700             }
00701 
00702             if($row[PR_RECIPIENT_TYPE] == MAPI_TO) {
00703                 array_push($message->to, $fulladdr);
00704             } else if($row[PR_RECIPIENT_TYPE] == MAPI_CC) {
00705                 array_push($message->cc, $fulladdr);
00706             }
00707         }
00708 
00709         if (is_array($message->to) && !empty($message->to)) $message->to = implode(", ", $message->to);
00710         if (is_array($message->cc) && !empty($message->cc)) $message->cc = implode(", ", $message->cc);
00711 
00712         // without importance some mobiles assume "0" (low) - Mantis #439
00713         if (!isset($message->importance))
00714             $message->importance = IMPORTANCE_NORMAL;
00715 
00716         //TODO contentclass and nativebodytype and internetcpid
00717         if (!isset($message->internetcpid)) $message->internetcpid = (defined('STORE_INTERNET_CPID')) ? constant('STORE_INTERNET_CPID') : INTERNET_CPID_WINDOWS1252;
00718         $this->setFlag($mapimessage, $message);
00719         $message->contentclass = DEFAULT_EMAIL_CONTENTCLASS;
00720         if (!isset($message->nativebodytype)) $message->nativebodytype = $this->getNativeBodyType($messageprops);
00721 
00722         return $message;
00723     }
00724 
00734     private function getNote($mapimessage, $contentparameters) {
00735         $message = new SyncNote();
00736 
00737         // Standard one-to-one mappings first
00738         $this->getPropsFromMAPI($message, $mapimessage, MAPIMapping::GetNoteMapping());
00739 
00740         //set the body according to contentparameters and supported AS version
00741         $this->setMessageBody($mapimessage, $contentparameters, $message);
00742 
00743         return $message;
00744     }
00745 
00754     public function GetFolder($mapifolder) {
00755         $folder = new SyncFolder();
00756 
00757         $folderprops = mapi_getprops($mapifolder, array(PR_DISPLAY_NAME, PR_PARENT_ENTRYID, PR_SOURCE_KEY, PR_PARENT_SOURCE_KEY, PR_ENTRYID, PR_CONTAINER_CLASS, PR_ATTR_HIDDEN));
00758         $storeprops = mapi_getprops($this->store, array(PR_IPM_SUBTREE_ENTRYID));
00759 
00760         if(!isset($folderprops[PR_DISPLAY_NAME]) ||
00761            !isset($folderprops[PR_PARENT_ENTRYID]) ||
00762            !isset($folderprops[PR_SOURCE_KEY]) ||
00763            !isset($folderprops[PR_ENTRYID]) ||
00764            !isset($folderprops[PR_PARENT_SOURCE_KEY]) ||
00765            !isset($storeprops[PR_IPM_SUBTREE_ENTRYID])) {
00766             ZLog::Write(LOGLEVEL_ERROR, "MAPIProvider->GetFolder(): invalid folder. Missing properties");
00767             return false;
00768         }
00769 
00770         // ignore hidden folders
00771         if (isset($folderprops[PR_ATTR_HIDDEN]) && $folderprops[PR_ATTR_HIDDEN] != false) {
00772             ZLog::Write(LOGLEVEL_DEBUG, sprintf("MAPIProvider->GetFolder(): invalid folder '%s' as it is a hidden folder (PR_ATTR_HIDDEN)", $folderprops[PR_DISPLAY_NAME]));
00773             return false;
00774         }
00775 
00776         $folder->serverid = bin2hex($folderprops[PR_SOURCE_KEY]);
00777         if($folderprops[PR_PARENT_ENTRYID] == $storeprops[PR_IPM_SUBTREE_ENTRYID])
00778             $folder->parentid = "0";
00779         else
00780             $folder->parentid = bin2hex($folderprops[PR_PARENT_SOURCE_KEY]);
00781         $folder->displayname = w2u($folderprops[PR_DISPLAY_NAME]);
00782         $folder->type = $this->GetFolderType($folderprops[PR_ENTRYID], isset($folderprops[PR_CONTAINER_CLASS])?$folderprops[PR_CONTAINER_CLASS]:false);
00783 
00784         return $folder;
00785     }
00786 
00797     public function GetFolderType($entryid, $class = false) {
00798         $storeprops = mapi_getprops($this->store, array(PR_IPM_OUTBOX_ENTRYID, PR_IPM_WASTEBASKET_ENTRYID, PR_IPM_SENTMAIL_ENTRYID));
00799         $inbox = mapi_msgstore_getreceivefolder($this->store);
00800         $inboxprops = mapi_getprops($inbox, array(PR_ENTRYID, PR_IPM_DRAFTS_ENTRYID, PR_IPM_TASK_ENTRYID, PR_IPM_APPOINTMENT_ENTRYID, PR_IPM_CONTACT_ENTRYID, PR_IPM_NOTE_ENTRYID, PR_IPM_JOURNAL_ENTRYID));
00801 
00802         if($entryid == $inboxprops[PR_ENTRYID])
00803             return SYNC_FOLDER_TYPE_INBOX;
00804         if($entryid == $inboxprops[PR_IPM_DRAFTS_ENTRYID])
00805             return SYNC_FOLDER_TYPE_DRAFTS;
00806         if($entryid == $storeprops[PR_IPM_WASTEBASKET_ENTRYID])
00807             return SYNC_FOLDER_TYPE_WASTEBASKET;
00808         if($entryid == $storeprops[PR_IPM_SENTMAIL_ENTRYID])
00809             return SYNC_FOLDER_TYPE_SENTMAIL;
00810         if($entryid == $storeprops[PR_IPM_OUTBOX_ENTRYID])
00811             return SYNC_FOLDER_TYPE_OUTBOX;
00812         if($entryid == $inboxprops[PR_IPM_TASK_ENTRYID])
00813             return SYNC_FOLDER_TYPE_TASK;
00814         if($entryid == $inboxprops[PR_IPM_APPOINTMENT_ENTRYID])
00815             return SYNC_FOLDER_TYPE_APPOINTMENT;
00816         if($entryid == $inboxprops[PR_IPM_CONTACT_ENTRYID])
00817             return SYNC_FOLDER_TYPE_CONTACT;
00818         if($entryid == $inboxprops[PR_IPM_NOTE_ENTRYID])
00819             return SYNC_FOLDER_TYPE_NOTE;
00820         if($entryid == $inboxprops[PR_IPM_JOURNAL_ENTRYID])
00821             return SYNC_FOLDER_TYPE_JOURNAL;
00822 
00823         // user created folders
00824         if ($class == "IPF.Note")
00825             return SYNC_FOLDER_TYPE_USER_MAIL;
00826         if ($class == "IPF.Task")
00827             return SYNC_FOLDER_TYPE_USER_TASK;
00828         if ($class == "IPF.Appointment")
00829             return SYNC_FOLDER_TYPE_USER_APPOINTMENT;
00830         if ($class == "IPF.Contact")
00831             return SYNC_FOLDER_TYPE_USER_CONTACT;
00832         if ($class == "IPF.StickyNote")
00833             return SYNC_FOLDER_TYPE_USER_NOTE;
00834         if ($class == "IPF.Journal")
00835             return  SYNC_FOLDER_TYPE_USER_JOURNAL;
00836 
00837         return SYNC_FOLDER_TYPE_OTHER;
00838     }
00839 
00840 
00855     public function SetMessage($mapimessage, $message) {
00856         // TODO check with instanceof
00857         switch(strtolower(get_class($message))) {
00858             case "synccontact":
00859                 return $this->setContact($mapimessage, $message);
00860             case "syncappointment":
00861                 return $this->setAppointment($mapimessage, $message);
00862             case "synctask":
00863                 return $this->setTask($mapimessage, $message);
00864             case "syncnote":
00865                 return $this->setNote($mapimessage, $message);
00866             default:
00867                 //for emails only flag (read and todo) changes are possible
00868                 return $this->setEmail($mapimessage, $message);
00869         }
00870     }
00871 
00878     private function setEmail($mapimessage, $message) {
00879         $flagmapping = MAPIMapping::GetMailFlagsMapping();
00880         $flagprops = MAPIMapping::GetMailFlagsProperties();
00881         $flagprops = array_merge($this->getPropIdsFromStrings($flagmapping), $this->getPropIdsFromStrings($flagprops));
00882         // flag specific properties to be set
00883         $props = $delprops = array();
00884         // unset message flags if:
00885         // flag is not set
00886         if (empty($message->flag) ||
00887             // flag status is not set
00888             !isset($message->flag->flagstatus) ||
00889             // flag status is 0 or empty
00890             (isset($message->flag->flagstatus) && ($message->flag->flagstatus == 0 || $message->flag->flagstatus == "")) ) {
00891             // if message flag is empty, some properties need to be deleted
00892             // and some set to 0 or false
00893 
00894             $props[$flagprops["todoitemsflags"]] = 0;
00895             $props[$flagprops["status"]] = 0;
00896             $props[$flagprops["completion"]] = 0.0;
00897             $props[$flagprops["flagtype"]] = "";
00898             $props[$flagprops["ordinaldate"]] = 0x7fffffff; // ordinal date is 12am 1.1.4501, set it to max possible value
00899             $props[$flagprops["subordinaldate"]] = "";
00900             $props[$flagprops["replyrequested"]] = false;
00901             $props[$flagprops["responserequested"]] = false;
00902             $props[$flagprops["reminderset"]] = false;
00903             $props[$flagprops["complete"]] = false;
00904 
00905             $delprops[] = $flagprops["todotitle"];
00906             $delprops[] = $flagprops["duedate"];
00907             $delprops[] = $flagprops["startdate"];
00908             $delprops[] = $flagprops["datecompleted"];
00909             $delprops[] = $flagprops["utcstartdate"];
00910             $delprops[] = $flagprops["utcduedate"];
00911             $delprops[] = $flagprops["completetime"];
00912             $delprops[] = $flagprops["flagstatus"];
00913             $delprops[] = $flagprops["flagicon"];
00914         }
00915         else {
00916             $this->setPropsInMAPI($mapimessage, $message->flag, $flagmapping);
00917             $props[$flagprops["todoitemsflags"]] = 1;
00918             $props[$flagprops["todotitle"]] = $message->subject;
00919             // ordinal date is utc current time
00920             if (!isset($message->flag->ordinaldate) || empty($message->flag->ordinaldate)) {
00921                 $props[$flagprops["ordinaldate"]] = time();
00922             }
00923             // the default value
00924             if (!isset($message->flag->subordinaldate) || empty($message->flag->subordinaldate)) {
00925                 $props[$flagprops["subordinaldate"]] = "5555555";
00926             }
00927             $props[$flagprops["flagicon"]] = 6; //red flag icon
00928             $props[$flagprops["replyrequested"]] = true;
00929             $props[$flagprops["responserequested"]] = true;
00930 
00931             if ($message->flag->flagstatus == SYNC_FLAGSTATUS_COMPLETE) {
00932                 $props[$flagprops["status"]] = olTaskComplete;
00933                 $props[$flagprops["completion"]] = 1.0;
00934                 $props[$flagprops["complete"]] = true;
00935                 $props[$flagprops["replyrequested"]] = false;
00936                 $props[$flagprops["responserequested"]] = false;
00937                 unset($props[$flagprops["flagicon"]]);
00938                 $delprops[] = $flagprops["flagicon"];
00939             }
00940         }
00941 
00942         if (!empty($props)) {
00943             mapi_setprops($mapimessage, $props);
00944         }
00945         if (!empty($delprops)) {
00946             mapi_deleteprops($mapimessage, $delprops);
00947         }
00948     }
00949 
00959     private function setAppointment($mapimessage, $appointment) {
00960         // Get timezone info
00961         if(isset($appointment->timezone))
00962             $tz = $this->getTZFromSyncBlob(base64_decode($appointment->timezone));
00963         else
00964             $tz = false;
00965 
00966         //calculate duration because without it some webaccess views are broken. duration is in min
00967         $localstart = $this->getLocaltimeByTZ($appointment->starttime, $tz);
00968         $localend = $this->getLocaltimeByTZ($appointment->endtime, $tz);
00969         $duration = ($localend - $localstart)/60;
00970 
00971         //nokia sends an yearly event with 0 mins duration but as all day event,
00972         //so make it end next day
00973         if ($appointment->starttime == $appointment->endtime && isset($appointment->alldayevent) && $appointment->alldayevent) {
00974             $duration = 1440;
00975             $appointment->endtime = $appointment->starttime + 24 * 60 * 60;
00976             $localend = $localstart + 24 * 60 * 60;
00977         }
00978 
00979         // is the transmitted UID OL compatible?
00980         // if not, encapsulate the transmitted uid
00981         $appointment->uid = Utils::GetOLUidFromICalUid($appointment->uid);
00982 
00983         mapi_setprops($mapimessage, array(PR_MESSAGE_CLASS => "IPM.Appointment"));
00984 
00985         $appointmentmapping = MAPIMapping::GetAppointmentMapping();
00986         $this->setPropsInMAPI($mapimessage, $appointment, $appointmentmapping);
00987         $appointmentprops = MAPIMapping::GetAppointmentProperties();
00988         $appointmentprops = array_merge($this->getPropIdsFromStrings($appointmentmapping), $this->getPropIdsFromStrings($appointmentprops));
00989         //appointment specific properties to be set
00990         $props = array();
00991 
00992         //we also have to set the responsestatus and not only meetingstatus, so we use another mapi tag
00993         if (isset($appointment->meetingstatus)) $props[$appointmentprops["meetingstatus"]] = $appointment->meetingstatus;
00994 
00995         //sensitivity is not enough to mark an appointment as private, so we use another mapi tag
00996         $private = (isset($appointment->sensitivity) && $appointment->sensitivity == 0) ? false : true;
00997 
00998         // Set commonstart/commonend to start/end and remindertime to start, duration, private and cleanGlobalObjectId
00999         $props[$appointmentprops["commonstart"]] = $appointment->starttime;
01000         $props[$appointmentprops["commonend"]] = $appointment->endtime;
01001         $props[$appointmentprops["reminderstart"]] = $appointment->starttime;
01002         // Set reminder boolean to 'true' if reminder is set
01003         $props[$appointmentprops["reminderset"]] = isset($appointment->reminder) ? true : false;
01004         $props[$appointmentprops["duration"]] = $duration;
01005         $props[$appointmentprops["private"]] = $private;
01006         $props[$appointmentprops["uid"]] = $appointment->uid;
01007         // Set named prop 8510, unknown property, but enables deleting a single occurrence of a recurring
01008         // type in OLK2003.
01009         $props[$appointmentprops["sideeffects"]] = 369;
01010 
01011 
01012         if(isset($appointment->reminder) && $appointment->reminder > 0) {
01013             // Set 'flagdueby' to correct value (start - reminderminutes)
01014             $props[$appointmentprops["flagdueby"]] = $appointment->starttime - $appointment->reminder * 60;
01015             $props[$appointmentprops["remindertime"]] = $appointment->reminder;
01016         }
01017         // unset the reminder
01018         else {
01019             $props[$appointmentprops["reminderset"]] = false;
01020         }
01021 
01022         if (isset($appointment->asbody)) {
01023             $this->setASbody($appointment->asbody, $props, $appointmentprops);
01024         }
01025 
01026         if(isset($appointment->recurrence)) {
01027             // Set PR_ICON_INDEX to 1025 to show correct icon in category view
01028             $props[$appointmentprops["icon"]] = 1025;
01029 
01030             //if there aren't any exceptions, use the 'old style' set recurrence
01031             $noexceptions = true;
01032 
01033             $recurrence = new Recurrence($this->store, $mapimessage);
01034             $recur = array();
01035             $this->setRecurrence($appointment, $recur);
01036 
01037             // set the recurrence type to that of the MAPI
01038             $props[$appointmentprops["recurrencetype"]] = $recur["recurrencetype"];
01039 
01040             $starttime = $this->gmtime($localstart);
01041             $endtime = $this->gmtime($localend);
01042 
01043             //set recurrence start here because it's calculated differently for tasks and appointments
01044             $recur["start"] = $this->getDayStartOfTimestamp($this->getGMTTimeByTZ($localstart, $tz));
01045 
01046             $recur["startocc"] = $starttime["tm_hour"] * 60 + $starttime["tm_min"];
01047             $recur["endocc"] = $recur["startocc"] + $duration; // Note that this may be > 24*60 if multi-day
01048 
01049             //only tasks can regenerate
01050             $recur["regen"] = false;
01051 
01052             // Process exceptions. The PDA will send all exceptions for this recurring item.
01053             if(isset($appointment->exceptions)) {
01054                 foreach($appointment->exceptions as $exception) {
01055                     // we always need the base date
01056                     if(!isset($exception->exceptionstarttime))
01057                         continue;
01058 
01059                     if(isset($exception->deleted) && $exception->deleted) {
01060                         // Delete exception
01061                         if(!isset($recur["deleted_occurences"]))
01062                             $recur["deleted_occurences"] = array();
01063 
01064                         array_push($recur["deleted_occurences"], $this->getDayStartOfTimestamp($exception->exceptionstarttime));
01065                     } else {
01066                         // Change exception
01067                         $basedate = $this->getDayStartOfTimestamp($exception->exceptionstarttime);
01068                         $mapiexception = array("basedate" => $basedate);
01069                         //other exception properties which are not handled in recurrence
01070                         $exceptionprops = array();
01071 
01072                         if(isset($exception->starttime)) {
01073                             $mapiexception["start"] = $this->getLocaltimeByTZ($exception->starttime, $tz);
01074                             $exceptionprops[$appointmentprops["starttime"]] = $exception->starttime;
01075                         }
01076                         if(isset($exception->endtime)) {
01077                             $mapiexception["end"] = $this->getLocaltimeByTZ($exception->endtime, $tz);
01078                             $exceptionprops[$appointmentprops["endtime"]] = $exception->endtime;
01079                         }
01080                         if(isset($exception->subject))
01081                             $exceptionprops[$appointmentprops["subject"]] = $mapiexception["subject"] = u2w($exception->subject);
01082                         if(isset($exception->location))
01083                             $exceptionprops[$appointmentprops["location"]] = $mapiexception["location"] = u2w($exception->location);
01084                         if(isset($exception->busystatus))
01085                             $exceptionprops[$appointmentprops["busystatus"]] = $mapiexception["busystatus"] = $exception->busystatus;
01086                         if(isset($exception->reminder)) {
01087                             $exceptionprops[$appointmentprops["reminderset"]] = $mapiexception["reminder_set"] = 1;
01088                             $exceptionprops[$appointmentprops["remindertime"]] = $mapiexception["remind_before"] = $exception->reminder;
01089                         }
01090                         if(isset($exception->alldayevent))
01091                             $exceptionprops[$appointmentprops["alldayevent"]] = $mapiexception["alldayevent"] = $exception->alldayevent;
01092 
01093 
01094                         if(!isset($recur["changed_occurences"]))
01095                             $recur["changed_occurences"] = array();
01096 
01097                         if (isset($exception->body))
01098                             $exceptionprops[$appointmentprops["body"]] = u2w($exception->body);
01099 
01100                         array_push($recur["changed_occurences"], $mapiexception);
01101 
01102                         if (!empty($exceptionprops)) {
01103                             $noexceptions = false;
01104                             if($recurrence->isException($basedate)){
01105                                 $recurrence->modifyException($exceptionprops, $basedate);
01106                             }
01107                             else {
01108                                 $recurrence->createException($exceptionprops, $basedate);
01109                             }
01110                         }
01111 
01112                     }
01113                 }
01114             }
01115 
01116             //setRecurrence deletes the attachments from an appointment
01117             if ($noexceptions) {
01118                 $recurrence->setRecurrence($tz, $recur);
01119             }
01120         }
01121         else {
01122             $props[$appointmentprops["isrecurring"]] = false;
01123         }
01124 
01125         // Do attendees
01126         if(isset($appointment->attendees) && is_array($appointment->attendees)) {
01127             $recips = array();
01128 
01129             //open addresss book for user resolve
01130             $addrbook = mapi_openaddressbook($this->session);
01131             //set the PR_SENT_REPRESENTING_* props so that the attendee status update also works with the webaccess
01132             if (!isset($props[$appointmentprops["representingentryid"]])) {
01133                 $props[$appointmentprops["representingname"]] = Request::GetAuthUser();
01134                 $props[$appointmentprops["representingentryid"]] = mapi_createoneoff(Request::GetAuthUser(), "ZARAFA", Request::GetAuthUser());
01135             }
01136 
01137             foreach($appointment->attendees as $attendee) {
01138                 $recip = array();
01139                 $recip[PR_EMAIL_ADDRESS] = u2w($attendee->email);
01140 
01141                 // lookup information in GAB if possible so we have up-to-date name for given address
01142                 $userinfo = array( array( PR_DISPLAY_NAME => $recip[PR_EMAIL_ADDRESS] ) );
01143                 $userinfo = mapi_ab_resolvename($addrbook, $userinfo, EMS_AB_ADDRESS_LOOKUP);
01144                 if(mapi_last_hresult() == NOERROR) {
01145                     $recip[PR_DISPLAY_NAME] = $userinfo[0][PR_DISPLAY_NAME];
01146                     $recip[PR_EMAIL_ADDRESS] = $userinfo[0][PR_EMAIL_ADDRESS];
01147                     $recip[PR_SEARCH_KEY] = $userinfo[0][PR_SEARCH_KEY];
01148                     $recip[PR_ADDRTYPE] = $userinfo[0][PR_ADDRTYPE];
01149                     $recip[PR_ENTRYID] = $userinfo[0][PR_ENTRYID];
01150                     $recip[PR_RECIPIENT_TYPE] = MAPI_TO;
01151                 }
01152                 else {
01153                     $recip[PR_DISPLAY_NAME] = u2w($attendee->name);
01154                     $recip[PR_SEARCH_KEY] = "SMTP:".$recip[PR_EMAIL_ADDRESS]."\0";
01155                     $recip[PR_ADDRTYPE] = "SMTP";
01156                     $recip[PR_RECIPIENT_TYPE] = MAPI_TO;
01157                     $recip[PR_ENTRYID] = mapi_createoneoff($recip[PR_DISPLAY_NAME], $recip[PR_ADDRTYPE], $recip[PR_EMAIL_ADDRESS]);
01158                 }
01159 
01160                 array_push($recips, $recip);
01161             }
01162 
01163             mapi_message_modifyrecipients($mapimessage, 0, $recips);
01164             $props[$appointmentprops["icon"]] = 1026;
01165             $props[$appointmentprops["mrwassent"]] = true;
01166         }
01167         mapi_setprops($mapimessage, $props);
01168     }
01169 
01179     private function setContact($mapimessage, $contact) {
01180         mapi_setprops($mapimessage, array(PR_MESSAGE_CLASS => "IPM.Contact"));
01181 
01182         // normalize email addresses
01183         if (isset($contact->email1address) && (($contact->email1address = $this->extractEmailAddress($contact->email1address)) === false))
01184             unset($contact->email1address);
01185 
01186         if (isset($contact->email2address) && (($contact->email2address = $this->extractEmailAddress($contact->email2address)) === false))
01187             unset($contact->email2address);
01188 
01189         if (isset($contact->email3address) && (($contact->email3address = $this->extractEmailAddress($contact->email3address)) === false))
01190             unset($contact->email3address);
01191 
01192         $contactmapping = MAPIMapping::GetContactMapping();
01193         $contactprops = MAPIMapping::GetContactProperties();
01194         $this->setPropsInMAPI($mapimessage, $contact, $contactmapping);
01195 
01197         $cname = $this->composeDisplayName($contact);
01198 
01199         //get contact specific mapi properties and merge them with the AS properties
01200         $contactprops = array_merge($this->getPropIdsFromStrings($contactmapping), $this->getPropIdsFromStrings($contactprops));
01201 
01202         //contact specific properties to be set
01203         $props = array();
01204 
01205         //need to be set in order to show contacts properly in outlook and wa
01206         $nremails = array();
01207         $abprovidertype = 0;
01208 
01209         $this->setEmailAddress($contact->email1address, $cname, 1, $props, $contactprops, $nremails, $abprovidertype);
01210         $this->setEmailAddress($contact->email2address, $cname, 2, $props, $contactprops, $nremails, $abprovidertype);
01211         $this->setEmailAddress($contact->email3address, $cname, 3, $props, $contactprops, $nremails, $abprovidertype);
01212 
01213         $props[$contactprops["addressbooklong"]] = $abprovidertype;
01214         $props[$contactprops["displayname"]] = $props[$contactprops["subject"]] = $cname;
01215 
01216         //pda multiple e-mail addresses bug fix for the contact
01217         if (!empty($nremails)) $props[$contactprops["addressbookmv"]] = $nremails;
01218 
01219 
01220         //set addresses
01221         $this->setAddress("home", $contact->homecity, $contact->homecountry, $contact->homepostalcode, $contact->homestate, $contact->homestreet, $props, $contactprops);
01222         $this->setAddress("business", $contact->businesscity, $contact->businesscountry, $contact->businesspostalcode, $contact->businessstate, $contact->businessstreet, $props, $contactprops);
01223         $this->setAddress("other", $contact->othercity, $contact->othercountry, $contact->otherpostalcode, $contact->otherstate, $contact->otherstreet, $props, $contactprops);
01224 
01225         //set the mailing address and its type
01226         if (isset($props[$contactprops["businessaddress"]])) {
01227             $props[$contactprops["mailingaddress"]] = 2;
01228             $this->setMailingAddress($contact->businesscity, $contact->businesscountry, $contact->businesspostalcode, $contact->businessstate, $contact->businessstreet, $props[$contactprops["businessaddress"]], $props, $contactprops);
01229         }
01230         elseif (isset($props[$contactprops["homeaddress"]])) {
01231             $props[$contactprops["mailingaddress"]] = 1;
01232             $this->setMailingAddress($contact->homecity, $contact->homecountry, $contact->homepostalcode, $contact->homestate, $contact->homestreet, $props[$contactprops["homeaddress"]], $props, $contactprops);
01233         }
01234         elseif (isset($props[$contactprops["otheraddress"]])) {
01235             $props[$contactprops["mailingaddress"]] = 3;
01236             $this->setMailingAddress($contact->othercity, $contact->othercountry, $contact->otherpostalcode, $contact->otherstate, $contact->otherstreet, $props[$contactprops["otheraddress"]], $props, $contactprops);
01237         }
01238 
01239         if (isset($contact->picture)) {
01240             $picbinary = base64_decode($contact->picture);
01241             $picsize = strlen($picbinary);
01242             if ($picsize < MAX_EMBEDDED_SIZE) {
01243                 $props[$contactprops["haspic"]] = false;
01244 
01245                 // TODO contact picture handling
01246                 // check if contact has already got a picture. delete it first in that case
01247                 // delete it also if it was removed on a mobile
01248                 $picprops = mapi_getprops($mapimessage, array($props[$contactprops["haspic"]]));
01249                 if (isset($picprops[$props[$contactprops["haspic"]]]) && $picprops[$props[$contactprops["haspic"]]]) {
01250                     ZLog::Write(LOGLEVEL_DEBUG, "Contact already has a picture. Delete it");
01251 
01252                     $attachtable = mapi_message_getattachmenttable($mapimessage);
01253                     mapi_table_restrict($attachtable, MAPIUtils::GetContactPicRestriction());
01254                     $rows = mapi_table_queryallrows($attachtable, array(PR_ATTACH_NUM));
01255                     if (isset($rows) && is_array($rows)) {
01256                         foreach ($rows as $row) {
01257                             mapi_message_deleteattach($mapimessage, $row[PR_ATTACH_NUM]);
01258                         }
01259                     }
01260                 }
01261 
01262                 // only set picture if there's data in the request
01263                 if ($picbinary !== false && $picsize > 0) {
01264                     $props[$contactprops["haspic"]] = true;
01265                     $pic = mapi_message_createattach($mapimessage);
01266                     // Set properties of the attachment
01267                     $picprops = array(
01268                         PR_ATTACH_LONG_FILENAME_A => "ContactPicture.jpg",
01269                         PR_DISPLAY_NAME => "ContactPicture.jpg",
01270                         0x7FFF000B => true,
01271                         PR_ATTACHMENT_HIDDEN => false,
01272                         PR_ATTACHMENT_FLAGS => 1,
01273                         PR_ATTACH_METHOD => ATTACH_BY_VALUE,
01274                         PR_ATTACH_EXTENSION_A => ".jpg",
01275                         PR_ATTACH_NUM => 1,
01276                         PR_ATTACH_SIZE => $picsize,
01277                         PR_ATTACH_DATA_BIN => $picbinary,
01278                     );
01279 
01280                     mapi_setprops($pic, $picprops);
01281                     mapi_savechanges($pic);
01282                 }
01283             }
01284         }
01285 
01286         if (isset($contact->asbody)) {
01287             $this->setASbody($contact->asbody, $props, $contactprops);
01288         }
01289 
01290         mapi_setprops($mapimessage, $props);
01291     }
01292 
01302     private function setTask($mapimessage, $task) {
01303         mapi_setprops($mapimessage, array(PR_MESSAGE_CLASS => "IPM.Task"));
01304 
01305         $taskmapping = MAPIMapping::GetTaskMapping();
01306         $taskprops = MAPIMapping::GetTaskProperties();
01307         $this->setPropsInMAPI($mapimessage, $task, $taskmapping);
01308         $taskprops = array_merge($this->getPropIdsFromStrings($taskmapping), $this->getPropIdsFromStrings($taskprops));
01309 
01310         // task specific properties to be set
01311         $props = array();
01312 
01313         if (isset($task->asbody)) {
01314             $this->setASbody($task->asbody, $props, $taskprops);
01315         }
01316 
01317         if(isset($task->complete)) {
01318             if($task->complete) {
01319                 // Set completion to 100%
01320                 // Set status to 'complete'
01321                 $props[$taskprops["completion"]] = 1.0;
01322                 $props[$taskprops["status"]] = 2;
01323             } else {
01324                 // Set completion to 0%
01325                 // Set status to 'not started'
01326                 $props[$taskprops["completion"]] = 0.0;
01327                 $props[$taskprops["status"]] = 0;
01328             }
01329         }
01330         if (isset($task->recurrence) && class_exists('TaskRecurrence')) {
01331             $deadoccur = false;
01332             if ((isset($task->recurrence->occurrences) && $task->recurrence->occurrences == 1) ||
01333                 (isset($task->recurrence->deadoccur) && $task->recurrence->deadoccur == 1)) //ios5 sends deadoccur inside the recurrence
01334                 $deadoccur = true;
01335 
01336             // Set PR_ICON_INDEX to 1281 to show correct icon in category view
01337             $props[$taskprops["icon"]] = 1281;
01338             // dead occur - false if new occurrences should be generated from the task
01339             // true - if it is the last ocurrence of the task
01340             $props[$taskprops["deadoccur"]] = $deadoccur;
01341             $props[$taskprops["isrecurringtag"]] = true;
01342 
01343             $recurrence = new TaskRecurrence($this->store, $mapimessage);
01344             $recur = array();
01345             $this->setRecurrence($task, $recur);
01346 
01347             // task specific recurrence properties which we need to set here
01348             // "start" and "end" are in GMT when passing to class.recurrence
01349             // set recurrence start here because it's calculated differently for tasks and appointments
01350             $recur["start"] = $task->recurrence->start;
01351             $recur["regen"] = $task->regenerate;
01352             //Also add dates to $recur
01353             $recur["duedate"] = $task->duedate;
01354             $recurrence->setRecurrence($recur);
01355         }
01356         mapi_setprops($mapimessage, $props);
01357     }
01358 
01368     private function setNote($mapimessage, $note) {
01369         // Touchdown does not send categories if all are unset or there is none.
01370         // Setting it to an empty array will unset the property in Zarafa as well
01371         if (!isset($note->categories)) $note->categories = array();
01372 
01373         $this->setPropsInMAPI($mapimessage, $note, MAPIMapping::GetNoteMapping());
01374 
01375         $noteprops = MAPIMapping::GetNoteProperties();
01376         $noteprops = $this->getPropIdsFromStrings($noteprops);
01377 
01378         // note specific properties to be set
01379         $props = array();
01380         $props[$noteprops["messageclass"]] = "IPM.StickyNote";
01381         // set body otherwise the note will be "broken" when editing it in outlook
01382         $props[$noteprops["body"]] = $note->asbody->data;
01383         mapi_setprops($mapimessage, $props);
01384     }
01385 
01398     private function GetTZOffset($ts) {
01399         $Offset = date("O", $ts);
01400 
01401         $Parity = $Offset < 0 ? -1 : 1;
01402         $Offset = $Parity * $Offset;
01403         $Offset = ($Offset - ($Offset % 100)) / 100 * 60 + $Offset % 100;
01404 
01405         return $Parity * $Offset;
01406     }
01407 
01416     private function gmtime($time) {
01417         $TZOffset = $this->GetTZOffset($time);
01418 
01419         $t_time = $time - $TZOffset * 60; #Counter adjust for localtime()
01420         $t_arr = localtime($t_time, 1);
01421 
01422         return $t_arr;
01423     }
01424 
01435     private function setPropsInMAPI($mapimessage, $message, $mapping) {
01436         $mapiprops = $this->getPropIdsFromStrings($mapping);
01437         $unsetVars = $message->getUnsetVars();
01438         $propsToDelete = array();
01439         $propsToSet = array();
01440 
01441         foreach ($mapiprops as $asprop => $mapiprop) {
01442             if(isset($message->$asprop)) {
01443 
01444                 // UTF8->windows1252.. this is ok for all numerical values
01445                 if(mapi_prop_type($mapiprop) != PT_BINARY && mapi_prop_type($mapiprop) != PT_MV_BINARY) {
01446                     if(is_array($message->$asprop))
01447                         $value = array_map("u2wi", $message->$asprop);
01448                     else
01449                         $value = u2wi($message->$asprop);
01450                 } else {
01451                     $value = $message->$asprop;
01452                 }
01453 
01454                 // Make sure the php values are the correct type
01455                 switch(mapi_prop_type($mapiprop)) {
01456                     case PT_BINARY:
01457                     case PT_STRING8:
01458                         settype($value, "string");
01459                         break;
01460                     case PT_BOOLEAN:
01461                         settype($value, "boolean");
01462                         break;
01463                     case PT_SYSTIME:
01464                     case PT_LONG:
01465                         settype($value, "integer");
01466                         break;
01467                 }
01468 
01469                 // decode base64 value
01470                 if($mapiprop == PR_RTF_COMPRESSED) {
01471                     $value = base64_decode($value);
01472                     if(strlen($value) == 0)
01473                         continue; // PDA will sometimes give us an empty RTF, which we'll ignore.
01474 
01475                     // Note that you can still remove notes because when you remove notes it gives
01476                     // a valid compressed RTF with nothing in it.
01477 
01478                 }
01479                 // if an "empty array" is to be saved, it the mvprop should be deleted - fixes Mantis #468
01480                 if (is_array($value) && empty($value)) {
01481                     $propsToDelete[] = $mapiprop;
01482                     ZLog::Write(LOGLEVEL_DEBUG, sprintf("MAPIProvider->setPropsInMAPI(): Property '%s' to be deleted as it is an empty array", $asprop));
01483                 }
01484                 else {
01485                     // all properties will be set at once
01486                     $propsToSet[$mapiprop] = $value;
01487                 }
01488             }
01489             elseif (in_array($asprop, $unsetVars)) {
01490                 $propsToDelete[] = $mapiprop;
01491             }
01492         }
01493 
01494         mapi_setprops($mapimessage, $propsToSet);
01495         if (mapi_last_hresult()) {
01496             Zlog::Write(LOGLEVEL_WARN, sprintf("Failed to set properties, trying to set them separately. Error code was:%x", mapi_last_hresult()));
01497             $this->setPropsIndividually($mapimessage, $propsToSet, $mapiprops);
01498         }
01499 
01500         mapi_deleteprops($mapimessage, $propsToDelete);
01501 
01502         //clean up
01503         unset($unsetVars, $propsToDelete);
01504     }
01505 
01516     private function setPropsIndividually(&$mapimessage, &$propsToSet, &$mapiprops) {
01517         foreach ($propsToSet as $prop => $value) {
01518             mapi_setprops($mapimessage, array($prop => $value));
01519             if (mapi_last_hresult()) {
01520                 Zlog::Write(LOGLEVEL_ERROR, sprintf("Failed setting property [%s] with value [%s], error code was:%x", array_search($prop, $mapiprops), $value, mapi_last_hresult()));
01521             }
01522         }
01523 
01524     }
01525 
01536     private function getPropsFromMAPI(&$message, $mapimessage, $mapping) {
01537         $messageprops = $this->getProps($mapimessage, $mapping);
01538         foreach ($mapping as $asprop => $mapiprop) {
01539              // Get long strings via openproperty
01540             if (isset($messageprops[mapi_prop_tag(PT_ERROR, mapi_prop_id($mapiprop))])) {
01541                 if ($messageprops[mapi_prop_tag(PT_ERROR, mapi_prop_id($mapiprop))] == MAPI_E_NOT_ENOUGH_MEMORY_32BIT ||
01542                     $messageprops[mapi_prop_tag(PT_ERROR, mapi_prop_id($mapiprop))] == MAPI_E_NOT_ENOUGH_MEMORY_64BIT) {
01543                     $messageprops[$mapiprop] = MAPIUtils::readPropStream($mapimessage, $mapiprop);
01544                 }
01545             }
01546 
01547             if(isset($messageprops[$mapiprop])) {
01548                 if(mapi_prop_type($mapiprop) == PT_BOOLEAN) {
01549                     // Force to actual '0' or '1'
01550                     if($messageprops[$mapiprop])
01551                         $message->$asprop = 1;
01552                     else
01553                         $message->$asprop = 0;
01554                 } else {
01555                     // Special handling for PR_MESSAGE_FLAGS
01556                     if($mapiprop == PR_MESSAGE_FLAGS)
01557                         $message->$asprop = $messageprops[$mapiprop] & 1; // only look at 'read' flag
01558                     else if($mapiprop == PR_RTF_COMPRESSED)
01559                         //do not send rtf to the mobile
01560                         continue;
01561                     else if(is_array($messageprops[$mapiprop]))
01562                         $message->$asprop = array_map("w2u", $messageprops[$mapiprop]);
01563                     else {
01564                         if(mapi_prop_type($mapiprop) != PT_BINARY && mapi_prop_type($mapiprop) != PT_MV_BINARY)
01565                             $message->$asprop = w2u($messageprops[$mapiprop]);
01566                         else
01567                             $message->$asprop = $messageprops[$mapiprop];
01568                     }
01569                 }
01570             }
01571         }
01572     }
01573 
01582     private function getPropIdsFromStrings(&$mapiprops) {
01583         return getPropIdsFromStrings($this->store, $mapiprops);
01584     }
01585 
01594     protected function getProps($mapimessage, &$mapiproperties) {
01595         $mapiproperties = $this->getPropIdsFromStrings($mapiproperties);
01596         return mapi_getprops($mapimessage, $mapiproperties);
01597     }
01598 
01605     private function getGMTTZ() {
01606         $tz = array(
01607             "bias" => 0,
01608             "tzname" => "",
01609             "dstendyear" => 0,
01610             "dstendmonth" => 10,
01611             "dstendday" => 0,
01612             "dstendweek" => 5,
01613             "dstendhour" => 2,
01614             "dstendminute" => 0,
01615             "dstendsecond" => 0,
01616             "dstendmillis" => 0,
01617             "stdbias" => 0,
01618             "tznamedst" => "",
01619             "dststartyear" => 0,
01620             "dststartmonth" => 3,
01621             "dststartday" => 0,
01622             "dststartweek" => 5,
01623             "dststarthour" => 1,
01624             "dststartminute" => 0,
01625             "dststartsecond" => 0,
01626             "dststartmillis" => 0,
01627             "dstbias" => -60
01628     );
01629 
01630         return $tz;
01631     }
01632 
01641     private function getTZFromMAPIBlob($data) {
01642         $unpacked = unpack("lbias/lstdbias/ldstbias/" .
01643                            "vconst1/vdstendyear/vdstendmonth/vdstendday/vdstendweek/vdstendhour/vdstendminute/vdstendsecond/vdstendmillis/" .
01644                            "vconst2/vdststartyear/vdststartmonth/vdststartday/vdststartweek/vdststarthour/vdststartminute/vdststartsecond/vdststartmillis", $data);
01645         return $unpacked;
01646     }
01647 
01656     private function getTZFromSyncBlob($data) {
01657         $tz = unpack(   "lbias/a64tzname/vdstendyear/vdstendmonth/vdstendday/vdstendweek/vdstendhour/vdstendminute/vdstendsecond/vdstendmillis/" .
01658                         "lstdbias/a64tznamedst/vdststartyear/vdststartmonth/vdststartday/vdststartweek/vdststarthour/vdststartminute/vdststartsecond/vdststartmillis/" .
01659                         "ldstbias", $data);
01660 
01661         // Make the structure compatible with class.recurrence.php
01662         $tz["timezone"] = $tz["bias"];
01663         $tz["timezonedst"] = $tz["dstbias"];
01664 
01665         return $tz;
01666     }
01667 
01676     private function getSyncBlobFromTZ($tz) {
01677         // set the correct TZ name (done using the Bias)
01678         if (!isset($tz["tzname"]) || !$tz["tzname"] || !isset($tz["tznamedst"]) || !$tz["tznamedst"])
01679             $tz = TimezoneUtil::FillTZNames($tz);
01680 
01681         $packed = pack("la64vvvvvvvv" . "la64vvvvvvvv" . "l",
01682                 $tz["bias"], $tz["tzname"], 0, $tz["dstendmonth"], $tz["dstendday"], $tz["dstendweek"], $tz["dstendhour"], $tz["dstendminute"], $tz["dstendsecond"], $tz["dstendmillis"],
01683                 $tz["stdbias"], $tz["tznamedst"], 0, $tz["dststartmonth"], $tz["dststartday"], $tz["dststartweek"], $tz["dststarthour"], $tz["dststartminute"], $tz["dststartsecond"], $tz["dststartmillis"],
01684                 $tz["dstbias"]);
01685 
01686         return $packed;
01687     }
01688 
01697     private function getMAPIBlobFromTZ($tz) {
01698         $packed = pack("lll" . "vvvvvvvvv" . "vvvvvvvvv",
01699                       $tz["bias"], $tz["stdbias"], $tz["dstbias"],
01700                       0, 0, $tz["dstendmonth"], $tz["dstendday"], $tz["dstendweek"], $tz["dstendhour"], $tz["dstendminute"], $tz["dstendsecond"], $tz["dstendmillis"],
01701                       0, 0, $tz["dststartmonth"], $tz["dststartday"], $tz["dststartweek"], $tz["dststarthour"], $tz["dststartminute"], $tz["dststartsecond"], $tz["dststartmillis"]);
01702 
01703         return $packed;
01704     }
01705 
01715     private function getGMTTimeByTZ($localtime, $tz) {
01716         if(!isset($tz) || !is_array($tz))
01717             return $localtime;
01718 
01719         if($this->isDST($localtime, $tz))
01720             return $localtime + $tz["bias"]*60 + $tz["dstbias"]*60;
01721         else
01722             return $localtime + $tz["bias"]*60;
01723     }
01724 
01734     private function getLocaltimeByTZ($gmttime, $tz) {
01735         if(!isset($tz) || !is_array($tz))
01736             return $gmttime;
01737 
01738         if($this->isDST($gmttime - $tz["bias"]*60, $tz)) // may bug around the switch time because it may have to be 'gmttime - bias - dstbias'
01739             return $gmttime - $tz["bias"]*60 - $tz["dstbias"]*60;
01740         else
01741             return $gmttime - $tz["bias"]*60;
01742     }
01743 
01753     private function isDST($localtime, $tz) {
01754         if( !isset($tz) || !is_array($tz) ||
01755             !isset($tz["dstbias"]) || $tz["dstbias"] == 0 ||
01756             !isset($tz["dststartmonth"]) || $tz["dststartmonth"] == 0 ||
01757             !isset($tz["dstendmonth"]) || $tz["dstendmonth"] == 0)
01758             return false;
01759 
01760         $year = gmdate("Y", $localtime);
01761         $start = $this->getTimestampOfWeek($year, $tz["dststartmonth"], $tz["dststartweek"], $tz["dststartday"], $tz["dststarthour"], $tz["dststartminute"], $tz["dststartsecond"]);
01762         $end = $this->getTimestampOfWeek($year, $tz["dstendmonth"], $tz["dstendweek"], $tz["dstendday"], $tz["dstendhour"], $tz["dstendminute"], $tz["dstendsecond"]);
01763 
01764         if($start < $end) {
01765             // northern hemisphere (july = dst)
01766           if($localtime >= $start && $localtime < $end)
01767               $dst = true;
01768           else
01769               $dst = false;
01770         } else {
01771             // southern hemisphere (january = dst)
01772           if($localtime >= $end && $localtime < $start)
01773               $dst = false;
01774           else
01775               $dst = true;
01776         }
01777 
01778         return $dst;
01779     }
01780 
01795     private function getTimestampOfWeek($year, $month, $week, $wday, $hour, $minute, $second) {
01796         if ($month == 0)
01797             return;
01798 
01799         $date = gmmktime($hour, $minute, $second, $month, 1, $year);
01800 
01801         // Find first day in month which matches day of the week
01802         while(1) {
01803             $wdaynow = gmdate("w", $date);
01804             if($wdaynow == $wday)
01805                 break;
01806             $date += 24 * 60 * 60;
01807         }
01808 
01809         // Forward $week weeks (may 'overflow' into the next month)
01810         $date = $date + $week * (24 * 60 * 60 * 7);
01811 
01812         // Reverse 'overflow'. Eg week '10' will always be the last week of the month in which the
01813         // specified weekday exists
01814         while(1) {
01815             $monthnow = gmdate("n", $date); // gmdate returns 1-12
01816             if($monthnow > $month)
01817                 $date = $date - (24 * 7 * 60 * 60);
01818             else
01819                 break;
01820         }
01821 
01822         return $date;
01823     }
01824 
01833     private function getDayStartOfTimestamp($timestamp) {
01834         return $timestamp - ($timestamp % (60 * 60 * 24));
01835     }
01836 
01845     private function getSMTPAddressFromEntryID($entryid) {
01846         $ab = mapi_openaddressbook($this->session);
01847 
01848         $mailuser = mapi_ab_openentry($ab, $entryid);
01849         if(!$mailuser)
01850             return "";
01851 
01852         $props = mapi_getprops($mailuser, array(PR_ADDRTYPE, PR_SMTP_ADDRESS, PR_EMAIL_ADDRESS));
01853 
01854         $addrtype = isset($props[PR_ADDRTYPE]) ? $props[PR_ADDRTYPE] : "";
01855 
01856         if(isset($props[PR_SMTP_ADDRESS]))
01857             return $props[PR_SMTP_ADDRESS];
01858 
01859         if($addrtype == "SMTP" && isset($props[PR_EMAIL_ADDRESS]))
01860             return $props[PR_EMAIL_ADDRESS];
01861 
01862         return "";
01863     }
01864 
01873     private function composeDisplayName(&$contact) {
01874         // Set display name and subject to a combined value of firstname and lastname
01875         $cname = (isset($contact->prefix))?u2w($contact->prefix)." ":"";
01876         $cname .= u2w($contact->firstname);
01877         $cname .= (isset($contact->middlename))?" ". u2w($contact->middlename):"";
01878         $cname .= " ". u2w($contact->lastname);
01879         $cname .= (isset($contact->suffix))?" ". u2w($contact->suffix):"";
01880         return trim($cname);
01881     }
01882 
01897     private function setEmailAddress($emailAddress, $displayName, $cnt, &$props, &$properties, &$nremails, &$abprovidertype){
01898         if (isset($emailAddress)) {
01899             $name = (isset($displayName)) ? $displayName : $emailAddress;
01900 
01901             $props[$properties["emailaddress$cnt"]] = $emailAddress;
01902             $props[$properties["emailaddressdemail$cnt"]] = $emailAddress;
01903             $props[$properties["emailaddressdname$cnt"]] = $name;
01904             $props[$properties["emailaddresstype$cnt"]] = "SMTP";
01905             $props[$properties["emailaddressentryid$cnt"]] = mapi_createoneoff($name, "SMTP", $emailAddress);
01906             $nremails[] = $cnt - 1;
01907             $abprovidertype |= 2 ^ ($cnt - 1);
01908         }
01909     }
01910 
01926      private function setAddress($type, &$city, &$country, &$postalcode, &$state, &$street, &$props, &$properties) {
01927         if (isset($city)) $props[$properties[$type."city"]] = $city = u2w($city);
01928 
01929         if (isset($country)) $props[$properties[$type."country"]] = $country = u2w($country);
01930 
01931         if (isset($postalcode)) $props[$properties[$type."postalcode"]] = $postalcode = u2w($postalcode);
01932 
01933         if (isset($state)) $props[$properties[$type."state"]] = $state = u2w($state);
01934 
01935         if (isset($street)) $props[$properties[$type."street"]] = $street = u2w($street);
01936 
01937         //set composed address
01938         $address = Utils::BuildAddressString($street, $postalcode, $city, $state, $country);
01939         if ($address) $props[$properties[$type."address"]] = $address;
01940     }
01941 
01957     private function setMailingAddress($city, $country, $postalcode,  $state, $street, $address, &$props, &$properties) {
01958         if (isset($city)) $props[$properties["city"]] = $city;
01959         if (isset($country)) $props[$properties["country"]] = $country;
01960         if (isset($postalcode)) $props[$properties["postalcode"]] = $postalcode;
01961         if (isset($state)) $props[$properties["state"]] = $state;
01962         if (isset($street)) $props[$properties["street"]] = $street;
01963         if (isset($address)) $props[$properties["postaladdress"]] = $address;
01964     }
01965 
01975     private function setRecurrence($message, &$recur) {
01976         if (isset($message->complete)) {
01977             $recur["complete"] = $message->complete;
01978         }
01979 
01980         if(!isset($message->recurrence->interval))
01981             $message->recurrence->interval = 1;
01982 
01983         //set the default value of numoccur
01984         $recur["numoccur"] = 0;
01985         //a place holder for recurrencetype property
01986         $recur["recurrencetype"] = 0;
01987 
01988         switch($message->recurrence->type) {
01989             case 0:
01990                 $recur["type"] = 10;
01991                 if(isset($message->recurrence->dayofweek))
01992                     $recur["subtype"] = 1;
01993                 else
01994                     $recur["subtype"] = 0;
01995 
01996                 $recur["everyn"] = $message->recurrence->interval * (60 * 24);
01997                 $recur["recurrencetype"] = 1;
01998                 break;
01999             case 1:
02000                 $recur["type"] = 11;
02001                 $recur["subtype"] = 1;
02002                 $recur["everyn"] = $message->recurrence->interval;
02003                 $recur["recurrencetype"] = 2;
02004                 break;
02005             case 2:
02006                 $recur["type"] = 12;
02007                 $recur["subtype"] = 2;
02008                 $recur["everyn"] = $message->recurrence->interval;
02009                 $recur["recurrencetype"] = 3;
02010                 break;
02011             case 3:
02012                 $recur["type"] = 12;
02013                 $recur["subtype"] = 3;
02014                 $recur["everyn"] = $message->recurrence->interval;
02015                 $recur["recurrencetype"] = 3;
02016                 break;
02017             case 4:
02018                 $recur["type"] = 13;
02019                 $recur["subtype"] = 1;
02020                 $recur["everyn"] = $message->recurrence->interval * 12;
02021                 $recur["recurrencetype"] = 4;
02022                 break;
02023             case 5:
02024                 $recur["type"] = 13;
02025                 $recur["subtype"] = 2;
02026                 $recur["everyn"] = $message->recurrence->interval * 12;
02027                 $recur["recurrencetype"] = 4;
02028                 break;
02029             case 6:
02030                 $recur["type"] = 13;
02031                 $recur["subtype"] = 3;
02032                 $recur["everyn"] = $message->recurrence->interval * 12;
02033                 $recur["recurrencetype"] = 4;
02034                 break;
02035         }
02036 
02037         // "start" and "end" are in GMT when passing to class.recurrence
02038         $recur["end"] = $this->getDayStartOfTimestamp(0x7fffffff); // Maximum GMT value for end by default
02039 
02040         if(isset($message->recurrence->until)) {
02041             $recur["term"] = 0x21;
02042             $recur["end"] = $message->recurrence->until;
02043         } else if(isset($message->recurrence->occurrences)) {
02044             $recur["term"] = 0x22;
02045             $recur["numoccur"] = $message->recurrence->occurrences;
02046         } else {
02047             $recur["term"] = 0x23;
02048         }
02049 
02050         if(isset($message->recurrence->dayofweek))
02051             $recur["weekdays"] = $message->recurrence->dayofweek;
02052         if(isset($message->recurrence->weekofmonth))
02053             $recur["nday"] = $message->recurrence->weekofmonth;
02054         if(isset($message->recurrence->monthofyear)) {
02055             // MAPI stores months as the amount of minutes until the beginning of the month in a
02056             // non-leapyear. Why this is, is totally unclear.
02057             $monthminutes = array(0,44640,84960,129600,172800,217440,260640,305280,348480,393120,437760,480960);
02058             $recur["month"] = $monthminutes[$message->recurrence->monthofyear-1];
02059         }
02060         if(isset($message->recurrence->dayofmonth))
02061             $recur["monthday"] = $message->recurrence->dayofmonth;
02062     }
02063 
02075     private function extractEmailAddress($email) {
02076         if (!isset($this->zRFC822)) $this->zRFC822 = new Mail_RFC822();
02077         $parsedAddress = $this->zRFC822->parseAddressList($email);
02078         if (!isset($parsedAddress[0]->mailbox) || !isset($parsedAddress[0]->host)) return false;
02079 
02080         return $parsedAddress[0]->mailbox.'@'.$parsedAddress[0]->host;
02081     }
02082 
02091     private function getBodyPreferenceBestMatch($bpTypes) {
02092         // The best choice is RTF, then HTML and then MIME in order to save bandwidth
02093         // because MIME is a complete message including the headers and attachments
02094         if (in_array(SYNC_BODYPREFERENCE_RTF, $bpTypes))  return SYNC_BODYPREFERENCE_RTF;
02095         if (in_array(SYNC_BODYPREFERENCE_HTML, $bpTypes)) return SYNC_BODYPREFERENCE_HTML;
02096         if (in_array(SYNC_BODYPREFERENCE_MIME, $bpTypes)) return SYNC_BODYPREFERENCE_MIME;
02097         return SYNC_BODYPREFERENCE_PLAIN;
02098     }
02099 
02110     private function setMessageBodyForType($mapimessage, $bpReturnType, &$message) {
02111         //default value is PR_BODY
02112         $property = PR_BODY;
02113         switch ($bpReturnType) {
02114             case SYNC_BODYPREFERENCE_HTML:
02115                 $property = PR_HTML;
02116                 break;
02117             case SYNC_BODYPREFERENCE_RTF:
02118                 $property = PR_RTF_COMPRESSED;
02119                 break;
02120             case SYNC_BODYPREFERENCE_MIME:
02121                 $stat = $this->imtoinet($mapimessage, $message);
02122                 if (isset($message->asbody))
02123                     $message->asbody->type = $bpReturnType;
02124                 return $stat;
02125        }
02126 
02127         $body = mapi_message_openproperty($mapimessage, $property);
02128         //set the properties according to supported AS version
02129         if (Request::GetProtocolVersion() >= 12.0) {
02130             $message->asbody = new SyncBaseBody();
02131             $message->asbody->type = $bpReturnType;
02132             $message->asbody->data = ($bpReturnType == SYNC_BODYPREFERENCE_RTF) ? base64_encode($body) :
02133                 (isset($message->internetcpid) && $message->internetcpid == INTERNET_CPID_WINDOWS1252 && $bpReturnType == SYNC_BODYPREFERENCE_HTML) ?
02134                    windows1252_to_utf8($body, "", true) : w2u($body);
02135             $message->asbody->estimatedDataSize = strlen($message->asbody->data);
02136             $message->asbody->truncated = 0;
02137         }
02138         else {
02139             $message->body = str_replace("\n","\r\n", w2u(str_replace("\r", "", $body)));
02140             $message->bodysize = strlen($message->body);
02141             $message->bodytruncated = 0;
02142         }
02143 
02144         return true;
02145     }
02146 
02156     private function imtoinet($mapimessage, &$message) {
02157         if (function_exists("mapi_inetmapi_imtoinet")) {
02158             $addrBook = mapi_openaddressbook($this->session);
02159             $mstream = mapi_inetmapi_imtoinet($this->session, $addrBook, $mapimessage, array());
02160 
02161             $mstreamstat = mapi_stream_stat($mstream);
02162             if ($mstreamstat['cb'] < MAX_EMBEDDED_SIZE) {
02163                 if (Request::GetProtocolVersion() >= 12.0) {
02164                     if (!isset($message->asbody))
02165                         $message->asbody = new SyncBaseBody();
02166                     //TODO data should be wrapped in a MapiStreamWrapper
02167                     $message->asbody->data = mapi_stream_read($mstream, MAX_EMBEDDED_SIZE);
02168                     $message->asbody->estimatedDataSize = $mstreamstat["cb"];
02169                     $message->asbody->truncated = 0;
02170                 }
02171                 else {
02172                     $message->mimetruncated = 0;
02173                     //TODO mimedata should be a wrapped in a MapiStreamWrapper
02174                     $message->mimedata = mapi_stream_read($mstream, MAX_EMBEDDED_SIZE);
02175                     $message->mimesize = $mstreamstat["cb"];
02176                 }
02177                 unset($message->body, $message->bodytruncated);
02178                 return true;
02179             }
02180         }
02181         return false;
02182     }
02183 
02191     private function setMessageBody($mapimessage, $contentparameters, &$message) {
02192         //get the available body preference types
02193         $bpTypes = $contentparameters->GetBodyPreference();
02194         if ($bpTypes !== false) {
02195             ZLog::Write(LOGLEVEL_DEBUG, sprintf("BodyPreference types: %s", implode(', ', $bpTypes)));
02196             //do not send mime data if the client requests it
02197             if (($contentparameters->GetMimeSupport() == SYNC_MIMESUPPORT_NEVER) && ($key = array_search(SYNC_BODYPREFERENCE_MIME, $bpTypes)!== false)) {
02198                 unset($bpTypes[$key]);
02199                 ZLog::Write(LOGLEVEL_DEBUG, sprintf("Remove mime body preference type because the device required no mime support. BodyPreference types: %s", implode(', ', $bpTypes)));
02200             }
02201             //get the best fitting preference type
02202             $bpReturnType = $this->getBodyPreferenceBestMatch($bpTypes);
02203             ZLog::Write(LOGLEVEL_DEBUG, sprintf("getBodyPreferenceBestMatch: %d", $bpReturnType));
02204             $bpo = $contentparameters->BodyPreference($bpReturnType);
02205             ZLog::Write(LOGLEVEL_DEBUG, sprintf("bpo: truncation size:'%d', allornone:'%d', preview:'%d'", $bpo->GetTruncationSize(), $bpo->GetAllOrNone(), $bpo->GetPreview()));
02206 
02207             $this->setMessageBodyForType($mapimessage, $bpReturnType, $message);
02208             //only set the truncation size data if device set it in request
02209             if ($bpo->GetTruncationSize() != false && $bpReturnType != SYNC_BODYPREFERENCE_MIME && $message->asbody->estimatedDataSize > $bpo->GetTruncationSize()) {
02210                 $message->asbody->data = Utils::Utf8_truncate($message->asbody->data, $bpo->GetTruncationSize());
02211                 $message->asbody->truncated = 1;
02212             }
02213         }
02214         else {
02215             // Override 'body' for truncation
02216             $truncsize = Utils::GetTruncSize($contentparameters->GetTruncation());
02217             $this->setMessageBodyForType($mapimessage, SYNC_BODYPREFERENCE_PLAIN, $message);
02218 
02219             if($message->bodysize > $truncsize) {
02220                 $message->body = Utils::Utf8_truncate($message->body, $truncsize);
02221                 $message->bodytruncated = 1;
02222             }
02223 
02224             if (!isset($message->body) || strlen($message->body) == 0)
02225                 $message->body = " ";
02226 
02227             if ($contentparameters->GetMimeSupport() == SYNC_MIMESUPPORT_ALWAYS) {
02228                 //set the html body for iphone in AS 2.5 version
02229                 $this->imtoinet($mapimessage, $message);
02230             }
02231         }
02232     }
02233 
02242     private function getNativeBodyType($messageprops) {
02243         //check if the properties are set and get the error code if needed
02244         if (!isset($messageprops[PR_BODY]))             $messageprops[PR_BODY]              = $this->getError(PR_BODY, $messageprops);
02245         if (!isset($messageprops[PR_RTF_COMPRESSED]))   $messageprops[PR_RTF_COMPRESSED]    = $this->getError(PR_RTF_COMPRESSED, $messageprops);
02246         if (!isset($messageprops[PR_HTML]))             $messageprops[PR_HTML]              = $this->getError(PR_HTML, $messageprops);
02247         if (!isset($messageprops[PR_RTF_IN_SYNC]))      $messageprops[PR_RTF_IN_SYNC]       = $this->getError(PR_RTF_IN_SYNC, $messageprops);
02248 
02249         if ( // 1
02250             ($messageprops[PR_BODY]             == MAPI_E_NOT_FOUND) &&
02251             ($messageprops[PR_RTF_COMPRESSED]   == MAPI_E_NOT_FOUND) &&
02252             ($messageprops[PR_HTML]             == MAPI_E_NOT_FOUND))
02253             return SYNC_BODYPREFERENCE_UNDEFINED;
02254         elseif ( // 2
02255             ($messageprops[PR_BODY]             == MAPI_E_NOT_ENOUGH_MEMORY) &&
02256             ($messageprops[PR_RTF_COMPRESSED]   == MAPI_E_NOT_FOUND) &&
02257             ($messageprops[PR_HTML]             == MAPI_E_NOT_FOUND))
02258             return SYNC_BODYPREFERENCE_PLAIN;
02259         elseif ( // 3
02260             ($messageprops[PR_BODY]             == MAPI_E_NOT_ENOUGH_MEMORY) &&
02261             ($messageprops[PR_RTF_COMPRESSED]   == MAPI_E_NOT_ENOUGH_MEMORY) &&
02262             ($messageprops[PR_HTML]             == MAPI_E_NOT_FOUND))
02263             return SYNC_BODYPREFERENCE_RTF;
02264         elseif ( // 4
02265             ($messageprops[PR_BODY]             == MAPI_E_NOT_ENOUGH_MEMORY) &&
02266             ($messageprops[PR_RTF_COMPRESSED]   == MAPI_E_NOT_ENOUGH_MEMORY) &&
02267             ($messageprops[PR_HTML]             == MAPI_E_NOT_ENOUGH_MEMORY) &&
02268             ($messageprops[PR_RTF_IN_SYNC]))
02269             return SYNC_BODYPREFERENCE_RTF;
02270         elseif ( // 5
02271             ($messageprops[PR_BODY]             == MAPI_E_NOT_ENOUGH_MEMORY) &&
02272             ($messageprops[PR_RTF_COMPRESSED]   == MAPI_E_NOT_ENOUGH_MEMORY) &&
02273             ($messageprops[PR_HTML]             == MAPI_E_NOT_ENOUGH_MEMORY) &&
02274             (!$messageprops[PR_RTF_IN_SYNC]))
02275             return SYNC_BODYPREFERENCE_HTML;
02276         elseif ( // 6
02277             ($messageprops[PR_RTF_COMPRESSED]   != MAPI_E_NOT_FOUND   || $messageprops[PR_RTF_COMPRESSED]  == MAPI_E_NOT_ENOUGH_MEMORY) &&
02278             ($messageprops[PR_HTML]             != MAPI_E_NOT_FOUND   || $messageprops[PR_HTML]            == MAPI_E_NOT_ENOUGH_MEMORY) &&
02279             ($messageprops[PR_RTF_IN_SYNC]))
02280             return SYNC_BODYPREFERENCE_RTF;
02281         elseif ( // 7
02282             ($messageprops[PR_RTF_COMPRESSED]   != MAPI_E_NOT_FOUND   || $messageprops[PR_RTF_COMPRESSED]  == MAPI_E_NOT_ENOUGH_MEMORY) &&
02283             ($messageprops[PR_HTML]             != MAPI_E_NOT_FOUND   || $messageprops[PR_HTML]            == MAPI_E_NOT_ENOUGH_MEMORY) &&
02284             (!$messageprops[PR_RTF_IN_SYNC]))
02285             return SYNC_BODYPREFERENCE_HTML;
02286         elseif ( // 8
02287             ($messageprops[PR_BODY]             != MAPI_E_NOT_FOUND   || $messageprops[PR_BODY]            == MAPI_E_NOT_ENOUGH_MEMORY) &&
02288             ($messageprops[PR_RTF_COMPRESSED]   != MAPI_E_NOT_FOUND   || $messageprops[PR_RTF_COMPRESSED]  == MAPI_E_NOT_ENOUGH_MEMORY) &&
02289             ($messageprops[PR_RTF_IN_SYNC]))
02290             return SYNC_BODYPREFERENCE_RTF;
02291         elseif ( // 9.1
02292             ($messageprops[PR_BODY]             != MAPI_E_NOT_FOUND   || $messageprops[PR_BODY]            == MAPI_E_NOT_ENOUGH_MEMORY) &&
02293             ($messageprops[PR_RTF_COMPRESSED]   != MAPI_E_NOT_FOUND   || $messageprops[PR_RTF_COMPRESSED]  == MAPI_E_NOT_ENOUGH_MEMORY) &&
02294             (!$messageprops[PR_RTF_IN_SYNC]))
02295             return SYNC_BODYPREFERENCE_PLAIN;
02296         elseif ( // 9.2
02297             ($messageprops[PR_RTF_COMPRESSED]   != MAPI_E_NOT_FOUND   || $messageprops[PR_RTF_COMPRESSED]  == MAPI_E_NOT_ENOUGH_MEMORY) &&
02298             ($messageprops[PR_BODY]             == MAPI_E_NOT_FOUND) &&
02299             ($messageprops[PR_HTML]             == MAPI_E_NOT_FOUND))
02300             return SYNC_BODYPREFERENCE_RTF;
02301         elseif ( // 9.3
02302             ($messageprops[PR_BODY]             != MAPI_E_NOT_FOUND   || $messageprops[PR_BODY]            == MAPI_E_NOT_ENOUGH_MEMORY) &&
02303             ($messageprops[PR_RTF_COMPRESSED]   == MAPI_E_NOT_FOUND) &&
02304             ($messageprops[PR_HTML]             == MAPI_E_NOT_FOUND))
02305             return SYNC_BODYPREFERENCE_PLAIN;
02306         elseif ( // 9.4
02307             ($messageprops[PR_HTML]             != MAPI_E_NOT_FOUND   || $messageprops[PR_HTML]            == MAPI_E_NOT_ENOUGH_MEMORY) &&
02308             ($messageprops[PR_BODY]             == MAPI_E_NOT_FOUND) &&
02309             ($messageprops[PR_RTF_COMPRESSED]   == MAPI_E_NOT_FOUND))
02310             return SYNC_BODYPREFERENCE_HTML;
02311         else // 10
02312             return SYNC_BODYPREFERENCE_PLAIN;
02313     }
02314 
02324     private function getError($tag, $messageprops) {
02325         $prBodyError = mapi_prop_tag(PT_ERROR, mapi_prop_id($tag));
02326         if(isset($messageprops[$prBodyError]) && mapi_is_error($messageprops[$prBodyError])) {
02327             if($messageprops[$prBodyError] == MAPI_E_NOT_ENOUGH_MEMORY_32BIT ||
02328                  $messageprops[$prBodyError] == MAPI_E_NOT_ENOUGH_MEMORY_64BIT) {
02329                     return MAPI_E_NOT_ENOUGH_MEMORY;
02330             }
02331         }
02332             return MAPI_E_NOT_FOUND;
02333     }
02334 
02344     private function setFlag($mapimessage, &$message){
02345         // do nothing if protocoll version is lower than 12.0 as flags haven't been defined before
02346         if (Request::GetProtocolVersion() < 12.0 ) return;
02347 
02348         $message->flag = new SyncMailFlags();
02349 
02350         $this->getPropsFromMAPI($message->flag, $mapimessage, MAPIMapping::GetMailFlagsMapping());
02351     }
02352 
02363     private function setASbody($asbody, &$props, $appointmentprops) {
02364         if (isset($asbody->type) && isset($asbody->data) && strlen($asbody->data) > 0) {
02365             switch ($asbody->type) {
02366                 case SYNC_BODYPREFERENCE_PLAIN:
02367                 default:
02368                 //set plain body if the type is not in valid range
02369                     $props[$appointmentprops["body"]] = u2w($asbody->data);
02370                     break;
02371                 case SYNC_BODYPREFERENCE_HTML:
02372                     $props[$appointmentprops["html"]] = u2w($asbody->data);
02373                     break;
02374                 case SYNC_BODYPREFERENCE_RTF:
02375                     break;
02376                 case SYNC_BODYPREFERENCE_MIME:
02377                     break;
02378             }
02379         }
02380         else {
02381             ZLog::Write(LOGLEVEL_INFO, "MAPIProvider->setASbody either type or data are not set. Setting to empty body");
02382             $props[$appointmentprops["body"]] = "";
02383         }
02384     }
02385 }
02386 
02387 ?>