/** * Test parsing GOID value. */ public function testParseGlobalObjectId() { // Outlook UID $fixture = 'BAAAAIIA4AB0xbcQGoLgCAfUCRDgQMnBJoXEAQAAAAAAAAAAEAAAAAvw7UtuTulOnjnjhns3jvM='; $uid = Horde_Mapi::getUidFromGoid($fixture); $this->assertEquals('040000008200E00074C5B7101A82E00800000000E040C9C12685C4010000000000000000100000000BF0ED4B6E4EE94E9E39E3867B378EF3', $uid); // vCal $fixture = 'BAAAAIIA4AB0xbcQGoLgCAAAAAAAAAAAAAAAAAAAAAAAAAAAMwAAAHZDYWwtVWlkAQAAAHs4MTQxMkQzQy0yQTI0LTRFOUQtQjIwRS0xMUY3QkJFOTI3OTl9AA=='; $uid = Horde_Mapi::getUidFromGoid($fixture); $this->assertEquals('{81412D3C-2A24-4E9D-B20E-11F7BBE92799}', $uid); }
/** * Build an MAPI TZ blob given a TZ Offset hash. * * @param array $offsets A TZ offset hash * * @return string A base64_encoded MAPI Timezone structure suitable * for transmitting via wbxml. */ public static function getSyncTZFromOffsets(array $offsets) { if (!Horde_Mapi::isLittleEndian()) { $offsets['bias'] = Horde_Mapi::chbo($offsets['bias']); $offsets['stdbias'] = Horde_Mapi::chbo($offsets['stdbias']); $offsets['dstbias'] = Horde_Mapi::chbo($offsets['dstbias']); } $packed = pack('la64vvvvvvvvla64vvvvvvvvl', $offsets['bias'], '', 0, $offsets['stdmonth'], $offsets['stdday'], $offsets['stdweek'], $offsets['stdhour'], $offsets['stdminute'], $offsets['stdsecond'], $offsets['stdmillis'], $offsets['stdbias'], '', 0, $offsets['dstmonth'], $offsets['dstday'], $offsets['dstweek'], $offsets['dsthour'], $offsets['dstminute'], $offsets['dstsecond'], $offsets['dstmillis'], $offsets['dstbias']); return base64_encode($packed); }
/** * Allow this object to set any MAPI attributes it needs to know about, * ignore any it doesn't care about. * * @param integer $type The attribute type descriptor. * @param integer $name The attribute name descriptor. */ public function setMapiAttribute($type, $name, $value, $ns = null) { if ($ns == Horde_Compress_Tnef::PSETID_COMMON) { switch ($name) { case Horde_Compress_Tnef::IPM_TASK_GUID: // Almost positive this is wrong :( $this->_guid = Horde_Mapi::getUidFromGoid(bin2hex($value)); break; case Horde_Compress_Tnef::MSG_EDITOR_FORMAT: // Map this? $this->_msgformat = $value; break; case Horde_Compress_Tnef::MAPI_TAG_BODY: // plaintext. Most likely set via the attBody TNEF attribute, // and not by the MAPI property. if (empty($this->_bodyPlain)) { $this->_bodyPlain = $value; } break; case Horde_Compress_Tnef::MAPI_TAG_HTML: // html $this->_bodyHtml = $value; break; case self::MAPI_TASK_COMMONSTART: try { $this->_start = new Horde_Date(Horde_Mapi::filetimeToUnixtime($value)); } catch (Horde_Date_Exception $e) { throw new Horde_Compress_Exception($e); } $this->_start = $this->_start->timestamp(); break; case self::MAPI_TASK_COMMONEND: try { $this->_due = new Horde_Date(Horde_Mapi::filetimeToUnixtime($value)); } catch (Horde_Mapi_Exception $e) { throw new Horde_Compress_Exception($e); } catch (Horde_Date_Exception $e) { throw new Horde_Compress_Exception($e); } $this->_due = $this->_due->timestamp(); break; } } elseif ($ns == Horde_Compress_Tnef::PSETID_TASK) { switch ($name) { case self::MAPI_TASK_OWNER: // This is the OWNER, not to be confused with the ORGANIZER. // I.e., this is the person the task has been assigned to. // The ORGANIZER is the person who created the task and has // assigned it. I.e., the person that any task updates are // sent back to by the owner. $this->_owner = str_replace(array('(', ')'), array('<', '>'), $value); break; case self::MAPI_TASK_DUEDATE: // Favor COMMONEND if (empty($this->_due)) { $this->_due = Horde_Mapi::filetimeToUnixtime($value); } break; case self::MAPI_TASK_STARTDATE: if (empty($this->_start)) { $this->_start = Horde_Mapi::filetimeToUnixtime($value); } break; case self::MAPI_TASK_DATECOMPLETED: $this->_completed = Horde_Mapi::filetimeToUnixtime($value); break; case self::MAPI_TASK_PERCENTCOMPLETE: $value = unpack('d', $value); $this->_percentComplete = $value[1] * 100; break; case self::MAPI_TASK_STATUS: switch ($value) { case self::STATUS_NOT_STARTED: case self::STATUS_WAIT: case self::STATUS_DEFERRED: $this->_percentComplete = 0; $this->_status = self::TASK_STATUS_ACTION; break; case self::STATUS_IN_PROGRESS: $this->_status = self::TASK_STATUS_IN_PROGRESS; break; case self::STATUS_COMPLETE: $this->_status = self::TASK_STATUS_COMPLETED; $this->_percentComplete = 1; break; } break; case self::MAPI_TASK_UPDATES: if (!empty($value)) { $this->_updates = true; } break; case self::MAPI_TASK_OWNERSHIP: $this->_ownership = $value; break; case self::MAPI_TASK_STATE: $this->_state = $value; break; // case self::MAPI_TASK_ASSIGNER: // // *sigh* This isn't set by Outlook/Exchange until AFTER the // // assignee receives the request. I.e., this is blank on the initial // // REQUEST so not a valid way to obtain the task creator. // //$this->_organizer = $value; // break; // case self::MAPI_TASK_LASTUSER: // // From MS-OXOTASK 2.2.2.2.25: // // Before client sends a REQUEST, it is set to the assigner. // // Before client sends an ACCEPT, it is set to the assignee. // // Before client sneds REJECT, it is set to the assigner, not assignee. // // Unfortunately, it is only the display name, not the email! // $this->_lastUser = $value; // break; } } else { // pidTag? switch ($name) { case Horde_Compress_Tnef::MAPI_SENT_REP_EMAIL_ADDR: $this->_organizer = $value; break; case Horde_Compress_Tnef::MAPI_LAST_MODIFIER_NAME: $this->_lastUser = $value; } } }
/** * Parses a vEvent into the message properties. * * @param Horde_Icalendar_Vevent $vevent The vEvent to parse. * @param string $method The method (e.g., 'REQUEST'). * * @throws Horde_ActiveSync_Exception */ protected function _parsevEvent($vevent, $method = 'REQUEST') { if ($method == 'REQUEST') { $this->responserequested = '1'; } else { $this->responserequested = '0'; } try { $organizer = parse_url($vevent->getAttribute('ORGANIZER')); $this->organizer = $organizer['path']; } catch (Horde_Icalendar_Exception $e) { } try { $this->globalobjid = Horde_Mapi::createGoid($vevent->getAttribute('UID')); $this->starttime = new Horde_Date($vevent->getAttribute('DTSTART')); $this->endtime = new Horde_Date($vevent->getAttribute('DTEND')); } catch (Horde_Exception $e) { throw new Horde_ActiveSync_Exception($e); } try { $this->dtstamp = new Horde_Date($vevent->getAttribute('DTSTAMP')); } catch (Horde_Exception $e) { } try { $this->location = Horde_String::truncate($vevent->getAttribute('LOCATION'), 255); } catch (Horde_Icalendar_Exception $e) { } try { $class = $vevent->getAttribute('CLASS'); if (!is_array($class)) { $this->sensitivity = $class == 'PRIVATE' ? Horde_ActiveSync_Message_Appointment::SENSITIVITY_PRIVATE : ($class == 'CONFIDENTIAL' ? Horde_ActiveSync_Message_Appointment::SENSITIVITY_CONFIDENTIAL : ($class == 'PERSONAL' ? Horde_ActiveSync_Message_Appointment::SENSITIVITY_PERSONAL : Horde_ActiveSync_Message_Appointment::SENSITIVITY_NORMAL)); } } catch (Horde_Icalendar_Exception $e) { } try { $status = $vevent->getAttribute('STATUS'); if (!is_array($status)) { $status = Horde_String::upper($status); $this->busystatus = $status == 'TENTATIVE' ? Horde_ActiveSync_Message_Appointment::BUSYSTATUS_TENTATIVE : ($status == 'CONFIRMED' ? Horde_ActiveSync_Message_Appointment::BUSYSTATUS_BUSY : Horde_ActiveSync_Message_Appointment::BUSYSTATUS_FREE); } } catch (Horde_Icalendar_Exception $e) { } // vCalendar 1.0 alarms try { $alarm = $vevent->getAttribute('AALARM'); if (!is_array($alarm) && intval($alarm)) { $this->reminder = intval($this->starttime->timestamp() - $alarm); } } catch (Horde_Icalendar_Exception $e) { } // vCalendar 2.0 alarms foreach ($vevent->getComponents() as $alarm) { if (!$alarm instanceof Horde_Icalendar_Valarm) { continue; } try { $trigger = $alarm->getAttribute('TRIGGER'); $triggerParams = $alarm->getAttribute('TRIGGER', true); } catch (Horde_Icalendar_Exception $e) { continue; } if (isset($triggerParams['VALUE']) && $triggerParams['VALUE'] == 'DATE-TIME') { if (isset($triggerParams['RELATED']) && $triggerParams['RELATED'] == 'END') { $this->reminder = intval($this->endtime->timestamp() - $trigger); } else { $this->reminder = intval($this->starttime->timestamp() - $trigger); } } else { $this->reminder = -intval($trigger); } } }
/** * Parse recurrence properties. * * @param string $value MAPI stream * * @return Horde_Date_Recurrence * @throws Horde_Compress_Exception */ protected function _parseRecurrence($value) { $deleted = $modified = array(); // both are 0x3004 (version strings); $this->_geti($value, 16); $this->_geti($value, 16); $freq = $this->_geti($value, 16); $pattern = $this->_geti($value, 16); $calendarType = $this->_geti($value, 16); $firstDt = $this->_geti($value, 32); $period = $this->_geti($value, 32); // Only used for tasks, otherwise value must be zero. $flag = $this->_geti($value, 32); // TypeSpecific field switch ($pattern) { case Horde_Compress_Tnef::PATTERN_DAY: // Nothing here to see, move along. break; case Horde_Compress_Tnef::PATTERN_WEEK: // Bits: 0/unused, 1/Saturday, 2/Friday, 3/Thursday, 4/Wednesday, // 5/Tuesday, 6/Monday, 7/Sunday. $day = $this->_geti($value, 8); // ?? $this->_geti($value, 24); break; case Horde_Compress_Tnef::PATTERN_MONTH: case Horde_Compress_Tnef::PATTERN_MONTH_END: // Day of month on which the recurrence falls. $day = $this->_geti($value, 32); break; case Horde_Compress_Tnef::PATTERN_MONTH_NTH: // Bits: 0/unused, 1/Saturday, 2/Friday, 3/Thursday, 4/Wednesday, // 5/Tuesday, 6/Monday, 7/Sunday. // For Nth Weekday of month $day = $this->_geti($value, 8); $this->_geti($value, 24); $n = $this->_geti($value, 32); break; } $end = $this->_geti($value, 32); $count = $this->_geti($value, 32); $fdow = $this->_geti($value, 32); $deletedCount = $this->_geti($value, 32); for ($i = 0; $i < $deletedCount; $i++) { $deleted[] = $this->_geti($value, 32); } $modifiedCount = $this->_geti($value, 32); for ($i = 0; $i < $modifiedCount; $i++) { $modified[] = $this->_geti($value, 32); } // What Timezone are these in? try { $startDate = new Horde_Date(Horde_Mapi::filetimeToUnixtime($this->_geti($value, 32))); $endDate = new Horde_Date(Horde_Mapi::filetimeToUnixtime($this->_geti($value, 32))); } catch (Horde_Mapi_Exception $e) { throw new Horde_Compress_Exception($e); } catch (Horde_Date_Exception $e) { throw new Horde_Compress_Exception($e); } $rrule = new Horde_Date_Recurrence($startDate); switch ($pattern) { case Horde_Compress_Tnef::PATTERN_DAY: $rrule->setRecurType(Horde_Date_Recurrence::RECUR_DAILY); break; case Horde_Compress_Tnef::PATTERN_WEEK: $rrule->setRecurType(Horde_Date_Recurrence::RECUR_WEEKLY); break; case Horde_Compress_Tnef::PATTERN_MONTH: case Horde_Compress_Tnef::PATTERN_MONTH_END: $rrule->setRecurType(Horde_Date_Recurrence::RECUR_MONTHLY_DATE); break; case Horde_Compress_Tnef::PATTERN_MONTH_NTH: $rrule->setRecurType(Horde_Date_Recurrence::RECUR_MONTHLY_WEEKDAY); break; default: if ($freq == Horde_Compress_Tnef::RECUR_YEARLY) { $rrule->setRecurType(Horde_Date_Recurrence::RECUR_YEARLY); } } switch ($end) { case Horde_Compress_Tnef::RECUR_END_N: $rrule->setRecurCount($count); break; case Horde_Compress_Tnef::RECUR_END_DATE: $rrule->setRecurEnd($endDate); break; } return $rrule; }
/** * Decodes TNEF attributes. * * @param [type] &$data [description] * @return [type] [description] */ protected function _decodeMessageProperty(&$data) { // This contains the type AND the attribute name. We should only check // against the name since this is very confusing (everything else is // checked against just name). Can't change until Horde 6 though since // the constants would have to change. Also, the type identifiers are // different between MAPI and TNEF. Of course... // $type = $this->_geti($data, 16); // $attribute = $this->_geti($data, 16); $attribute = $this->_geti($data, 32); $this->_logger->debug(sprintf('TNEF: Message property 0x%X found.', $attribute)); $value = false; switch ($attribute) { case self::AMCLASS: // Start of a new message. $message_class = trim($this->_decodeAttribute($data)); $this->_logger->debug(sprintf('TNEF: Message class: %s', $message_class)); switch ($message_class) { case self::IPM_MEETING_REQUEST: $this->_currentObject = new Horde_Compress_Tnef_Icalendar($this->_logger); $this->_currentObject->setMethod('REQUEST', $message_class); $this->_files[] = $this->_currentObject; break; case self::IPM_MEETING_RESPONSE_TENT: case self::IPM_MEETING_RESPONSE_NEG: case self::IPM_MEETING_RESPONSE_POS: $this->_currentObject = new Horde_Compress_Tnef_Icalendar($this->_logger); $this->_currentObject->setMethod('REPLY', $message_class); $this->_files[] = $this->_currentObject; break; case self::IPM_MEETING_REQUEST_CANCELLED: $this->_currentObject = new Horde_Compress_Tnef_Icalendar($this->_logger); $this->_currentObject->setMethod('CANCEL', $message_class); $this->_files[] = $this->_currentObject; break; case self::IPM_TASK_REQUEST: $this->_currentObject = new Horde_Compress_Tnef_VTodo($this->_logger, null, array('parent' => &$this)); $this->_files[] = $this->_currentObject; break; default: $this->_logger->debug(sprintf('Unknown message class: %s', $message_class)); } break; case self::AMAPIPROPS: $this->_logger->debug('TNEF: Extracting encapsulated message properties (idMsgProps)'); $properties = $this->_decodeAttribute($data); $this->_extractMapiAttributes($properties); break; case self::APRIORITY: case self::AOWNER: case self::ARECIPIENTTABLE: case self::ABODY: case self::ASTATUS: case self::ACONVERSATIONID: case self::APARENTID: case self::AMESSAGEID: case self::ASUBJECT: case self::AORIGINALMCLASS: $value = $this->_decodeAttribute($data); break; case self::ADATERECEIVED: case self::ADATESENT: case self::ADATEMODIFIED: case self::ID_DATE_END: try { $value = new Horde_Date(Horde_Mapi::filetimeToUnixtime($this->_decodeAttribute($data)), 'UTC'); } catch (Horde_Mapi_Exception $e) { throw new Horde_Compress_Exception($e); } catch (Horde_Date_Exception $e) { throw new Horde_Compress_Exception($e); } break; case self::AFROM: case self::ASENTFOR: $msgObj = $this->_decodeAttribute($data); $display_name = $this->_getx($msgObj, $this->_geti($msgObj, 16)); $email = $this->_getx($msgObj, $this->_geti($msgObj, 16)); $value = $email; // @todo - Do we need to pass display name too? break; default: $size = $this->_geti($data, 32); $value = $this->_getx($data, $size); $this->_geti($data, 16); // Checksum. } if ($value && $this->_currentObject) { $this->_currentObject->setTnefAttribute($attribute, $value, empty($size) ? strlen($value) : $size); } }