ConvertFrom-String and locating a specific object.

Setup: Example info

$strings = "X-Possible-Header1:Value1X-Possible-Header2:Value2X-Possible-Header3:Value3X-File-Name:MyFileX-File-Size:32X-File-Type:PDFX-File-Date:04:22:78
"
$strings = $strings.Replace(":",":`r`n")
$strings = $strings.Replace("X-","`r`nX-")
$strings = $strings | ConvertFrom-String
$strings

The possible headers in the example above may or may not be there. So then with the convertfrom-string provides the P# objects the order can change.

Ask:

I am trying to figure out how to locate a statically named object and take the value of the next P# object which can be different.

So for example:
P8 : X-File-Name:
P9 : MyFile

How would I locate X-File-Name regardless of its P# position and take P#+1 as the value I want returned.

I’m not sure that’s really the use case for ConvertFrom-String. It’s not designed to necessarily make it quicker to locate paired data like you’re asking. I think what you’d want is to help it understand that oil name, file size, and file date, are all part of one single object. That might mean parsing the string once for the first set of headers, and then again to get the file info, so that you get a single object with the file name, size, and date as properties. In other words, let it do a bit more of the work, here.

Thank you for responding.

Here is the use case. I am attempting to parse out MSG files to pull a specific X-Header. The filtering software that places the X-header always does it exactly the same way but the value is random. Additionally, Exchange sometimes does odd modifications to the value causing it to sometimes be encapsulated in markup like US-ASCII or UTF-8. Also, sometimes it contains return carriages/newlines etc.

So to try to solve for it I turned the entire header into one long string with no spaces, newlines, or return carriages (Similar to the example above). I then tried to figure out a way to isolate the header and header value. This was done using the example above. T

This allowed me to isolate the header and the value as objects but since their place is random I cannot figure out how to identify systematically the value I am looking for to a variable for further manipulation.

Why not just use regex to pull the data you want out of your string?

$strings = "X-Possible-Header1:Value1X-Possible-Header2:Value2X-Possible-Header3:Value3X-File-Name:MyFileX-File-Size:32X-File-Type:PDFX-File-Date:04:22:78" -match "X-Possible-Header3:(.*?)X-"
$matches[1]

Value3

SOB, that did it.

That narrows it down enough that I can clean it up fine.

Thanks a bunch.

Hey Gorstag,
The examples provided in the last post seem like 1 Key/Value pair, but the previous posts seem like multiple key/value pairs in a string. So I’m guessing you have something more like the below sample.

$header = @"
X-Possible-Header1:
 Value1
X-Brightmail-Tracker:
 H4sIAAAAAAAAC+NgFvrEKsWRWlGSWpSXmKPExsXiIGUzUZf958IIg5lT9m1jsfjdNY/JgdHj0LwVrAGMUeyZxbrFBYm5CewZs9vbmQo2feWseHTgP3sD48XznF2MXBxCAusZJT6s/c8E4VxilOjo/MncxcjJwSagL/HxyDV2EFtEQE5i+7qNYDazgIREx5vjbCC2sIC6xMTPs8HqWQRUJZ4ef84IYvMKOEh8O/kXyhaUODnzCUsXIwdQb6jEnKPWIGEJAQWJHWdfM0KM15N4tmE3M8R4cYmXR4+ArRICGv/y/Bs2iPpAiTcX1rOCjJEQsJJY9MQfIuwgMfPNb6iwvcSNrTIQYSuJng0dzBC2jsSbibOZYOItnTegyoMket8KQ4SNJHZNmMIOYetLzHp5lRXigAKJ46tb2SYwSs5C8soshFdmITkZwo6WaFk1nRXC1pFYsPsTG4QtL7H97RxmCFtb4syBx0ww9rKFr6HinhJb709lgbAVJaZ0P4SaGSjRsq6TaQEj9ypG4SBnX0/XCCNvQwO9ouTcTL3k/NxNjMCEwMUlx76D8c5b90OMkhxMSqK8sZsWRgjxJeWnVGYkFmfEF5XmpBYfYpTh4FCS4NX7DpQTLEpNT61Iy8wBpiaYNBMH5yFGCQ4eJRFe0R9ANbyg1FOcmQ6RP8Voz9Fz8vJLJo5j70Dkn4tXXzNxtHVdf80kxAJSKSXOux1ktACIk1GaBzcZllRfMYpzMCoJ85qAVPFk5pXAzX4FtJYJaC1rHNjakkSElFQDY6D+j6VXWW6ZHm5kmBO0YHnw8SMH7qRc+j4zwK5k+91EKd4GDf4i7fXFcepLhN1lbiX2nM1179f/+OVZz5z7UefsLaMYso+qnmJb9nBWkNL0jMcrvLm5mlfctt2fXrjEyO4xx48YSTmhPTt1V732mvRxvS5D/oznVZarjq3pEGm1dUvb/NFtvhJLcUaioRZzUXEiAC9AEWGFAwAA
