# General utilities for MAPI and MAPI objects. # We used to use these old names from the 'types' module... TupleType = tuple ListType = list IntType = int import pythoncom from pywintypes import TimeType from . import mapi, mapitags prTable = {} def GetPropTagName(pt): if not prTable: for name, value in mapitags.__dict__.items(): if name[:3] == "PR_": # Store both the full ID (including type) and just the ID. # This is so PR_FOO_A and PR_FOO_W are still differentiated, # but should we get a PT_FOO with PT_ERROR set, we fallback # to the ID. # String types should have 3 definitions in mapitags.py # PR_BODY = PROP_TAG( PT_TSTRING, 4096) # PR_BODY_W = PROP_TAG( PT_UNICODE, 4096) # PR_BODY_A = PROP_TAG( PT_STRING8, 4096) # The following change ensures a lookup using only the the # property id returns the conditional default. # PT_TSTRING is a conditional assignment for either PT_UNICODE or # PT_STRING8 and should not be returned during a lookup. if ( mapitags.PROP_TYPE(value) == mapitags.PT_UNICODE or mapitags.PROP_TYPE(value) == mapitags.PT_STRING8 ): if name[-2:] == "_A" or name[-2:] == "_W": prTable[value] = name else: prTable[mapitags.PROP_ID(value)] = name else: prTable[value] = name prTable[mapitags.PROP_ID(value)] = name try: try: return prTable[pt] except KeyError: # Can't find it exactly - see if the raw ID exists. return prTable[mapitags.PROP_ID(pt)] except KeyError: # god-damn bullshit hex() warnings: I don't see a way to get the # old behaviour without a warning!! ret = hex(int(pt)) # -0x8000000L -> 0x80000000 if ret[0] == "-": ret = ret[1:] if ret[-1] == "L": ret = ret[:-1] return ret mapiErrorTable = {} def GetScodeString(hr): if not mapiErrorTable: for name, value in mapi.__dict__.items(): if name[:7] in ["MAPI_E_", "MAPI_W_"]: mapiErrorTable[value] = name return mapiErrorTable.get(hr, pythoncom.GetScodeString(hr)) ptTable = {} def GetMapiTypeName(propType, rawType=True): """Given a mapi type flag, return a string description of the type""" if not ptTable: for name, value in mapitags.__dict__.items(): if name[:3] == "PT_": # PT_TSTRING is a conditional assignment # for either PT_UNICODE or PT_STRING8 and # should not be returned during a lookup. if name in ["PT_TSTRING", "PT_MV_TSTRING"]: continue ptTable[value] = name if rawType: propType = propType & ~mapitags.MV_FLAG return ptTable.get(propType, str(hex(propType))) def GetProperties(obj, propList): """Given a MAPI object and a list of properties, return a list of property values. Allows a single property to be passed, and the result is a single object. Each request property can be an integer or a string. Of a string, it is automatically converted to an integer via the GetIdsFromNames function. If the property fetch fails, the result is None. """ bRetList = 1 if type(propList) not in [TupleType, ListType]: bRetList = 0 propList = (propList,) realPropList = [] rc = [] for prop in propList: if type(prop) != IntType: # Integer props = ((mapi.PS_PUBLIC_STRINGS, prop),) propIds = obj.GetIDsFromNames(props, 0) prop = mapitags.PROP_TAG( mapitags.PT_UNSPECIFIED, mapitags.PROP_ID(propIds[0]) ) realPropList.append(prop) hr, data = obj.GetProps(realPropList, 0) if hr != 0: data = None return None if bRetList: return [v[1] for v in data] else: return data[0][1] def GetAllProperties(obj, make_tag_names=True): tags = obj.GetPropList(0) hr, data = obj.GetProps(tags) ret = [] for tag, val in data: if make_tag_names: hr, tags, array = obj.GetNamesFromIDs((tag,)) if type(array[0][1]) == type(""): name = array[0][1] else: name = GetPropTagName(tag) else: name = tag ret.append((name, val)) return ret _MapiTypeMap = { type(0.0): mapitags.PT_DOUBLE, type(0): mapitags.PT_I4, type("".encode("ascii")): mapitags.PT_STRING8, # bytes type(""): mapitags.PT_UNICODE, # str type(None): mapitags.PT_UNSPECIFIED, # In Python 2.2.2, bool isn't a distinct type (type(1==1) is type(0)). # (markh thinks the above is trying to say that in 2020, we probably *do* # want bool in this map? :) } def SetPropertyValue(obj, prop, val): if type(prop) != IntType: props = ((mapi.PS_PUBLIC_STRINGS, prop),) propIds = obj.GetIDsFromNames(props, mapi.MAPI_CREATE) if val == (1 == 1) or val == (1 == 0): type_tag = mapitags.PT_BOOLEAN else: type_tag = _MapiTypeMap.get(type(val)) if type_tag is None: raise ValueError( "Don't know what to do with '%r' ('%s')" % (val, type(val)) ) prop = mapitags.PROP_TAG(type_tag, mapitags.PROP_ID(propIds[0])) if val is None: # Delete the property obj.DeleteProps((prop,)) else: obj.SetProps(((prop, val),)) def SetProperties(msg, propDict): """Given a Python dictionary, set the objects properties. If the dictionary key is a string, then a property ID is queried otherwise the ID is assumed native. Coded for maximum efficiency wrt server calls - ie, maximum of 2 calls made to the object, regardless of the dictionary contents (only 1 if dictionary full of int keys) """ newProps = [] # First pass over the properties we should get IDs for. for key, val in propDict.items(): if type(key) == str: newProps.append((mapi.PS_PUBLIC_STRINGS, key)) # Query for the new IDs if newProps: newIds = msg.GetIDsFromNames(newProps, mapi.MAPI_CREATE) newIdNo = 0 newProps = [] for key, val in propDict.items(): if type(key) == str: type_val = type(val) if type_val == str: tagType = mapitags.PT_UNICODE elif type_val == IntType: tagType = mapitags.PT_I4 elif type_val == TimeType: tagType = mapitags.PT_SYSTIME else: raise ValueError( "The type of object %s(%s) can not be written" % (repr(val), type_val) ) key = mapitags.PROP_TAG(tagType, mapitags.PROP_ID(newIds[newIdNo])) newIdNo = newIdNo + 1 newProps.append((key, val)) msg.SetProps(newProps)