Codeunit 5320 Exchange Web Services Client, source in 29
Source29
src/Layers/W1/BaseApp/System/Exchange/ExchangeWebServicesClient.Codeunit.al383 lines, Copyright (c) Microsoft Corporation. MIT
namespace System.Integration;
using Microsoft.CRM.Outlook;
using System;
using System.Environment;
codeunit 5320 "Exchange Web Services Client"
{
trigger OnRun()
begin
end;
var
TempExchangeFolder: Record "Exchange Folder" temporary;
[RunOnClient]
ServiceOnClient: DotNet ExchangeServiceWrapper;
ConnectionFailedErr: Label 'Connection to the Exchange server failed.';
LongPathsOmittedMsg: Label 'Folders with a path that exceeds 250 characters have been omitted.';
ServiceOnServer: DotNet ExchangeServiceWrapper;
LongPathsDetected: Boolean;
CategoryTxt: Label 'AL EWS Client', Locked = true;
FolderFoundOnServerTxt: Label 'Folder has been found on server.', Locked = true;
FolderFoundOnClientTxt: Label 'Folder has been found on client.', Locked = true;
FolderNotFoundOnServerTxt: Label 'Folder has not been found on server.', Locked = true;
FolderNotFoundOnClientTxt: Label 'Folder has not been found on client.', Locked = true;
InitializedOnServerWithImpersonationTxt: Label 'Service has been initialized on server with impersonation.', Locked = true;
InitializedOnServerTxt: Label 'Service has been initialized on server.', Locked = true;
InitializedOnClientTxt: Label 'Service has been initialized on client.', Locked = true;
NotInitializedOnServerWithImpersonationTxt: Label 'Service has not been initialized on server with impersonation.', Locked = true;
NotInitializedOnServerTxt: Label 'Service has not been initialized on server.', Locked = true;
NotInitializedOnClientTxt: Label 'Service has not been initialized on client.', Locked = true;
ServiceInvalidatedTxt: Label 'Service has been invlidated.', Locked = true;
ConnectionFailedTxt: Label 'Connection to the Exchange server failed.', Locked = true;
InvalidCredentialsOnClientTxt: Label 'Invalid credentials on client.', Locked = true;
InvalidCredentialsOnServerTxt: Label 'Invalid credentials on server.', Locked = true;
ValidCredentialsOnServerTxt: Label 'Credentials successfully validated on server.', Locked = true;
ValidCredentialsOnClientTxt: Label 'Credentials successfully validated on server.', Locked = true;
PublicFolderFoundOnClientTxt: Label 'Public folder has been found on client.', Locked = true;
PublicFolderFoundOnServerTxt: Label 'Public folder has been found on server.', Locked = true;
PublicFolderNotFoundTxt: Label 'Public folder has not been found.', Locked = true;
PublicFolderCachedTxt: Label 'Public folder is cached.', Locked = true;
ServiceOnServerLastErrorTxt: Label 'Service on server last error: %1.', Locked = true;
ServiceOnClientLastErrorTxt: Label 'Service on client last error: %1.', Locked = true;
[Scope('OnPrem')]
procedure GetPublicFolders(var ExchangeFolder: Record "Exchange Folder"): Boolean
begin
if not IsServiceValid() then begin
Session.LogMessage('0000D87', ConnectionFailedTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', CategoryTxt);
Error(ConnectionFailedErr);
end;
if IsNull(ServiceOnServer) then begin
if GetPublicFoldersOnClient(ExchangeFolder) then begin
Session.LogMessage('0000D88', PublicFolderFoundOnClientTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', CategoryTxt);
exit(true);
end;
Session.LogMessage('0000DA2', PublicFolderNotFoundTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', CategoryTxt);
exit(false);
end;
if GetPublicFoldersOnServer(ExchangeFolder) then begin
Session.LogMessage('0000D89', PublicFolderFoundOnServerTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', CategoryTxt);
exit(true);
end;
Session.LogMessage('0000D8A', PublicFolderNotFoundTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', CategoryTxt);
exit(false);
end;
local procedure GetPublicFoldersOnClient(var ExchangeFolder: Record "Exchange Folder") FoundAny: Boolean
var
[RunOnClient]
ParentInfo: DotNet FolderInfo;
[RunOnClient]
SubFolders: DotNet FolderInfoEnumerator;
begin
if ExchangeFolder.Cached then begin
Session.LogMessage('0000D8B', PublicFolderCachedTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', CategoryTxt);
exit(false);
end;
if ExchangeFolder."Unique ID".HasValue() then begin
ParentInfo := ParentInfo.FolderInfo(ExchangeFolder.GetUniqueID(), ExchangeFolder.FullPath);
ExchangeFolder.Cached := true;
ExchangeFolder.Modify();
end;
FoundAny := false;
LongPathsDetected := false;
SubFolders := ServiceOnClient.GetPublicFolders(ParentInfo, 1000);
if not IsNull(SubFolders) then begin
while SubFolders.MoveNextPage() do
while SubFolders.MoveNext() do
if StrLen(SubFolders.Current.FullPath) > 250 then
LongPathsDetected := true
else
if not TempExchangeFolder.Get(CopyStr(SubFolders.Current.FullPath, 1, 250)) then
if IsAllowedFolderType(SubFolders.Current.FolderClass) then begin
FoundAny := true;
TempExchangeFolder.Init();
TempExchangeFolder.FullPath := SubFolders.Current.FullPath;
TempExchangeFolder.Depth := SubFolders.Current.Depth;
TempExchangeFolder.SetUniqueID(SubFolders.Current.UniqueId);
TempExchangeFolder.Name := SubFolders.Current.Name;
TempExchangeFolder.Cached := false;
TempExchangeFolder.Insert();
end;
if LongPathsDetected then
Message(LongPathsOmittedMsg);
ReadBuffer(ExchangeFolder);
end;
if ServiceOnClient.LastError <> '' then begin
LogLastClientError();
Message(ServiceOnClient.LastError);
end;
exit(FoundAny);
end;
local procedure GetPublicFoldersOnServer(var ExchangeFolder: Record "Exchange Folder") FoundAny: Boolean
var
ParentInfo: DotNet FolderInfo;
SubFolders: DotNet FolderInfoEnumerator;
begin
if ExchangeFolder.Cached then begin
Session.LogMessage('0000D8D', PublicFolderCachedTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', CategoryTxt);
exit(false);
end;
if ExchangeFolder."Unique ID".HasValue() then begin
ParentInfo := ParentInfo.FolderInfo(ExchangeFolder.GetUniqueID(), ExchangeFolder.FullPath);
ExchangeFolder.Cached := true;
ExchangeFolder.Modify();
end;
FoundAny := false;
LongPathsDetected := false;
SubFolders := ServiceOnServer.GetPublicFolders(ParentInfo, 1000);
if not IsNull(SubFolders) then begin
while SubFolders.MoveNextPage() do
while SubFolders.MoveNext() do
if StrLen(SubFolders.Current.FullPath) > 250 then
LongPathsDetected := true
else
if not TempExchangeFolder.Get(CopyStr(SubFolders.Current.FullPath, 1, 250)) then
if IsAllowedFolderType(SubFolders.Current.FolderClass) then begin
FoundAny := true;
TempExchangeFolder.Init();
TempExchangeFolder.FullPath := SubFolders.Current.FullPath;
TempExchangeFolder.Depth := SubFolders.Current.Depth;
TempExchangeFolder.SetUniqueID(SubFolders.Current.UniqueId);
TempExchangeFolder.Name := SubFolders.Current.Name;
TempExchangeFolder.Cached := false;
TempExchangeFolder.Insert();
end;
if LongPathsDetected then
Message(LongPathsOmittedMsg);
ReadBuffer(ExchangeFolder);
end;
if ServiceOnServer.LastError <> '' then begin
LogLastServerError();
Message(ServiceOnServer.LastError);
end;
exit(FoundAny);
end;
[Scope('OnPrem')]
procedure InitializeOnClient(AutodiscoveryEmail: Text[250]; ServiceUri: Text): Boolean
var
ClientTypeManagement: Codeunit "Client Type Management";
[RunOnClient]
ServiceFactoryOnClient: DotNet ServiceWrapperFactory;
Initialized: Boolean;
begin
if ClientTypeManagement.GetCurrentClientType() <> CLIENTTYPE::Windows then
exit(false);
if IsNull(ServiceOnClient) then begin
InvalidateService();
ServiceOnClient := ServiceFactoryOnClient.CreateServiceWrapper();
end;
if ServiceUri <> '' then
ServiceOnClient.ExchangeServiceUrl := ServiceUri;
Initialized := ServiceOnClient.ExchangeServiceUrl <> '';
if not Initialized then
Initialized := ServiceOnClient.AutodiscoverServiceUrl(AutodiscoveryEmail);
if Initialized then
Session.LogMessage('0000D8F', InitializedOnClientTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', CategoryTxt)
else begin
Session.LogMessage('0000D8G', NotInitializedOnClientTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', CategoryTxt);
LogLastClientError();
end;
exit(Initialized);
end;
[Scope('OnPrem')]
procedure InitializeOnServer(AutodiscoveryEmail: Text[250]; ServiceUri: Text; Credentials: DotNet NetworkCredential): Boolean
var
ServiceFactoryOnServer: DotNet ServiceWrapperFactory;
Initialized: Boolean;
begin
if IsNull(ServiceOnServer) then begin
InvalidateService();
ServiceOnServer := ServiceFactoryOnServer.CreateServiceWrapperWithCredentials(Credentials);
end;
if ServiceUri <> '' then
ServiceOnServer.ExchangeServiceUrl := ServiceUri;
Initialized := ServiceOnServer.ExchangeServiceUrl <> '';
if not Initialized then
Initialized := ServiceOnServer.AutodiscoverServiceUrl(AutodiscoveryEmail);
if Initialized then
Session.LogMessage('0000D8H', InitializedOnServerTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', CategoryTxt)
else begin
Session.LogMessage('0000D8I', NotInitializedOnServerTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', CategoryTxt);
LogLastServerError();
end;
exit(Initialized);
end;
[Scope('OnPrem')]
procedure InitializeOnServerWithImpersonation(AutodiscoveryEmail: Text[250]; ServiceUri: Text; Credentials: DotNet OAuthCredentials): Boolean
var
ServiceFactoryOnServer: DotNet ServiceWrapperFactory;
Initialized: Boolean;
begin
if IsNull(ServiceOnServer) then begin
InvalidateService();
ServiceOnServer := ServiceFactoryOnServer.CreateServiceWrapper();
end;
ServiceOnServer.SetNetworkCredential(Credentials);
ServiceOnServer.SetImpersonatedIdentity(AutodiscoveryEmail);
if ServiceUri <> '' then
ServiceOnServer.ExchangeServiceUrl := ServiceUri;
Initialized := ServiceOnServer.ExchangeServiceUrl <> '';
if not Initialized then
Initialized := ServiceOnServer.AutodiscoverServiceUrl(AutodiscoveryEmail);
if Initialized then
Session.LogMessage('0000D8J', InitializedOnServerWithImpersonationTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', CategoryTxt)
else begin
Session.LogMessage('0000D8K', NotInitializedOnServerWithImpersonationTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', CategoryTxt);
LogLastServerError();
end;
exit(Initialized);
end;
[Scope('OnPrem')]
procedure FolderExists(UniqueID: Text): Boolean
var
Exists: Boolean;
begin
if not IsServiceValid() then begin
Session.LogMessage('0000D8L', ConnectionFailedTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', CategoryTxt);
Error(ConnectionFailedErr);
end;
if IsNull(ServiceOnServer) then begin
Exists := ServiceOnClient.FolderExists(UniqueID);
if Exists then
Session.LogMessage('0000D8M', FolderFoundOnClientTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', CategoryTxt)
else begin
Session.LogMessage('0000D8N', FolderNotFoundOnClientTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', CategoryTxt);
LogLastClientError();
end;
end else begin
Exists := ServiceOnServer.FolderExists(UniqueID);
if Exists then
Session.LogMessage('0000D8O', FolderFoundOnServerTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', CategoryTxt)
else begin
Session.LogMessage('0000D8P', FolderNotFoundOnServerTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', CategoryTxt);
LogLastServerError();
end;
end;
exit(Exists);
end;
procedure ReadBuffer(var DestExchangeFolder: Record "Exchange Folder"): Boolean
begin
if TempExchangeFolder.FindSet() then
repeat
if not DestExchangeFolder.Get(TempExchangeFolder.FullPath) then begin
TempExchangeFolder.CalcFields("Unique ID");
DestExchangeFolder.Init();
DestExchangeFolder.TransferFields(TempExchangeFolder);
DestExchangeFolder.Insert();
end;
until TempExchangeFolder.Next() = 0
else
exit(false);
exit(true);
end;
local procedure IsAllowedFolderType(FolderClass: Text): Boolean
begin
if FolderClass = '' then
exit(true);
if FolderClass = 'IPF.Note' then
exit(true);
exit(false);
end;
local procedure IsServiceValid(): Boolean
begin
if IsNull(ServiceOnServer) and IsNull(ServiceOnClient) then
exit(false);
if IsNull(ServiceOnServer) then
exit(ServiceOnClient.ExchangeServiceUrl <> '');
exit(ServiceOnServer.ExchangeServiceUrl <> '');
end;
procedure InvalidateService()
begin
Clear(ServiceOnClient);
Clear(ServiceOnServer);
Clear(TempExchangeFolder);
Clear(LongPathsDetected);
Session.LogMessage('0000D8Q', ServiceInvalidatedTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', CategoryTxt);
end;
[Scope('OnPrem')]
procedure ValidateCredentialsOnServer(): Boolean
begin
if not ServiceOnServer.ValidateCredentials() then begin
Session.LogMessage('0000D8R', InvalidCredentialsOnServerTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', CategoryTxt);
LogLastServerError();
exit(false);
end;
Session.LogMessage('0000D8S', ValidCredentialsOnServerTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', CategoryTxt);
exit(true);
end;
[Scope('OnPrem')]
procedure ValidateCredentialsOnClient(): Boolean
begin
if not ServiceOnClient.ValidateCredentials() then begin
Session.LogMessage('0000D8T', InvalidCredentialsOnClientTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', CategoryTxt);
LogLastClientError();
exit(false);
end;
Session.LogMessage('0000D8U', ValidCredentialsOnClientTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', CategoryTxt);
exit(true);
end;
local procedure LogLastServerError()
var
LastError: Text;
begin
LastError := ServiceOnServer.LastError();
if LastError <> '' then
Session.LogMessage('0000D8E', StrSubstNo(ServiceOnServerLastErrorTxt, LastError), Verbosity::Warning, DataClassification::CustomerContent, TelemetryScope::ExtensionPublisher, 'Category', CategoryTxt);
end;
local procedure LogLastClientError()
var
LastError: Text;
begin
LastError := ServiceOnClient.LastError();
if LastError <> '' then
Session.LogMessage('0000D8C', StrSubstNo(ServiceOnClientLastErrorTxt, LastError), Verbosity::Warning, DataClassification::CustomerContent, TelemetryScope::ExtensionPublisher, 'Category', CategoryTxt);
end;
}