Adapted from:
http://stackoverflow.com/questions/610603/help-me-translate-long-value-expressed-in-hex-back-in-to-a-date-time
public static filetimeToUnixtime ( string $ft ) : integer | ||
$ft | string | Binary representation of FILETIME from a pTypDate MAPI property. |
return | integer | The unix timestamp. |
public function testFiletimeToUnixTime() { $data = file_get_contents(__DIR__ . '/fixtures/filetime'); try { $this->assertEquals(Horde_Mapi::filetimeToUnixtime($data), 1387818000); } catch (Horde_Mapi_Exception $e) { $this->markTestSkipped("bcmath extension isn't loaded"); } }
/** * 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; } } }
/** * 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); } }
/** * 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; }
public function testFiletimeToUnixTime() { $data = file_get_contents(__DIR__ . '/fixtures/filetime'); $this->assertEquals(Horde_Mapi::filetimeToUnixtime($data), 1387818000); }