Codeunit 5321 Exchange Web Services Server, source in 29
Source29
src/Layers/W1/BaseApp/System/Exchange/ExchangeWebServicesServer.Codeunit.al375 lines, Copyright (c) Microsoft Corporation. MIT
namespace System.Integration;
#if not CLEAN28
using Microsoft.CRM.Outlook;
#endif
using System;
using System.Azure.Identity;
codeunit 5321 "Exchange Web Services Server"
{
trigger OnRun()
begin
end;
var
Service: DotNet ExchangeServiceWrapper;
ProdEndpointTxt: Label 'https://outlook.office365.com/EWS/Exchange.asmx', Locked = true;
CategoryTxt: Label 'AL EWS Server', Locked = true;
#if not CLEAN28
ExpiredTokenErr: Label 'Trying to reconnect. Please close and reopen the add-in. Error from the service: %1', Comment = '%1 = the error message from the service.';
ExpiredTokenTxt: Label 'Expired token.', Locked = true;
#endif
InvalidCredentialsTxt: Label 'Invalid credentials.', Locked = true;
InitializedTxt: Label 'Service has been initialized.', Locked = true;
NotInitializedTxt: Label 'Service has not been initialized.', Locked = true;
ServiceLastErrorTxt: Label 'Service last error: %1.', Locked = true;
local procedure InitializeForVersion(AutodiscoveryEmail: Text[250]; var ServiceUri: Text; Credentials: DotNet ExchangeCredentials; Rediscover: Boolean; ExchangeVersion: DotNet ExchangeVersion; Impersonation: Boolean) Result: Boolean
var
ServiceFactory: DotNet ServiceWrapperFactory;
begin
if IsNull(Service) then
Service := ServiceFactory.CreateServiceWrapperForVersion(ExchangeVersion);
if (ServiceUri = '') and not Rediscover then
ServiceUri := GetEndpoint();
Service.ExchangeServiceUrl := ServiceUri;
if not IsNull(Credentials) then begin
Service.SetNetworkCredential(Credentials);
if Impersonation then
SetImpersonatedIdentity(AutodiscoveryEmail);
end;
if (Service.ExchangeServiceUrl = '') or Rediscover then begin
Result := Service.AutodiscoverServiceUrl(AutodiscoveryEmail);
ServiceUri := Service.ExchangeServiceUrl;
end else
Result := true;
if Result then
Session.LogMessage('0000D8V', InitializedTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', CategoryTxt)
else begin
Session.LogMessage('0000D8W', NotInitializedTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', CategoryTxt);
LogLastError();
end;
end;
[Scope('OnPrem')]
procedure Initialize(AutodiscoveryEmail: Text[250]; ServiceUri: Text; Credentials: DotNet ExchangeCredentials; Rediscover: Boolean) Result: Boolean
var
ExchangeVersion: DotNet ExchangeVersion;
begin
Result := InitializeForVersion(AutodiscoveryEmail, ServiceUri, Credentials, Rediscover, ExchangeVersion.Exchange2013, false);
end;
[Scope('OnPrem')]
procedure InitializeAndValidate(AutodiscoveryEmail: Text[250]; var ServiceUri: Text; Credentials: DotNet ExchangeCredentials) Result: Boolean
var
ExchangeVersion: DotNet ExchangeVersion;
begin
if ServiceUri = '' then
ServiceUri := GetEndpoint();
if InitializeForVersion(AutodiscoveryEmail, ServiceUri, Credentials, false, ExchangeVersion.Exchange2013, false) then begin
Result := ValidCredentials();
// If the email address was not found in the exchange server (404 error) then attempt to discover the exchange service endpoint.
if not Result and (StrPos(GetLastErrorText, '404') > 0) then
if InitializeForVersion(AutodiscoveryEmail, ServiceUri, Credentials, true, ExchangeVersion.Exchange2013, false) and
ValidCredentials()
then begin
Result := true;
ServiceUri := Service.ExchangeServiceUrl;
end;
end;
end;
[Scope('OnPrem')]
procedure Initialize2010(AutodiscoveryEmail: Text[250]; ServiceUri: Text; Credentials: DotNet ExchangeCredentials; Rediscover: Boolean) Result: Boolean
var
ExchangeVersion: DotNet ExchangeVersion;
begin
Result := InitializeForVersion(AutodiscoveryEmail, ServiceUri, Credentials, Rediscover, ExchangeVersion.Exchange2010, false);
end;
[Scope('OnPrem')]
procedure Initialize2010WithUserImpersonation(AutodiscoveryEmail: Text[250]; ServiceUri: Text; Credentials: DotNet ExchangeCredentials; Rediscover: Boolean) Result: Boolean
var
ExchangeVersion: DotNet ExchangeVersion;
begin
Result := InitializeForVersion(AutodiscoveryEmail, ServiceUri, Credentials, Rediscover, ExchangeVersion.Exchange2010, true);
end;
[Scope('OnPrem')]
procedure InitializeWithCertificate(ApplicationID: Guid; Thumbprint: Text[40]; AuthenticationEndpoint: Text[250]; ExchangeEndpoint: Text[250]; ResourceUri: Text[250])
var
ServiceFactory: DotNet ServiceWrapperFactory;
begin
Service := ServiceFactory.CreateServiceWrapperWithCertificate(ApplicationID, Thumbprint, AuthenticationEndpoint, ResourceUri);
Service.ExchangeServiceUrl := ExchangeEndpoint;
end;
[Scope('OnPrem')]
procedure InitializeWithOAuthToken(Token: SecretText; ExchangeEndpoint: Text)
var
AzureADMgt: Codeunit "Azure AD Mgt.";
begin
if ExchangeEndpoint = '' then
ExchangeEndpoint := GetEndpoint();
AzureADMgt.CreateExchangeServiceWrapperWithToken(Token, Service);
Service.ExchangeServiceUrl := ExchangeEndpoint;
end;
[TryFunction]
[Scope('OnPrem')]
procedure ValidCredentials()
var
AzureADAuthFlow: Codeunit "Azure AD Auth Flow";
begin
if AzureADAuthFlow.CanHandle() then
if not Service.ValidateCredentials() then begin
Session.LogMessage('0000D8X', InvalidCredentialsTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', CategoryTxt);
LogLastError();
Error('');
end;
end;
[Scope('OnPrem')]
procedure SetImpersonatedIdentity(Email: Text[250])
begin
Service.SetImpersonatedIdentity(Email);
end;
[Scope('OnPrem')]
procedure InstallApp(ManifestPath: InStream)
begin
Service.InstallApp(ManifestPath);
end;
[Scope('OnPrem')]
procedure CreateAppointment(var Appointment: DotNet IAppointment)
begin
Appointment := Service.CreateAppointment();
end;
[TryFunction]
[Scope('OnPrem')]
procedure SendEmailMessageWithAttachment(Subject: Text; RecipientAddress: Text; BodyHTML: Text; AttachmentPath: Text; SenderAddress: Text)
begin
OnBeforeSendEmailMessageWithAttachment(Subject, RecipientAddress, BodyHTML, SenderAddress);
Service.SendMessageAndSaveToSentItems(Subject, RecipientAddress, BodyHTML, AttachmentPath, SenderAddress, '');
end;
[Scope('OnPrem')]
procedure SaveEmailToInbox(EmailMessage: Text)
begin
Service.SaveEmlMessageToInbox(EmailMessage);
end;
[Scope('OnPrem')]
procedure SaveHTMLEmailToInbox(EmailSubject: Text; EmailBodyHTML: Text; SenderAddress: Text; SenderName: Text; RecipientAddress: Text)
begin
OnBeforeSaveHTMLEmailToInbox(EmailSubject, EmailBodyHTML, SenderAddress, SenderName, RecipientAddress);
Service.SaveHtmlMessageToInbox(EmailSubject, EmailBodyHTML, SenderAddress, SenderName, RecipientAddress);
end;
[Scope('OnPrem')]
procedure GetEmailFolder(FolderId: Text; var Folder: DotNet IEmailFolder): Boolean
begin
Folder := Service.GetEmailFolder(FolderId);
exit(not IsNull(Folder));
end;
[Scope('OnPrem')]
procedure IdenticalMailExists(SampleMessage: DotNet IEmailMessage; TargetFolder: DotNet IEmailFolder; var TargetMessage: DotNet IEmailMessage): Boolean
var
FindResults: DotNet IFindEmailsResults;
Enumerator: DotNet IEnumerator;
FolderOffset: Integer;
begin
TargetFolder.UseSampleEmailAsFilter(SampleMessage);
FolderOffset := 0;
repeat
FindResults := TargetFolder.FindEmailMessages(50, FolderOffset);
if FindResults.TotalCount > 0 then begin
Enumerator := FindResults.GetEnumerator();
while Enumerator.MoveNext() do begin
TargetMessage := Enumerator.Current;
if SampleMessage.Subject = TargetMessage.Subject then
if SampleMessage.Body = TargetMessage.Body then
if CompareEmailAttachments(SampleMessage, TargetMessage) then
exit(true);
end;
FolderOffset := FindResults.NextPageOffset;
end;
until not FindResults.MoreAvailable;
exit(false);
end;
[Scope('OnPrem')]
procedure CompareEmailAttachments(LeftMsg: DotNet IEmailMessage; RightMsg: DotNet IEmailMessage): Boolean
var
LeftEnum: DotNet IEnumerator;
RightEnum: DotNet IEnumerator;
LeftAttrib: DotNet IAttachment;
RightAttrib: DotNet IAttachment;
LeftFlag: Boolean;
RightFlag: Boolean;
begin
LeftEnum := LeftMsg.Attachments.GetEnumerator();
RightEnum := RightMsg.Attachments.GetEnumerator();
LeftFlag := LeftEnum.MoveNext();
RightFlag := RightEnum.MoveNext();
while LeftFlag and RightFlag do begin
LeftAttrib := LeftEnum.Current;
RightAttrib := RightEnum.Current;
if (LeftAttrib.ContentId <> RightAttrib.ContentId) or (LeftAttrib.ContentType <> RightAttrib.ContentType) then
exit(false);
LeftFlag := LeftEnum.MoveNext();
RightFlag := RightEnum.MoveNext();
end;
exit(LeftFlag = RightFlag);
end;
#if not CLEAN28
[Obsolete('Please use Microsoft Graph API instead e.g `Email - Outlook REST API`', '28.0')]
[TryFunction]
local procedure TryGetEmailWithAttachments(var EmailMessage: DotNet IEmailMessage; ItemID: Text[250])
begin
EmailMessage := Service.GetEmailWithAttachments(ItemID);
end;
[Obsolete('Please use Microsoft Graph API instead e.g `Email - Outlook REST API`', '28.0')]
[Scope('OnPrem')]
procedure GetEmailAndAttachments(ItemID: Text[250]; var TempExchangeObject: Record "Exchange Object" temporary; "Action": Option InitiateSendToOCR,InitiateSendToIncomingDocuments,InitiateSendToWorkFlow,IntiateSendToAttachments; RecRef: RecordRef)
var
EmailMessage: DotNet IEmailMessage;
Attachments: DotNet IEnumerable;
Attachment: DotNet IAttachment;
begin
if TryGetEmailWithAttachments(EmailMessage, ItemID) then begin
if not IsNull(EmailMessage) then begin
TempExchangeObject.Init();
TempExchangeObject.Validate("Item ID", EmailMessage.Id);
TempExchangeObject.Validate(Type, TempExchangeObject.Type::Email);
TempExchangeObject.Validate(Name, EmailMessage.Subject);
TempExchangeObject.Validate(Owner, UserSecurityId());
TempExchangeObject.SetBody(EmailMessage.TextBody);
TempExchangeObject.SetContent(EmailMessage.Content);
TempExchangeObject.SetViewLink(EmailMessage.LinkUrl);
if not TempExchangeObject.Insert(true) then
TempExchangeObject.Modify(true);
Attachments := EmailMessage.Attachments;
foreach Attachment in Attachments do begin
TempExchangeObject.Init();
TempExchangeObject.Validate(Type, TempExchangeObject.Type::Attachment);
TempExchangeObject.Validate("Item ID", Attachment.Id);
TempExchangeObject.Validate(Name, Attachment.Name);
TempExchangeObject.Validate("Parent ID", EmailMessage.Id);
TempExchangeObject.Validate("Content Type", Attachment.ContentType);
TempExchangeObject.Validate(InitiatedAction, Action);
TempExchangeObject.Validate(IsInline, Attachment.IsInline);
TempExchangeObject.Validate(RecId, RecRef.RecordId());
TempExchangeObject.SetContent(Attachment.Content);
if not TempExchangeObject.Insert(true) then
TempExchangeObject.Modify(true);
end;
end else begin
Session.LogMessage('0000D8Y', ExpiredTokenTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', CategoryTxt);
Error(ExpiredTokenErr, Service.LastError);
end;
end else begin
Session.LogMessage('0000D8Z', ExpiredTokenTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', CategoryTxt);
Error(ExpiredTokenErr, Service.LastError);
end;
end;
[Obsolete('Please use Microsoft Graph API instead e.g `Email - Outlook REST API`', '28.0')]
[Scope('OnPrem')]
procedure GetEmailBody(ItemID: Text[250]) EmailBody: Text
var
EmailMessage: DotNet IEmailMessage;
begin
if TryGetEmailWithAttachments(EmailMessage, ItemID) and not IsNull(EmailMessage) then
EmailBody := EmailMessage.TextBody
else begin
Session.LogMessage('0000D90', ExpiredTokenTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', CategoryTxt);
Error(ExpiredTokenErr, Service.LastError);
end;
end;
[Obsolete('Please use Microsoft Graph API instead e.g `Email - Outlook REST API`', '28.0')]
[TryFunction]
local procedure TryEmailHasAttachments(var HasAttachments: Boolean; ItemID: Text[250])
begin
HasAttachments := Service.AttachmentsExists(ItemID);
end;
[Obsolete('Please use Microsoft Graph API instead e.g `Email - Outlook REST API`', '28.0')]
[Scope('OnPrem')]
procedure EmailHasAttachments(ItemID: Text[250]): Boolean
var
HasAttachments: Boolean;
begin
if not TryEmailHasAttachments(HasAttachments, ItemID) then
exit(false);
exit(HasAttachments)
end;
#endif
procedure GetEndpoint() Endpoint: Text
begin
Endpoint := ProdEndpoint();
end;
procedure ProdEndpoint() ProdEndpointText: Text
begin
ProdEndpointText := ProdEndpointTxt;
OnAfterGetProdEndpoint(ProdEndpointText);
end;
procedure IsDefaultProdEndpoint(Endpoint: Text): Boolean
begin
exit(Endpoint = ProdEndpointTxt);
end;
[Scope('OnPrem')]
procedure GetCurrentUserTimeZone(): Text
begin
exit(Service.GetExchangeUserTimeZone());
end;
local procedure LogLastError()
var
LastError: Text;
begin
LastError := Service.LastError();
if LastError <> '' then
Session.LogMessage('0000F63', StrSubstNo(ServiceLastErrorTxt, LastError), Verbosity::Warning, DataClassification::CustomerContent, TelemetryScope::ExtensionPublisher, 'Category', CategoryTxt);
end;
[IntegrationEvent(false, false)]
local procedure OnBeforeSaveHTMLEmailToInbox(var EmailSubject: Text; var EmailBodyHTML: Text; var SenderAddress: Text; var SenderName: Text; var RecipientAddress: Text)
begin
end;
[IntegrationEvent(false, false)]
local procedure OnBeforeSendEmailMessageWithAttachment(var Subject: Text; var RecipientAddress: Text; var BodyHTML: Text; var SenderAddress: Text)
begin
end;
[IntegrationEvent(false, false)]
local procedure OnAfterGetProdEndpoint(var ProdEndPointText: Text)
begin
end;
}