Skip to content

Commit

Permalink
Merge pull request #96 from nylas/reverting-commits-back-between-11/7…
Browse files Browse the repository at this point in the history
…/2022-and-4/1/2023-2

Revert between 11/7/2022 4/1/2023
  • Loading branch information
pengfeiye authored Apr 18, 2023
2 parents 0a6d82b + bfc6857 commit 49d1ff9
Show file tree
Hide file tree
Showing 6 changed files with 23 additions and 115 deletions.
15 changes: 11 additions & 4 deletions exchangelib/items.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,9 +288,11 @@ def _create(self, message_disposition, send_meeting_invitations):
res = self.account.bulk_create(
items=[self], folder=self.folder, message_disposition=message_disposition,
send_meeting_invitations=send_meeting_invitations)
if not res:
if message_disposition in (SEND_ONLY, SEND_AND_SAVE_COPY):
if res:
raise ValueError('Got a response in non-save mode')
return None
if len(res) > 1:
if len(res) != 1:
raise ValueError('Expected result length 1, but got %s' % res)
if isinstance(res[0], Exception):
raise res[0]
Expand Down Expand Up @@ -711,7 +713,10 @@ def send(self, save_copy=True, copy_to_folder=None, conflict_resolution=AUTO_RES
send_meeting_invitations=send_meeting_invitations)
return None

return self._create(message_disposition=SEND_ONLY, send_meeting_invitations=send_meeting_invitations)
res = self._create(message_disposition=SEND_ONLY, send_meeting_invitations=send_meeting_invitations)
if res:
raise ValueError('Unexpected response in send-only mode')
return None

