def test_optimize_doubleand(root): restriction = SAndRestriction([ SAndRestriction([ SContentRestriction(FL_SUBSTRING | FL_IGNORECASE, PR_SUBJECT, SPropValue(PR_SUBJECT, b'unknown')), SContentRestriction(FL_SUBSTRING | FL_IGNORECASE, PR_SUBJECT, SPropValue(PR_SUBJECT, b'unknown')) ]) ]) assert_no_results(root, restriction)
def test_optimize_ormultianndfalse(root): restriction = SOrRestriction([ SAndRestriction([ SNotRestriction(SExistRestriction(PR_SUBJECT)), SExistRestriction(PR_SUBJECT) ]), SAndRestriction([ SContentRestriction(FL_SUBSTRING | FL_IGNORECASE, PR_SUBJECT, SPropValue(PR_SUBJECT, b'unknown')), SContentRestriction(FL_SUBSTRING | FL_IGNORECASE, PR_SUBJECT, SPropValue(PR_SUBJECT, b'unknown')) ]) ]) assert_no_results(root, restriction)
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 restriction(self, type_, store): if self.field: # determine proptag for term, eg 'subject' proptag = TYPE_KEYWORD_PROPMAP[type_][self.field] flag = None subobj = None recipient_type = None # property in sub-object (attachments/recipient): use sub-restriction if isinstance(proptag, tuple): if(proptag[0]) == PR_MESSAGE_ATTACHMENTS: subobj, proptag = proptag elif(proptag[0]) == PR_MESSAGE_RECIPIENTS: subobj, proptag, recipient_type = proptag elif len(proptag) == 2: proptag, flag = proptag # named property: resolve local proptag elif len(proptag) == 4: proptag = store._name_id(proptag[:3]) | proptag[3] # make restriction on proptag(s) if isinstance(proptag, list): restr = SOrRestriction([ self.prop_restriction(proptag, flag) for proptag in proptag ]) else: restr = self.prop_restriction(proptag, flag) # turn restriction into sub-restriction if subobj: if recipient_type is not None: restr = SAndRestriction([ restr, SPropertyRestriction( RELOP_EQ, PR_RECIPIENT_TYPE, SPropValue(PR_RECIPIENT_TYPE, recipient_type) ) ]) restr = SSubRestriction(subobj, restr) else: defaults = [(store._name_id(proptag[:3]) | proptag[3]) if isinstance(proptag, tuple) else proptag for proptag in DEFAULT_PROPTAGS[type_]] restr = SOrRestriction([ SContentRestriction( FL_SUBSTRING | FL_IGNORECASE, p, SPropValue(p, self.value) ) for p in defaults ]) if self.sign == '-': restr = SNotRestriction(restr) return restr
def test_optimize_orandfalse(root): restriction = SOrRestriction([ SAndRestriction([ SNotRestriction(SExistRestriction(PR_SUBJECT)), SExistRestriction(PR_SUBJECT) ]) ]) assert_no_results(root, restriction)
def _interval_restriction(proptag, start, end): start = datetime_to_filetime(start) end = datetime_to_filetime(end) return SAndRestriction([ SPropertyRestriction(RELOP_GE, proptag, SPropValue(proptag, start)), SPropertyRestriction(RELOP_LT, proptag, SPropValue(proptag, end)) ])
def GetUserList(session, companyname=None, flags=0): restriction = SAndRestriction([ SPropertyRestriction(RELOP_EQ, PR_OBJECT_TYPE, SPropValue(PR_OBJECT_TYPE, MAPI_MAILUSER)), SPropertyRestriction(RELOP_NE, PR_DISPLAY_TYPE, SPropValue(PR_DISPLAY_TYPE, DT_REMOTE_MAILUSER)) ]) return GetAbObjectList(session, restriction, companyname, flags)
def _save_rule(store, userids, deletion): # remove existing rule # XXX update for rule in store.inbox.rules(): if rule.mapirow[PR_RULE_PROVIDER_W] == u'Schedule+ EMS Interface' and \ PR_RULE_ID in rule.mapirow: pr_rule_id = rule.mapirow[PR_RULE_ID] rulerows = [ ROWENTRY(ROW_REMOVE, [SPropValue(PR_RULE_ID, pr_rule_id)]) ] table = store.inbox.mapiobj.OpenProperty( PR_RULES_TABLE, IID_IExchangeModifyTable, 0, 0) table.ModifyTable(0, rulerows) # create new rule row = [ SPropValue(PR_RULE_LEVEL, 0), SPropValue(PR_RULE_NAME_W, u"Delegate Meetingrequest service"), SPropValue(PR_RULE_PROVIDER_W, u"Schedule+ EMS Interface"), SPropValue(PR_RULE_SEQUENCE, 0), SPropValue(PR_RULE_STATE, 1), SPropValue(PR_RULE_PROVIDER_DATA, b''), ] actions = [] userprops = [] for userid in userids: user = store.server.gab.OpenEntry(userid, None, MAPI_BEST_ACCESS) props = user.GetProps(USERPROPS, MAPI_UNICODE) # Hardcode recipient type to TO props.append(SPropValue(PR_RECIPIENT_TYPE, MAPI_TO)) userprops.append(props) actions.append( ACTION(ACTTYPE.OP_DELEGATE, 0, None, None, 0, actFwdDelegate(userprops))) if deletion: actions.append(ACTION(ACTTYPE.OP_DELETE, 0, None, None, 0, None)) row.append(SPropValue(PR_RULE_ACTIONS, ACTIONS(1, actions))) cond = SAndRestriction([ SContentRestriction( FL_PREFIX, PR_MESSAGE_CLASS_W, SPropValue(PR_MESSAGE_CLASS_W, u"IPM.Schedule.Meeting")), SNotRestriction(SExistRestriction(PR_DELEGATED_BY_RULE)), SOrRestriction([ SNotRestriction(SExistRestriction(PR_SENSITIVITY)), SPropertyRestriction(RELOP_NE, PR_SENSITIVITY, SPropValue(PR_SENSITIVITY, 2)) ]) ]) row.append(SPropValue(PR_RULE_CONDITION, cond)) rulerows = [ROWENTRY(ROW_ADD, row)] table = store.inbox.mapiobj.OpenProperty(PR_RULES_TABLE, IID_IExchangeModifyTable, 0, 0) table.ModifyTable(0, rulerows)
def restriction(self, type_, store): if self.op == 'AND': return SAndRestriction( [arg.restriction(type_, store) for arg in self.args]) elif self.op == 'OR': return SOrRestriction( [arg.restriction(type_, store) for arg in self.args]) elif self.op == 'NOT': return SNotRestriction(self.args[0].restriction(type_, store))
def GetGab(session): ab = session.OpenAddressBook(0, None, 0) root = ab.OpenEntry(None, None, 0) table = root.GetHierarchyTable(0) table.SetColumns([PR_ENTRYID], TBL_BATCH) restriction = SOrRestriction([ SPropertyRestriction(RELOP_EQ, PR_DISPLAY_TYPE, SPropValue(PR_DISPLAY_TYPE, DT_GLOBAL)), SAndRestriction([ SExistRestriction(PR_EMS_AB_CONTAINERID), SPropertyRestriction(RELOP_EQ, PR_EMS_AB_CONTAINERID, SPropValue(PR_EMS_AB_CONTAINERID, 0)) ]) ]) table.FindRow(restriction, BOOKMARK_BEGINNING, 0) eid = table.QueryRows(1, 0)[0][0].Value return ab.OpenEntry(eid, None, 0)
def restriction(self, type_, store): if self.field: # determine proptag for term, eg 'subject' proptag = TYPE_KEYWORD_PROPMAP[type_][self.field] flag = None subobj = None recipient_type = None # property in sub-object (attachments/recipient): use sub-restriction if isinstance(proptag, tuple) and len(proptag) == 2: if(proptag[0]) == PR_MESSAGE_ATTACHMENTS: subobj, proptag = proptag elif(proptag[0]) == PR_MESSAGE_RECIPIENTS: subobj, recipient_type = proptag proptag = PR_DISPLAY_NAME_W # TODO email else: proptag, flag = proptag # named property: resolve local proptag elif isinstance(proptag, tuple) and len(proptag) == 4: proptag = store._name_id(proptag[:3]) | proptag[3] # comparison operator if self.op in ('<', '>', '>=', '<=', '<>'): if PROP_TYPE(proptag) == PT_SYSTIME: d = dateutil.parser.parse(self.value, dayfirst=True) d = datetime_to_filetime(d) restr = SPropertyRestriction( OP_RELOP[self.op], proptag, SPropValue(proptag, d) ) else: value = self.value unit = '' if [x for x in ('KB', 'MB', 'GB') if value.endswith(x)]: value, unit = value[:-2], value[-2:] if PROP_TYPE(proptag) in (PT_FLOAT, PT_DOUBLE): value = float(value) else: value = int(value) if unit == 'KB': value *= 1024 elif unit == 'MB': value *= 1024**2 elif unit == 'GB': value *= 1024**3 restr = SPropertyRestriction( OP_RELOP[self.op], proptag, SPropValue(proptag, value) ) # contains/equals operator elif self.op in (':', '='): if PROP_TYPE(proptag) == PT_UNICODE: restr = SContentRestriction( FL_SUBSTRING | FL_IGNORECASE, proptag, SPropValue(proptag, self.value) ) elif flag or PROP_TYPE(proptag) == PT_BOOLEAN: if flag: restr = SBitMaskRestriction( BMR_NEZ if self.value in ('yes', 'true') else BMR_EQZ, proptag, flag ) else: restr = SPropertyRestriction( RELOP_EQ, proptag, SPropValue(proptag, self.value in ('yes', 'true')) ) elif PROP_TYPE(proptag) == PT_MV_UNICODE: proptag2 = (proptag ^ PT_MV_UNICODE) | PT_UNICODE # funky! restr = SContentRestriction( FL_SUBSTRING | FL_IGNORECASE, proptag, SPropValue(proptag2, self.value) ) elif PROP_TYPE(proptag) in (PT_SHORT, PT_LONG, PT_LONGLONG, PT_FLOAT, PT_DOUBLE): conv = float if PROP_TYPE(proptag) in (PT_FLOAT, PT_DOUBLE) else int if '..' in self.value: val1, val2 = self.value.split('..') restr = SAndRestriction([ SPropertyRestriction( RELOP_GE, proptag, SPropValue(proptag, conv(val1)) ), SPropertyRestriction( RELOP_LT, proptag, SPropValue(proptag, conv(val2)) ) ]) else: restr = SPropertyRestriction( RELOP_EQ, proptag, SPropValue(proptag, conv(self.value)) ) elif PROP_TYPE(proptag) == PT_SYSTIME: if self.value == 'today': d = datetime.datetime.now().date() d2 = d + datetime.timedelta(days=1) restr = _interval_restriction(proptag, d, d2) elif self.value == 'yesterday': d2 = datetime.datetime.now().date() d = d2 - datetime.timedelta(days=1) restr = _interval_restriction(proptag, d, d2) elif self.value == 'this week': d2 = datetime.datetime.now() d = d2.date() - datetime.timedelta(days=d2.weekday()) restr = _interval_restriction(proptag, d, d2) elif self.value == 'this month': d2 = datetime.datetime.now() d = d2.date() - datetime.timedelta(days=d2.day-1) restr = _interval_restriction(proptag, d, d2) elif self.value == 'last month': now = datetime.datetime.now() d2 = now.date() - datetime.timedelta(days=now.day-1) d = (d2 - datetime.timedelta(days=1)).replace(day=1) restr = _interval_restriction(proptag, d, d2) elif self.value == 'this year': d2 = datetime.datetime.now() d = datetime.datetime(d2.year, 1, 1) restr = _interval_restriction(proptag, d, d2) elif self.value == 'last year': now = datetime.datetime.now() d2 = datetime.datetime(now.year, 1, 1) d = datetime.datetime(d2.year-1, 1, 1) restr = _interval_restriction(proptag, d, d2) elif '..' in self.value: date1, date2 = self.value.split('..') # TODO hours etc d = dateutil.parser.parse(date1, dayfirst=True) d2 = dateutil.parser.parse(date2, dayfirst=True) restr = _interval_restriction(proptag, d, d2) else: d = dateutil.parser.parse(self.value, dayfirst=True) # TODO hours etc d2 = d + datetime.timedelta(days=1) restr = _interval_restriction(proptag, d, d2) # turn restriction into sub-restriction if subobj: if recipient_type is not None: restr = SAndRestriction([ restr, SPropertyRestriction( RELOP_EQ, PR_RECIPIENT_TYPE, SPropValue(PR_RECIPIENT_TYPE, recipient_type) ) ]) restr = SSubRestriction(subobj, restr) else: defaults = [(store._name_id(proptag[:3]) | proptag[3]) if isinstance(proptag, tuple) else proptag for proptag in DEFAULT_PROPTAGS[type_]] restr = SOrRestriction([ SContentRestriction( FL_SUBSTRING | FL_IGNORECASE, p, SPropValue(p, self.value) ) for p in defaults ]) if self.sign == '-': restr = SNotRestriction(restr) return restr
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 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 prop_restriction(self, proptag, flag): # comparison operator if self.op in ('<', '>', '>=', '<=', '<>'): if PROP_TYPE(proptag) == PT_SYSTIME: d = dateutil.parser.parse(self.value) d = datetime_to_filetime(d) restr = SPropertyRestriction( OP_RELOP[self.op], proptag, SPropValue(proptag, d) ) else: value = self.value unit = '' if [x for x in ('KB', 'MB', 'GB') if value.endswith(x)]: value, unit = value[:-2], value[-2:] value = int(value) if unit == 'KB': value *= 1024 elif unit == 'MB': value *= 1024**2 elif unit == 'GB': value *= 1024**3 restr = SPropertyRestriction( OP_RELOP[self.op], proptag, SPropValue(proptag, value) ) # contains/equals operator elif self.op in (':', '='): if PROP_TYPE(proptag) == PT_UNICODE: restr = SContentRestriction( FL_SUBSTRING | FL_IGNORECASE, proptag, SPropValue(proptag, self.value) ) elif flag or PROP_TYPE(proptag) == PT_BOOLEAN: if flag: restr = SBitMaskRestriction( BMR_NEZ if self.value in ('yes', 'true') else BMR_EQZ, proptag, flag ) else: restr = SPropertyRestriction( RELOP_EQ, proptag, SPropValue(proptag, self.value in ('yes', 'true')) ) elif PROP_TYPE(proptag) == PT_MV_UNICODE: proptag2 = (proptag ^ PT_MV_UNICODE) | PT_UNICODE # funky! restr = SContentRestriction( FL_SUBSTRING | FL_IGNORECASE, proptag, SPropValue(proptag2, self.value) ) elif PROP_TYPE(proptag) in (PT_SHORT, PT_LONG, PT_LONGLONG, PT_FLOAT, PT_DOUBLE): conv = float if PROP_TYPE(proptag) in (PT_FLOAT, PT_DOUBLE) else int if '..' in self.value: val1, val2 = self.value.split('..') restr = SAndRestriction([ SPropertyRestriction( RELOP_GE, proptag, SPropValue(proptag, conv(val1)) ), SPropertyRestriction( RELOP_LT, proptag, SPropValue(proptag, conv(val2)) ) ]) else: restr = SPropertyRestriction( RELOP_EQ, proptag, SPropValue(proptag, conv(self.value)) ) elif PROP_TYPE(proptag) == PT_SYSTIME: if self.value == 'today': d = datetime.datetime.now().date() d2 = d + datetime.timedelta(days=1) restr = _interval_restriction(proptag, d, d2) elif self.value == 'yesterday': d2 = datetime.datetime.now().date() d = d2 - datetime.timedelta(days=1) restr = _interval_restriction(proptag, d, d2) elif self.value == 'this week': d2 = datetime.datetime.now() d = d2.date() - datetime.timedelta(days=d2.weekday()) restr = _interval_restriction(proptag, d, d2) elif self.value == 'this month': d2 = datetime.datetime.now() d = d2.date() - datetime.timedelta(days=d2.day-1) restr = _interval_restriction(proptag, d, d2) elif self.value == 'last month': now = datetime.datetime.now() d2 = now.date() - datetime.timedelta(days=now.day-1) d = (d2 - datetime.timedelta(days=1)).replace(day=1) restr = _interval_restriction(proptag, d, d2) elif self.value == 'this year': d2 = datetime.datetime.now() d = datetime.datetime(d2.year, 1, 1) restr = _interval_restriction(proptag, d, d2) elif self.value == 'last year': now = datetime.datetime.now() d2 = datetime.datetime(now.year, 1, 1) d = datetime.datetime(d2.year-1, 1, 1) restr = _interval_restriction(proptag, d, d2) elif '..' in self.value: date1, date2 = self.value.split('..') # TODO hours etc d = dateutil.parser.parse(date1) d2 = dateutil.parser.parse(date2) restr = _interval_restriction(proptag, d, d2) else: d = dateutil.parser.parse(self.value) # TODO hours etc d2 = d + datetime.timedelta(days=1) restr = _interval_restriction(proptag, d, d2) return restr
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 users(self, remote=False, system=False, parse=True, page_start=None, page_limit=None, order=None, hidden=True, inactive=True, _server=None, _company=None, query=None): """Return all :class:`users <User>` on server. :param remote: Include users on remote server nodes (default False) :param system: Include system users (default False) :param hidden: Include hidden users (default True) :param inactive: Include inactive users (default True) :param query: Search query (optional) """ # users specified on command-line if parse and getattr(self.options, 'users', None): for username in self.options.users: yield _user.User(username, self) return # global listing on multitenant setup if self.multitenant and not _company: if (page_limit is None and page_start is None and \ query is None and order is None): for company in self.companies(): for user in company.users(remote=remote, system=system, parse=parse, hidden=hidden, inactive=inactive): yield user return else: raise NotSupportedError('unsupported method of user listing') # find right addressbook container (global or for company) gab = self.gab restriction = SAndRestriction([ SPropertyRestriction(RELOP_EQ, PR_OBJECT_TYPE, SPropValue(PR_OBJECT_TYPE, MAPI_MAILUSER)), SPropertyRestriction(RELOP_NE, PR_DISPLAY_TYPE, SPropValue(PR_DISPLAY_TYPE, DT_REMOTE_MAILUSER)) ]) if _company and _company.name != 'Default': htable = gab.GetHierarchyTable(0) htable.SetColumns([PR_ENTRYID], TBL_BATCH) # TODO(longsleep): Find a way to avoid finding the limited table, # if the gab is itself already limited to the same. try: htable.FindRow(SContentRestriction( FL_FULLSTRING | FL_IGNORECASE, PR_DISPLAY_NAME_W, SPropValue(PR_DISPLAY_NAME_W, _company.name)), BOOKMARK_BEGINNING, 0) except MAPIErrorNotFound: # If not, found we do not have permission to access that row. and # instead fall back to the gab and let it handle access and # limits based on user authentication. container = gab else: row = htable.QueryRows(1, 0)[0] container = gab.OpenEntry(row[0].Value, None, 0) else: container = gab table = container.GetContentsTable(0) table.SetColumns([PR_ENTRYID], MAPI_UNICODE) # apply query restriction if query is not None: store = _store.Store(mapiobj=self.mapistore, server=self) restriction.lpRes.append( _query_to_restriction(query, 'user', store).mapiobj) table.Restrict(restriction, TBL_BATCH) # TODO apply order argument here def include(user, ecuser): return ((system or user.name != 'SYSTEM') and (remote or ecuser.Servername in (self.name, '')) and (hidden or not user.hidden) and (inactive or user.active)) # TODO simpler/faster if sa.GetUserList could do restrictions, # ordering, pagination.. # since then we can always work with ecuser objects in bulk userid_ecuser = None if page_limit is None and page_start is None and query is None: userid_ecuser = {} if _company and _company.name != 'Default': ecusers = self.sa.GetUserList( _company._eccompany.CompanyID, MAPI_UNICODE) else: ecusers = self.sa.GetUserList(None, MAPI_UNICODE) for ecuser in ecusers: userid_ecuser[ecuser.UserID] = ecuser # fast path: just get all users if order is None: user = _user.User(server=self, ecuser=ecuser) if include(user, ecuser): yield user if order is None: return # loop over rows and paginate pos = 0 count = 0 while True: rows = table.QueryRows(50, 0) if not rows: break for row in rows: userid = row[0].Value if userid_ecuser is None: try: user = _user.User(server=self, userid=_benc(userid)) except NotFoundError: continue else: try: user = _user.User( server=self, ecuser=userid_ecuser[userid]) except KeyError: continue if include(user, user._ecuser): if page_start is None or pos >= page_start: yield user count += 1 if page_limit is not None and count >= page_limit: return pos += 1