def test_publish_fb(store, freebusy_user): eid = store.GetProps([PR_MAILBOX_OWNER_ENTRYID], 0)[0].Value update, status = freebusy_user.LoadFreeBusyUpdate([eid], None) assert status == 1 assert update timestamp = int(time.time()) update.PublishFreeBusy([FreeBusyBlock(0, timestamp, 1)]) update.SaveChanges(unixtime(0), unixtime(timestamp)) data, status = freebusy_user.LoadFreeBusyData([eid], None) start, end = data.GetFBPublishRange() assert start assert end enum = data.EnumBlocks(FileTime(0), FileTime(0xFFFFFFFFFFFFFFFF)) blocks = enum.Next(100) assert blocks assert blocks[0].status == 1 # Reset freebusy update.ResetPublishedFreeBusy() update.SaveChanges(unixtime(0), unixtime(timestamp)) data, status = freebusy_user.LoadFreeBusyData([eid], None) enum = data.EnumBlocks(FileTime(0), FileTime(0xFFFFFFFFFFFFFFFF)) blocks = enum.Next(100) assert not blocks
def occurrences(self, start=None, end=None): if start and end: startstamp = time.mktime(start.timetuple()) endstamp = time.mktime(end.timetuple()) # XXX use shortcuts and default type (database) to avoid MAPI snake wrestling NAMED_PROPS = [ MAPINAMEID(PSETID_Appointment, MNID_ID, x) for x in (33293, 33294, 33315) ] ids = self.mapiobj.GetIDsFromNames(NAMED_PROPS, 0) startdate = ids[0] | PT_SYSTIME enddate = ids[1] | PT_SYSTIME recurring = ids[2] | PT_BOOLEAN # only look at non-recurring items which overlap and all recurring items restriction = SOrRestriction([ SAndRestriction([ SPropertyRestriction( RELOP_GT, enddate, SPropValue(enddate, unixtime(startstamp))), SPropertyRestriction( RELOP_LT, startdate, SPropValue(startdate, unixtime(endstamp))), ]), SAndRestriction([ SPropertyRestriction(RELOP_EQ, recurring, SPropValue(recurring, True)) ]) ]) table = Table( self.server, self.mapiobj, self.mapiobj.GetContentsTable(MAPI_DEFERRED_ERRORS), PR_CONTAINER_CONTENTS, columns=[PR_ENTRYID], ) table.mapitable.Restrict(restriction, 0) for row in table.rows(): entryid = _benc(row[0].value) for occurrence in self.item(entryid).occurrences(start, end): yield occurrence else: for item in self: for occurrence in item.occurrences(start, end): yield occurrence
def start(self, value): if value is None: self.store.mapiobj.DeleteProps([PR_EC_OUTOFOFFICE_FROM]) else: value = unixtime(time.mktime(value.timetuple())) self.store.mapiobj.SetProps([SPropValue(PR_EC_OUTOFOFFICE_FROM, value)]) _utils._save(self.store.mapiobj)
def start(self, value): if value is None: self.store.mapiobj.DeleteProps([PR_EC_OUTOFOFFICE_FROM]) else: value = unixtime(time.mktime(value.timetuple())) self.store.mapiobj.SetProps( [SPropValue(PR_EC_OUTOFOFFICE_FROM, value)]) self.store.mapiobj.SaveChanges(KEEP_OPEN_READWRITE)
def create_prop(self, mapiobj, proptag, value=None, proptype=None): if isinstance(proptag, int) or \ (isinstance(proptag, str) and ':' not in proptag): if isinstance(proptag, str): proptag2 = getattr(MAPI.Tags, proptag) else: proptag2 = proptag proptype2 = proptype or PROP_TYPE(proptag) else: # named property proptag2, proptype2, _, _ = \ _name_to_proptag(proptag, mapiobj, proptype) if proptype2 is None: # TODO exception too general? raise Error('Missing type to create named property') if value is None: if proptype2 in (PT_STRING8, PT_UNICODE): value = '' elif proptype2 == PT_BINARY: value = b'' elif proptype2 == PT_SYSTIME: value = unixtime(0) elif proptype2 & MV_FLAG: value = [] else: value = 0 else: if proptype2 == PT_SYSTIME: if value.tzinfo is None: value = _timezone._to_utc(value, _timezone.LOCAL) else: value = value.astimezone(_timezone.UTC) value = unixtime(calendar.timegm(value.utctimetuple())) # handle invalid type versus value. # For example proptype=PT_UNICODE and value=True try: mapiobj.SetProps([SPropValue(proptag2, value)]) _utils._save(mapiobj) except TypeError: raise Error('Could not create property, type and value did not match') return prop(self, mapiobj, proptag, proptype=proptype2)
def create_prop(self, mapiobj, proptag, value=None, proptype=None): # XXX selfie if _is_int(proptag) or \ (_is_str(proptag) and ':' not in proptag): if _is_str(proptag): proptag2 = getattr(MAPI.Tags, proptag) else: proptag2 = proptag proptype2 = proptype or PROP_TYPE(proptag) else: # named property proptag2, proptype2, _, _ = _name_to_proptag(proptag, mapiobj, proptype) if proptype2 is None: raise Error('Missing type to create named property' ) # XXX exception too general? if value is None: if proptype2 in (PT_STRING8, PT_UNICODE): value = u'' elif proptype2 == PT_BINARY: value = b'' elif proptype2 == PT_SYSTIME: value = unixtime(0) elif proptype2 & MV_FLAG: value = [] else: value = 0 else: if proptype2 == PT_SYSTIME: value = unixtime(time.mktime(value.timetuple())) # handle invalid type versus value. For example proptype=PT_UNICODE and value=True try: mapiobj.SetProps([SPropValue(proptag2, value)]) _utils._save(mapiobj) except TypeError: raise Error('Could not create property, type and value did not match') return prop(self, mapiobj, proptag, proptype=proptype2)
def value(self, value): self._value = value if self.type_ == PT_SYSTIME: # Ensure that time is stored as UTC. if value.tzinfo is None: value = _timezone._to_utc(value, _timezone.LOCAL) else: value = value.astimezone(_timezone.UTC) value = unixtime(calendar.timegm(value.utctimetuple())) self._parent_mapiobj.SetProps([SPropValue(self.proptag, value)]) _utils._save(self._parent_mapiobj)
def test_mvtypes2(store, message): props = [ SPropValue(PROP_TAG(PT_MV_SHORT, 0x6601), [-1, -2, -3]), SPropValue(PROP_TAG(PT_MV_LONG, 0x6602), [-1, -2, -3]), SPropValue(PROP_TAG(PT_MV_FLOAT, 0x6603), [-1, -2, -3]), SPropValue(PROP_TAG(PT_MV_DOUBLE, 0x6604), [-1, -2, -3]), SPropValue(PROP_TAG(PT_MV_CURRENCY, 0x6605), [-1, -2, -3]), SPropValue(PROP_TAG(PT_MV_APPTIME, 0x6606), [-1, -2, -3]), SPropValue(PROP_TAG(PT_MV_LONGLONG, 0x6607), [-1, -2, -3]), SPropValue(PROP_TAG(PT_MV_STRING8, 0x6608), [b'', b'', b'']), SPropValue(PROP_TAG(PT_MV_UNICODE, 0x6609), ['', '', '']), SPropValue(PROP_TAG(PT_MV_SYSTIME, 0x660a), [unixtime(0), unixtime(0), unixtime(0)]), SPropValue(PROP_TAG(PT_MV_CLSID, 0x660b), [b'0000000000000000', b'AAAAAAAAAAAAAAAA', b'FFFFFFFFFFFFFFFF']), SPropValue(PROP_TAG(PT_MV_BINARY, 0x660c), [b'', b'', b'']) ] message.SetProps(props) message.SaveChanges(0) entryidprops = message.GetProps([PR_ENTRYID], 0) entryid = entryidprops[0].Value message = store.OpenEntry(entryid, None, 0) results = message.GetProps([ PROP_TAG(PT_MV_SHORT, 0x6601), PROP_TAG(PT_MV_LONG, 0x6602), PROP_TAG(PT_MV_FLOAT, 0x6603), PROP_TAG(PT_MV_DOUBLE, 0x6604), PROP_TAG(PT_MV_CURRENCY, 0x6605), PROP_TAG(PT_MV_APPTIME, 0x6606), PROP_TAG(PT_MV_LONGLONG, 0x6607), PROP_TAG(PT_MV_STRING8, 0x6608), PROP_TAG(PT_MV_UNICODE, 0x6609), PROP_TAG(PT_MV_SYSTIME, 0x660a), PROP_TAG(PT_MV_CLSID, 0x660b), PROP_TAG(PT_MV_BINARY, 0x660c)], 0) assert results == props
def test_mvprops(store, message): props = [ SPropValue(PROP_TAG(PT_MV_SHORT, 0x6601), [1, 2, 3]), SPropValue(PROP_TAG(PT_MV_LONG, 0x6602), [1, 2, 3]), SPropValue(PROP_TAG(PT_MV_FLOAT, 0x6603), [1, 2, 3]), SPropValue(PROP_TAG(PT_MV_DOUBLE, 0x6604), [1, 2, 3]), SPropValue(PROP_TAG(PT_MV_CURRENCY, 0x6605), [1, 2, 3]), SPropValue(PROP_TAG(PT_MV_APPTIME, 0x6606), [1, 2, 3]), SPropValue(PROP_TAG(PT_MV_LONGLONG, 0x6607), [1, 2, 3]), SPropValue(PROP_TAG(PT_MV_STRING8, 0x6608), [b'a', b'b', b'c']), SPropValue(PROP_TAG(PT_MV_UNICODE, 0x6609), ['こん', 'に', 'ちは']), SPropValue(PROP_TAG(PT_MV_SYSTIME, 0x660a), [unixtime(0), unixtime(1), unixtime(2)]), SPropValue(PROP_TAG(PT_MV_CLSID, 0x660b), [b'1234567890123456', b'6543210987654321', b'0000000000000000']), SPropValue(PROP_TAG(PT_MV_BINARY, 0x660c), [b'bin', b'', b'data'])] message.SetProps(props) message.SaveChanges(0) entryidprops = message.GetProps([PR_ENTRYID], 0) entryid = entryidprops[0].Value message = store.OpenEntry(entryid, None, 0) results = message.GetProps([ PROP_TAG(PT_MV_SHORT, 0x6601), PROP_TAG(PT_MV_LONG, 0x6602), PROP_TAG(PT_MV_FLOAT, 0x6603), PROP_TAG(PT_MV_DOUBLE, 0x6604), PROP_TAG(PT_MV_CURRENCY, 0x6605), PROP_TAG(PT_MV_APPTIME, 0x6606), PROP_TAG(PT_MV_LONGLONG, 0x6607), PROP_TAG(PT_MV_STRING8, 0x6608), PROP_TAG(PT_MV_UNICODE, 0x6609), PROP_TAG(PT_MV_SYSTIME, 0x660a), PROP_TAG(PT_MV_CLSID, 0x660b), PROP_TAG(PT_MV_BINARY, 0x660c)], 0) assert results == props
def test_types2(store, message): props = [ SPropValue(PROP_TAG(PT_NULL, 0x6601), 0), SPropValue(PROP_TAG(PT_SHORT, 0x6602), -1), SPropValue(PROP_TAG(PT_LONG, 0x6603), -2), SPropValue(PROP_TAG(PT_FLOAT, 0x6604), -3), SPropValue(PROP_TAG(PT_DOUBLE, 0x6605), -4), SPropValue(PROP_TAG(PT_CURRENCY, 0x6606), -5), SPropValue(PROP_TAG(PT_APPTIME, 0x6607), -6), SPropValue(PROP_TAG(PT_BOOLEAN, 0x6608), False), SPropValue(PROP_TAG(PT_LONGLONG, 0x6609), -7), SPropValue(PROP_TAG(PT_STRING8, 0x6610), b''), SPropValue(PROP_TAG(PT_UNICODE, 0x6611), u'こんにちは'), SPropValue(PROP_TAG(PT_SYSTIME, 0x6612), unixtime(0)), SPropValue(PROP_TAG(PT_CLSID, 0x6613), b'1234567890123456'), SPropValue(PROP_TAG(PT_BINARY, 0x6614), b'')] message.SetProps(props) message.SaveChanges(0) entryidprops = message.GetProps([PR_ENTRYID], 0) entryid = entryidprops[0].Value message = store.OpenEntry(entryid, None, 0) results = message.GetProps([ PROP_TAG(PT_NULL, 0x6601), PROP_TAG(PT_SHORT, 0x6602), PROP_TAG(PT_LONG, 0x6603), PROP_TAG(PT_FLOAT, 0x6604), PROP_TAG(PT_DOUBLE, 0x6605), PROP_TAG(PT_CURRENCY, 0x6606), PROP_TAG(PT_APPTIME, 0x6607), PROP_TAG(PT_BOOLEAN, 0x6608), PROP_TAG(PT_LONGLONG, 0x6609), PROP_TAG(PT_STRING8, 0x6610), PROP_TAG(PT_UNICODE, 0x6611), PROP_TAG(PT_SYSTIME, 0x6612), PROP_TAG(PT_CLSID, 0x6613), PROP_TAG(PT_BINARY, 0x6614)], 0) props.remove(SPropValue(PROP_TAG(PT_NULL, 0x6601), 0)) props.insert(0, SPropValue(PROP_TAG(PT_ERROR, 0x6601), MAPI_E_NOT_FOUND)) assert results == props
def occurrences(self, start=None, end=None, page_start=None, page_limit=None, order=None): count = 0 pos = 0 if start and end: startstamp = time.mktime(start.timetuple()) endstamp = time.mktime(end.timetuple()) # XXX use shortcuts and default type (database) to avoid MAPI snake wrestling NAMED_PROPS = [ MAPINAMEID(PSETID_Appointment, MNID_ID, x) for x in (33285, 33293, 33294, 33315, 33301, 33333, 33334) ] ids = self.mapiobj.GetIDsFromNames(NAMED_PROPS, MAPI_CREATE) busystatus = ids[0] | PT_LONG startdate = ids[1] | PT_SYSTIME enddate = ids[2] | PT_SYSTIME recurring = ids[3] | PT_BOOLEAN all_day = ids[4] | PT_BOOLEAN clip_start = ids[5] | PT_SYSTIME clip_end = ids[6] | PT_SYSTIME restriction = SOrRestriction([ # non-recurring: normal start/end SAndRestriction([ SPropertyRestriction( RELOP_GT, enddate, SPropValue(enddate, unixtime(startstamp))), SPropertyRestriction( RELOP_LT, startdate, SPropValue(startdate, unixtime(endstamp))), ]), # recurring: range start/end SAndRestriction([ SPropertyRestriction( RELOP_GT, clip_end, SPropValue(clip_end, unixtime(startstamp))), SPropertyRestriction( RELOP_LT, clip_start, SPropValue(clip_start, unixtime(endstamp))), ]), # exceptions: exception start/end in attachment SAndRestriction([ SPropertyRestriction(RELOP_EQ, recurring, SPropValue(recurring, True)), SSubRestriction( PR_MESSAGE_ATTACHMENTS, SAndRestriction([ SPropertyRestriction( RELOP_LT, PR_EXCEPTION_STARTTIME, SPropValue(PR_EXCEPTION_STARTTIME, unixtime(endstamp))), SPropertyRestriction( RELOP_GT, PR_EXCEPTION_ENDTIME, SPropValue(PR_EXCEPTION_ENDTIME, unixtime(startstamp))), ])) ]) ]) columns = [ PR_ENTRYID, PR_SUBJECT_W, # watch out: table unicode data is max 255 chars PR_LAST_MODIFICATION_TIME, PR_CHANGE_KEY, startdate, enddate, recurring, all_day, busystatus ] table = Table( self.server, self.mapiobj, self.mapiobj.GetContentsTable(MAPI_DEFERRED_ERRORS), PR_CONTAINER_CONTENTS, columns=columns, ) table.mapitable.Restrict(restriction, 0) for row in table.rows(): item = _item.Item(self, entryid=row[0].value, content_flag=self.content_flag, cache=dict(zip(columns, row))) for occurrence in item.occurrences(start, end): if page_start is None or pos >= page_start: yield occurrence count += 1 if page_limit is not None and count >= page_limit: break pos += 1 if page_limit is not None and count >= page_limit: break else: for item in self: for occurrence in item.occurrences(start, end): if page_start is None or pos >= page_start: yield occurrence count += 1 if page_limit is not None and count >= page_limit: break pos += 1 if page_limit is not None and count >= page_limit: break
def _update_embedded(self, basedate, message, item=None, copytags=None, create=False, **kwargs): basetime = basedate + datetime.timedelta(minutes=self._starttime_offset) cal_item = self.item item2 = item or cal_item if copytags: props = item2.mapiobj.GetProps(copytags, 0) message.mapiobj.SetProps(props) elif not kwargs: props = [p.mapiobj for p in item2.props() if p.proptag != PR_ICON_INDEX] message.mapiobj.SetProps(props) message.recurring = False if not item: if kwargs: if 'start' in kwargs: message[PidLidAppointmentStartWhole] = kwargs['start'] if 'end' in kwargs: message[PidLidAppointmentEndWhole] = kwargs['end'] else: message[PidLidAppointmentStartWhole] = basedate + datetime.timedelta(minutes=self._starttime_offset) message[PidLidAppointmentEndWhole] = basedate + datetime.timedelta(minutes=self._endtime_offset) message[PR_MESSAGE_CLASS_W] = u'IPM.OLE.CLASS.{00061055-0000-0000-C000-000000000046}' message[PidLidExceptionReplaceTime] = basetime intended_busystatus = cal_item.get(PidLidIntendedBusyStatus) # XXX tentative? merge with modify_exc? if intended_busystatus is not None: message[PidLidBusyStatus] = intended_busystatus sub_type = cal_item.get(PidLidAppointmentSubType) if sub_type is not None: message[PidLidAppointmentSubType] = sub_type props = [ SPropValue(PR_ATTACHMENT_FLAGS, 2), # XXX cannot find spec SPropValue(PR_ATTACHMENT_HIDDEN, True), SPropValue(PR_ATTACHMENT_LINKID, 0), SPropValue(PR_ATTACH_FLAGS, 0), SPropValue(PR_ATTACH_METHOD, ATTACH_EMBEDDED_MSG), SPropValue(PR_DISPLAY_NAME_W, u'Exception'), ] if 'subject' in kwargs: props.append(SPropValue(PR_SUBJECT_W, kwargs['subject'])) if 'location' in kwargs: message[PidLidLocation] = kwargs['location'] if item is None: # TODO pick up kwargs['start/end'] start = basetime end = basetime + datetime.timedelta(minutes=self._endtime_offset-self._starttime_offset) else: start = message.get(PidLidAppointmentStartWhole) end = message.prop(PidLidAppointmentEndWhole).value if start is not None: start_local = unixtime(time.mktime(_timezone._from_utc(start, self._tzinfo).timetuple())) # XXX why local?? props.append(SPropValue(PR_EXCEPTION_STARTTIME, start_local)) if end is not None: end_local = unixtime(time.mktime(_timezone._from_utc(end, self._tzinfo).timetuple())) # XXX why local?? props.append(SPropValue(PR_EXCEPTION_ENDTIME, end_local)) message._attobj.SetProps(props) if not create: # XXX php bug? props = props[:-2] message.mapiobj.SetProps(props) if 'canceled' in kwargs: message[PidLidAppointmentStateFlags] |= ASF_CANCELED _utils._save(message.mapiobj) _utils._save(message._attobj)
def _update_embedded(self, basedate, message, item, copytags=None, create=False): basetime = basedate + datetime.timedelta(minutes=self.starttime_offset) cal_item = self.item if copytags: props = item.mapiobj.GetProps(copytags, 0) else: # XXX remove? props = [ p.mapiobj for p in item.props() if p.proptag != PR_ICON_INDEX ] message.mapiobj.SetProps(props) message[ PR_MESSAGE_CLASS_W] = u'IPM.OLE.CLASS.{00061055-0000-0000-C000-000000000046}' message[PidLidExceptionReplaceTime] = basetime intended_busystatus = cal_item.get( PidLidIntendedBusyStatus) # XXX tentative? merge with modify_exc? if intended_busystatus is not None: message[PidLidBusyStatus] = intended_busystatus sub_type = cal_item.get(PidLidAppointmentSubType) if sub_type is not None: message[PidLidAppointmentSubType] = sub_type props = [ SPropValue(PR_ATTACHMENT_FLAGS, 2), # XXX cannot find spec SPropValue(PR_ATTACHMENT_HIDDEN, True), SPropValue(PR_ATTACHMENT_LINKID, 0), SPropValue(PR_ATTACH_FLAGS, 0), SPropValue(PR_ATTACH_METHOD, ATTACH_EMBEDDED_MSG), SPropValue(PR_DISPLAY_NAME_W, u'Exception'), ] start = message.get(PidLidAppointmentStartWhole) if start is not None: start_local = unixtime( time.mktime(_utils._from_gmt( start, self.tz).timetuple())) # XXX why local?? props.append(SPropValue(PR_EXCEPTION_STARTTIME, start_local)) end = message.prop(PidLidAppointmentEndWhole).value if end is not None: end_local = unixtime( time.mktime(_utils._from_gmt( end, self.tz).timetuple())) # XXX why local?? props.append(SPropValue(PR_EXCEPTION_ENDTIME, end_local)) message._attobj.SetProps(props) if not create: # XXX php bug? props = props[:-2] message.mapiobj.SetProps(props) message.mapiobj.SaveChanges(KEEP_OPEN_READWRITE) message._attobj.SaveChanges(KEEP_OPEN_READWRITE)
def sync(server, syncobj, importer, state, log, max_changes, associated=False, window=None, begin=None, end=None, stats=None): importer = TrackingContentsImporter(server, importer, log, stats) exporter = syncobj.OpenProperty(PR_CONTENTS_SYNCHRONIZER, IID_IExchangeExportChanges, 0, 0) stream = IStream() stream.Write(_bdec(state)) stream.Seek(0, STREAM_SEEK_SET) restriction = None if window: # sync window of last N seconds propval = SPropValue(PR_MESSAGE_DELIVERY_TIME, unixtime(int(time.time()) - window)) restriction = SPropertyRestriction(RELOP_GE, PR_MESSAGE_DELIVERY_TIME, propval) elif begin or end: restrs = [] if begin: propval = SPropValue(PR_MESSAGE_DELIVERY_TIME, unixtime(time.mktime(begin.timetuple()))) restrs.append(SPropertyRestriction(RELOP_GE, PR_MESSAGE_DELIVERY_TIME, propval)) if end: propval = SPropValue(PR_MESSAGE_DELIVERY_TIME, unixtime(time.mktime(end.timetuple()))) restrs.append(SPropertyRestriction(RELOP_LT, PR_MESSAGE_DELIVERY_TIME, propval)) if len(restrs) == 1: restriction = restrs[0] else: restriction = SAndRestriction(restrs) flags = SYNC_NORMAL | SYNC_UNICODE if associated: flags |= SYNC_ASSOCIATED try: exporter.Config(stream, flags, importer, restriction, None, None, 0) except MAPIErrorNotFound: # syncid purged because of 'sync_lifetime' option in server.cfg: get new syncid. if log: log.warn("Sync state does not exist on server (anymore); requesting new one") syncid, changeid = struct.unpack('<II', _bdec(state)) stream = IStream() stream.Write(struct.pack('<II', 0, changeid)) stream.Seek(0, STREAM_SEEK_SET) exporter.Config(stream, flags, importer, restriction, None, None, 0) step = retry = changes = 0 sleep_time = 0.4 while True: try: try: (steps, step) = exporter.Synchronize(step) finally: importer.skip = False changes += 1 retry = 0 if (steps == step) or (max_changes and changes >= max_changes): break except MAPIError as e: if log: log.warn("Received a MAPI error or timeout (error=0x%x, retry=%d/5)", e.hr, retry) time.sleep(sleep_time) if sleep_time < 5.0: sleep_time *= 2.0 if retry < 5: retry += 1 else: if log: log.error("Too many retries, skipping change") if stats: stats['errors'] += 1 importer.skip = True # in case of a timeout or other issue, try to skip the change after trying several times retry = 0 exporter.UpdateState(stream) stream.Seek(0, STREAM_SEEK_SET) return _benc(stream.Read(0xFFFFF))
def test_Y10K(item): # datetime can't handle 4+ digit years, so we cut them off at 9999-1-1 value = unixtime(10000 * 365 * 24 * 3600) item.mapiobj.SetProps([SPropValue(PR_MESSAGE_DELIVERY_TIME, value)]) item.mapiobj.SaveChanges(0) assert item.prop(PR_MESSAGE_DELIVERY_TIME).value == datetime(9999, 1, 1)
def value(self, value): self._value = value if self.type_ == PT_SYSTIME: value = unixtime(time.mktime(value.timetuple())) self._parent_mapiobj.SetProps([SPropValue(self.proptag, value)]) _utils._save(self._parent_mapiobj)
def sync(server, syncobj, importer, state, max_changes, associated=False, window=None, begin=None, end=None, stats=None): log = server.log importer = TrackingContentsImporter(server, importer, stats) exporter = syncobj.OpenProperty(PR_CONTENTS_SYNCHRONIZER, IID_IExchangeExportChanges, 0, 0) stream = IStream() stream.Write(_bdec(state)) stream.Seek(0, STREAM_SEEK_SET) restriction = None if window: # sync window of last N seconds propval = SPropValue(PR_MESSAGE_DELIVERY_TIME, unixtime(int(time.time()) - window)) restriction = SPropertyRestriction(RELOP_GE, PR_MESSAGE_DELIVERY_TIME, propval) elif begin or end: restrs = [] if begin: propval = SPropValue(PR_MESSAGE_DELIVERY_TIME, unixtime(time.mktime(begin.timetuple()))) restrs.append( SPropertyRestriction(RELOP_GE, PR_MESSAGE_DELIVERY_TIME, propval)) if end: propval = SPropValue(PR_MESSAGE_DELIVERY_TIME, unixtime(time.mktime(end.timetuple()))) restrs.append( SPropertyRestriction(RELOP_LT, PR_MESSAGE_DELIVERY_TIME, propval)) if len(restrs) == 1: restriction = restrs[0] else: restriction = SAndRestriction(restrs) flags = SYNC_NORMAL | SYNC_UNICODE | SYNC_READ_STATE if associated: flags |= SYNC_ASSOCIATED try: if TESTING and os.getenv('PYKO_TEST_NOT_FOUND'): raise MAPIErrorNotFound() exporter.Config(stream, flags, importer, restriction, None, None, 0) except MAPIErrorNotFound: # syncid purged because of 'sync_lifetime' option in server.cfg: get new syncid. log.warn( "Sync state does not exist on server (anymore); requesting new one" ) syncid, changeid = struct.unpack('<II', _bdec(state)) stream = IStream() stream.Write(struct.pack('<II', 0, changeid)) stream.Seek(0, STREAM_SEEK_SET) exporter.Config(stream, flags, importer, restriction, None, None, 0) step = retry = changes = 0 sleep_time = 0.4 while True: try: try: if TESTING and os.getenv( 'PYKO_TEST_NETWORK_ERROR') and not importer.skip: raise MAPIErrorNetworkError() (steps, step) = exporter.Synchronize(step) finally: importer.skip = False changes += 1 retry = 0 if (steps == step) or (max_changes and changes >= max_changes): break except MAPIError as e: log.warn( "Received a MAPI error or timeout (error=0x%x, retry=%d/5)", e.hr, retry) time.sleep(sleep_time) if sleep_time < 5.0: sleep_time *= 2.0 if retry < 5: retry += 1 else: log.error("Too many retries, skipping change") if stats is not None: stats['errors'] += 1 importer.skip = True # in case of a timeout or other issue, try to skip the change after trying several times retry = 0 exporter.UpdateState(stream) stream.Seek(0, STREAM_SEEK_SET) state = stream.Read(0xFFFFF) # because changes may be reordered for efficiency, we are not always # linearly following the change journal. so the current state cannot # always be represented as a single change id. instead, the current state # may contain changes which have been synced, relative to a certain # change id (so we have synced until this change id, plus these changes). # in pyko though, we always sync until there are no further changes, # so this should normally not occur. # TODO add an ICS flag to disable reordering! if len(state) != 8: log.error('sync state %d bytes, expect problems', len(state)) return _benc(state)
def set_value(self, value): self._value = value if self.type_ == PT_SYSTIME: value = unixtime(time.mktime(value.timetuple())) self._parent_mapiobj.SetProps([SPropValue(self.proptag, value)]) self._parent_mapiobj.SaveChanges(KEEP_OPEN_READWRITE)