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 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 create_calendar( self, name, color = "", allow_online_meeting_providers = ["unknown"], default_online_meeting_provider = "unknown" ): """Create a new calendar. Args: name (str): calendar name. color (str): calendar color name. Defaults to an empty string. allow_online_meeting_providers (list): allow online meeting providers like "skypeForBusiness", "skypeForCustomer", and etc. Defaults to ["unknown"]. default_online_meeting_provider (str): default online meeting providers. Defaults to "unknown". Returns: Folder: the created calendar. Todo: "allow_online_meeting_providers" needs be to supported. """ restriction = Restriction(SPropertyRestriction( RELOP_EQ, PR_CONTAINER_CLASS_W, SPropValue(PR_CONTAINER_CLASS_W, 'IPF.Appointment') )) folder = self.create_folder( name=name, restriction=restriction, container_class="IPF.Appointment", color=color, default_online_meeting_provider=default_online_meeting_provider ) return folder
def favorites(self): """Returns all favorite folders""" table = self.common_views.mapiobj.GetContentsTable(MAPI_ASSOCIATED) table.SetColumns([ PR_MESSAGE_CLASS, PR_SUBJECT, PR_WLINK_ENTRYID, PR_WLINK_FLAGS, PR_WLINK_ORDINAL, PR_WLINK_STORE_ENTRYID, PR_WLINK_TYPE ], 0) table.Restrict( SPropertyRestriction( RELOP_EQ, PR_MESSAGE_CLASS, SPropValue(PR_MESSAGE_CLASS, "IPM.Microsoft.WunderBar.Link")), TBL_BATCH) for row in table.QueryRows(-1, 0): store_entryid = bin2hex(row[5].Value) try: if store_entryid == self.entryid: # XXX: Handle favorites from public stores yield self.folder(entryid=bin2hex(row[2].Value)) else: store = Store(entryid=store_entryid, server=self.server) yield store.folder(entryid=bin2hex(row[2].Value)) except NotFoundError: pass
def favorites(self): """Returns all favorite :class:`folders <Folder>`.""" restriction = Restriction( SPropertyRestriction( RELOP_EQ, PR_MESSAGE_CLASS_W, SPropValue(PR_MESSAGE_CLASS_W, 'IPM.Microsoft.WunderBar.Link'))) table = Table( self.server, self.mapiobj, self.common_views.mapiobj.GetContentsTable(MAPI_ASSOCIATED), restriction=restriction, columns=[PR_WLINK_ENTRYID, PR_WLINK_STORE_ENTRYID], ) for entryid, store_entryid in table: try: # TODO: Handle favorites from public stores if store_entryid == self.entryid: yield self.folder(entryid=_benc(entryid.value)) else: store = Store(entryid=_benc(store_entryid.value), server=self.server) yield store.folder(entryid=_benc(entryid.value)) except NotFoundError: pass
def searches(self): """Return all permanent search folders.""" findroot = self.root.folder('FINDER_ROOT') # TODO # extract special type of guid from search folder # PR_EXTENDED_FOLDER_FLAGS to match against guid_folder = {} for folder in findroot.folders(): try: prop = folder.prop(PR_EXTENDED_FOLDER_FLAGS) subprops = self._subprops(prop.value) guid_folder[subprops[2]] = folder except NotFoundError: pass # match common_views SFInfo records against these guids table = self.common_views.mapiobj.GetContentsTable(MAPI_ASSOCIATED) table.SetColumns([PR_MESSAGE_CLASS_W, PR_WB_SF_ID], MAPI_UNICODE) table.Restrict( SPropertyRestriction( RELOP_EQ, PR_MESSAGE_CLASS_W, SPropValue(PR_MESSAGE_CLASS_W, 'IPM.Microsoft.WunderBar.SFInfo')), TBL_BATCH) for row in table.QueryRows(-1, 0): try: yield guid_folder[row[1].Value] except KeyError: pass
def item(self, entryid=None, sourcekey=None): """ Return :class:`Item` with given entryid or sourcekey :param entryid: item entryid :param sourcekey: item sourcekey """ # resolve sourcekey to entryid if sourcekey is not None: restriction = SPropertyRestriction(RELOP_EQ, PR_SOURCE_KEY, SPropValue(PR_SOURCE_KEY, _bdec(sourcekey))) table = self.mapiobj.GetContentsTable(MAPI_DEFERRED_ERRORS) table.SetColumns([PR_ENTRYID, PR_SOURCE_KEY], 0) table.Restrict(restriction, 0) rows = list(table.QueryRows(-1, 0)) if not rows: raise NotFoundError("no item with sourcekey '%s'" % sourcekey) entryid = _benc(rows[0][0].Value) # open message with entryid try: mapiobj = _utils.openentry_raw(self.store.mapiobj, _bdec(entryid), self.content_flag) except MAPIErrorNotFound: raise NotFoundError("no item with entryid '%s'" % entryid) item = _item.Item(self, mapiobj=mapiobj) return item
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 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 _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 gab_user(gab): username = os.getenv('KOPANO_TEST_USER') table = gab.GetContentsTable(0) table.FindRow( SPropertyRestriction(RELOP_EQ, PR_ACCOUNT_W, SPropValue(PR_ACCOUNT_W, username)), BOOKMARK_BEGINNING, 0) row = table.QueryRows(1, 0) return row
def test_subject(item): item.subject = 'test' mapiobj = SPropertyRestriction(RELOP_EQ, PR_SUBJECT, SPropValue(PR_SUBJECT, b'test')) restriction = Restriction(mapiobj=mapiobj) assert restriction.match(item) item.subject = 'henk' assert not restriction.match(item)
def test_deletedontimestamp(root, message): eid = message.GetProps([PR_ENTRYID], 0)[0] root.DeleteMessages([eid.Value], 0, None, 0) table = root.GetContentsTable(SHOW_SOFT_DELETES) table.SetColumns([PR_SUBJECT, PR_ENTRYID, PR_DELETED_ON], 0) table.FindRow(SPropertyRestriction(RELOP_EQ, PR_ENTRYID, eid), 0, 0) rows = table.QueryRows(1, 0) assert PpropFindProp(rows[0], PR_DELETED_ON)
def calendar_item(self): """Global calendar item :class:`item <Item>` (possibly in delegator store).""" goid = self.item.get_prop(PidLidCleanGlobalObjectId) if goid is not None: restriction = Restriction(SPropertyRestriction( RELOP_EQ, goid.proptag, SPropValue(goid.proptag, goid.mapiobj.Value)) ) return next(self.calendar.items(restriction), None)
def folder(self, path=None, entryid=None, recurse=False, create=False): """ Return :class:`Folder` with given path or entryid :param key: name, path or entryid """ if entryid is not None: try: return Folder(self.store, entryid) except (MAPIErrorInvalidEntryid, MAPIErrorNotFound, TypeError): raise NotFoundError('no folder with entryid "%s"' % entryid) elif path is None: raise ArgumentError('missing argument to identify folder') if path is None: raise ArgumentError('no path or entryid specified') if '/' in path.replace( '\\/', '' ): # XXX MAPI folders may contain '/' (and '\') in their names.. subfolder = self for name in UNESCAPED_SLASH_RE.split(path): subfolder = subfolder.folder(name, create=create, recurse=False) return subfolder if self == self.store.subtree and path in ENGLISH_FOLDER_MAP: # XXX depth==0? f = getattr(self.store, ENGLISH_FOLDER_MAP[path], None) if f: path = f.name if create: name = path.replace('\\/', '/') try: mapifolder = self.mapiobj.CreateFolder(FOLDER_GENERIC, _unicode(name), u'', None, MAPI_UNICODE) return Folder( self.store, _benc(HrGetOneProp(mapifolder, PR_ENTRYID).Value)) except MAPIErrorCollision: pass name = path.replace('\\/', '/') restriction = Restriction( SPropertyRestriction(RELOP_EQ, PR_DISPLAY_NAME_W, SPropValue(PR_DISPLAY_NAME_W, _unicode(name)))) folders = list(self.folders(recurse=recurse, restriction=restriction)) if len(folders) == 0: raise NotFoundError("no such folder: '%s'" % path) return folders[0]
def get_all(cls, req, resp, store, server, userid): restriction = None if cls.needs_restriction: restriction = Restriction( SPropertyRestriction( RELOP_EQ, PR_CONTAINER_CLASS_W, SPropValue(PR_CONTAINER_CLASS_W, cls.container_class))) data = cls.generator(req, cls.default_folders_list(store), 0, restriction) cls.respond(req, resp, data, cls.fields)
def calendar_item( self ): # TODO ambiguous: split in two (match exact GOID or parent recurrence?) """ Global calendar item :class:`item <Item>` (possibly in delegator store) """ goid = self.item.get_prop(PidLidCleanGlobalObjectId) if goid is not None: restriction = Restriction( SPropertyRestriction( RELOP_EQ, goid.proptag, SPropValue(goid.proptag, goid.mapiobj.Value))) return next(self.calendar.items(restriction), None)
def type_(self): """Store type (*private*, *public*, *archive*).""" table = self.server.sa.OpenUserStoresTable(MAPI_UNICODE) table.Restrict(SPropertyRestriction(RELOP_EQ, PR_EC_STOREGUID, SPropValue(PR_EC_STOREGUID, _bdec(self.guid))), TBL_BATCH) for row in table.QueryRows(1, 0): storetype = PpropFindProp(row, PR_EC_STORETYPE) if storetype: return { ECSTORE_TYPE_PRIVATE: 'private', ECSTORE_TYPE_ARCHIVE: 'archive', ECSTORE_TYPE_PUBLIC: 'public', }[storetype.Value]
def _store(self, guid): if len(guid) != 32: raise Error("invalid store id: '%s'" % guid) try: storeid = _bdec(guid) except: raise Error("invalid store id: '%s'" % guid) table = self.ems.GetMailboxTable(None, 0) # XXX merge with Store.__init__ table.SetColumns([PR_ENTRYID], 0) table.Restrict(SPropertyRestriction(RELOP_EQ, PR_STORE_RECORD_KEY, SPropValue(PR_STORE_RECORD_KEY, storeid)), TBL_BATCH) for row in table.QueryRows(-1, 0): return self._store2(row[0].Value) raise NotFoundError("no such store: '%s'" % guid)
def stores(self): """Return all company :class:`stores <Store>`.""" if self.server.multitenant: table = self.server.sa.OpenUserStoresTable(MAPI_UNICODE) restriction = SPropertyRestriction(RELOP_EQ, PR_EC_COMPANY_NAME_W, SPropValue(PR_EC_COMPANY_NAME_W, self.name)) table.Restrict(restriction, TBL_BATCH) for row in table.QueryRows(-1, 0): prop = PpropFindProp(row, PR_EC_STOREGUID) if prop: yield _store.Store(_hex(prop.Value), self.server) else: for store in self.server.stores(): yield store
def test_deletedontimestamp(root, message): message.SetProps([SPropValue(PR_SUBJECT, b'Test')]) message.SaveChanges(KEEP_OPEN_READWRITE) eid = message.GetProps([PR_ENTRYID], 0)[0] root.DeleteMessages([eid.Value], 0, None, 0) table = root.GetContentsTable(SHOW_SOFT_DELETES) table.SetColumns([PR_SUBJECT, PR_ENTRYID, PR_DELETED_ON], 0) table.FindRow(SPropertyRestriction(RELOP_EQ, PR_ENTRYID, eid), 0, 0) rows = table.QueryRows(1, 0) assert PpropFindProp(rows[0], PR_DELETED_ON) # Clean up message root.DeleteMessages([rows[0][1].Value], 0, None, 0)
def config_item(self, name): """Retrieve the config item for the given name. :param name: The config item name """ table = self.subtree.mapiobj.GetContentsTable(MAPI_DEFERRED_ERRORS | MAPI_ASSOCIATED) table.Restrict(SPropertyRestriction(RELOP_EQ, PR_SUBJECT, SPropValue(PR_SUBJECT, name)), 0) rows = table.QueryRows(1,0) # No config item found, create new message if len(rows) == 0: item = self.subtree.associated.create_item(message_class='IPM.Zarafa.Configuration', subject=name) else: mapiobj = self.subtree.mapiobj.OpenEntry(rows[0][0].Value, None, MAPI_MODIFY) item = _item.Item(mapiobj=mapiobj) return item
def company(self): """Store :class:`company <Company>`.""" if self.server.multitenant: table = self.server.sa.OpenUserStoresTable(MAPI_UNICODE) table.Restrict(SPropertyRestriction(RELOP_EQ, PR_EC_STOREGUID, SPropValue(PR_EC_STOREGUID, _bdec(self.guid))), TBL_BATCH) for row in table.QueryRows(1, 0): storetype = PpropFindProp(row, PR_EC_STORETYPE) if storetype.Value == ECSTORE_TYPE_PUBLIC: companyname = PpropFindProp(row, PR_EC_USERNAME_W) # XXX bug in ECUserStoreTable.cpp? if not companyname: companyname = PpropFindProp(row, PR_EC_COMPANY_NAME_W) # XXX else: companyname = PpropFindProp(row, PR_EC_COMPANY_NAME_W) return self.server.company(companyname.Value) else: return next(self.server.companies())
def mail_folders(self, **kwargs): # Mail folders are a fixed set of folders which all can contain mail # items, plus any other customly created folder of container class # IPF.Note. static = {} for n in ('inbox', 'outbox', 'sentmail', 'wastebasket', 'drafts', 'junk'): f = getattr(self, n) if f is not None: static[f.entryid] = True yield f # Find additional folders using a restrictions. restriction = Restriction( SPropertyRestriction(RELOP_EQ, PR_CONTAINER_CLASS_W, SPropValue(PR_CONTAINER_CLASS_W, 'IPF.Note'))) for folder in self.folders(restriction=restriction, **kwargs): # Yield additional folders, filtering by the fixed ones. if folder.entryid not in static: yield folder
def item(self, entryid=None, sourcekey=None): """ Return :class:`Item` with given entryid or sourcekey :param entryid: item entryid :param sourcekey: item sourcekey """ if entryid is not None: eid = _utils._bdec_eid(entryid) elif sourcekey is not None: # TODO this is horribly slow with nothing cached.. 1 SQL per row!? try: restriction = SPropertyRestriction( RELOP_EQ, PR_SOURCE_KEY, SPropValue(PR_SOURCE_KEY, _bdec(sourcekey))) except: raise ArgumentError("invalid sourcekey: %r" % sourcekey) table = self.mapiobj.GetContentsTable(MAPI_DEFERRED_ERRORS) table.SetColumns([PR_ENTRYID, PR_SOURCE_KEY], 0) table.Restrict(restriction, 0) rows = list(table.QueryRows(-1, 0)) if not rows: raise NotFoundError("no item with sourcekey '%s'" % sourcekey) eid = rows[0][0].Value else: raise ArgumentError("no entryid or sourcekey specified") # open message with entryid try: mapiobj = _utils.openentry_raw(self.store.mapiobj, eid, self.content_flag) except MAPIErrorNotFound: if sourcekey is not None: raise NotFoundError("no item with sourcekey '%s'" % sourcekey) else: raise NotFoundError("no item with entryid '%s'" % entryid) except MAPIErrorInvalidEntryid: raise ArgumentError("invalid entryid: %r" % entryid) item = _item.Item(self, mapiobj=mapiobj) return item
def favorites(self): """Returns all favorite folders""" table = Table( self.server, self.common_views.mapiobj.GetContentsTable(MAPI_ASSOCIATED), columns=[PR_WLINK_ENTRYID, PR_WLINK_STORE_ENTRYID], restriction=Restriction( SPropertyRestriction( RELOP_EQ, PR_MESSAGE_CLASS, SPropValue(PR_MESSAGE_CLASS, b"IPM.Microsoft.WunderBar.Link")))) for entryid, store_entryid in table: try: if store_entryid == self.entryid: # XXX: Handle favorites from public stores yield self.folder(entryid=_hex(entryid.value)) else: store = Store(entryid=_hex(store_entryid.value), server=self.server) yield store.folder(entryid=_hex(entryid.value)) except NotFoundError: pass
def _recipients(self, reciptype, addrs): """Sets the recipient table according to the type and given addresses. Since the recipienttable contains to, cc and bcc addresses, and item.to = [] should only set the to recipients (not append), the to recipients are first removed and then added again. This approach works but seems suboptimal, since it queries the MAPI_TO items, removes them and adds the new entries while leaving the other recipients intact. :param reciptype: MAPI_TO, MAPI_CC or MAPI_BCC :param addrs: list of addresses to set. """ restriction = Restriction(SPropertyRestriction(RELOP_EQ, PR_RECIPIENT_TYPE, SPropValue(PR_RECIPIENT_TYPE, reciptype))) table = self.table(PR_MESSAGE_RECIPIENTS, columns=[PR_ROWID], restriction=restriction) rows = [[SPropValue(PR_ROWID, row[PR_ROWID])] for row in table.dict_rows()] if rows: self.mapiobj.ModifyRecipients(MODRECIP_REMOVE, rows) if _is_str(addrs): addrs = [x.strip() for x in _unicode(addrs).split(';')] elif isinstance(addrs, _user.User): addrs = [addrs] names = [] for addr in addrs: pr_addrtype, pr_dispname, pr_email, pr_entryid = self._addr_props(addr) names.append([ SPropValue(PR_RECIPIENT_TYPE, reciptype), SPropValue(PR_DISPLAY_NAME_W, pr_dispname), SPropValue(PR_ADDRTYPE_W, _unicode(pr_addrtype)), SPropValue(PR_EMAIL_ADDRESS_W, _unicode(pr_email)), SPropValue(PR_ENTRYID, pr_entryid), ]) self.mapiobj.ModifyRecipients(MODRECIP_ADD, names) _utils._save(self.mapiobj) # XXX needed?
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 accept(self, tentative=False, response=True, add_bcc=False, subject_prefix='Accepted'): """Accept meeting request. :param tentative: accept tentatively (default False) :param response: send response message (default True) """ if not self.is_request: raise Error('item is not a meeting request') if self._check_processed(): self.log.warning('meeting request already processed') return calendar = self.calendar cal_item = self.calendar_item basedate = self.basedate # do nothing for invitation to self # (esp. don't try to add organizer/BCC) if self.item.store.user.email == self.item.from_.email: return self.item.mapiobj.SetReadFlag(SUPPRESS_RECEIPT) merge = False if basedate: # update existing recurrence if cal_item and cal_item.recurring: recurrence = cal_item.recurrence if recurrence._is_exception(basedate): recurrence._modify_exception(basedate, self.item) else: recurrence._create_exception(basedate, self.item) # otherwise replace calendar item else: if cal_item and cal_item[PidLidGlobalObjectId] == \ self.item[PidLidGlobalObjectId]: calendar.delete(cal_item) cal_item = self.item.copy(calendar) self._init_calitem(cal_item, tentative) else: # preserve categories # TODO preserve everything unrelated? categories = cal_item.categories if cal_item else [] # remove existing recurrence if cal_item and cal_item.recurring: mr_counter = self.update_counter cal_counter = cal_item.meetingrequest.update_counter if mr_counter is not None and cal_counter is not None and \ mr_counter <= cal_counter: raise Error('trying to accept out-of-date meeting request') calendar.delete(cal_item) # determine existing exceptions goid = self.item.get_prop(PidLidCleanGlobalObjectId) if goid is not None: restriction = Restriction(SPropertyRestriction( RELOP_EQ, goid.proptag, SPropValue(goid.proptag, goid.mapiobj.Value)) ) existing_items = list(calendar.items(restriction)) # TODO check php existing_items.sort( key=lambda i: i.get(PidLidAppointmentStartWhole)) else: existing_items = [] # create new recurrence cal_item = self.item.copy(calendar) if categories: cal_item.categories = categories # merge existing exceptions if cal_item.recurring and existing_items: merge = True rec = cal_item.recurrence for item in existing_items: if not rec._is_exception(item.meetingrequest.basedate): rec._create_exception( item.meetingrequest.basedate, item, merge=True) # TODO else update..? calendar.delete(existing_items) self._init_calitem(cal_item, tentative, merge) # TODO why filter recipients? if merge or not add_bcc: table = cal_item.mapiobj.OpenProperty( PR_MESSAGE_RECIPIENTS, IID_IMAPITable, MAPI_UNICODE, 0) table.SetColumns(RECIP_PROPS, 0) rows = table.QueryRows(2147483647, 0) cal_item.mapiobj.ModifyRecipients(MODRECIP_MODIFY, rows) _utils._save(cal_item.mapiobj) # add self as BCC for ZCP-9901 TODO still relevant? proptags = [ PR_ACCOUNT_W, PR_ADDRTYPE_W, PR_DISPLAY_NAME_W, PR_DISPLAY_TYPE, PR_DISPLAY_TYPE_EX, PR_EMAIL_ADDRESS_W, PR_ENTRYID, PR_OBJECT_TYPE, PR_SEARCH_KEY, PR_SMTP_ADDRESS_W, ] user = self.item.store.user props = user.mapiobj.GetProps(proptags, 0) props.extend([ SPropValue(PR_RECIPIENT_ENTRYID, props[6].Value), SPropValue(PR_RECIPIENT_FLAGS, (recipOriginal | recipSendable)), SPropValue(PR_RECIPIENT_TRACKSTATUS, 0), SPropValue(PR_RECIPIENT_TYPE, MAPI_BCC), ]) if add_bcc and not merge: cal_item.mapiobj.ModifyRecipients(MODRECIP_ADD, [props]) _utils._save(cal_item.mapiobj) # send response if response: if tentative: message_class = 'IPM.Schedule.Meeting.Resp.Tent' else: message_class = 'IPM.Schedule.Meeting.Resp.Pos' self._respond(subject_prefix, message_class)