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 create_attendee(self, type_, address): # TODO move to Attendee class reciptype = { 'required': 1, 'optional': 2, 'resource': 3 }[type_] table = self.table(PR_MESSAGE_RECIPIENTS) names = [] pr_addrtype, pr_dispname, pr_email, pr_entryid = self._addr_props(address) 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)
def process_cancellation(self, delete=False): """Process meeting request cancellation. :param delete: delete appointment from calendar (default False) """ if not self.is_cancellation: raise Error('item is not a meeting request cancellation') cal_item = self.calendar_item if not cal_item: self.log.debug('no appointment matches cancellation') return basedate = self.basedate if basedate: if cal_item.recurring: recurrence = cal_item.recurrence copytags = _copytags(cal_item.mapiobj) if delete: recurrence._delete_exception( basedate, self.item, copytags) else: if recurrence._is_exception(basedate): recurrence._modify_exception( basedate, self.item, copytags) else: recurrence._create_exception( basedate, self.item, copytags) message = recurrence._exception_message(basedate) message[PidLidBusyStatus] = libfreebusy.fbFree message[PR_MESSAGE_FLAGS] = MSGFLAG_UNSENT | MSGFLAG_READ _utils._save(message._attobj) else: if delete: self.calendar.delete(cal_item) else: cal_item.cancel() else: if delete: self.calendar.delete(cal_item) else: self.item.mapiobj.CopyTo( [], [], 0, None, IID_IMessage, cal_item.mapiobj, 0) cal_item.mapiobj.SetProps( [SPropValue(PR_MESSAGE_CLASS_W, 'IPM.Appointment')]) if cal_item and not delete: _utils._save(cal_item.mapiobj)
def _set_special_folder(self, folder, proptag, container_class=None): _root = self.mapiobj.OpenEntry(None, None, MAPI_MODIFY) if isinstance(proptag, tuple): proptag, idx = proptag try: value = HrGetOneProp(self._root, proptag).Value except MAPIErrorNotFound: value = 5 * [b''] value[idx] = _bdec(folder.entryid) _root.SetProps([SPropValue(proptag, value)]) else: _root.SetProps([SPropValue(proptag, _bdec(folder.entryid))]) _utils._save(self._root) if container_class: folder.container_class = container_class else: prop = folder.get_prop(PR_CONTAINER_CLASS_W) if prop: folder.delete(prop)
def create_item(self, message_flags=None): """ Create embedded :class:`item <Item>` """ (id_, attach) = self.mapiobj.CreateAttach(None, 0) attach.SetProps([ SPropValue(PR_ATTACH_METHOD, ATTACH_EMBEDDED_MSG), SPropValue(PR_ATTACHMENT_FLAGS, 2), SPropValue(PR_ATTACHMENT_HIDDEN, True), SPropValue(PR_ATTACHMENT_LINKID, 0), SPropValue(PR_ATTACH_FLAGS, 0), ]) msg = attach.OpenProperty(PR_ATTACH_DATA_OBJ, IID_IMessage, 0, MAPI_CREATE | MAPI_MODIFY) if message_flags is not None: msg.SetProps([SPropValue(PR_MESSAGE_FLAGS, message_flags)]) msg.SaveChanges(KEEP_OPEN_READWRITE) attach.SaveChanges(KEEP_OPEN_READWRITE) self.mapiobj.SaveChanges(KEEP_OPEN_READWRITE) # XXX needed? item = Item(mapiobj=msg) item.server = self.server item._attobj = attach return item
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 permission(obj, member, create): for permission in obj.permissions(): if permission.member == member: return permission if create: acl_table = obj.mapiobj.OpenProperty(PR_ACL_TABLE, IID_IExchangeModifyTable, 0, 0) if isinstance(member, _user.User): # XXX *.id_ or something..? memberid = member.userid elif isinstance(member, _group.Group): memberid = member.groupid else: memberid = member.companyid acl_table.ModifyTable(0, [ ROWENTRY(ROW_ADD, [ SPropValue(PR_MEMBER_ENTRYID, _bdec(memberid)), SPropValue(PR_MEMBER_RIGHTS, 0) ]) ]) return obj.permission(member) else: raise NotFoundError("no permission entry for '%s'" % member.name)
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 test_deleteprops_afterreopen(store, message): tag = PROP_TAG(PT_STRING8, 0x6600) message.SetProps([SPropValue(tag, b'test')]) message.SaveChanges(0) props = message.GetProps([PR_ENTRYID], 0) entryid = props[0].Value message = store.OpenEntry(entryid, None, MAPI_MODIFY) message.DeleteProps([tag]) message.SaveChanges(0) message = store.OpenEntry(entryid, None, 0) props = message.GetProps([tag], 0) assert props[0].ulPropTag == PROP_TAG(PT_ERROR, 0x6600)
def settings_loads(self, data): data = _utils.pickle_loads(data) self.delegations_loads(data['delegations']) self.permissions_loads(data['permissions']) props = [] for proptag, value in data['props'].items(): if proptag in SETTINGS_PROPTAGS: props.append(SPropValue(proptag, value)) self.mapiobj.SetProps(props) self.mapiobj.SaveChanges(KEEP_OPEN_READWRITE)
def message_class(self, messageclass): # FIXME: Add all possible PR_MESSAGE_CLASS values """ MAPI Message classes: * IPM.Note.SMIME.MultipartSigned - smime signed email * IMP.Note - normal email * IPM.Note.SMIME - smime encypted email * IPM.StickyNote - note * IPM.Appointment - appointment * IPM.Task - task """ self.mapiobj.SetProps([SPropValue(PR_MESSAGE_CLASS_W, _unicode(messageclass))]) _utils._save(self.mapiobj)
def test_recipienttable_addmore(addressbook, defaultmessage, make_recip_smtp_entry): recipients = [make_recip_smtp_entry(addressbook, 0, b'user4', b'*****@*****.**', MAPI_TO), make_recip_smtp_entry(addressbook, 0, b'user5', b'*****@*****.**', MAPI_TO)] defaultmessage.ModifyRecipients(MODRECIP_ADD, recipients) defaultmessage.SaveChanges(0) table = defaultmessage.GetRecipientTable(0) table.SetColumns([PR_DISPLAY_NAME_A, PR_DISPLAY_NAME_W], 0) rows = table.QueryRows(10, 0) rowsresult = [[SPropValue(PR_DISPLAY_NAME_A, b'????'), SPropValue(PR_DISPLAY_NAME_W, u'ザラファ')], [SPropValue(PR_DISPLAY_NAME_A, b'user2'), SPropValue(PR_DISPLAY_NAME_W, u'user2')], [SPropValue(PR_DISPLAY_NAME_A, b'user4'), SPropValue(PR_DISPLAY_NAME_W, u'user4')], [SPropValue(PR_DISPLAY_NAME_A, b'user5'), SPropValue(PR_DISPLAY_NAME_W, u'user5')]] assert rows == rowsresult
def __init__(self, name=None, server=None, email=None, ecuser=None, userid=None): self.server = server or _server.Server(_skip_check=True, parse_args=False) self._ecuser = None self._name = None if ecuser: self._ecuser = ecuser elif userid: try: self._ecuser = \ self.server.sa.GetUser(_bdec(userid), MAPI_UNICODE) except (binascii.Error, TypeError): # TODO generalize? raise ArgumentError("invalid entryid: '%s'" % userid) except MAPIErrorNotFound: raise NotFoundError("no user found with userid '%s'" % userid) elif email or name: if email: try: self._name = str( self.server.gab.ResolveNames( [PR_EMAIL_ADDRESS_W], MAPI_UNICODE | EMS_AB_ADDRESS_LOOKUP, [[SPropValue(PR_DISPLAY_NAME_W, str(email))]], [MAPI_UNRESOLVED])[0][0][1].Value) except (MAPIErrorNotFound, MAPIErrorInvalidParameter, \ IndexError): raise NotFoundError("no such user '%s'" % email) else: self._name = str(name) try: self._ecuser = self.server.sa.GetUser( self.server.sa.ResolveUserName(self._name, MAPI_UNICODE), MAPI_UNICODE) # multi-tenant, but no '@' in username.. except (MAPIErrorNotFound, MAPIErrorInvalidParameter): raise NotFoundError("no such user: '******'" % self.name) if self._ecuser: self._name = self._ecuser.Username self._userid = _benc(self._ecuser.UserID) self._mapiobj = None
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 create_attachment(self, name, data): """Create a new attachment :param name: the attachment name :param data: string containing the attachment data """ # XXX: use file object instead of data? (id_, attach) = self.mapiobj.CreateAttach(None, 0) name = _unicode(name) props = [ SPropValue(PR_ATTACH_LONG_FILENAME_W, name), SPropValue(PR_ATTACH_METHOD, ATTACH_BY_VALUE) ] attach.SetProps(props) stream = attach.OpenProperty(PR_ATTACH_DATA_BIN, IID_IStream, STGM_WRITE | STGM_TRANSACTED, MAPI_MODIFY | MAPI_CREATE) stream.Write(data) stream.Commit(0) attach.SaveChanges(KEEP_OPEN_READWRITE) self.mapiobj.SaveChanges(KEEP_OPEN_READWRITE) # XXX needed? return Attachment(mapiobj=attach)
def test_HTMLToRTFAndBack(message, copy): # test for #7244 message.SetProps([ SPropValue(PR_INTERNET_CPID, 28605), SPropValue(PR_HTML, b'<html><body>I’d like that.<br></body></html>') ]) plain = message.GetProps([PR_BODY_W], 0)[0].Value assert plain == 'I’d like that.\r\n' stream = message.OpenProperty(PR_RTF_COMPRESSED, IID_IStream, 0, 0) rtfprop = WrapCompressedRTFStream(stream, 0) rtf = rtfprop.Read(10240) assert rtf == b'{\\rtf1\\ansi\\ansicpg1252\\fromhtml1 \\deff0{\\fonttbl\r\n{\\f0\\fswiss\\fcharset0 Arial;}\r\n{\\f1\\fmodern Courier New;}\r\n{\\f2\\fnil\\fcharset2 Symbol;}\r\n{\\f3\\fmodern\\fcharset0 Courier New;}\r\n{\\f4\\fswiss\\fcharset0 Arial;}\r\n{\\f5\\fswiss Tahoma;}\r\n{\\f6\\fswiss\\fcharset0 Times New Roman;}}\r\n{\\colortbl\\red0\\green0\\blue0;\\red0\\green0\\blue255;\\red0\\green0\\blue255;}\r\n\\uc1\\pard\\plain\\deftab360 \\f0\\fs24 \r\n{\\*\\htmltag19 <html>}{\\*\\htmltag64}\r\n{\\*\\htmltag50 <body>}{\\*\\htmltag64}I\\htmlrtf \\u8217 ?\\htmlrtf0{\\*\\htmltag80’}d like that.\r\n{\\*\\htmltag112 <br>}{\\*\\htmltag64}\\htmlrtf \\line \\htmlrtf0 \r\n{\\*\\htmltag58 </body>}{\\*\\htmltag64}\r\n{\\*\\htmltag27 </html>}{\\*\\htmltag64}}\r\n' message.SaveChanges(0) # note: this copies the PR_RTF_COMPRESSED, and re-creates the plain+html bodies in copy. (rtf->html + rtf->plain) message.CopyTo([], None, 0, None, IID_IMessage, copy, 0) html = copy.GetProps([PR_HTML], 0)[0].Value # previously, we had a leading enter here because of rtf conversion (we could strip that in Util::HrHtmlToRtf()) # but since we now copy from plain again, the enter isn't present anymore assert html == b"<html><body>I’d like that.<br></body></html>" plain = copy.GetProps([PR_BODY_W], 0)[0].Value assert plain == 'I’d like that.\r\n'
def search_start(self, folders, text, recurse=False): # specific restriction format, needed to reach indexer restriction = SOrRestriction([ SContentRestriction(FL_SUBSTRING | FL_IGNORECASE, PR_SUBJECT_W, SPropValue(PR_SUBJECT_W, _unicode(text))), SContentRestriction(FL_SUBSTRING | FL_IGNORECASE, PR_BODY_W, SPropValue(PR_BODY_W, _unicode(text))), SContentRestriction(FL_SUBSTRING | FL_IGNORECASE, PR_DISPLAY_TO_W, SPropValue(PR_DISPLAY_TO_W, _unicode(text))), SContentRestriction(FL_SUBSTRING | FL_IGNORECASE, PR_DISPLAY_NAME_W, SPropValue(PR_DISPLAY_NAME_W, _unicode(text))), # XXX add all default fields.. BUT perform full-text search by default! ]) if isinstance(folders, Folder): folders = [folders] search_flags = 0 if recurse: search_flags = SEARCH_RECURSIVE self.mapiobj.SetSearchCriteria(restriction, [_bdec(f.entryid) for f in folders], search_flags)
def test_attachmen_table(defaultmessage, flags): table = defaultmessage.GetAttachmentTable(flags) table.SetColumns([PR_DISPLAY_NAME_A, PR_DISPLAY_NAME_W], 0) rows = table.QueryRows(10, 0) rowsresult = [[SPropValue(PR_DISPLAY_NAME_A, b'????1'), SPropValue(PR_DISPLAY_NAME_W, u'ザラファ1')], [SPropValue(PR_DISPLAY_NAME_A, b'????2'), SPropValue(PR_DISPLAY_NAME_W, u'ザラファ2')], [SPropValue(PR_DISPLAY_NAME_A, b'display 3'), SPropValue(PR_DISPLAY_NAME_W, u'display 3')]] assert rows == rowsresult
def test_recordkey(message): props = message.GetProps([PR_RECORD_KEY], 0) assert props[0] == SPropValue(PROP_TAG(PT_ERROR, PROP_ID(PR_RECORD_KEY)), MAPI_E_NOT_FOUND) message.SaveChanges(KEEP_OPEN_READWRITE) props = message.GetProps([PR_RECORD_KEY, PR_ENTRYID], 0) assert props[0].ulPropTag == PR_RECORD_KEY assert len(props[0].Value) == 48 assert props[0].Value == props[1].Value (id, att) = message.CreateAttach(None, 0) att.SetProps([SPropValue(PR_ATTACH_METHOD, ATTACH_EMBEDDED_MSG)]) sub = att.OpenProperty(PR_ATTACH_DATA_OBJ, IID_IMessage, 0, MAPI_CREATE | MAPI_MODIFY) props = sub.GetProps([PR_RECORD_KEY, PR_ENTRYID, PR_SOURCE_KEY], 0) # Unsaved submessages have a source key but no record key or entryid assert props[0].ulPropTag == PROP_TAG(PT_ERROR, PROP_ID(PR_RECORD_KEY)) assert props[0].Value == MAPI_E_NOT_FOUND assert props[1].ulPropTag == PROP_TAG(PT_ERROR, PROP_ID(PR_ENTRYID)) assert props[1].Value == MAPI_E_NOT_FOUND assert props[2].ulPropTag == PR_SOURCE_KEY assert len(props[2].Value) == 22 sub.SaveChanges(0) att.SaveChanges(0) message.SaveChanges(0) att = message.OpenAttach(0, None, 0) sub = att.OpenProperty(PR_ATTACH_DATA_OBJ, IID_IMessage, 0, 0) props = sub.GetProps([PR_RECORD_KEY, PR_ENTRYID, PR_SOURCE_KEY], 0) # Saved submessages have a PR_SOURCE_KEY but no record key or entryid assert props[0].ulPropTag == PROP_TAG(PT_ERROR, PROP_ID(PR_RECORD_KEY)) assert props[0].Value == MAPI_E_NOT_FOUND assert props[1].ulPropTag == PROP_TAG(PT_ERROR, PROP_ID(PR_ENTRYID)) assert props[1].Value == MAPI_E_NOT_FOUND assert props[2].ulPropTag == PR_SOURCE_KEY assert len(props[2].Value) == 22
def folder(self, path=None, entryid=None, recurse=False, create=False): """Return :class:`Folder` with given path or entryid :param path: Folder path (optional) :param entryid: Folder entryid (optional) :param create: Create folder if it doesn't exist (default False) """ 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') # TODO MAPI folders may contain '/' (and '\') in their names.. if '/' in path.replace('\\/', ''): subfolder = self for name in UNESCAPED_SLASH_RE.split(path): subfolder = subfolder.folder(name, create=create, recurse=False) return subfolder # TODO depth==0? if self == self.store.subtree and path in ENGLISH_FOLDER_MAP: 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, str(name), '', 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, str(name)))) folders = list(self.folders(recurse=recurse, restriction=restriction)) if not folders: raise NotFoundError("no such folder: '%s'" % path) return folders[0]
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('cannot open folder with entryid "%s"' % entryid) 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? path = getattr(self.store, ENGLISH_FOLDER_MAP[path]).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 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 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 __init__(self, name=None, server=None, email=None, ecuser=None, userid=None): self.server = server or _server.Server() self._ecuser = None self._name = None if ecuser: self._ecuser = ecuser elif userid: try: self._ecuser = self.server.sa.GetUser(_bdec(userid), MAPI_UNICODE) except MAPIErrorNotFound: raise NotFoundError("no user found with userid '%s'" % userid) elif email or name: if email: try: self._name = _unicode( self.server.gab.ResolveNames( [PR_EMAIL_ADDRESS_W], MAPI_UNICODE | EMS_AB_ADDRESS_LOOKUP, [[SPropValue(PR_DISPLAY_NAME_W, _unicode(email))]], [MAPI_UNRESOLVED])[0][0][1].Value) except (MAPIErrorNotFound, MAPIErrorInvalidParameter, IndexError): raise NotFoundError("no such user '%s'" % email) else: self._name = _unicode(name) try: self._ecuser = self.server.sa.GetUser( self.server.sa.ResolveUserName(self._name, MAPI_UNICODE), MAPI_UNICODE) except (MAPIErrorNotFound, MAPIErrorInvalidParameter ): # multi-tenant, but no '@' in username.. raise NotFoundError("no such user: '******'" % self.name) if self._ecuser: self._name = self._ecuser.Username self._userid = _benc(self._ecuser.UserID) self._mapiobj = None
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 process_cancellation(self, delete=False): """ Process meeting request cancellation :param delete: delete appointment from calendar """ if not self.is_cancellation: raise Error('item is not a meeting request cancellation') cal_item = self.calendar_item if not cal_item: return basedate = self.basedate if basedate: if cal_item.recurring: recurrence = cal_item.recurrence copytags = _copytags(cal_item.mapiobj) if delete: recurrence.delete_exception(basedate, self.item, copytags) else: if recurrence.is_exception(basedate): recurrence.modify_exception(basedate, self.item, copytags) else: recurrence.create_exception(basedate, self.item, copytags) message = recurrence.exception_message(basedate) message[PidLidBusyStatus] = libfreebusy.fbFree message[PR_MESSAGE_FLAGS] = MSGFLAG_UNSENT | MSGFLAG_READ message._attobj.SaveChanges(KEEP_OPEN_READWRITE) else: if delete: self.calendar.delete(cal_item) else: self.item.mapiobj.CopyTo([], [], 0, None, IID_IMessage, cal_item.mapiobj, 0) cal_item.mapiobj.SetProps( [SPropValue(PR_MESSAGE_CLASS_W, u'IPM.Appointment')]) if cal_item: cal_item.mapiobj.SaveChanges(KEEP_OPEN_READWRITE)
def settings_loads(self, data): """Deserialize (overriding) all store settings. :param data: Serialized data """ data = _utils.pickle_loads(data) self.delegations_loads(data['delegations']) self.permissions_loads(data['permissions']) props = [] for proptag, value in data['props'].items(): if proptag in SETTINGS_PROPTAGS: props.append(SPropValue(proptag, value)) self.mapiobj.SetProps(props) self.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)