def _convert_to_smtp(self, props, tag_data): if not hasattr(self.server, '_smtp_cache'): # XXX speed hack, discuss self.server._smtp_cache = {} for addrtype, email, entryid, name, searchkey in ADDR_PROPS: if addrtype not in tag_data or entryid not in tag_data or name not in tag_data: continue if tag_data[addrtype][1] in (u'SMTP', u'MAPIPDL'): # XXX MAPIPDL==distlist.. can we just dump this? continue eid = tag_data[entryid][1] if eid in self.server._smtp_cache: email_addr = self.server._smtp_cache[eid] else: try: mailuser = self.server.ab.OpenEntry(eid, IID_IMailUser, 0) email_addr = HrGetOneProp(mailuser, PR_SMTP_ADDRESS_W).Value except MAPIErrorUnknownEntryid: # XXX corrupt data? keep going but log problem continue except MAPIErrorNotFound: # XXX deleted user, or no email address? or user with multiple entryids..heh? continue except MAPIErrorInterfaceNotSupported: # XXX ZARAFA group? continue self.server._smtp_cache[eid] = email_addr tag_data[addrtype][1] = u'SMTP' if email in tag_data: tag_data[email][1] = email_addr else: props.append([email, email_addr, None]) tag_data[entryid][1] = self.server.ab.CreateOneOff(tag_data[name][1], u'SMTP', email_addr, MAPI_UNICODE) key = b'SMTP:' + email_addr.upper().encode('ascii') if searchkey in tag_data: # XXX probably need to create, also email tag_data[searchkey][1] = key else: props.append([searchkey, key, None])
def _arch_item( self ): # open archive store explicitly so we can handle otherwise silenced errors (MAPI errors in mail bodies for example) if self._architem is None: if self.stubbed: ids = self.mapiobj.GetIDsFromNames(NAMED_PROPS_ARCHIVER, 0) PROP_STORE_ENTRYIDS = CHANGE_PROP_TYPE(ids[0], PT_MV_BINARY) PROP_ITEM_ENTRYIDS = CHANGE_PROP_TYPE(ids[1], PT_MV_BINARY) # support for multiple archives was a mistake, and is not and _should not_ be used. so we just pick nr 0. arch_storeid = HrGetOneProp(self.mapiobj, PROP_STORE_ENTRYIDS).Value[0] item_entryid = HrGetOneProp(self.mapiobj, PROP_ITEM_ENTRYIDS).Value[0] try: arch_store = self.server._store2(arch_storeid) except MAPIErrorUnconfigured: raise Error( 'could not open archive store (check SSL settings?)') self._architem = arch_store.OpenEntry(item_entryid, None, 0) else: self._architem = self.mapiobj return self._architem
def prop(self, mapiobj, proptag, create=False, value=None, proptype=None): if isinstance(proptag, int) or \ (isinstance(proptag, str) and ':' not in proptag): # search for property if isinstance(proptag, str): proptag = getattr(MAPI.Tags, proptag) try: sprop = HrGetOneProp(mapiobj, proptag) except MAPIErrorNotEnoughMemory: data = _utils.stream(mapiobj, proptag) sprop = SPropValue(proptag, data) except MAPIErrorNotFound: # not found, create it? if create: return create_prop(self, mapiobj, proptag, value=value, proptype=proptype) else: raise NotFoundError('no such property: %s' % REV_TAG.get(proptag, hex(proptag))) return Property(mapiobj, sprop) else: # named property proptag2, proptype2, namespace, name = \ _name_to_proptag(proptag, mapiobj, proptype) # search for property if proptype2: try: # TODO merge two main branches? sprop = HrGetOneProp(mapiobj, proptag2) return Property(mapiobj, sprop) except MAPIErrorNotEnoughMemory: data = _utils.stream(mapiobj, proptag2) sprop = SPropValue(proptag2, data) return Property(mapiobj, sprop) except MAPIErrorNotFound: pass else: # TODO sloow, streaming? default pidlid type-db? for prop in self.props(namespace=namespace): if prop.name == name: return prop # not found, create it? if create: return create_prop(self, mapiobj, proptag, value=value, proptype=proptype) else: raise NotFoundError('no such property: %s' % proptag)
def subtree(self): """:class:`Folder` designated as IPM.Subtree.""" try: if self.public: ipmsubtreeid = HrGetOneProp(self.mapiobj, PR_IPM_PUBLIC_FOLDERS_ENTRYID).Value else: ipmsubtreeid = HrGetOneProp(self.mapiobj, PR_IPM_SUBTREE_ENTRYID).Value return _folder.Folder(self, _benc(ipmsubtreeid)) except (MAPIErrorNotFound, NotFoundError): pass
def subtree(self): """The user-visible store root :class:`Folder`.""" try: if self.public: ipmsubtreeid = HrGetOneProp( self.mapiobj, PR_IPM_PUBLIC_FOLDERS_ENTRYID).Value else: ipmsubtreeid = HrGetOneProp(self.mapiobj, PR_IPM_SUBTREE_ENTRYID).Value return _folder.Folder(self, _benc(ipmsubtreeid)) except (MAPIErrorNotFound, NotFoundError): pass
def _fbmsg_delgs(self): fbeid = self.root.prop(PR_FREEBUSY_ENTRYIDS).value[1] fbmsg = self.mapiobj.OpenEntry(fbeid, None, MAPI_MODIFY) try: entryids = HrGetOneProp(fbmsg, PR_SCHDINFO_DELEGATE_ENTRYIDS) names = HrGetOneProp(fbmsg, PR_SCHDINFO_DELEGATE_NAMES_W) flags = HrGetOneProp(fbmsg, PR_DELEGATE_FLAGS) except MAPIErrorNotFound: entryids = SPropValue(PR_SCHDINFO_DELEGATE_ENTRYIDS, []) names = SPropValue(PR_SCHDINFO_DELEGATE_NAMES_W, []) flags = SPropValue(PR_DELEGATE_FLAGS, []) return fbmsg, (entryids, names, flags)
def journal(self): """The store :class:`journal folder <Folder>`.""" try: return _folder.Folder(self, _benc(HrGetOneProp( self._root, PR_IPM_JOURNAL_ENTRYID).Value)) except (MAPIErrorNotFound, NotFoundError): pass
def _resolve_email(self, entryid=None): try: mailuser = self.mapisession.OpenEntry(entryid, None, 0) return self.user(HrGetOneProp(mailuser, PR_ACCOUNT_W).Value ).email # XXX PR_SMTP_ADDRESS_W from mailuser? except (Error, MAPIErrorNotFound): # XXX deleted user return '' # XXX groups
def rules_dumps(self, stats=None): server = self.server log = server.log ruledata = None with _log.log_exc(log, stats): try: ruledata = self.prop(PR_RULES_DATA).value except (MAPIErrorNotFound, NotFoundError): pass else: etxml = ElementTree.fromstring(ruledata) for actions in etxml.findall('./item/item/actions'): for movecopy in actions.findall('.//moveCopy'): try: s = movecopy.findall('store')[0] store = server.mapisession.OpenMsgStore( 0, _unbase64(s.text), None, 0) entryid = HrGetOneProp(store, PR_STORE_ENTRYID).Value store = server.store(entryid=_benc(entryid)) if store.public: s.text = 'public' else: s.text = store.user.name if store != self.store else '' f = movecopy.findall('folder')[0] path = store.folder( entryid=_benc(_unbase64(f.text))).path f.text = path except Exception as e: log.warning("could not resolve rule target: %s", str(e)) ruledata = ElementTree.tostring(etxml) return _utils.pickle_dumps({b'data': ruledata})
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)]) if self.inbox: self.inbox.mapiobj.SetProps([SPropValue(proptag, value)]) else: _root.SetProps([SPropValue(proptag, _bdec(folder.entryid))]) if self.inbox: self.inbox.mapiobj.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 dump_rules(folder, user, server, stats, log): """ dump rules for given folder """ ruledata = None with log_exc(log, stats): try: ruledata = folder.prop(PR_RULES_DATA).value except (MAPIErrorNotFound, kopano.NotFoundError): pass else: etxml = ElementTree.fromstring(ruledata) for actions in etxml.findall('./item/item/actions'): for movecopy in actions.findall('.//moveCopy'): try: s = movecopy.findall('store')[0] store = server.mapisession.OpenMsgStore(0, _unbase64(s.text), None, 0) guid = _hex(HrGetOneProp(store, PR_STORE_RECORD_KEY).Value) store = server.store(guid) # XXX guid doesn't work for multiserver? if store.public: s.text = 'public' else: s.text = store.user.name if store != user.store else '' f = movecopy.findall('folder')[0] path = store.folder(entryid=_hex(_unbase64(f.text))).path f.text = path except (MAPIErrorNotFound, kopano.NotFoundError, binascii.Error): log.warning("cannot serialize rule for unknown store/folder") ruledata = ElementTree.tostring(etxml) return pickle_dumps(ruledata)
def enabled(self): """Auto-processing is enabled.""" prop = CHANGE_PROP_TYPE(self._ids[0], PT_BOOLEAN) try: return HrGetOneProp(self._fb, prop).Value except MAPIErrorNotFound: return True
def user(self): """Store :class:`owner <User>`.""" try: userid = HrGetOneProp(self.mapiobj, PR_MAILBOX_OWNER_ENTRYID).Value # XXX return _user.User(self.server.sa.GetUser(userid, MAPI_UNICODE).Username, self.server) except (MAPIErrorNotFound, NotFoundError): pass
def junk(self): """:class:`Folder` designated as junk.""" # PR_ADDITIONAL_REN_ENTRYIDS is a multi-value property, 4th entry is the junk folder try: return _folder.Folder(self, _benc(HrGetOneProp(self._root, PR_ADDITIONAL_REN_ENTRYIDS).Value[4])) except (MAPIErrorNotFound, NotFoundError): pass
def __init__(self, parent_mapiobj, mapiobj): # XXX rethink attributes, names.. add guidname..? self._parent_mapiobj = parent_mapiobj #: Property tag, e.g. 0x37001f for PR_SUBJECT self.proptag = mapiobj.ulPropTag if PROP_TYPE( mapiobj.ulPropTag ) == PT_ERROR and mapiobj.Value == MAPI_E_NOT_ENOUGH_MEMORY: if PROP_ID( self.proptag) == PROP_ID(PR_BODY_W): # avoid slow guessing self.proptag = PR_BODY_W mapiobj = SPropDelayedValue(parent_mapiobj, self.proptag) elif PROP_ID(self.proptag) in (PROP_ID(PR_RTF_COMPRESSED), PROP_ID(PR_HTML)): self.proptag = PROP_TAG(PT_BINARY, PROP_ID(self.proptag)) mapiobj = SPropDelayedValue(parent_mapiobj, self.proptag) else: # XXX possible to use above trick to infer all proptags? for proptype in (PT_BINARY, PT_UNICODE): # XXX slow, incomplete? proptag = (mapiobj.ulPropTag & 0xffff0000) | proptype try: HrGetOneProp( parent_mapiobj, proptag ) # XXX: Unicode issue?? calls GetProps([proptag], 0) self.proptag = proptag # XXX isn't it strange we can get here except MAPIErrorNotEnoughMemory: mapiobj = SPropDelayedValue(parent_mapiobj, proptag) self.proptag = proptag break except MAPIErrorNotFound: pass self.id_ = self.proptag >> 16 self.mapiobj = mapiobj self._value = None self.idname = REV_TAG.get(self.proptag) self.type_ = PROP_TYPE(self.proptag) self.typename = REV_TYPE.get(self.type_) self.named = False self.kind = None self.kindname = None self.guid = None self.name = None self.namespace = None if self.id_ >= 0x8000: # possible named prop try: lpname = self._parent_mapiobj.GetNamesFromIDs([self.proptag], None, 0)[0] if lpname: self.guid = bin2hex(lpname.guid) self.namespace = GUID_NAMESPACE.get(lpname.guid) self.name = lpname.id self.kind = lpname.kind self.kindname = 'MNID_STRING' if lpname.kind == MNID_STRING else 'MNID_ID' self.named = True except MAPIErrorNoSupport: # XXX user.props()? pass
def tasks(self): """The store :class:`tasks folder <Folder>`.""" try: return _folder.Folder(self, _benc(HrGetOneProp(self._root, PR_IPM_TASK_ENTRYID).Value)) except (MAPIErrorNotFound, NotFoundError): pass
def root(self): """:class:`Folder` designated as store root.""" try: return _folder.Folder( self, _benc(HrGetOneProp(self._root, PR_ENTRYID).Value)) except (MAPIErrorNotFound, NotFoundError): pass
def __init__(self, parent_mapiobj, mapiobj): self._parent_mapiobj = parent_mapiobj #: MAPI proptag, for example 0x37001f for PR_SUBJECT_W self.proptag = mapiobj.ulPropTag if (PROP_TYPE(mapiobj.ulPropTag) == PT_ERROR and \ mapiobj.Value == MAPI_E_NOT_ENOUGH_MEMORY): # avoid slow guessing if PROP_ID(self.proptag) == PROP_ID(PR_BODY_W): self.proptag = PR_BODY_W mapiobj = SPropDelayedValue(parent_mapiobj, self.proptag) elif PROP_ID(self.proptag) in \ (PROP_ID(PR_RTF_COMPRESSED), PROP_ID(PR_HTML)): self.proptag = CHANGE_PROP_TYPE(self.proptag, PT_BINARY) mapiobj = SPropDelayedValue(parent_mapiobj, self.proptag) else: # TODO possible to use above trick to infer all proptags? # TODO slow, incomplete? for proptype in (PT_BINARY, PT_UNICODE): proptag = (mapiobj.ulPropTag & 0xffff0000) | proptype try: # TODO: Unicode issue?? calls GetProps([proptag], 0) HrGetOneProp(parent_mapiobj, proptag) # TODO how did we end up here, why is this possible self.proptag = proptag except MAPIErrorNotEnoughMemory: mapiobj = SPropDelayedValue(parent_mapiobj, proptag) self.proptag = proptag break except MAPIErrorNotFound: pass self.mapiobj = mapiobj self._value = None self.__lpname = None
def findroot(self): """The user-invisible store search folder :class:`Folder`.""" try: return _folder.Folder(self, _benc(HrGetOneProp(self.mapiobj, PR_FINDER_ENTRYID).Value)) except (MAPIErrorNotFound, NotFoundError): pass
def __init__(self, store=None, entryid=None, associated=False, deleted=False, mapiobj=None, _check_mapiobj=True, cache={}): if store: self.store = store self.server = store.server # XXX else, determine store dynamically? if mapiobj: self._mapiobj = mapiobj self._entryid = HrGetOneProp(self.mapiobj, PR_ENTRYID).Value elif entryid: try: self._entryid = _bdec(entryid) except: raise ArgumentError('invalid entryid: %r' % entryid) self.content_flag = MAPI_ASSOCIATED if associated else ( SHOW_SOFT_DELETES if deleted else 0) self._sourcekey = None self._mapiobj = None if _check_mapiobj: # raise error for specific key self.mapiobj self._cache = cache self._iter = None
def calendar(self): """The store (default) :class:`calendar <Folder>`.""" try: return _folder.Folder(self,_benc(HrGetOneProp( self._root, PR_IPM_APPOINTMENT_ENTRYID).Value)) except (MAPIErrorNotFound, NotFoundError): pass
def __init__(self, parent_mapiobj, mapiobj): self._parent_mapiobj = parent_mapiobj self.proptag = mapiobj.ulPropTag if PROP_TYPE( mapiobj.ulPropTag ) == PT_ERROR and mapiobj.Value == MAPI_E_NOT_ENOUGH_MEMORY: if PROP_ID( self.proptag) == PROP_ID(PR_BODY_W): # avoid slow guessing self.proptag = PR_BODY_W mapiobj = SPropDelayedValue(parent_mapiobj, self.proptag) elif PROP_ID(self.proptag) in (PROP_ID(PR_RTF_COMPRESSED), PROP_ID(PR_HTML)): self.proptag = CHANGE_PROP_TYPE(self.proptag, PT_BINARY) mapiobj = SPropDelayedValue(parent_mapiobj, self.proptag) else: # XXX possible to use above trick to infer all proptags? for proptype in (PT_BINARY, PT_UNICODE): # XXX slow, incomplete? proptag = (mapiobj.ulPropTag & 0xffff0000) | proptype try: HrGetOneProp( parent_mapiobj, proptag ) # XXX: Unicode issue?? calls GetProps([proptag], 0) self.proptag = proptag # XXX isn't it strange we can get here except MAPIErrorNotEnoughMemory: mapiobj = SPropDelayedValue(parent_mapiobj, proptag) self.proptag = proptag break except MAPIErrorNotFound: pass self.mapiobj = mapiobj self._value = None self.__lpname = None
def outbox(self): """The store :class:`outbox <Folder>`.""" try: return _folder.Folder(self, _benc(HrGetOneProp(self.mapiobj, PR_IPM_OUTBOX_ENTRYID).Value)) except (MAPIErrorNotFound, NotFoundError): pass
def root(self): """The user-invisible store root :class:`Folder`.""" try: return _folder.Folder( self, _benc(HrGetOneProp(self._root, PR_ENTRYID).Value)) except (MAPIErrorNotFound, NotFoundError): pass
def contacts(self): """The store (default) :class:`contacts folder <Folder>`.""" try: return _folder.Folder(self, _benc(HrGetOneProp(self._root, PR_IPM_CONTACT_ENTRYID).Value)) except (MAPIErrorNotFound, NotFoundError): pass
def wastebasket(self): """The store :class:`wastebasket <Folder>`.""" try: return _folder.Folder(self, _benc(HrGetOneProp( self.mapiobj, PR_IPM_WASTEBASKET_ENTRYID).Value)) except (MAPIErrorNotFound, NotFoundError): pass
def sentmail(self): """The store :class:`sentmail folder <Folder>`.""" try: return _folder.Folder(self, _benc(HrGetOneProp( self.mapiobj, PR_IPM_SENTMAIL_ENTRYID).Value)) except (MAPIErrorNotFound, NotFoundError): pass
def archive_folder(self): """ Archive :class:`Folder` """ ids = self.mapiobj.GetIDsFromNames(NAMED_PROPS_ARCHIVER, 0) # XXX merge namedprops stuff PROP_STORE_ENTRYIDS = CHANGE_PROP_TYPE(ids[0], PT_MV_BINARY) PROP_ITEM_ENTRYIDS = CHANGE_PROP_TYPE(ids[1], PT_MV_BINARY) try: # support for multiple archives was a mistake, and is not and _should not_ be used. so we just pick nr 0. arch_storeid = HrGetOneProp(self.mapiobj, PROP_STORE_ENTRYIDS).Value[0] arch_folderid = HrGetOneProp(self.mapiobj, PROP_ITEM_ENTRYIDS).Value[0] except MAPIErrorNotFound: return archive_store = self.server._store2(arch_storeid) return _store.Store(mapiobj=archive_store, server=self.server).folder(entryid=_benc(arch_folderid))
def company(self): """:class:`Company` the user belongs to.""" try: return _company.Company(HrGetOneProp(self.mapiobj, PR_EC_COMPANY_NAME_W).Value, self.server) except MAPIErrorNoSupport: return _company.Company('Default', self.server)
def folder(self): """ Parent :class:`Folder` of an item """ if self._folder: return self._folder try: return _folder.Folder(self.store, _benc(HrGetOneProp(self.mapiobj, PR_PARENT_ENTRYID).Value)) except (MAPIErrorNotFound, NotFoundError): pass