def occurrence(self, entryid): entryid = _bdec(entryid) pos = 2 + _utils.unpack_short(entryid, 0) basedate_val = _utils.unpack_long(entryid, pos) for exc in self.exceptions: # TODO subject etc if exc['original_start_date'] in ( basedate_val, basedate_val - self.starttime_offset): # TODO pick one start = datetime.datetime.fromtimestamp( _utils.rectime_to_unixtime(exc['start_datetime'])) start = _utils._to_gmt(start, self.tz) break else: # TODO check that date is (still) valid start = datetime.datetime.fromtimestamp( _utils.rectime_to_unixtime(basedate_val)) start = _utils._to_gmt(start, self.tz) return Occurrence( self.item, start, start + datetime.timedelta(minutes=self.endtime_offset - self.starttime_offset), basedate_val=basedate_val, )
def event(self, eventid): eventid = _bdec(eventid) leid = _utils.unpack_short(eventid, 1) item = self.item(_benc(eventid[3:3+leid])) if eventid[0] == 0: return item else: return item.occurrence(_benc(eventid[1:]))
def _extract_ipm_ol2007_entryid(self, offset): # Extracts entryids from PR_IPM_OL2007_ENTRYIDS blob using logic from common/Util.cpp Util::ExtractAdditionalRenEntryID blob = self.inbox.prop(PR_IPM_OL2007_ENTRYIDS).value pos = 0 while True: blocktype = _utils.unpack_short(blob, pos) if blocktype == 0: break pos += 2 totallen = _utils.unpack_short(blob, pos) pos += 2 if blocktype == offset: pos += 2 # skip check sublen = _utils.unpack_short(blob, pos) pos += 2 return _benc(blob[pos:pos + sublen]) else: pos += totallen raise NotFoundError('entryid not found')
def occurrence(self, entryid): entryid = _bdec(entryid) pos = 2 + _utils.unpack_short(entryid, 0) basedate_val = _utils.unpack_long(entryid, pos) start = end = subject = location = None for exc, ext in zip(self._exceptions, self._extended_exceptions): if exc['original_start_date'] in (basedate_val, basedate_val - self._starttime_offset): # TODO pick one start = datetime.datetime.utcfromtimestamp(_utils.rectime_to_unixtime(exc['start_datetime'])) end = datetime.datetime.utcfromtimestamp(_utils.rectime_to_unixtime(exc['end_datetime'])) if exc['override_flags'] & ARO_SUBJECT: subject = ext.get('subject') if exc['override_flags'] & ARO_LOCATION: location = ext.get('location') break else: # TODO check that date is (still) valid start = datetime.datetime.utcfromtimestamp(_utils.rectime_to_unixtime(basedate_val)) end = start + datetime.timedelta(minutes=self._endtime_offset - self._starttime_offset) return Occurrence(self.item, start, end, subject, location, basedate_val=basedate_val)
def _parse(self): # AppointmentRecurrencePattern value = self.item.prop(PidLidAppointmentRecur).value # RecurrencePattern self._reader_version = _utils.unpack_short(value, 0) self._writer_version = _utils.unpack_short(value, SHORT) self._recur_frequency = _utils.unpack_short(value, 2 * SHORT) self._pattern_type = _utils.unpack_short(value, 3 * SHORT) self._calendar_type = _utils.unpack_short(value, 4 * SHORT) self._first_datetime = _utils.unpack_long(value, 5 * SHORT) self._period = _utils.unpack_long(value, 5 * SHORT + LONG) self._sliding_flag = _utils.unpack_long(value, 5 * SHORT + 2 * LONG) pos = 5 * SHORT + 3 * LONG self._pattern_type_specific = [0, 0] if self._pattern_type != PATTERN_DAILY: self._pattern_type_specific[0] = _utils.unpack_long(value, pos) pos += LONG if self._pattern_type in (PATTERN_MONTHNTH, PATTERN_HJMONTHNTH): self._pattern_type_specific[1] = _utils.unpack_long(value, pos) pos += LONG self._end_type = _utils.unpack_long(value, pos) pos += LONG self._occurrence_count = _utils.unpack_long(value, pos) pos += LONG self._first_dow = _utils.unpack_long(value, pos) pos += LONG self.deleted_instance_count = _utils.unpack_long(value, pos) pos += LONG self._deleted_instance_dates = [] for _ in range(0, self.deleted_instance_count): self._deleted_instance_dates.append(_utils.unpack_long(value, pos)) pos += LONG self._modified_instance_count = _utils.unpack_long(value, pos) pos += LONG self._modified_instance_dates = [] for _ in range(0, self._modified_instance_count): self._modified_instance_dates.append(_utils.unpack_long(value, pos)) pos += LONG self._start_date = _utils.unpack_long(value, pos) pos += LONG self._end_date = _utils.unpack_long(value, pos) pos += LONG # AppointmentRecurrencePattern self._reader_version2 = _utils.unpack_long(value, pos) pos += LONG self._writer_version2 = _utils.unpack_long(value, pos) pos += LONG self._starttime_offset = _utils.unpack_long(value, pos) pos += LONG self._endtime_offset = _utils.unpack_long(value, pos) pos += LONG self.exception_count = _utils.unpack_short(value, pos) # TODO rename to _exception_count? pos += SHORT # ExceptionInfo self._exceptions = [] for i in range(0, self._modified_instance_count): # using modcount, as PHP seems to not update exception_count? equal according to docs exception = {} val = _utils.unpack_long(value, pos) exception['start_datetime'] = val pos += LONG val = _utils.unpack_long(value, pos) exception['end_datetime'] = val pos += LONG val = _utils.unpack_long(value, pos) exception['original_start_date'] = val pos += LONG exception['override_flags'] = _utils.unpack_short(value, pos) pos += SHORT # We have modified the subject if exception['override_flags'] & ARO_SUBJECT: pos += SHORT subject_length2 = _utils.unpack_short(value, pos) pos += SHORT exception['subject'] = value[pos:pos + subject_length2] pos += subject_length2 if exception['override_flags'] & ARO_MEETINGTYPE: exception['meeting_type'] = _utils.unpack_long(value, pos) pos += LONG if exception['override_flags'] & ARO_REMINDERDELTA: exception['reminder_delta'] = _utils.unpack_long(value, pos) # XXX: datetime? pos += LONG if exception['override_flags'] & ARO_REMINDERSET: exception['reminder_set'] = _utils.unpack_long(value, pos) # XXX: bool? pos += LONG if exception['override_flags'] & ARO_LOCATION: pos += SHORT location_length2 = _utils.unpack_short(value, pos) pos += SHORT exception['location'] = value[pos:pos + location_length2] pos += location_length2 if exception['override_flags'] & ARO_BUSYSTATUS: exception['busy_status'] = _utils.unpack_long(value, pos) pos += LONG if exception['override_flags'] & ARO_ATTACHMENT: exception['attachment'] = _utils.unpack_long(value, pos) pos += LONG if exception['override_flags'] & ARO_SUBTYPE: exception['sub_type'] = _utils.unpack_long(value, pos) pos += LONG if exception['override_flags'] & ARO_APPTCOLOR: exception['appointment_color'] = _utils.unpack_long(value, pos) pos += LONG self._exceptions.append(exception) # according to the specs, the number of exceptions and extended exceptions # should always be equal, but some clients apparently do not respect this self._has_extended = True if pos==len(value): self._extended_exceptions = [{} for ext in self._exceptions] self._has_extended = False return # ReservedBlock1Size pos += _utils.unpack_long(value, pos) + LONG # ExtendedException self._extended_exceptions = [] for exception in self._exceptions: extended_exception = {} # ChangeHighlight if self._writer_version2 >= 0x3009: change_highlight_size = _utils.unpack_long(value, pos) pos += LONG change_highlight_value = _utils.unpack_long(value, pos) extended_exception['change_highlight'] = change_highlight_value pos += change_highlight_size # ReservedBlockEE1 pos += _utils.unpack_long(value, pos) + LONG # StartDateTime, EndDateTime, OriginalStartDate if exception['override_flags'] & ARO_SUBJECT or exception['override_flags'] & ARO_LOCATION: extended_exception['start_datetime'] = _utils.unpack_long(value, pos) pos += LONG extended_exception['end_datetime'] = _utils.unpack_long(value, pos) pos += LONG extended_exception['original_start_date'] = _utils.unpack_long(value, pos) pos += LONG # WideCharSubject if exception['override_flags'] & ARO_SUBJECT: length = _utils.unpack_short(value, pos) pos += SHORT extended_exception['subject'] = value[pos:pos + 2 * length].decode('utf-16-le') pos += 2 * length # WideCharLocation if exception['override_flags'] & ARO_LOCATION: length = _utils.unpack_short(value, pos) pos += SHORT extended_exception['location'] = value[pos:pos + 2 * length].decode('utf-16-le') pos += 2 * length # ReservedBlockEE2 if exception['override_flags'] & ARO_SUBJECT or exception['override_flags'] & ARO_LOCATION: pos += _utils.unpack_long(value, pos) + LONG self._extended_exceptions.append(extended_exception)
def _parse(self): # AppointmentRecurrencePattern value = self.item.prop(PidLidAppointmentRecur).value # RecurrencePattern self.reader_version = _utils.unpack_short(value, 0) self.writer_version = _utils.unpack_short(value, SHORT) self.recur_frequency = _utils.unpack_short(value, 2 * SHORT) self.pattern_type = _utils.unpack_short(value, 3 * SHORT) self.calendar_type = _utils.unpack_short(value, 4 * SHORT) self.first_datetime = _utils.unpack_long(value, 5 * SHORT) self.period = _utils.unpack_long(value, 5 * SHORT + LONG) self.sliding_flag = _utils.unpack_long(value, 5 * SHORT + 2 * LONG) pos = 5 * SHORT + 3 * LONG self.pattern_type_specific = [] if self.pattern_type != 0: self.pattern_type_specific.append(_utils.unpack_long(value, pos)) pos += LONG if self.pattern_type in (3, 0xB): self.pattern_type_specific.append( _utils.unpack_long(value, pos)) pos += LONG self.end_type = _utils.unpack_long(value, pos) pos += LONG self.occurrence_count = _utils.unpack_long(value, pos) pos += LONG self.first_dow = _utils.unpack_long(value, pos) pos += LONG self.deleted_instance_count = _utils.unpack_long(value, pos) pos += LONG self.deleted_instance_dates = [] for _ in range(0, self.deleted_instance_count): self.deleted_instance_dates.append(_utils.unpack_long(value, pos)) pos += LONG self.modified_instance_count = _utils.unpack_long(value, pos) pos += LONG self.modified_instance_dates = [] for _ in range(0, self.modified_instance_count): self.modified_instance_dates.append(_utils.unpack_long(value, pos)) pos += LONG self.start_date = _utils.unpack_long(value, pos) pos += LONG self.end_date = _utils.unpack_long(value, pos) pos += LONG # AppointmentRecurrencePattern self.reader_version2 = _utils.unpack_long(value, pos) pos += LONG self.writer_version2 = _utils.unpack_long(value, pos) pos += LONG self.starttime_offset = _utils.unpack_long(value, pos) pos += LONG self.endtime_offset = _utils.unpack_long(value, pos) pos += LONG self.exception_count = _utils.unpack_short(value, pos) pos += SHORT # ExceptionInfo self.exceptions = [] for i in range( 0, self.modified_instance_count ): # using modcount, as PHP seems to not update exception_count? equal according to docs exception = {} val = _utils.unpack_long(value, pos) exception['start_datetime'] = val pos += LONG val = _utils.unpack_long(value, pos) exception['end_datetime'] = val pos += LONG val = _utils.unpack_long(value, pos) exception['original_start_date'] = val pos += LONG exception['override_flags'] = _utils.unpack_short(value, pos) pos += SHORT # We have modified the subject if exception['override_flags'] & ARO_SUBJECT: subject_length = _utils.unpack_short(value, pos) # XXX: unused? pos += SHORT subject_length2 = _utils.unpack_short(value, pos) pos += SHORT exception['subject'] = value[pos:pos + subject_length2] pos += subject_length2 if exception['override_flags'] & ARO_MEETINGTYPE: exception['meeting_type'] = _utils.unpack_long(value, pos) pos += LONG if exception['override_flags'] & ARO_REMINDERDELTA: exception['reminder_delta'] = _utils.unpack_long( value, pos) # XXX: datetime? pos += LONG if exception['override_flags'] & ARO_REMINDERSET: exception['reminder_set'] = _utils.unpack_long( value, pos) # XXX: bool? pos += LONG if exception['override_flags'] & ARO_LOCATION: location_length = _utils.unpack_short(value, pos) # XXX: unused? pos += SHORT location_length2 = _utils.unpack_short(value, pos) pos += SHORT exception['location'] = value[pos:pos + location_length2] pos += location_length2 if exception['override_flags'] & ARO_BUSYSTATUS: exception['busy_status'] = _utils.unpack_long(value, pos) pos += LONG if exception['override_flags'] & ARO_ATTACHMENT: exception['attachment'] = _utils.unpack_long(value, pos) pos += LONG if exception['override_flags'] & ARO_SUBTYPE: exception['sub_type'] = _utils.unpack_long(value, pos) pos += LONG if exception['override_flags'] & ARO_APPTCOLOR: exception['appointment_color'] = _utils.unpack_long(value, pos) pos += LONG self.exceptions.append(exception) # ReservedBlock1Size pos += _utils.unpack_long(value, pos) + LONG # ExtendedException self.extended_exceptions = [] for exception in self.exceptions: extended_exception = {} # ChangeHighlight if self.writer_version2 >= 0x3009: change_highlight_size = _utils.unpack_long(value, pos) pos += LONG change_highlight_value = _utils.unpack_long(value, pos) extended_exception['change_highlight'] = change_highlight_value pos += change_highlight_size # ReservedBlockEE1 pos += _utils.unpack_long(value, pos) + LONG # StartDateTime, EndDateTime, OriginalStartDate if exception['override_flags'] & ARO_SUBJECT or exception[ 'override_flags'] & ARO_LOCATION: extended_exception['start_datetime'] = _utils.unpack_long( value, pos) pos += LONG extended_exception['end_datetime'] = _utils.unpack_long( value, pos) pos += LONG extended_exception['original_start_date'] = _utils.unpack_long( value, pos) pos += LONG # WideCharSubject if exception['override_flags'] & ARO_SUBJECT: length = _utils.unpack_short(value, pos) pos += SHORT extended_exception['subject'] = value[pos:pos + 2 * length].decode( 'utf-16-le') pos += 2 * length # WideCharLocation if exception['override_flags'] & ARO_LOCATION: length = _utils.unpack_short(value, pos) pos += SHORT extended_exception['location'] = value[pos:pos + 2 * length].decode( 'utf-16-le') pos += 2 * length # ReservedBlockEE2 if exception['override_flags'] & ARO_SUBJECT or exception[ 'override_flags'] & ARO_LOCATION: pos += _utils.unpack_long(value, pos) + LONG self.extended_exceptions.append(extended_exception)