X-Possible-Header3:
 Value3
"@

$strings = (-split $header) -join "" -match "X-Brightmail-Tracker:(.*?)X-"
$matches[1]

H4sIAAAAAAAAC+NgFvrEKsWRWlGSWpSXmKPExsXiIGUzUZf958IIg5lT9m1jsfjdNY/JgdHj0LwVrAGMUeyZxbrFBYm5CewZs9vbmQo2feWseHTgP3sD48XznF2MXBxCAusZJT6s/c8E4VxilOjo/MncxcjJwSagL/HxyDV2EFtEQE5i+7qNYDazgIREx5vjbCC2sIC6xMTPs8HqWQRUJZ4ef84IYvMKOEh8O/kXyhaUODnzCUsXIwdQb6jEnKPWIG
EJAQWJHWdfM0KM15N4tmE3M8R4cYmXR4+ArRICGv/y/Bs2iPpAiTcX1rOCjJEQsJJY9MQfIuwgMfPNb6iwvcSNrTIQYSuJng0dzBC2jsSbibOZYOItnTegyoMket8KQ4SNJHZNmMIOYetLzHp5lRXigAKJ46tb2SYwSs5C8soshFdmITkZwo6WaFk1nRXC1pFYsPsTG4QtL7H97RxmCFtb4syBx0ww9rKFr6HinhJb709lgbAVJaZ0P4SaGSjRsq6T
aQEj9ypG4SBnX0/XCCNvQwO9ouTcTL3k/NxNjMCEwMUlx76D8c5b90OMkhxMSqK8sZsWRgjxJeWnVGYkFmfEF5XmpBYfYpTh4FCS4NX7DpQTLEpNT61Iy8wBpiaYNBMH5yFGCQ4eJRFe0R9ANbyg1FOcmQ6RP8Voz9Fz8vJLJo5j70Dkn4tXXzNxtHVdf80kxAJSKSXOux1ktACIk1GaBzcZllRfMYpzMCoJ85qAVPFk5pXAzX4FtJYJaC1rHNjakk
SElFQDY6D+j6VXWW6ZHm5kmBO0YHnw8SMH7qRc+j4zwK5k+91EKd4GDf4i7fXFcepLhN1lbiX2nM1179f/+OVZz5z7UefsLaMYso+qnmJb9nBWkNL0jMcrvLm5mlfctt2fXrjEyO4xx48YSTmhPTt1V732mvRxvS5D/oznVZarjq3pEGm1dUvb/NFtvhJLcUaioRZzUXEiAC9AEWGFAwAA

or

$header = @"
X-Possible-Header1:
 Value1
