Exchange & Autonomy

My organization uses Autonomy (now owned by HP) to integrate our Mail and Document Management Systems.  Currently we’ve got it configured to run in multiple ways including automatic folder-based filing and point in time filing.  From what the team who runs the Autonomy system tell me, it has horrible logging when an item doesn’t get filed.  They asked me to report some of that stuff from Exchange, and I didn’t know how (at first).

It took me many months of playing with Exchange Web Services to get it running the way that I expected.  For everyone’s amusement, I’m including the complete script here.  Most of the basis for this and logic behind it was driven by Glen’s Exchange Dev Blog.  He has been a true hero to those of us playing with Exchange Web Services.  Check out his blog if you want more information.

I’ll probably be coming back in the future to make a few edits and explain some of the crazier code that I used, but here’s the basics.

function Get-DMSItems {
<#

.SYNOPSIS

Searches a mailbox for DMS Items which encountered filing errors.

.DESCRIPTION

Leverages Exchange Web Services Managed API to query mailboxes for DMS Items.
By default, the program will only search for DMS filing errors or items which
are still queued for filing within the "Inbox."

Additional parameters can be used to gather more information, including
searching within subfolders, changing the root of the search to a folder other
than the inbox, and searching for all DMS items (not just filing errors).

.PARAMETER EmailAddress

The Email Address for the mailbox to query.  This is mandatory.

.PARAMETER FolderPath

The folder in which to search, or in the event of using Recurse, the
new root for search.  Folder paths must begin with a Backslash (\) to be 
considered valid.  Omitting this parameter will result in searching the
mailbox root.

.PARAMETER IncludeFiled

Include all DMS items, not just those that encountered an error in filing.

.PARAMETER Recurse

Use the Inbox or optional FolderPath parameter as a search root and search
through all child folders.

.PARAMETER ImpersonationCredential

Credentials used for connectivity to Exchange Web Services.

.EXAMPLE

PS C:\> Get-DMSItems -EmailAddress philip.j.fry@planetexpress.com -ImpersonationCredential $EwsCreds
Searches within Philip's Inbox for all DMS Items which have not been filed and
are reported as a Filing Error or Queued to be Filed.

.EXAMPLE

PS C:\> Get-DMSItems -EmailAddress turanga.leela@planetexpress.com -IncludeFiled -ImpersonationCredential $EwsCreds
Searches within Leela's mailbox for all DMS Items.  Returns those which have
been filed, those with errors, or those which are still queued to be filed.

.EXAMPLE

PS C:\> Get-DMSItems -EmailAddress bender.rodriguez@planetexpress.com -ImpersonationCredential $EwsCreds
Searches within Bender's mailbox for errored or queued items in any folder in
the mailbox.

.EXAMPLE

PS C:\> Get-DMSItems -EmailAddress hermes.conrad@planetexpress.com -FolderPath "\Inbox\Central Bureaucracy" -Recurse -ImpersonationCredential $EwsCreds
Searches within Hermes' mailbox for errors.  This search begins at the Central
Bureaucracy folder under the inbox and includes all child folders.

.EXAMPLE

PS C:\> Get-DMSItems -EmailAddress john.zoidberg@planetexpress.com -FolderPath "\Inbox" -IncludeFiled -Recurse -ImpersonationCredential $EwsCreds
Searches within Dr. Zoidberg's mailbox for all DMS Items.  This search begins at
the mailbox root and includes all child folders.

.EXAMPLE

PS C:\> $EmailAddress = ConvertFrom-MailboxID -MailboxID "0000000038A1BB1005E5101AA1BB08002B2A56C20000454D534D44422E444C4C00000000000000001B55FA20AA6611CD9BC800AA002FC45A0C00000053414E4445584D42583031002F4F3D50697065722026204D6172627572792F4F553D504D2F636E3D526563697069656E74732F636E3D616265636B737465616400"
PS C:\> Get-DMSItems -EmailAddress $EmailAddress -ImpersonationCredential $EwsCreds
Searches within the mailbox with the MailboxID listed for all DMS Errors and
Items yet to be filed.

.NOTES

This function requires Exchange Web Services Managed API version 1.2.
The EWS Managed API can be obtained from: http://www.microsoft.com/en-us/download/details.aspx?id=28952

#>
[CmdletBinding()]
Param(
 	[Parameter(Position=0,Mandatory=$true)]
	[string]$EmailAddress,
	[Parameter(Position=1,Mandatory=$false)]
	[string]$FolderPath = "\",
	[Parameter(Position=2,Mandatory=$false)]
	[switch]$IncludeFiled = $false,
	[Parameter(Position=3,Mandatory=$false)]
	[switch]$Recurse,
	[Parameter(Position=4)]
	[System.Net.NetworkCredential]$ImpersonationCredential
)

	if ( $Recurse )
	{
		Write-Warning "Enumeration of all subfolders can take a considerable amount of time.  Please be patient."
	}

	#region Variable Definition
	$MaxResults = 100
	#endregion Variable Definition

	#region Function-based Variables
	$FolderIDs = @()
	#endregion Function-based Variables

	#region Define Custom Functions
	function ConvertToString($ipInputString)
	{
	    $Val1Text = ""
	    for ( $clInt=0; $clInt -lt $ipInputString.length; $clInt++)
		{  
			$Val1Text = $Val1Text + [Convert]::ToString([Convert]::ToChar([Convert]::ToInt32($ipInputString.Substring($clInt,2),16)))  
			$clInt++  
	    }  
	    return $Val1Text  
	} 
	#endregion Define Custom Functions

	#region PoshCode #624 - Trust all SSL Certificates
	## Shamelessly stolen from PowerShell Code Website
    ## Source: http://poshcode.org/624
	$Provider = New-Object -TypeName Microsoft.CSharp.CSharpCodeProvider
	$Compiler = $Provider.CreateCompiler()
	$Params = New-Object -TypeName System.CodeDom.Compiler.CompilerParameters
	$Params.GenerateExecutable      = $False
	$Params.GenerateInMemory        = $True
	$Params.IncludeDebugInformation = $False
	$Params.ReferencedAssemblies.Add("System.DLL") | Out-Null

$TASource=@'
  namespace Local.ToolkitExtensions.Net.CertificatePolicy{
    public class TrustAll : System.Net.ICertificatePolicy {
      public TrustAll() { 
      }
      public bool CheckValidationResult(System.Net.ServicePoint sp,
        System.Security.Cryptography.X509Certificates.X509Certificate cert, 
        System.Net.WebRequest req, int problem) {
        return true;
      }
    }
  }
'@ 
	$TAResults  = $Provider.CompileAssemblyFromSource($Params,$TASource)
	$TAAssembly = $TAResults.CompiledAssembly

	## We now create an instance of the TrustAll and attach it to the ServicePointManager
	$TrustAll = $TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll")
	[System.Net.ServicePointManager]::CertificatePolicy = $TrustAll
	#endregion PoshCode #624 - Trust all SSL Certificates

	#region Load Exchange Web Services Managed API
	try
	{
		$EwsPath = ( Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Exchange\Web Services\1.2" -Name "Install Directory" )."Install Directory"
		$EwsDLL = $EwsPath + "Microsoft.Exchange.WebServices.dll"
		$EwsLoadResults = [Reflection.Assembly]::LoadFile($EwsDLL)
		$ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2007_SP1
        $ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP2
		$Service = New-Object -TypeName Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)
	}
	catch
	{
		Write-Error "Error loading Exchange Web Services Managed API DLL"
        Write-Host "Download and install the Exchange Web Services Managed API version 1.2"
        Write-Host "`tVersion 1.2: http://www.microsoft.com/en-us/download/details.aspx?id=28952"
		return $null
	}
    try
    {
    	if ( -not $ImpersonationCredential )
        {
            Write-Verbose "Using Default Credentials for Connection"
            $Service.UseDefaultCredentials = $true
    	    $Service.ImpersonatedUserId = $null
        }
        else
        {
            Write-Verbose "Using Impersonation Credentials for Connection [$( $ImpersonationCredential.Username )]"
    	    $Service.Credentials = $ImpersonationCredential
    	    $Service.ImpersonatedUserId = New-Object -TypeName Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $EmailAddress)
        }
    }
    catch
    {
        Write-Error ( "Unable to authenticate with provided credentials: " + $ImpersonationCredential.UserName )
    }
    try
    {
        $Service.AutodiscoverURL( $EmailAddress )
        Write-Verbose ( "Connecting to [$EmailAddress] using: " + $Service.URL )
    }
    catch
    {
        Write-Error "Unable to use AutoDiscover (REQUIRED)"
    }

    #endregion Load Exchange Web Services Managed API

	#region Build Extended Property Set for Folder Results
	# Build the Folder Property Set and then add Properties that we want
	$psFolderPropertySet = New-Object -TypeName Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)

	# Define the Folder Extended Property Set Elements
	$PR_Folder_Path      = New-Object -TypeName Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(26293, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String)
	$PR_Folder_Type      = New-Object -TypeName Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(13825, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer)

	# Add to the Folder Property Set Collection
	$psFolderPropertySet.Add($PR_Folder_Path)
	$psFolderPropertySet.Add($PR_Folder_Type)
	#endregion Build Extended Property Set for Folder Results

	#region Build Extended Property Set for Item Results
	# Build the Item Property Set and then add the Properties that we want
	$psItemPropertySet           = New-Object -TypeName Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)

	# Define the Item Extended Property Set Elements
	$PR_Item_FilingStatus        = New-Object -TypeName Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition([Microsoft.Exchange.WebServices.Data.DefaultExtendedPropertySet]::PublicStrings, "FilingStatus" ,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String)
	$PR_Item_FilingDate          = New-Object -TypeName Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition([Microsoft.Exchange.WebServices.Data.DefaultExtendedPropertySet]::PublicStrings, "FilingDate" , [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::SystemTime)
	$PR_Item_FilingFolder        = New-Object -TypeName Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition([Microsoft.Exchange.WebServices.Data.DefaultExtendedPropertySet]::PublicStrings, "FilingFolder" , [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String)
	$PR_Item_FilingStatusCode    = New-Object -TypeName Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition([Microsoft.Exchange.WebServices.Data.DefaultExtendedPropertySet]::PublicStrings, "FilingStatusCode" , [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer)
	$PR_Item_FilingOriginalClass = New-Object -TypeName Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition([Microsoft.Exchange.WebServices.Data.DefaultExtendedPropertySet]::PublicStrings, "FilingOriginalClass" , [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String)
	$PR_Item_FilingDocumentID    = New-Object -TypeName Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition([Microsoft.Exchange.WebServices.Data.DefaultExtendedPropertySet]::PublicStrings, "FilingDocumentID" , [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String)

	# Add to Item Property Set Collection
	$psItemPropertySet.Add($PR_Item_FilingStatus)
	$psItemPropertySet.Add($PR_Item_FilingDate)
	$psItemPropertySet.Add($PR_Item_FilingFolder)
	$psItemPropertySet.Add($PR_Item_FilingStatusCode)
	$psItemPropertySet.Add($PR_Item_FilingOriginalClass)
	$psItemPropertySet.Add($PR_Item_FilingDocumentID)
	#endregion Build Extended Property Set for Item Results

	#region Find the Initial Folder
    if ( $FolderPath -eq "\" )
    {
        $FolderRootID    = New-Object -TypeName Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot, $EmailAddress)
		$FolderRoot      = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($Service, $FolderRootID)
    }
    else
	{
        if ( $FolderPath[0] -eq "\" )
		{
			$MsgFolderRootID              = New-Object -TypeName Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot, $EmailAddress)
			$MsgFolderRoot                = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($Service, $MsgFolderRootID)
			$SearchFolderView             = New-Object -TypeName Microsoft.Exchange.WebServices.Data.FolderView($MaxResults)
			$SearchFolderView.Traversal   = [Microsoft.Exchange.WebServices.Data.FolderTraversal]::Deep
			$SearchFolderView.PropertySet = $psFolderPropertySet

			$FolderSearchFilter           = New-Object -TypeName Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo($PR_Folder_Type, "1")
			$FolderSearchResults          = $null
			Do
			{
                $FolderSearchResults = $Service.FindFolders($MsgFolderRootID, $FolderSearchFilter, $SearchFolderView)
                ForEach ( $ResultFolder in $FolderSearchResults.Folders )
				{
                    $FolderPathByRef = $null
					if ( $ResultFolder.TryGetProperty($PR_Folder_Path, [ref]$FolderPathByRef ) )
			        {  
			            $binarry          = [Text.Encoding]::UTF8.GetBytes($FolderPathByRef)  
			            $hexArr           = $binarry | ForEach-Object { $_.ToString("X2") }  
			            $hexString        = $hexArr -join ''  
			            $hexString        = $hexString.Replace("FEFF", "5C00")  
			            $FolderPathString = ConvertToString($hexString)
                    }
					if ( $FolderPathString -eq $FolderPath )
					{
						Write-Verbose ( "[" + $EmailAddress + "] Root Folder Found: [$FolderPathString]" )
                        #$FolderIDs    += $ResultFolder.ID
				        $FolderIDs += New-Object PSObject -Property @{
							FolderID = $ResultFolder.ID
							FolderPath = $FolderPathString
						}
						$FolderRootID  = $ResultFolder.ID
                        $FolderFound   = $true
                        break
					}
				}
				$SearchFolderView.Offset += $FolderSearchResults.Folders.Count
			} While ( $FolderSearchResults.MoreAvailable -and -not $FolderFound )

        }
		else
		{
			Write-Error "The FolderPath Parameter must begin with a backslash '\'"
		}
	}

    if ( -not $FolderRootID )
    {
        Write-Error "No Such Folder Found: $FolderPath"
    }
    #endregion Find the Initial Folder

	#region List Child Folders for the Recurse Switch
	if ( $Recurse -and $FolderFound )
	{
		# Bind to Provided Folder ($Folder)
		# Perform a Search for all Folders under this folder
		# Store the Folder Paths

		$SearchFolderRoot             = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($Service, $FolderRootID)
		$SearchFolderView             = New-Object -TypeName Microsoft.Exchange.WebServices.Data.FolderView($MaxResults)
		$SearchFolderView.Traversal   = [Microsoft.Exchange.WebServices.Data.FolderTraversal]::Deep
		$SearchFolderView.PropertySet = $psFolderPropertySet

		$FolderSearchFilter           = New-Object -TypeName Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo($PR_Folder_Type, "1")
		$FolderSearchResults          = $null

        Do
		{
			$FolderSearchResults = $Service.FindFolders($FolderRootID, $FolderSearchFilter, $SearchFolderView)
			ForEach ( $ResultFolder in $FolderSearchResults.Folders )
			{
				$FolderPathByRef = $null
				if ( $ResultFolder.TryGetProperty($PR_Folder_Path, [ref]$FolderPathByRef ) )
		        {  
		            $binarry          = [Text.Encoding]::UTF8.GetBytes($FolderPathByRef)  
		            $hexArr           = $binarry | ForEach-Object { $_.ToString("X2") }  
		            $hexString        = $hexArr -join ''  
		            $hexString        = $hexString.Replace("FEFF", "5C00")  
		            $FolderPathString = ConvertToString($hexString)  
		        }
				Write-Verbose ( "[" + $EmailAddress + "] Child Found: [$FolderPathString]" )
                $FolderIDs += New-Object PSObject -Property @{
					FolderID = $ResultFolder.ID
					FolderPath = $FolderPathString
				}
			}
			$SearchFolderView.Offset += $FolderSearchResults.Folders.Count
		} While ( $FolderSearchResults.MoreAvailable )

    }
    #endregion List Child Folders for the Recurse Switch

    #region List Contents of Folders in the FolderIDs Object
	$ItemCollection = @()
	$FolderCount = $FolderIDs.Count
    $i = 1
    ForEach ( $FolderID in $FolderIDs )
	{
		$Activity         = ( "Searching Mailbox: " + $EmailAddress )
        $Status           = ( "Searching Mailbox Subfolders [" + $ItemCollection.Count + " item(s) found]" )
        $CurrentOperation = ( "Searching Folder: " + $FolderID.FolderPath )
        $PercentComplete  = ( $i / $FolderCount ) * 100
        Write-Progress -Activity $Activity -Status $Status -CurrentOperation $CurrentOperation -PercentComplete $PercentComplete -ParentId -1
        Write-Verbose ( "[" + $EmailAddress + "] Searching for items in: " + $FolderID.FolderPath )
        $ItemView = New-Object -TypeName Microsoft.Exchange.WebServices.Data.ItemView($MaxResults)
		if ( $IncludeFiled )
		{
			$ItemSearchFilter = New-Object -TypeName Microsoft.Exchange.WebServices.Data.SearchFilter+ContainsSubString([Microsoft.Exchange.WebServices.Data.ItemSchema]::ItemClass,"IPM.Note.WorkSite")
		}
		else
		{
			$ItemSearchFilterCollection = New-Object -TypeName Microsoft.Exchange.WebServices.Data.SearchFilter+SearchFilterCollection( [Microsoft.Exchange.WebServices.Data.LogicalOperator]::Or )
            $ItemSearchFilterCollection.Add( ( New-Object -TypeName Microsoft.Exchange.WebServices.Data.SearchFilter+ContainsSubString([Microsoft.Exchange.WebServices.Data.ItemSchema]::ItemClass,"IPM.Note.WorkSite.Ems.Error") ) )
            $ItemSearchFilterCollection.Add( ( New-Object -TypeName Microsoft.Exchange.WebServices.Data.SearchFilter+ContainsSubString([Microsoft.Exchange.WebServices.Data.ItemSchema]::ItemClass,"IPM.Note.WorkSite.Ems.Queued") ) )
            $ItemSearchFilter = $ItemSearchFilterCollection
		}
		Do
		{
			$FolderItems = $Service.FindItems($FolderID.FolderID, $ItemSearchFilter, $ItemView)
			if ( $FolderItems.Items )
			{
				[Void]$Service.LoadPropertiesForItems($FolderItems, $psItemPropertySet)
				ForEach ( $Item in $FolderItems.Items )
				{
					$Status = ( "Searching Mailbox Subfolders [" + $ItemCollection.Count + " item(s) found]" )
					Write-Progress -Activity $Activity -Status $Status -CurrentOperation $CurrentOperation -PercentComplete $PercentComplete -ParentId -1

					$ItemCollectionObject = New-Object PSObject

					$ItemCollectionObject | Add-Member -MemberType NoteProperty -Name Mailbox -Value $null
					$ItemCollectionObject | Add-Member -MemberType NoteProperty -Name FolderPath -Value $null
					$ItemCollectionObject | Add-Member -MemberType NoteProperty -Name OriginalItemClass -Value $null
                    $ItemCollectionObject | Add-Member -MemberType NoteProperty -Name ItemClass -Value $null
					$ItemCollectionObject | Add-Member -MemberType NoteProperty -Name ItemSubject -Value $null
					$ItemCollectionObject | Add-Member -MemberType NoteProperty -Name Sender -Value $null
					$ItemCollectionObject | Add-Member -MemberType NoteProperty -Name Sent -Value $null
					$ItemCollectionObject | Add-Member -MemberType NoteProperty -Name Size -Value $null
					$ItemCollectionObject | Add-Member -MemberType NoteProperty -Name FilingStatus -Value $null
					$ItemCollectionObject | Add-Member -MemberType NoteProperty -Name FilingStatusCode -Value $null
					$ItemCollectionObject | Add-Member -MemberType NoteProperty -Name FilingDate -Value $null
					$ItemCollectionObject | Add-Member -MemberType NoteProperty -Name DMSServer -Value $null
					$ItemCollectionObject | Add-Member -MemberType NoteProperty -Name DMSDatabase -Value $null
					$ItemCollectionObject | Add-Member -MemberType NoteProperty -Name DMSDocNumber -Value $null
					$ItemCollectionObject | Add-Member -MemberType NoteProperty -Name DMSDocVersion -Value $null
					$ItemCollectionObject | Add-Member -MemberType NoteProperty -Name DMSFolder -Value $null
                    $ItemCollectionObject | Add-Member -MemberType NoteProperty -Name FolderID -Value $null
                    $ItemCollectionObject | Add-Member -MemberType NoteProperty -Name MessageID -Value $null

					$ItemCollectionObject.Mailbox     = $EmailAddress
					$ItemCollectionObject.FolderID    = $null # To Be Updated
                    $ItemCollectionObject.FolderPath  = $FolderID.FolderPath
                    $ItemCollectionObject.FolderID    = ConvertTo-HexId -EmailAddress $EmailAddress -EWSid ( $Item.ParentFolderId.UniqueId )
					$ItemCollectionObject.ItemSubject = $Item.Subject
                    $ItemCollectionObject.MessageID   = ConvertTo-HexId -EmailAddress $EmailAddress -EWSid ( $Item.Id )
					$ItemCollectionObject.ItemClass   = $Item.ItemClass
					if ( $Item.Sender )
                    {
                        try
                        {
                            $ItemCollectionObject.Sender      = $Item.Sender.Address.ToString()
                        }
                        catch
                        {
                            $ItemCollectionObject.Sender      = $null
                        }
                    }
					$ItemCollectionObject.Sent        = $Item.DateTimeSent
					$ItemCollectionObject.Size        = $Item.Size

					$FilingStatus        = $null
					$FilingStatusCode    = $null
					$FilingDate          = $null
					$FilingDocumentID    = $null
					$FilingFolder        = $null
                    $FilingOriginalClass = $null

					if ( $Item.TryGetProperty($PR_Item_FilingStatus, [ref]$FilingStatus) )
					{
						$ItemCollectionObject.FilingStatus = $FilingStatus
					}

					if ( $Item.TryGetProperty($PR_Item_FilingStatusCode, [ref]$FilingStatusCode) )
					{
						$ItemCollectionObject.FilingStatusCode = $FilingStatusCode
					}

					if ( $Item.TryGetProperty($PR_Item_FilingDate, [ref]$FilingDate) )
					{
						$ItemCollectionObject.FilingDate = [datetime]$FilingDate
					}

					if ( $Item.TryGetProperty($PR_Item_FilingFolder, [ref]$FilingFolder) )
					{
						$ItemCollectionObject.DMSFolder = $FilingFolder
					}

                    if ( $Item.TryGetProperty($PR_Item_FilingOriginalClass, [ref]$FilingOriginalClass) )
					{
						$ItemCollectionObject.OriginalItemClass = $FilingOriginalClass
					}

					if ( $Item.TryGetProperty($PR_Item_FilingDocumentID, [ref]$FilingDocumentID) )
					{
						try
						{
							if ( $FilingDocumentID.Contains("!") )
							{
								# Start by splitting by the bang "!", then parse down further
								$Breakdown                          = $FilingDocumentID.Split("!")
								$ItemCollectionObject.DMSServer     = $Breakdown[2].Split(":")[1]
								$ItemCollectionObject.DMSDatabase   = $Breakdown[3].Split(":")[1]
								$FilingDocumentNumVer               = $Breakdown[4].Split(":")[1]
								$ItemCollectionObject.DMSDocNumber  = $FilingDocumentNumVer.Split(",")[0]
								$ItemCollectionObject.DMSDocVersion = $FilingDocumentNumVer.Split(",")[1]
							}
						}
						catch [System.Exception]
						{
							Write-Error ( "Error thrown obtaining document ID for message """ + $Item.Subject + """ in [" + $ItemCollectionObject.Mailbox + "]:" + $ItemCollectionObject.FolderPath )
                            $ItemCollectionObject.DMSServer         = $null
                            $ItemCollectionObject.DMSDatabase       = $null
                            $ItemCollectionObject.DMSDocNumber      = $null
                            $ItemCollectionObject.DMSDocVersion     = $null
						}
					}

					$ItemCollection += $ItemCollectionObject
				}

			}

		$ItemView.Offset += $FolderItems.Items.Count
		} While ( $FolderItems.MoreAvailable )
        Write-Progress -Activity $Activity -Status "Completed" -Completed -ParentId -1
        Write-Verbose ( "`t[Found " + $ItemCollection.Count + " Total Items in this search]" )

	}
	#endregion List Contents of Folders in the FolderIDs Object
    $ItemCollection
}

#region Byte Array Convert Functions
# The Convert-HexStringToByteArray and Convert-ByteArrayToString functions are from
# Link: http://www.sans.org/windows-security/2010/02/11/powershell-byte-array-hex-convert

function Convert-HexStringToByteArray {
################################################################
#.Synopsis
# Convert a string of hex data into a System.Byte[] array. An
# array is always returned, even if it contains only one byte.
#.Parameter String
# A string containing hex data in any of a variety of formats,
# including strings like the following, with or without extra
# tabs, spaces, quotes or other non-hex characters:
# 0x41,0x42,0x43,0x44
# \x41\x42\x43\x44
# 41-42-43-44
# 41424344
# The string can be piped into the function too.
################################################################
[CmdletBinding()]
Param ( [Parameter(Mandatory = $True, ValueFromPipeline = $True)] [String] $String )

#Clean out whitespaces and any other non-hex crud.
$String = $String.ToLower() -replace '[^a-f0-9\\\,x\-\:]',''

#Try to put into canonical colon-delimited format.
$String = $String -replace '0x|\\x|\-|,',':'

#Remove beginning and ending colons, and other detritus.
$String = $String -replace '^:+|:+$|x|\\',''

#Maybe there's nothing left over to convert...
if ($String.Length -eq 0) { ,@() ; return } 

#Split string with or without colon delimiters.
if ($String.Length -eq 1)
{ ,@([System.Convert]::ToByte($String,16)) }
elseif (($String.Length % 2 -eq 0) -and ($String.IndexOf(":") -eq -1))
{ ,@($String -split '([a-f0-9]{2})' | foreach-object { if ($_) {[System.Convert]::ToByte($_,16)}}) }
elseif ($String.IndexOf(":") -ne -1)
{ ,@($String -split ':+' | foreach-object {[System.Convert]::ToByte($_,16)}) }
else
{ ,@() }
#The strange ",@(...)" syntax is needed to force the output into an
#array even if there is only one element in the output (or none).
}

function Convert-ByteArrayToString {
################################################################
#.Synopsis
# Returns the string representation of a System.Byte[] array.
# ASCII string is the default, but Unicode, UTF7, UTF8 and
# UTF32 are available too.
#.Parameter ByteArray
# System.Byte[] array of bytes to put into the file. If you
# pipe this array in, you must pipe the [Ref] to the array.
# Also accepts a single Byte object instead of Byte[].
#.Parameter Encoding
# Encoding of the string: ASCII, Unicode, UTF7, UTF8 or UTF32.
# ASCII is the default.
################################################################
[CmdletBinding()] Param (
 [Parameter(Mandatory = $True, ValueFromPipeline = $True)] [System.Byte[]] $ByteArray,
 [Parameter()] [String] $Encoding = "ASCII"
)

switch ( $Encoding.ToUpper() )
{
	 "ASCII"   { $EncodingType = "System.Text.ASCIIEncoding" }
	 "UNICODE" { $EncodingType = "System.Text.UnicodeEncoding" }
	 "UTF7"    { $EncodingType = "System.Text.UTF7Encoding" }
	 "UTF8"    { $EncodingType = "System.Text.UTF8Encoding" }
	 "UTF32"   { $EncodingType = "System.Text.UTF32Encoding" }
	 Default   { $EncodingType = "System.Text.ASCIIEncoding" }
}
$Encode = new-object $EncodingType
$Encode.GetString($ByteArray)
}
#endregion Byte Array Convert Functions

#region Exchange Conversion Functions
function ConvertTo-MailboxIdentification {
[CmdletBinding()] Param (
 [Parameter(Mandatory = $True, ValueFromPipeline = $True)] [String] $EncodedString
)

	$ByteArray   = Convert-HexStringToByteArray -String $EncodedString
	$ByteArray   = $ByteArray | Where-Object { ( $_ -ge 32 -and $_ -le 127 ) -or $_ -eq 0 }
	$ByteString  = Convert-ByteArrayToString -ByteArray $ByteArray -Encoding ASCII
	$StringArray = $ByteString.Split([char][int](0))
	$StringArray[21]
}

function ConvertFrom-MailboxID {
<#

.SYNOPSIS

Convert Encoded Mailbox ID to Email Address

.PARAMETER MailboxID

The mailbox identification string as provided by the DMS System

.DESCRIPTION

Takes the encoded Mailbox ID from the DMS System and returns the email 
address of the end user.

.EXAMPLE

PS C:\> ConvertFrom-MailboxID -MailboxID "0000000038A1BB1005E5101AA1BB08002B2A56C20000454D534D44422E444C4C00000000000000001B55FA20AA6611CD9BC800AA002FC45A0C00000053414E4445584D42583031002F4F3D50697065722026204D6172627572792F4F553D504D2F636E3D526563697069656E74732F636E3D616265636B737465616400"

John.Zoidberg@planetexpress.com

.NOTES

Requires active connection to the Active Directory infrastructure

#>
[CmdletBinding()]
Param(
 	[Parameter(Position=0,Mandatory=$true)]
	[string]$MailboxID
)

$MailboxDN = ConvertTo-MailboxIdentification -EncodedString $MailboxID

$ADSISearch = [DirectoryServices.DirectorySearcher]""
$ADSISearch.Filter = "(&(&(&(objectCategory=user)(objectClass=user)(legacyExchangeDN=" + $MailboxDN + "))))"
$SearchResults = $ADSISearch.FindOne()
if ( -not $SearchResults )
{
    $ADSISearch.Filter = "(&(objectclass=user)(objectcategory=person)(proxyaddresses=x500:" + $MailboxDN + "))"
    $SearchResults = $ADSISearch.FindOne()    
}
$EmailAddress = $SearchResults.Properties.mail
$EmailAddress
}

function ConvertFrom-FolderID {
<#

.SYNOPSIS

Convert Encoded Folder ID to Folder Path

.PARAMETER EmailAddress

The email address of the mailbox in question.  Can also be used as the return
value from ConvertFrom-MailboxID

.PARAMETER FolderID

The mailbox identification string as provided by the DMS System

.PARAMETER ImpersonationCredential

The credential to use when accessing Exchange Web Services.

.DESCRIPTION

Takes the encoded Folder ID from the DMS System and returns the folder path for
the Folder ID with the user mailbox.

.EXAMPLE

PS C:\> ConvertFrom-FolderID -EmailAddress "hubert.farnsworth@planetexpress.com" -FolderID "0000000038A1BB1005E5101AA1BB08002B2A56C20000454D534D44422E444C4C00000000000000001B55FA20AA6611CD9BC800AA002FC45A0C00000053414E4445584D42583031002F4F3D50697065722026204D6172627572792F4F553D504D2F636E3D526563697069656E74732F636E3D616265636B737465616400" -ImpersonationCredential $EWSAdmin

\Inbox\Omicron Persei 8\Lrrr

.EXAMPLE

PS C:\> $EmailAddress = ConvertFrom-MailboxID -MailboxID "0000000038A1BB1005E5101AA1BB08002B2A56C20000454D534D44422E444C4C00000000000000001B55FA20AA6611CD9BC800AA002FC45A0C00000042414C5445584D42583033002F4F3D50697065722026204D6172627572792F4F553D504D2F636E3D526563697069656E74732F636E3D6162313836353600D83521F3C10000000100000014000000850000002F6F3D50697065722026204D6172627572792F6F753D45786368616E67652041646D696E6973747261746976652047726F7570202846594449424F484632335350444C54292F636E3D436F6E66696775726174696F6E2F636E3D536572766572732F636E3D42414C5445584D4258303300420041004C005400450058004D0042005800300033002E00500069007000650072002E0052006F006F0074002E004C006F00630061006C0000000000"
PS C:\> ConvertFrom-FolderID -EmailAddress $EmailAddress -FolderID "0000000038A1BB1005E5101AA1BB08002B2A56C20000454D534D44422E444C4C00000000000000001B55FA20AA6611CD9BC800AA002FC45A0C00000053414E4445584D42583031002F4F3D50697065722026204D6172627572792F4F553D504D2F636E3D526563697069656E74732F636E3D616265636B737465616400" -ImpersonationCredential $EWSAdmin

\Inbox\Amphibios 9\Kif Kroker

.NOTES

This function requires Exchange Web Services Managed API version 1.2.
The EWS Managed API can be obtained from: http://www.microsoft.com/en-us/download/details.aspx?id=28952

#>
[CmdletBinding()]
Param(
 	[Parameter(Position=0,Mandatory=$true)]
	[string]$EmailAddress,
    [Parameter(Position=1,Mandatory=$true)]
	[string]$FolderID,
    [Parameter(Position=2,Mandatory=$true)]
	[System.Net.NetworkCredential]$ImpersonationCredential,
	[Parameter(Position=3,Mandatory=$false)]
	[ValidateSet("EwsLegacyId", "EwsId", "EntryId", "HexEntryId", "StoreId", "OwaId")]
	[string]$InputFormat = "EwsId",
	[Parameter(Position=4,Mandatory=$false)]
	[ValidateSet("FolderPath", "EwsLegacyId", "EwsId", "EntryId", "HexEntryId", "StoreId", "OwaId")]
	[string]$OutputFormat = "FolderPath"
)
	Write-Verbose "Converting $FolderID from $InputFormat to $OutputFormat"

	#region Load Exchange Web Services Managed API
	try
	{
		$EwsPath = ( Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Exchange\Web Services\1.2" -Name "Install Directory" )."Install Directory"
		$EwsDLL = $EwsPath + "Microsoft.Exchange.WebServices.dll"
		$EwsLoadResults = [Reflection.Assembly]::LoadFile($EwsDLL)
		$ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2007_SP1
		$Service = New-Object -TypeName Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)
	}
	catch
	{
		Write-Error "Error loading Exchange Web Services Managed API DLL"
        Write-Host "Download and install the Exchange Web Services Managed API version 1.2"
        Write-Host "`tVersion 1.2: http://www.microsoft.com/en-us/download/details.aspx?id=28952"
		return $null
	}
    try
    {
        if ( -not $ImpersonationCredential )
        {
            $ImpersonationCredential = New-Object System.Net.NetworkCredential($EWSUsername,[string]$EWSPassword, $EWSDomain) 
        }
    	$Service.Credentials = $ImpersonationCredential
    	$Service.ImpersonatedUserId = New-Object -TypeName Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $EmailAddress)
        $Service.AutodiscoverURL( $EmailAddress )
        Write-Verbose ( "Connecting to [$EmailAddress] using:" + $Service.URL )
    }
    catch
    {
        Write-Error "Error with Autodiscovery"
        return $null
    }

    #endregion Load Exchange Web Services Managed API

    #region Build Alternative ID Object
    $AlternativeIdItem          = New-Object Microsoft.Exchange.WebServices.Data.AlternateId
	$AlternativeIdItem.Mailbox  = $EmailAddress
	$AlternativeIdItem.UniqueId = $FolderID
	$AlternativeIdItem.Format   = [Microsoft.Exchange.WebServices.Data.IdFormat]::$InputFormat
    #endregion Build Alternative ID Object

    #region Retrieve Folder Path from EWS
    try
    {
        if ( $OutputFormat -eq "FolderPath" )
		{
			#region Build Extended Property Set for Folder Results
			# Build the Folder Property Set and then add Properties that we want
			$psFolderPropertySet = New-Object -TypeName Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)

			# Define the Folder Extended Property Set Elements
			$PR_Folder_Path      = New-Object -TypeName Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(26293, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String)

			# Add to the Folder Property Set Collection
			$psFolderPropertySet.Add($PR_Folder_Path)
			#endregion Build Extended Property Set for Folder Results

			$EwsFolderID = $Service.ConvertId($AlternativeIdItem, [Microsoft.Exchange.WebServices.Data.IdFormat]::EwsId)
	        $EwsFolder = New-Object Microsoft.Exchange.WebServices.Data.FolderID($EwsFolderID.UniqueId)
	        $TargetFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($Service, $EwsFolder, $psFolderPropertySet)
	        # Retrieve the first Property (Folder Path in a Raw State)
	        $FolderPathRAW = $TargetFolder.ExtendedProperties[0].Value
	        # The Folder Path attribute actually contains non-ascii characters in place of the backslashes
	        #   Since the first character is one of these non-ascii characters, we use that for the replace method
	        $ConvertedFolderId    = $FolderPathRAW.Replace($FolderPathRAW[0], "\")
		}
		else
		{
			$EwsFolderID = $Service.ConvertId($AlternativeIdItem, [Microsoft.Exchange.WebServices.Data.IdFormat]::$OutputFormat )
			$ConvertedFolderId = $EwsFolderId.UniqueId

		}

    }
    catch
    {
        $ConvertedFolderId = $null
    }
    finally
    {
        $ConvertedFolderId
    }
    #endregion Retrieve Folder Path from EWS
}

function ConvertTo-HexId{    
	param (
	        $EWSid ,
            $EmailAddress
		  )
	process{
	    $aiItem = New-Object Microsoft.Exchange.WebServices.Data.AlternateId      
	    $aiItem.Mailbox = $EmailAddress
	    $aiItem.UniqueId = $EWSid   
	    $aiItem.Format = [Microsoft.Exchange.WebServices.Data.IdFormat]::EWSId;      
	    $convertedId = $service.ConvertId($aiItem, [Microsoft.Exchange.WebServices.Data.IdFormat]::HexEntryId) 
		return $convertedId.UniqueId
	}
}
#endregion Exchange Conversion Functions

<#
Sample Run with Impersonation Credentials
#$EwsCreds          = New-Object -TypeName System.Net.NetworkCredential
#$EwsCreds.Domain   = "DOMAIN"
#$EwsCreds.UserName = "ImpersonationUser"
#$EwsCreds.Password = "ImpersonationPassword"

Get-DMSItems -Mailbox "me@domain.com" -FolderPath "\" -Recurse -ImpersonationCredential $EwsCred
#>

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.