function _getContact($mapimessage, $truncsize, $mimesupport = 0) { $message = new SyncContact(); $this->_getPropsFromMAPI($message, $mapimessage, $this->_contactmapping); //check the picture $haspic = $this->_getPropIDFromString("PT_BOOLEAN:{00062004-0000-0000-C000-000000000046}:0x8015"); $messageprops = mapi_getprops($mapimessage, array($haspic)); if (isset($messageprops[$haspic]) && $messageprops[$haspic]) { // Add attachments $attachtable = mapi_message_getattachmenttable($mapimessage); mapi_table_restrict($attachtable, getContactPicRestriction()); $rows = mapi_table_queryallrows($attachtable, array(PR_ATTACH_NUM, PR_ATTACH_SIZE)); foreach ($rows as $row) { if (isset($row[PR_ATTACH_NUM])) { if (isset($row[PR_ATTACH_SIZE]) && $row[PR_ATTACH_SIZE] < MAX_EMBEDDED_SIZE) { $mapiattach = mapi_message_openattach($mapimessage, $row[PR_ATTACH_NUM]); $message->picture = base64_encode(mapi_attach_openbin($mapiattach, PR_ATTACH_DATA_BIN)); } } } } return $message; }
/** * Reads a contact object from MAPI * * @param mixed $mapimessage * @param ContentParameters $contentparameters * * @access private * @return SyncContact */ private function getContact($mapimessage, $contentparameters) { $message = new SyncContact(); // Standard one-to-one mappings first $this->getPropsFromMAPI($message, $mapimessage, MAPIMapping::GetContactMapping()); // Contact specific props $contactproperties = MAPIMapping::GetContactProperties(); $messageprops = $this->getProps($mapimessage, $contactproperties); //set the body according to contentparameters and supported AS version $this->setMessageBody($mapimessage, $contentparameters, $message); //check the picture if (isset($messageprops[$contactproperties["haspic"]]) && $messageprops[$contactproperties["haspic"]]) { // Add attachments $attachtable = mapi_message_getattachmenttable($mapimessage); mapi_table_restrict($attachtable, MAPIUtils::GetContactPicRestriction()); $rows = mapi_table_queryallrows($attachtable, array(PR_ATTACH_NUM, PR_ATTACH_SIZE)); foreach ($rows as $row) { if (isset($row[PR_ATTACH_NUM])) { if (isset($row[PR_ATTACH_SIZE]) && $row[PR_ATTACH_SIZE] < MAX_EMBEDDED_SIZE) { $mapiattach = mapi_message_openattach($mapimessage, $row[PR_ATTACH_NUM]); $message->picture = base64_encode(mapi_attach_openbin($mapiattach, PR_ATTACH_DATA_BIN)); } } } } return $message; }
/** * Convert vObject to an array of properties * @param array $properties * @param object $vCard */ public function propertiesToVObject($contact, &$vCard) { $this->logger->debug("Generating contact vCard from properties"); $p = $this->bridge->getExtendedProperties(); $contactProperties = mapi_getprops($contact); // $this->bridge->getProperties($contactId); $dump = print_r($contactProperties, true); $this->logger->trace("Contact properties:\n{$contactProperties}"); // Version check switch ($this->version) { case 2: $vCard->add('VERSION', '2.1'); break; case 3: $vCard->add('VERSION', '3.0'); break; case 4: $vCard->add('VERSION', '4.0'); break; default: $this->logger->fatal("Unrecognised VCard version: " . $this->version); return; } // Private contact ? if (isset($contactProperties[$p['private']]) && $contactProperties[$p['private']]) { $vCard->add('CLASS', 'PRIVATE'); // Not in VCARD 4.0 but keep it for compatibility } // Mandatory FN $this->setVCard($vCard, 'FN', $contactProperties, $p['display_name']); // Contact name and pro information // N property /* Special note: The structured property value corresponds, in sequence, to the Family Names (also known as surnames), Given Names, Additional Names, Honorific Prefixes, and Honorific Suffixes. The text components are separated by the SEMICOLON character (U+003B). Individual text components can include multiple text values separated by the COMMA character (U+002C). This property is based on the semantics of the X.520 individual name attributes [CCITT.X520.1988]. The property SHOULD be present in the vCard object when the name of the object the vCard represents follows the X.520 model. The SORT-AS parameter MAY be applied to this property. */ $contactInfos = array(); $contactInfos[] = isset($contactProperties[$p['surname']]) ? $contactProperties[$p['surname']] : ''; $contactInfos[] = isset($contactProperties[$p['given_name']]) ? $contactProperties[$p['given_name']] : ''; $contactInfos[] = isset($contactProperties[$p['middle_name']]) ? $contactProperties[$p['middle_name']] : ''; $contactInfos[] = isset($contactProperties[$p['display_name_prefix']]) ? $contactProperties[$p['display_name_prefix']] : ''; $contactInfos[] = isset($contactProperties[$p['generation']]) ? $contactProperties[$p['generation']] : ''; $element = new Sabre_VObject_Property("N"); $element->setValue(implode(';', $contactInfos)); // $element->offsetSet("SORT-AS", '"' . $contactProperties[$p['fileas']] . '"'); $vCard->add($element); $this->setVCard($vCard, 'SORT-AS', $contactProperties, $p['fileas']); $this->setVCard($vCard, 'NICKNAME', $contactProperties, $p['nickname']); $this->setVCard($vCard, 'TITLE', $contactProperties, $p['title']); $this->setVCard($vCard, 'ROLE', $contactProperties, $p['profession']); $this->setVCard($vCard, 'ORG', $contactProperties, $p['company_name']); $this->setVCard($vCard, 'OFFICE', $contactProperties, $p['office_location']); if ($this->version >= 4) { if (isset($contactProperties[$p['assistant']])) { if (!empty($contactProperties[$p['assistant']])) { $element = new Sabre_VObject_Property('RELATED'); $element->setValue($contactProperties[$p['assistant']]); $element->offsetSet('TYPE', 'assistant'); // Not RFC compliant $vCard->add($element); } } if (isset($contactProperties[$p['manager_name']])) { if (!empty($contactProperties[$p['manager_name']])) { $element = new Sabre_VObject_Property('RELATED'); $element->setValue($contactProperties[$p['manager_name']]); $element->offsetSet('TYPE', 'manager'); // Not RFC compliant $vCard->add($element); } } if (isset($contactProperties[$p['spouse_name']])) { if (!empty($contactProperties[$p['spouse_name']])) { $element = new Sabre_VObject_Property('RELATED'); $element->setValue($contactProperties[$p['spouse_name']]); $element->offsetSet('TYPE', 'spouse'); $vCard->add($element); } } } // older syntax - may be needed by some clients so keep it! $this->setVCard($vCard, 'X-MS-ASSISTANT', $contactProperties, $p['assistant']); $this->setVCard($vCard, 'X-MS-MANAGER', $contactProperties, $p['manager_name']); $this->setVCard($vCard, 'X-MS-SPOUSE', $contactProperties, $p['spouse_name']); // Dates if (isset($contactProperties[$p['birthday']]) && $contactProperties[$p['birthday']] > 0) { $vCard->add('BDAY', date(DATE_PATTERN, $contactProperties[$p['birthday']])); } if (isset($contactProperties[$p['wedding_anniversary']]) && $contactProperties[$p['wedding_anniversary']] > 0) { if ($this->version >= 4) { $vCard->add('ANNIVERSARY', date(DATE_PATTERN, $contactProperties[$p['wedding_anniversary']])); } else { $vCard->add('X-ANNIVERSARY', date(DATE_PATTERN, $contactProperties[$p['wedding_anniversary']])); } } // Telephone numbers // webaccess can handle 19 telephone numbers... $this->setVCard($vCard, 'TEL;TYPE=HOME,VOICE', $contactProperties, $p['home_telephone_number']); $this->setVCard($vCard, 'TEL;TYPE=HOME,VOICE', $contactProperties, $p['home2_telephone_number']); $this->setVCard($vCard, 'TEL;TYPE=CELL', $contactProperties, $p['cellular_telephone_number']); $this->setVCard($vCard, 'TEL;TYPE=WORK,VOICE', $contactProperties, $p['office_telephone_number']); $this->setVCard($vCard, 'TEL;TYPE=WORK,VOICE', $contactProperties, $p['business2_telephone_number']); $this->setVCard($vCard, 'TEL;TYPE=WORK,FAX', $contactProperties, $p['business_fax_number']); $this->setVCard($vCard, 'TEL;TYPE=HOME,FAX', $contactProperties, $p['home_fax_number']); $this->setVCard($vCard, 'TEL;TYPE=PAGER', $contactProperties, $p['pager_telephone_number']); $this->setVCard($vCard, 'TEL;TYPE=ISDN', $contactProperties, $p['isdn_number']); $this->setVCard($vCard, 'TEL;TYPE=WORK', $contactProperties, $p['company_telephone_number']); $this->setVCard($vCard, 'TEL;TYPE=CAR', $contactProperties, $p['car_telephone_number']); $this->setVCard($vCard, 'TEL;TYPE=SECR', $contactProperties, $p['assistant_telephone_number']); // There are unmatched telephone numbers in zarafa, use them! $unmatchedProperties = array("callback_telephone_number", "other_telephone_number", "primary_fax_number", "primary_telephone_number", "radio_telephone_number", "telex_telephone_number", "ttytdd_telephone_number"); if (in_array(DEFAULT_TELEPHONE_NUMBER_PROPERTY, $unmatchedProperties)) { // unmatched found a match! $this->setVCard($vCard, 'TEL', $contactProperties, $p[DEFAULT_TELEPHONE_NUMBER_PROPERTY]); } $this->setVCardAddress($vCard, 'HOME', $contactProperties, 'home'); $this->setVCardAddress($vCard, 'WORK', $contactProperties, 'business'); $this->setVCardAddress($vCard, 'OTHER', $contactProperties, 'other'); // emails for ($i = 1; $i <= 3; $i++) { if (isset($contactProperties[$p["email_address_{$i}"]])) { // Zarafa needs an email display name $emailProperty = new Sabre_VObject_Property('EMAIL', $contactProperties[$p["email_address_{$i}"]]); // Get display name $dn = isset($contactProperties[$p["email_address_display_name_{$i}"]]) ? $contactProperties[$p["email_address_display_name_{$i}"]] : $contactProperties[$p['display_name']]; $emailProperty->offsetSet("X-CN", '"' . $dn . '"'); $vCard->add($emailProperty); } } // URL and Instant Messenging (vCard 3.0 extension) $this->setVCard($vCard, 'URL', $contactProperties, $p["webpage"]); $this->setVCard($vCard, 'IMPP', $contactProperties, $p["im"]); // Categories $contactCategories = ''; if (isset($contactProperties[$p['categories']])) { if (is_array($contactProperties[$p['categories']])) { $contactCategories = implode(',', $contactProperties[$p['categories']]); } else { $contactCategories = $contactProperties[$p['categories']]; } } if ($contactCategories != '') { $vCard->add('CATEGORIES', $contactCategories); } // Contact picture? $hasattachProp = mapi_getprops($contact, array(PR_HASATTACH)); $photo = NULL; $photoMime = ''; if (isset($hasattachProp[PR_HASATTACH]) && $hasattachProp[PR_HASATTACH]) { $attachmentTable = mapi_message_getattachmenttable($contact); $attachments = mapi_table_queryallrows($attachmentTable, array(PR_ATTACH_NUM, PR_ATTACH_SIZE, PR_ATTACH_LONG_FILENAME, PR_ATTACH_FILENAME, PR_ATTACHMENT_HIDDEN, PR_DISPLAY_NAME, PR_ATTACH_METHOD, PR_ATTACH_CONTENT_ID, PR_ATTACH_MIME_TAG, PR_ATTACHMENT_CONTACTPHOTO, PR_EC_WA_ATTACHMENT_HIDDEN_OVERRIDE)); $dump = print_r($attachments, true); $this->logger->trace("Contact attachments:\n{$dump}"); foreach ($attachments as $attachmentRow) { if (isset($attachmentRow[PR_ATTACHMENT_CONTACTPHOTO]) && $attachmentRow[PR_ATTACHMENT_CONTACTPHOTO]) { $attach = mapi_message_openattach($contact, $attachmentRow[PR_ATTACH_NUM]); $photo = mapi_attach_openbin($attach, PR_ATTACH_DATA_BIN); if (isset($attachmentRow[PR_ATTACH_MIME_TAG])) { $photoMime = $attachmentRow[PR_ATTACH_MIME_TAG]; } else { $photoMime = 'image/jpeg'; } break; } } } if ($photo != NULL) { // SogoConnector does not like image/jpeg if ($photoMime == 'image/jpeg') { $photoMime = 'JPEG'; } $this->logger->trace("Adding contact picture to VCard"); $photoEncoded = base64_encode($photo); $photoProperty = new Sabre_VObject_Property('PHOTO', $photoEncoded); $photoProperty->offsetSet('TYPE', $photoMime); $photoProperty->offsetSet('ENCODING', 'b'); $vCard->add($photoProperty); } // Misc $vCard->add('UID', "urn:uuid:" . substr($contactProperties[PR_CARDDAV_URI], 0, -4)); // $this->entryIdToStr($contactProperties[PR_ENTRYID])); $this->setVCard($vCard, 'NOTE', $contactProperties, $p['notes']); $vCard->add('PRODID', VCARD_PRODUCT_ID); $vCard->add('REV', date('c', $contactProperties[$p['last_modification_time']])); }