X-Brightmail-Tracker: =?us-ascii?Q?H4sIAAAAAAAAC02SbUhTYRTHee7u7u5Wi+ssdlrL9EYYpab2gmjkLKREQjOC?=
 =?us-ascii?Q?UVjdbLqVrtg07FNqamqZSn1QC5lWFlFhNrMXspi6ULQhlU1xUKmhm1oalL3S?=
 =?us-ascii?Q?vXum9uXyO//zf/7nuQ+HFik6KRWty83WmYxcJkvJSI1qa1Nowp96bfgHZ14r?=
 =?us-ascii?Q?GfW4eYrUoF22ulviZLRfYjCHmk9yWYcl+kp7ofikdUaaO+gsFuUhV4+0DMlo?=
 =?us-ascii?Q?BXMfQf7NQTEuSgg4+3GMwoUTgfXrZ7IMSWmK2QDTHf0SgZcyy2H4noUSWMQA?=
 =?us-ascii?Q?lEy89LI/EwyO6lKvh2TWwLTrkvesnNHAnVkHgdkPumpGeJ3mz+4D+7fjggxM?=
 =?us-ascii?Q?IDzq9SAcHwb91Z8JHK+E8c4OiWAHJgDeN8dh+1qYdLklmIPh++iQSGDEpEFv?=
 =?us-ascii?Q?T7cY60Fgf+4iMIfDNccshVkND+vqfGddErj1eCWO3wgN/UmCrOAjxx0TPvse?=
 =?us-ascii?Q?eNDS5rtBNDSMJGFZA01jRSSWY8HZosZyNFxoKhFhDoGJqivEnF5Y6hRjewqU?=
 =?us-ascii?Q?T/pXotW1/z1I7cKD1P7345gPwOinERHmELA8naEwr4LWyas+fT30vBgm5rix?=
 =?us-ascii?Q?3uPTE2Cm4pwYcxBcPv/Bl7kHivKdpAUtuo38U3bsTojXRiZGhIeZ0rIMYWkn?=
 =?us-ascii?Q?spoRv1syWYDkERqa3GlDy2mCXSZPba7XKpYcOXH0tJ4z6w+ZcjJ1ZhtS0zQL?=
 =?us-ascii?Q?8pmffM/PpMvQ5aYbMvkFnWsTtNSGgF7MLpX//cF75MJCmg0ZuN+NYukLXa/H?=
 =?us-ascii?Q?Cdo+JXx/9731EHRx2TsPoSAFp0op/y1EM0KhzzHOJ89tvxspacT6y8d+8a7F?=
 =?us-ascii?Q?BmP2fLabH0vwY8UHvWOzuYWWKg8xp+oX/YlnpY0NocQPonGgKqBG/Z2aigtj?=
 =?us-ascii?Q?N76KLIjqoCJjH5QMrw1IfdbuTj6oTWz767Ecm4piVHcvcjru22zxE1spx247?=
 =?us-ascii?Q?PJb+aXtScsUZroVKPaB+1npjnXtvQWD3yKbygc1W+/WXBTFvtsTtr1Q2PYwp?=
 =?us-ascii?Q?roIVRuizfmnXukJZ0qznItaJTGbuHxsPujrYAwAA?=
 X-Possible-Header3:
 Value3
"@

$strings = (-split $header) -join "" -match "X-Brightmail-Tracker:(.*?)X-"
$matches[1]

=?us-ascii?Q?H4sIAAAAAAAAC02SbUhTYRTHee7u7u5Wi+ssdlrL9EYYpab2gmjkLKREQjOC?==?us-ascii?Q?UVjdbLqVrtg07FNqamqZSn1QC5lWFlFhNrMXspi6ULQhlU1xUKmhm1oalL3S?==?us-ascii?Q?vXum9uXyO//zf/7nuQ+HFik6KRWty83WmYxcJkvJSI1qa1Nowp96bfgHZ14r?==?us-ascii?Q?GfW4eYrUoF22ulviZLRf
YjCHmk9yWYcl+kp7ofikdUaaO+gsFuUhV4+0DMlo?==?us-ascii?Q?BXMfQf7NQTEuSgg4+3GMwoUTgfXrZ7IMSWmK2QDTHf0SgZcyy2H4noUSWMQA?==?us-ascii?Q?lEy89LI/EwyO6lKvh2TWwLTrkvesnNHAnVkHgdkPumpGeJ3mz+4D+7fjggxM?==?us-ascii?Q?IDzq9SAcHwb91Z8JHK+E8c4OiWAHJgDeN8dh+1qYdLklmIPh++iQS
GDEpEFv?==?us-ascii?Q?T7cY60Fgf+4iMIfDNccshVkND+vqfGddErj1eCWO3wgN/UmCrOAjxx0TPvse?==?us-ascii?Q?eNDS5rtBNDSMJGFZA01jRSSWY8HZosZyNFxoKhFhDoGJqivEnF5Y6hRjewqU?==?us-ascii?Q?T/pXotW1/z1I7cKD1P7345gPwOinERHmELA8naEwr4LWyas+fT30vBgm5rix?==?us-ascii?Q?3uPTE2Cm4pw
YcxBcPv/Bl7kHivKdpAUtuo38U3bsTojXRiZGhIeZ0rIMYWkn?==?us-ascii?Q?spoRv1syWYDkERqa3GlDy2mCXSZPba7XKpYcOXH0tJ4z6w+ZcjJ1ZhtS0zQL?==?us-ascii?Q?8pmffM/PpMvQ5aYbMvkFnWsTtNSGgF7MLpX//cF75MJCmg0ZuN+NYukLXa/H?==?us-ascii?Q?Cdo+JXx/9731EHRx2TsPoSAFp0op/y1EM0KhzzHOJ89t
vxspacT6y8d+8a7F?==?us-ascii?Q?BmP2fLabH0vwY8UHvWOzuYWWKg8xp+oX/YlnpY0NocQPonGgKqBG/Z2aigtj?==?us-ascii?Q?N76KLIjqoCJjH5QMrw1IfdbuTj6oTWz767Ecm4piVHcvcjru22zxE1spx247?==?us-ascii?Q?PJb+aXtScsUZroVKPaB+1npjnXtvQWD3yKbygc1W+/WXBTFvtsTtr1Q2PYwp?==?us-ascii?Q?ro
IVRuizfmnXukJZ0qznItaJTGbuHxsPujrYAwAA?=