def send_and_save(self, update_fields=None, conflict_resolution=AUTO_RESOLVE,
send_meeting_invitations=SEND_TO_NONE):
Expand All @@ -732,10 +737,12 @@ def send_and_save(self, update_fields=None, conflict_resolution=AUTO_RESOLVE,
self.send(save_copy=False, conflict_resolution=conflict_resolution,
send_meeting_invitations=send_meeting_invitations)
else:
return self._create(
res = self._create(
message_disposition=SEND_AND_SAVE_COPY,
send_meeting_invitations=send_meeting_invitations
)
if res:
raise ValueError('Unexpected response in send-only mode')

def reply(self, subject, body, to_recipients=None, cc_recipients=None, bcc_recipients=None):
if not self.account:
Expand Down
58 changes: 0 additions & 58 deletions exchangelib/properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,6 @@

log = logging.getLogger(__name__)

ENTRY_ID = "EntryId" # The base64-encoded PR_ENTRYID property
EWS_ID = "EwsId" # The EWS format used in Exchange 2007 SP1 and later
EWS_LEGACY_ID = "EwsLegacyId" # The EWS format used in Exchange 2007 before SP1
HEX_ENTRY_ID = "HexEntryId" # The hexadecimal representation of the PR_ENTRYID property
OWA_ID = "OwaId" # The OWA format for Exchange 2007 and 2010
STORE_ID = "StoreId" # The Exchange Store format
# IdFormat enum
ID_FORMATS = (ENTRY_ID, EWS_ID, EWS_LEGACY_ID, HEX_ENTRY_ID, OWA_ID, STORE_ID)


class Body(text_type):
# Helper to mark the 'body' field as a complex attribute.
Expand Down Expand Up @@ -771,52 +762,3 @@ class FailedMailbox(EWSElement):
class SyncState(EWSElement):
ELEMENT_NAME = 'SyncState'
NAMESPACE = MNS


class AlternateId(EWSElement):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/alternateid"""

ELEMENT_NAME = "AlternateId"

FIELDS = [
CharField("id", field_uri="Id", is_required=True, is_attribute=True),
ChoiceField(
"format", field_uri="Format", is_required=True, is_attribute=True, choices={Choice(c) for c in ID_FORMATS}
),
EmailAddressField("mailbox", field_uri="Mailbox", is_required=True, is_attribute=True),
BooleanField("is_archive", field_uri="IsArchive", is_required=False, is_attribute=True),
]

@classmethod
def response_tag(cls):
# This element is in TNS in the request and MNS in the response...
return "{{{}}}{}".format(MNS, cls.ELEMENT_NAME)


class AlternatePublicFolderId(EWSElement):
"""MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/alternatepublicfolderid"""

ELEMENT_NAME = "AlternatePublicFolderId"

FIELDS = [
CharField("folder_id", field_uri="FolderId", is_required=True, is_attribute=True),
ChoiceField(
"format", field_uri="Format", is_required=True, is_attribute=True, choices={Choice(c) for c in ID_FORMATS}
)
]


class AlternatePublicFolderItemId(EWSElement):
"""MSDN:
https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/alternatepublicfolderitemid
"""

ELEMENT_NAME = "AlternatePublicFolderItemId"

FIELDS = [
CharField("folder_id", field_uri="FolderId", is_required=True, is_attribute=True),
ChoiceField(
"format", field_uri="Format", is_required=True, is_attribute=True, choices={Choice(c) for c in ID_FORMATS}
),
CharField("item_id", field_uri="ItemId", is_required=True, is_attribute=True),
]
3 changes: 2 additions & 1 deletion exchangelib/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,8 @@ def __init__(self, *args, **kwargs):
# Autodetect authentication type if necessary
# pylint: disable=access-member-before-definition
if self.auth_type is None:
self.auth_type = get_service_authtype(service_endpoint=self.service_endpoint, versions=API_VERSIONS)
self.auth_type = get_service_authtype(service_endpoint=self.service_endpoint, versions=API_VERSIONS,
name=self.credentials.username)

# Try to behave nicely with the Exchange server. We want to keep the connection open between requests.
# We also want to re-use sessions, to avoid the NTLM auth handshake on every request.
Expand Down
43 changes: 0 additions & 43 deletions exchangelib/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -1980,49 +1980,6 @@ def _get_soap_payload(cls, soap_response):
return [response]


class ConvertId(EWSService):
"""Take a list of IDs to convert. Returns a list of converted IDs or exception instances, in the same order as the
input list.
MSDN: https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/convertid-operation
"""

SERVICE_NAME = "ConvertId"

def call(self, items, destination_format):
from .properties import AlternateId, AlternatePublicFolderId, AlternatePublicFolderItemId
elements = self._get_elements(payload=self.get_payload(
items=items,
destination_format=destination_format,
))
cls_map = {cls.response_tag(): cls for cls in
(AlternateId, AlternatePublicFolderId, AlternatePublicFolderItemId)}
for elem in elements:
# Allow None here. Some services don't return an ID if the target folder is outside the mailbox.
if isinstance(elem, (Exception, type(None))):
yield elem
continue
yield cls_map[elem.tag].from_xml(elem, account=None)

def get_payload(self, items, destination_format):
payload = create_element("m:{}".format(self.SERVICE_NAME), DestinationFormat=destination_format)
item_ids = create_element("m:SourceIds")
for item in items:
set_xml_value(item_ids, item, version=None)
payload.append(item_ids)
return payload

@classmethod
def _get_elements_in_container(cls, container):
# We may have other elements in here, e.g. 'ResponseCode'. Filter away those.
from .properties import AlternateId, AlternatePublicFolderId, AlternatePublicFolderItemId
return [
container.findall(AlternateId.response_tag())
+ container.findall(AlternatePublicFolderId.response_tag())
+ container.findall(AlternatePublicFolderItemId.response_tag())
]


class ResolveNames(EWSService):
"""
MSDN: https://msdn.microsoft.com/en-us/library/office/aa565329(v=exchg.150).aspx
Expand Down
18 changes: 10 additions & 8 deletions exchangelib/transport.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ def get_autodiscover_authtype(service_endpoint, data):
return _get_auth_method_from_response(response=r)


def get_service_authtype(service_endpoint, versions):
def get_service_authtype(service_endpoint, versions, name):
# Get auth type by tasting headers from the server. Only do POST requests. HEAD is too error prone, and some servers
# are set up to redirect to OWA on all requests except POST to /EWS/Exchange.asmx
log.debug('Getting service auth type for %s', service_endpoint)
Expand All @@ -124,7 +124,7 @@ def get_service_authtype(service_endpoint, versions):
from .protocol import BaseProtocol
with BaseProtocol.raw_session() as s:
for version in versions:
data = dummy_xml(version=version)
data = dummy_xml(version=version, name=name)
log.debug('Requesting %s from %s', data, service_endpoint)
r = s.post(url=service_endpoint, headers=DEFAULT_HEADERS.copy(), data=data, allow_redirects=True,
timeout=BaseProtocol.TIMEOUT)
Expand Down Expand Up @@ -197,11 +197,13 @@ def _tokenize(val):
return auth_tokens


def dummy_xml(version):
def dummy_xml(version, name):
# Generate a minimal, valid EWS request
from .properties import ENTRY_ID, EWS_ID, AlternateId
from .services import ConvertId
return wrap(content=ConvertId(protocol=None).get_payload(
items=[AlternateId(id="DUMMY", format=EWS_ID, mailbox="DUMMY")],
destination_format=ENTRY_ID,
from .services import ResolveNames # Avoid circular import
return wrap(content=ResolveNames(protocol=None).get_payload(
unresolved_entries=[name],
parent_folders=None,
return_full_contact_data=False,
search_scope=None,
contact_data_shape=None,
), version=version)
1 change: 0 additions & 1 deletion exchangelib/winzone.py
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,6 @@ def generate_map(timeout=10):
PYTZ_TO_MS_TIMEZONE_MAP = dict(CLDR_TO_MS_TIMEZONE_MAP, **{
'Asia/Kolkata': 'India Standard Time',
'Canada/Pacific': 'Pacific Standard Time',
'Canada/Eastern': 'Eastern Standard Time',
'EST': 'Eastern Standard Time (Mexico)',
'UTC': 'UTC',
'US/Arizona': 'US Mountain Standard Time',
Expand Down

0 comments on commit 49d1ff9

Please sign in to comment.