/** * Returns an SMTP address from an entry id * * @param string $entryid * * @access private * @return string */ private function getSMTPAddressFromEntryID($entryid) { $addrbook = $this->getAddressbook(); $mailuser = mapi_ab_openentry($addrbook, $entryid); if (!$mailuser) { return ""; } $props = mapi_getprops($mailuser, array(PR_ADDRTYPE, PR_SMTP_ADDRESS, PR_EMAIL_ADDRESS)); $addrtype = isset($props[PR_ADDRTYPE]) ? $props[PR_ADDRTYPE] : ""; if (isset($props[PR_SMTP_ADDRESS])) { return $props[PR_SMTP_ADDRESS]; } if ($addrtype == "SMTP" && isset($props[PR_EMAIL_ADDRESS])) { return $props[PR_EMAIL_ADDRESS]; } elseif ($addrtype == "ZARAFA" && isset($props[PR_EMAIL_ADDRESS])) { $userinfo = mapi_zarafa_getuser_by_name($this->store, $props[PR_EMAIL_ADDRESS]); if (is_array($userinfo) && isset($userinfo["emailaddress"])) { return $userinfo["emailaddress"]; } } return ""; }
function _getAppointment($mapimessage, $truncsize, $mimesupport = 0) { $message = new SyncAppointment(); // Standard one-to-one mappings first $this->_getPropsFromMAPI($message, $mapimessage, $this->_appointmentmapping); // Disable reminder if it is off $reminderset = $this->_getPropIDFromString("PT_BOOLEAN:{00062008-0000-0000-C000-000000000046}:0x8503"); $remindertime = $this->_getPropIDFromString("PT_LONG:{00062008-0000-0000-C000-000000000046}:0x8501"); $messageprops = mapi_getprops($mapimessage, array($reminderset, $remindertime)); if (!isset($messageprops[$reminderset]) || $messageprops[$reminderset] == false) { $message->reminder = ""; } else { if ($messageprops[$remindertime] == 0x5ae980e1) { $message->reminder = 15; } else { $message->reminder = $messageprops[$remindertime]; } } $messageprops = mapi_getprops($mapimessage, array(PR_SOURCE_KEY)); if (!isset($message->uid)) { $message->uid = bin2hex($messageprops[PR_SOURCE_KEY]); } else { $message->uid = getICalUidFromOLUid($message->uid); } // Get organizer information if it is a meetingrequest $meetingstatustag = $this->_getPropIDFromString("PT_LONG:{00062002-0000-0000-C000-000000000046}:0x8217"); $messageprops = mapi_getprops($mapimessage, array($meetingstatustag, PR_SENT_REPRESENTING_ENTRYID, PR_SENT_REPRESENTING_NAME)); if (isset($messageprops[$meetingstatustag]) && $messageprops[$meetingstatustag] > 0 && isset($messageprops[PR_SENT_REPRESENTING_ENTRYID]) && isset($messageprops[PR_SENT_REPRESENTING_NAME])) { $message->organizeremail = w2u($this->_getSMTPAddressFromEntryID($messageprops[PR_SENT_REPRESENTING_ENTRYID])); $message->organizername = w2u($messageprops[PR_SENT_REPRESENTING_NAME]); } $isrecurringtag = $this->_getPropIDFromString("PT_BOOLEAN:{00062002-0000-0000-C000-000000000046}:0x8223"); $recurringstate = $this->_getPropIDFromString("PT_BINARY:{00062002-0000-0000-C000-000000000046}:0x8216"); $timezonetag = $this->_getPropIDFromString("PT_BINARY:{00062002-0000-0000-C000-000000000046}:0x8233"); $recurrenceend = $this->_getPropIDFromString("PT_SYSTIME:{00062002-0000-0000-C000-000000000046}:0x8236"); // Now, get and convert the recurrence and timezone information $recurprops = mapi_getprops($mapimessage, array($isrecurringtag, $recurringstate, $timezonetag, $recurrenceend)); if (isset($recurprops[$timezonetag])) { $tz = $this->_getTZFromMAPIBlob($recurprops[$timezonetag]); } else { $tz = $this->_getGMTTZ(); } $message->timezone = base64_encode($this->_getSyncBlobFromTZ($tz)); if (isset($recurprops[$isrecurringtag]) && $recurprops[$isrecurringtag]) { // Process recurrence $message->recurrence = new SyncRecurrence(); $this->_getRecurrence($mapimessage, $recurprops, $message, $message->recurrence, $tz); } // Do attendees $reciptable = mapi_message_getrecipienttable($mapimessage); $rows = mapi_table_queryallrows($reciptable, array(PR_DISPLAY_NAME, PR_EMAIL_ADDRESS, PR_SMTP_ADDRESS, PR_ADDRTYPE)); if (count($rows) > 0) { $message->attendees = array(); } foreach ($rows as $row) { $attendee = new SyncAttendee(); $attendee->name = w2u($row[PR_DISPLAY_NAME]); //smtp address is always a proper email address if (isset($row[PR_SMTP_ADDRESS])) { $attendee->email = w2u($row[PR_SMTP_ADDRESS]); } elseif (isset($row[PR_ADDRTYPE]) && isset($row[PR_EMAIL_ADDRESS])) { //if address type is SMTP, it's also a proper email address if ($row[PR_ADDRTYPE] == "SMTP") { $attendee->email = w2u($row[PR_EMAIL_ADDRESS]); } elseif ($row[PR_ADDRTYPE] == "ZARAFA") { $userinfo = mapi_zarafa_getuser_by_name($this->_store, $row[PR_EMAIL_ADDRESS]); if (is_array($userinfo) && isset($userinfo["emailaddress"])) { $attendee->email = w2u($userinfo["emailaddress"]); } } } // Some attendees have no email or name (eg resources), and if you // don't send one of those fields, the phone will give an error ... so // we don't send it in that case. // also ignore the "attendee" if the email is equal to the organizers' email if (isset($attendee->name) && isset($attendee->email) && $attendee->email != "" && (!isset($message->organizeremail) || isset($message->organizeremail) && $attendee->email != $message->organizeremail)) { array_push($message->attendees, $attendee); } } // Force the 'alldayevent' in the object at all times. (non-existent == 0) if (!isset($message->alldayevent) || $message->alldayevent == "") { $message->alldayevent = 0; } return $message; }
/** * Setup the backend to work on a specific store or checks ACLs there. * If only the $store is submitted, all Import/Export/Fetch/Etc operations should be * performed on this store (switch operations store). * If the ACL check is enabled, this operation should just indicate the ACL status on * the submitted store, without changing the store for operations. * For the ACL status, the currently logged on user MUST have access rights on * - the entire store - admin access if no folderid is sent, or * - on a specific folderid in the store (secretary/full access rights) * * The ACLcheck MUST fail if a folder of the authenticated user is checked! * * @param string $store target store, could contain a "domain\user" value * @param boolean $checkACLonly if set to true, Setup() should just check ACLs * @param string $folderid if set, only ACLs on this folderid are relevant * * @access public * @return boolean */ public function Setup($store, $checkACLonly = false, $folderid = false) { list($user, $domain) = Utils::SplitDomainUser($store); if (!isset($this->mainUser)) { return false; } if ($user === false) { $user = $this->mainUser; } // This is a special case. A user will get it's entire folder structure by the foldersync by default. // The ACL check is executed when an additional folder is going to be sent to the mobile. // Configured that way the user could receive the same folderid twice, with two different names. if ($this->mainUser == $user && $checkACLonly && $folderid) { ZLog::Write(LOGLEVEL_DEBUG, "ZarafaBackend->Setup(): Checking ACLs for folder of the users defaultstore. Fail is forced to avoid folder duplications on mobile."); return false; } // get the users store $userstore = $this->openMessageStore($user); // only proceed if a store was found, else return false if ($userstore) { // only check permissions if ($checkACLonly == true) { // check for admin rights if (!$folderid) { if ($user != $this->mainUser) { $zarafauserinfo = @mapi_zarafa_getuser_by_name($this->defaultstore, $this->mainUser); $admin = isset($zarafauserinfo['admin']) && $zarafauserinfo['admin'] ? true : false; } else { $admin = true; } ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZarafaBackend->Setup(): Checking for admin ACLs on store '%s': '%s'", $user, Utils::PrintAsString($admin))); return $admin; } else { $rights = $this->hasSecretaryACLs($userstore, $folderid); ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZarafaBackend->Setup(): Checking for secretary ACLs on '%s' of store '%s': '%s'", $folderid, $user, Utils::PrintAsString($rights))); return $rights; } } else { // switch active store $this->store = $userstore; $this->storeName = $user; return true; } } return false; }
function _getAppointment($mapimessage, $truncsize) { $message = new SyncAppointment(); // Standard one-to-one mappings first $this->_getPropsFromMAPI($message, $mapimessage, $this->_appointmentmapping); // Disable reminder if it is off $reminderset = $this->_getPropIDFromString("PT_BOOLEAN:{00062008-0000-0000-C000-000000000046}:0x8503"); $remindertime = $this->_getPropIDFromString("PT_LONG:{00062008-0000-0000-C000-000000000046}:0x8501"); $messageprops = mapi_getprops($mapimessage, array($reminderset, $remindertime)); if (!isset($messageprops[$reminderset]) || $messageprops[$reminderset] == false) { $message->reminder = ""; } else { if ($messageprops[$remindertime] == 0x5ae980e1) { $message->reminder = 15; } else { $message->reminder = $messageprops[$remindertime]; } } $messageprops = mapi_getprops($mapimessage, array(PR_SOURCE_KEY)); if (!isset($message->uid)) { $message->uid = $messageprops[PR_SOURCE_KEY]; } $isrecurringtag = $this->_getPropIDFromString("PT_BOOLEAN:{00062002-0000-0000-C000-000000000046}:0x8223"); $recurringstate = $this->_getPropIDFromString("PT_BINARY:{00062002-0000-0000-C000-000000000046}:0x8216"); $timezonetag = $this->_getPropIDFromString("PT_BINARY:{00062002-0000-0000-C000-000000000046}:0x8233"); // Now, get and convert the recurrence and timezone information $recurprops = mapi_getprops($mapimessage, array($isrecurringtag, $recurringstate, $timezonetag)); if (isset($recurprops[$timezonetag])) { $tz = $this->_getTZFromMAPIBlob($recurprops[$timezonetag]); } else { $tz = $this->_getGMTTZ(); } if (isset($recurprops[$isrecurringtag]) && $recurprops[$isrecurringtag]) { // Process recurrence $recurrence = new Recurrence($this->_store, $recurprops); $message->recurrence = new SyncRecurrence(); switch ($recurrence->recur["type"]) { case 10: // daily switch ($recurrence->recur["subtype"]) { default: $message->recurrence->type = 0; break; case 1: $message->recurrence->type = 0; $message->recurrence->dayofweek = 62; // mon-fri break; } break; case 11: // weekly $message->recurrence->type = 1; break; case 12: // monthly switch ($recurrence->recur["subtype"]) { default: $message->recurrence->type = 2; break; case 3: $message->recurrence->type = 3; break; } break; case 13: // yearly switch ($recurrence->recur["subtype"]) { default: $message->recurrence->type = 4; break; case 2: $message->recurrence->type = 5; break; case 3: $message->recurrence->type = 6; } } // Termination switch ($recurrence->recur["term"]) { case 0x21: $message->recurrence->until = $recurrence->recur["end"]; break; case 0x22: $message->recurrence->occurrences = $recurrence->recur["numoccur"]; break; case 0x23: // never ends break; } // Correct 'alldayevent' because outlook fails to set it on recurring items of 24 hours or longer if ($recurrence->recur["endocc"] - $recurrence->recur["startocc"] >= 1440) { $message->alldayevent = true; } // Interval is different according to the type/subtype switch ($recurrence->recur["type"]) { case 10: if ($recurrence->recur["subtype"] == 0) { $message->recurrence->interval = (int) ($recurrence->recur["everyn"] / 1440); } // minutes break; case 11: case 12: $message->recurrence->interval = $recurrence->recur["everyn"]; break; // months / weeks // months / weeks case 13: $message->recurrence->interval = (int) ($recurrence->recur["everyn"] / 12); break; // months } if (isset($recurrence->recur["weekdays"])) { $message->recurrence->dayofweek = $recurrence->recur["weekdays"]; } // bitmask of days (1 == sunday, 128 == saturday if (isset($recurrence->recur["nday"])) { $message->recurrence->weekofmonth = $recurrence->recur["nday"]; } // N'th {DAY} of {X} (0-5) if (isset($recurrence->recur["month"])) { $message->recurrence->monthofyear = (int) ($recurrence->recur["month"] / (60 * 24 * 29)) + 1; } // works ok due to rounding. see also $monthminutes below (1-12) if (isset($recurrence->recur["monthday"])) { $message->recurrence->dayofmonth = $recurrence->recur["monthday"]; } // day of month (1-31) // All changed exceptions are appointments within the 'exceptions' array. They contain the same items as a normal appointment foreach ($recurrence->recur["changed_occurences"] as $change) { $exception = new SyncAppointment(); // start, end, basedate, subject, remind_before, reminderset, location, busystatus, alldayevent, label if (isset($change["start"])) { $exception->starttime = $this->_getGMTTimeByTZ($change["start"], $tz); } if (isset($change["end"])) { $exception->endtime = $this->_getGMTTimeByTZ($change["end"], $tz); } if (isset($change["basedate"])) { $exception->exceptionstarttime = $this->_getGMTTimeByTZ($this->_getDayStartOfTimestamp($change["basedate"]) + $recurrence->recur["startocc"] * 60, $tz); } if (isset($change["subject"])) { $exception->subject = w2u($change["subject"]); } if (isset($change["reminder_before"]) && $change["reminder_before"]) { $exception->reminder = $change["remind_before"]; } if (isset($change["location"])) { $exception->location = w2u($change["location"]); } if (isset($change["busystatus"])) { $exception->busystatus = $change["busystatus"]; } if (isset($change["alldayevent"])) { $exception->alldayevent = $change["alldayevent"]; } if (!isset($message->exceptions)) { $message->exceptions = array(); } array_push($message->exceptions, $exception); } // Deleted appointments contain only the original date (basedate) and a 'deleted' tag foreach ($recurrence->recur["deleted_occurences"] as $deleted) { $exception = new SyncAppointment(); $exception->exceptionstarttime = $this->_getGMTTimeByTZ($this->_getDayStartOfTimestamp($deleted) + $recurrence->recur["startocc"] * 60, $tz); $exception->deleted = "1"; if (!isset($message->exceptions)) { $message->exceptions = array(); } array_push($message->exceptions, $exception); } } if ($tz) { $message->timezone = base64_encode($this->_getSyncBlobFromTZ($tz)); } // Get organizer information if it is a meetingrequest $meetingstatustag = $this->_getPropIDFromString("PT_LONG:{00062002-0000-0000-C000-000000000046}:0x8217"); $messageprops = mapi_getprops($mapimessage, array($meetingstatustag, PR_SENT_REPRESENTING_ENTRYID, PR_SENT_REPRESENTING_NAME)); if (isset($messageprops[$meetingstatustag]) && $messageprops[$meetingstatustag] > 0 && isset($messageprops[PR_SENT_REPRESENTING_ENTRYID]) && isset($messageprops[PR_SENT_REPRESENTING_NAME])) { $message->organizeremail = $this->_getSMTPAddressFromEntryID($messageprops[PR_SENT_REPRESENTING_ENTRYID]); $message->organizername = w2u($messageprops[PR_SENT_REPRESENTING_NAME]); } // Do attendees $reciptable = mapi_message_getrecipienttable($mapimessage); $rows = mapi_table_queryallrows($reciptable, array(PR_DISPLAY_NAME, PR_EMAIL_ADDRESS, PR_SMTP_ADDRESS, PR_ADDRTYPE)); if (count($rows) > 0) { $message->attendees = array(); } foreach ($rows as $row) { $attendee = new SyncAttendee(); $attendee->name = w2u($row[PR_DISPLAY_NAME]); //smtp address is always a proper email address if (isset($row[PR_SMTP_ADDRESS])) { $attendee->email = $row[PR_SMTP_ADDRESS]; } elseif (isset($row[PR_ADDRTYPE]) && isset($row[PR_EMAIL_ADDRESS])) { //if address type is SMTP, it's also a proper email address if (PR_ADDRTYPE == "SMTP") { $attendee->email = $row[PR_EMAIL_ADDRESS]; } elseif (PR_ADDRTYPE == "ZARAFA") { $userinfo = mapi_zarafa_getuser_by_name($this->_store, $row[PR_EMAIL_ADDRESS]); if (is_array($userinfo) && isset($userinfo["emailaddress"])) { $attendee->email = $userinfo["emailaddress"]; } } } // Some attendees have no email or name (eg resources), and if you // don't send one of those fields, the phone will give an error ... so // we don't send it in that case. // also ignore the "attendee" if the email is equal to the organizers' email if (isset($attendee->name) && isset($attendee->email) && (!isset($message->organizeremail) || isset($message->organizeremail) && $attendee->email != $message->organizeremail)) { array_push($message->attendees, $attendee); } } // Force the 'alldayevent' in the object at all times. (non-existent == 0) if (!isset($message->alldayevent) || $message->alldayevent == "") { $message->alldayevent = 0; } return $message; }
function listfolders_zarafa_admin_setup($mapi, $user, $pass) { $session = @mapi_logon_zarafa($user, $pass, $mapi); if (!$session) { echo "User '{$user}' could not login. The script will exit. Errorcode: 0x" . sprintf("%x", mapi_last_hresult()) . "\n"; exit(1); } $stores = @mapi_getmsgstorestable($session); $storeslist = @mapi_table_queryallrows($stores); $adminStore = @mapi_openmsgstore($session, $storeslist[0][PR_ENTRYID]); $zarafauserinfo = @mapi_zarafa_getuser_by_name($adminStore, $user); $admin = isset($zarafauserinfo['admin']) && $zarafauserinfo['admin'] ? true : false; if (!$stores || !$storeslist || !$adminStore || !$admin) { echo "There was error trying to log in as admin or retrieving admin info. The script will exit.\n"; exit(1); } return array("session" => $session, "adminStore" => $adminStore); }
/** * Reads an appointment object from MAPI * * @param mixed $mapimessage * @param ContentParameters $contentparameters * * @access private * @return SyncAppointment */ private function getAppointment($mapimessage, $contentparameters) { $message = new SyncAppointment(); // Standard one-to-one mappings first $this->getPropsFromMAPI($message, $mapimessage, MAPIMapping::GetAppointmentMapping()); // Appointment specific props $appointmentprops = MAPIMapping::GetAppointmentProperties(); $messageprops = $this->getProps($mapimessage, $appointmentprops); //set the body according to contentparameters and supported AS version $this->setMessageBody($mapimessage, $contentparameters, $message); // Set reminder time if reminderset is true if (isset($messageprops[$appointmentprops["reminderset"]]) && $messageprops[$appointmentprops["reminderset"]] == true) { if ($messageprops[$appointmentprops["remindertime"]] == 0x5ae980e1) { $message->reminder = 15; } else { $message->reminder = $messageprops[$appointmentprops["remindertime"]]; } } if (!isset($message->uid)) { $message->uid = bin2hex($messageprops[$appointmentprops["sourcekey"]]); } else { $message->uid = Utils::GetICalUidFromOLUid($message->uid); } // Get organizer information if it is a meetingrequest if (isset($messageprops[$appointmentprops["meetingstatus"]]) && $messageprops[$appointmentprops["meetingstatus"]] > 0 && isset($messageprops[$appointmentprops["representingentryid"]]) && isset($messageprops[$appointmentprops["representingname"]])) { $message->organizeremail = w2u($this->getSMTPAddressFromEntryID($messageprops[$appointmentprops["representingentryid"]])); $message->organizername = w2u($messageprops[$appointmentprops["representingname"]]); } if (isset($messageprops[$appointmentprops["timezonetag"]])) { $tz = $this->getTZFromMAPIBlob($messageprops[$appointmentprops["timezonetag"]]); } else { // set server default timezone (correct timezone should be configured!) $tz = TimezoneUtil::GetFullTZ(); } $message->timezone = base64_encode($this->getSyncBlobFromTZ($tz)); if (isset($messageprops[$appointmentprops["isrecurring"]]) && $messageprops[$appointmentprops["isrecurring"]]) { // Process recurrence $message->recurrence = new SyncRecurrence(); $this->getRecurrence($mapimessage, $messageprops, $message, $message->recurrence, $tz); } // Do attendees $reciptable = mapi_message_getrecipienttable($mapimessage); // Only get first 256 recipients, to prevent possible load issues. $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); // Exception: we do not synchronize appointments with more than 250 attendees if (count($rows) > 250) { $message->id = bin2hex($messageprops[$appointmentprops["sourcekey"]]); $mbe = new SyncObjectBrokenException("Appointment has too many attendees"); $mbe->SetSyncObject($message); throw $mbe; } if (count($rows) > 0) { $message->attendees = array(); } foreach ($rows as $row) { $attendee = new SyncAttendee(); $attendee->name = w2u($row[PR_DISPLAY_NAME]); //smtp address is always a proper email address if (isset($row[PR_SMTP_ADDRESS])) { $attendee->email = w2u($row[PR_SMTP_ADDRESS]); } elseif (isset($row[PR_ADDRTYPE]) && isset($row[PR_EMAIL_ADDRESS])) { //if address type is SMTP, it's also a proper email address if ($row[PR_ADDRTYPE] == "SMTP") { $attendee->email = w2u($row[PR_EMAIL_ADDRESS]); } elseif ($row[PR_ADDRTYPE] == "ZARAFA") { $userinfo = mapi_zarafa_getuser_by_name($this->store, $row[PR_EMAIL_ADDRESS]); if (is_array($userinfo) && isset($userinfo["emailaddress"])) { $attendee->email = w2u($userinfo["emailaddress"]); } } } //set attendee's status and type if they're available if (isset($row[PR_RECIPIENT_TRACKSTATUS])) { $attendee->attendeestatus = $row[PR_RECIPIENT_TRACKSTATUS]; } if (isset($row[PR_RECIPIENT_TYPE])) { $attendee->attendeetype = $row[PR_RECIPIENT_TYPE]; } // Some attendees have no email or name (eg resources), and if you // don't send one of those fields, the phone will give an error ... so // we don't send it in that case. // also ignore the "attendee" if the email is equal to the organizers' email if (isset($attendee->name) && isset($attendee->email) && $attendee->email != "" && (!isset($message->organizeremail) || isset($message->organizeremail) && $attendee->email != $message->organizeremail)) { array_push($message->attendees, $attendee); } } if (!isset($message->nativebodytype)) { $message->nativebodytype = $this->getNativeBodyType($messageprops); } return $message; }
/** * Returns the email address and the display name of the user. Used by autodiscover. * * @param string $username The username * * @access public * @return Array */ public function GetUserDetails($username) { ZLog::Write(LOGLEVEL_WBXML, sprintf("ZarafaBackend->GetUserDetails for '%s'.", $username)); $zarafauserinfo = @mapi_zarafa_getuser_by_name($this->store, $username); $userDetails['emailaddress'] = isset($zarafauserinfo['emailaddress']) && $zarafauserinfo['emailaddress'] ? $zarafauserinfo['emailaddress'] : false; $userDetails['fullname'] = isset($zarafauserinfo['fullname']) && $zarafauserinfo['fullname'] ? $zarafauserinfo['fullname'] : false; return $userDetails; }
/** * Get connected user email address * @return email address */ public function getConnectedUserMailAddress() { $this->logger->trace("getConnectedUserMailAddress"); $userInfo = mapi_zarafa_getuser_by_name($this->store, $this->connectedUser); $this->logger->debug("User email address: " . $userInfo["emailaddress"]); return $userInfo["emailaddress"]; }