Hi,

I’ve been playing around with this a bit as I have had time. And I came to a realization that I hadn’t noticed before. Email headers have a very specific set of rules that govern them (Who would have thought:)

Each new header has to begin at the start of the line with no whitespace prior. They then have to have a Value: (ValueColon Space ). Additionally, each line of data that has a space or tab (any whitespace really) will be part of that header’s data. It will end when the next object repeats the ValueColon Space at the beginning of the line. Finally, the header ends when a new line contains only whitespace.

So an example:

X-HeaderOne: My First Value
 My Second Value
X-headerTwo:
 My first value
 My second value
X-headerThree: My value

Header ended above ^

 

So what I really need to figure out is how to capture everything between the static header I know and the “next” start. So for example between X-headerTwo: and end at the start of X-headerthree OR on a completely blank line.

Edit: Looks like i might have the regex dialed in

 (X-Brightmail-Tracker:.*\r?\n(?:^\ +.*\r?\n)*) 

If your pattern as described holds true, you could just convert your headers into a hash table and call the specific header you want from the hash table similar to the below.

$data = @'
X-HeaderOne: My First Value
 My Second Value
X-headerTwo:
 My first value
 My second value
X-headerThree: My value
'@ -split "`n"

function parse-headers {
    Param (
        $headers
    )
    $pheaders = @{}
    $headers |
    ForEach-Object {
        If ($_.substring(0,2) -eq "X-") {
            $header = $_.substring(0,$_.indexof(":"))
            $value = ($_.substring($_.indexof(":") + 1)).trim()
        } else {
            $value = ($_.substring($_.indexof(":") + 1)).trim()
        }
        $pheaders[$header] += $value
    }
    $pheaders
}

$myheaders = parse-headers $data

$myheaders['X-headerTwo']
My first valueMy second value

I was able to isolate it using the following method (Because it was something I knew how to do):

$GetMsgs = Get-ChildItem ./ -Filter '*.msg'
$GetMsgs = $GetMsgs.fullname
$reg1 = "(?m)(X-Brightmail-Tracker:.*\r?\n(?:^\ +.*\r?\n)*)"

#Then running the following in a loop

foreach ($GetMsg in $GetMsgs) {
$ol = New-Object -ComObject Outlook.Application
$msg = $ol.CreateItemFromTemplate("$GetMsg")
$headers = $msg.PropertyAccessor.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x007D001E")


$headers = $headers -replace ":\s",":`r`n "

$headers = $headers | select-string -Pattern $reg1 -AllMatches | % { $_.Matches.Value }
}

# I then output each result to a txt file for each item in the loop
 

I am going to poke around with the method you are using because it looks clean and is not something I’ve done before. Thanks again for the help.

Now that I see exactly what you are doing, you can also do this.

function parse-headers {
    Param(
        [string]$allheadersstring
    )
    # First we replace all the Carriage Return/Line Feed/Space patterns found with just a space
    # to get all values on the same line.  Then we split the string by the Carriage Return/Line
    # Feed pattern to get all of the individual headers as separate elements in the collection.
    $collectionIndividualHeaders = $allheadersstring -replace "`r`n "," " -split "`r`n"
    $collectionIndividualHeaders | ForEach-Object {
        $IndividualHeader = $_ -split ": "
        [pscustomobject]@{
            header = $IndividualHeader[0];
            value = $IndividualHeader[1]
        }
    }
}

$getMsgs = ($GetMsgs = Get-ChildItem ./ -Filter '*.msg').FullName
foreach ($GetMsg in $GetMsgs) {
    $ol = New-Object -ComObject Outlook.Application
    $msg = $ol.CreateItemFromTemplate("$GetMsg")
    $headers = $msg.PropertyAccessor.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x007D001E")
    parse-headers $headers | Where-Object {$_.header -eq "X-Brightmail-Tracker"} | Select-Object -ExpandProperty Value

}

The function takes your string of headers as input, and then outputs a Custom PowerShell object for each header/value. You can then use the pipeline to further filter the objects to get the exact header/value you want. Just change your where-object to get a different header.