Last active
October 16, 2019 19:19
-
-
Save joerodgers/33460b8849c0b241f5fa3854997ccff1 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add-PSSnapin Microsoft.SharePoint.Powershell | |
function ConvertFrom-HexStringToByteArray | |
{ | |
[CmdletBinding()] | |
param | |
( | |
[parameter(Mandatory=$true)][string]$String | |
) | |
$bytes = New-Object byte[]($string.Length/2) | |
for( $i = 0; $i -lt $String.Length; $i+=2 ) | |
{ | |
$bytes[$i/2] = [System.Convert]::ToByte( $string.Substring($i, 2), 16) | |
} | |
return $bytes | |
} | |
function Decompress-String | |
{ | |
[CmdletBinding()] | |
param | |
( | |
[parameter(Mandatory=$true)][string]$String | |
) | |
begin | |
{ | |
} | |
process | |
{ | |
try | |
{ | |
$String = $String -replace "^0x", "" | |
$inputBytes = ConvertFrom-HexStringToByteArray -String $String | |
$sourceStream = New-Object System.IO.MemoryStream(,$inputBytes) | |
$destinationStream = New-Object System.IO.MemoryStream | |
# https://docs.microsoft.com/en-us/openspecs/sharepoint_protocols/ms-wssfo3/6ca01cb6-d75a-431e-8131-8c780ba46cd8 | |
# ID (2 bytes): MUST be 0xA8A9. | |
$sourceStream.ReadByte() | Out-Null # 0xA8 | |
$sourceStream.ReadByte() | Out-Null # 0xA9 | |
# Version (2 bytes): MUST be 0x3031 (ASCII "01") | |
$sourceStream.ReadByte() | Out-Null # 0x30 | |
$sourceStream.ReadByte() | Out-Null # 0x31 | |
# FileHeaderSize (4 bytes): A 4-byte, unsigned integer specifying the size of the header. The value is 0x0C000000. | |
$sourceStream.ReadByte() | Out-Null # 0x0C | |
$sourceStream.ReadByte() | Out-Null # 0x00 | |
$sourceStream.ReadByte() | Out-Null # 0x00 | |
$sourceStream.ReadByte() | Out-Null # 0x00 | |
# OrigSize (4 bytes): A 4-byte, unsigned integer specifying the size of the original content. It MUST be the size of the uncompressed stream before compression. | |
$sourceStream.ReadByte() | Out-Null # 0xFC | |
$sourceStream.ReadByte() | Out-Null # 0x00 | |
$sourceStream.ReadByte() | Out-Null # 0x00 | |
$sourceStream.ReadByte() | Out-Null # 0x00 | |
# ? | |
$sourceStream.ReadByte() | Out-Null # 0x78 | |
$sourceStream.ReadByte() | Out-Null # 0x9C | |
# zlib decompression | |
$decompressionStream = New-Object System.IO.Compression.DeflateStream( $sourceStream, [System.IO.Compression.CompressionMode]::Decompress ) | |
$decompressionStream.CopyTo($destinationStream) | |
# convert to string | |
[System.text.encoding]::UTF8.GetString( $destinationStream.ToArray() ) | |
} | |
finally | |
{ | |
if( $destinationStream ) | |
{ | |
$destinationStream.Close() | |
$destinationStream.Dispose() | |
} | |
if( $sourceStream ) | |
{ | |
$sourceStream.Close() | |
$sourceStream.Dispose() | |
} | |
} | |
} | |
end | |
{ | |
} | |
} | |
function Compress-String | |
{ | |
[CmdletBinding()] | |
param | |
( | |
[parameter(Mandatory=$true)][string]$String | |
) | |
try | |
{ | |
$String = $String -replace "\0", "" | |
$String = ([Regex]::Replace($String, "(\r\n *<)", "<")) | |
$inputBytes = [System.Text.Encoding]::UTF8.GetBytes($String) | |
$originalFileStream = New-Object System.IO.MemoryStream(,$inputBytes ) | |
$compressedStream = New-Object System.IO.MemoryStream | |
# zlib compression | |
$compressor = New-Object System.IO.Compression.DeflateStream( $compressedStream, [System.IO.Compression.CompressionMode]::Compress, $true ) | |
$originalFileStream.CopyTo( $compressor ) | |
$compressor.Close() | |
$inputBytes2 = New-Object byte[] $($compressedStream.ToArray().Length + 14) | |
$inputBytes2[0] = 0xA8 # HEADER | |
$inputBytes2[1] = 0xA9 # HEADER | |
$inputBytes2[2] = 0x30 # VERSION | |
$inputBytes2[3] = 0x31 # VERSION | |
$inputBytes2[4] = 0x0C # HEADER SIZE | |
$inputBytes2[5] = 0x00 # HEADER SIZE | |
$inputBytes2[6] = 0x00 # HEADER SIZE | |
$inputBytes2[7] = 0x00 # HEADER SIZE | |
$inputBytes2[8] = $inputBytes.Length # ORIG SIZE | |
$inputBytes2[9] = 0x00 # ORIG SIZE | |
$inputBytes2[10] = 0x00 # ORIG SIZE | |
$inputBytes2[11] = 0x00 # ORIG SIZE | |
$inputBytes2[13] = 0x78 # ? | |
$inputBytes2[14] = 0x96 # ? | |
$compressedStream.ToArray().CopyTo( $inputBytes2, 14 ) | |
ConvertFrom-ArrayToHexString -Array $inputBytes2 | |
} | |
finally | |
{ | |
if( $deflator ) | |
{ | |
$deflator.Close() | |
$deflator.Dispose() | |
} | |
if( $originalFileStream ) | |
{ | |
$originalFileStream.Close() | |
$originalFileStream.Dispose() | |
} | |
if( $compressedStream ) | |
{ | |
$compressedStream.Close() | |
$compressedStream.Dispose() | |
} | |
} | |
} | |
function Get-DataTable | |
{ | |
[cmdletbinding()] | |
param | |
( | |
[Parameter(Mandatory=$true,ParameterSetName="Individual")] | |
[string]$DatabaseName, | |
[Parameter(Mandatory=$true,ParameterSetName="Individual")] | |
[string]$DatabaseServer, | |
[Parameter(Mandatory=$true,ParameterSetName="ConnectionString")] | |
[string]$ConnectionString, | |
[Parameter(Mandatory=$true,ParameterSetName="Individual")] | |
[Parameter(Mandatory=$true,ParameterSetName="ConnectionString")] | |
[string]$Query, | |
[Parameter(Mandatory=$false,ParameterSetName="Individual")] | |
[Parameter(Mandatory=$false,ParameterSetName="ConnectionString")] | |
[int]$CommandTimeout=30, # The default is 30 seconds | |
[Parameter(Mandatory=$false,ParameterSetName="Individual")] | |
[Parameter(Mandatory=$false,ParameterSetName="ConnectionString")] | |
[HashTable]$Parameters = @{} | |
) | |
begin | |
{ | |
if( $PSCmdlet.ParameterSetName -eq "Individual" ) | |
{ | |
$ConnectionString = "Data Source=$DatabaseServer;Initial Catalog=$DatabaseName;Integrated Security=True;Enlist=False;Connect Timeout=5" | |
} | |
} | |
process | |
{ | |
try | |
{ | |
$dataSet = New-Object System.Data.DataSet | |
$dataAdapter = New-Object System.Data.SqlClient.SqlDataAdapter( $Query, $ConnectionString ) | |
foreach( $Parameter in $Parameters.GetEnumerator() ) | |
{ | |
$param = $dataAdapter.SelectCommand.Parameters.AddWithValue( "@$($Parameter.Key)", $Parameter.Value ) | |
} | |
Write-Debug -Message "$(Get-Date) - Executing Query: $Query" | |
$dataAdapter.Fill($dataSet) | Out-Null | |
return $dataSet.Tables[0] | |
} | |
catch | |
{ | |
throw $_.Exception | |
} | |
finally | |
{ | |
if($dataSet) | |
{ | |
$dataSet.Dispose() | |
} | |
if($dataAdapter) | |
{ | |
$dataAdapter.Dispose() | |
} | |
} | |
} | |
end | |
{ | |
} | |
} | |
function Invoke-NonQuery | |
{ | |
[cmdletbinding()] | |
param | |
( | |
[Parameter(Mandatory=$true,ParameterSetName="Individual")] | |
[string]$DatabaseName, | |
[Parameter(Mandatory=$true,ParameterSetName="Individual")] | |
[string]$DatabaseServer, | |
[Parameter(Mandatory=$true,ParameterSetName="ConnectionString")] | |
[string]$ConnectionString, | |
[Parameter(Mandatory=$true,ParameterSetName="Individual")] | |
[Parameter(Mandatory=$true,ParameterSetName="ConnectionString")] | |
[string]$Query, | |
[Parameter(Mandatory=$false,ParameterSetName="Individual")] | |
[Parameter(Mandatory=$false,ParameterSetName="ConnectionString")] | |
[int]$CommandTimeout=30, # The default is 30 seconds | |
[Parameter(Mandatory=$false,ParameterSetName="Individual")] | |
[Parameter(Mandatory=$false,ParameterSetName="ConnectionString")] | |
[HashTable]$Parameters = @{} | |
) | |
begin | |
{ | |
if( $PSCmdlet.ParameterSetName -eq "Individual" ) | |
{ | |
$ConnectionString = "Data Source=$DatabaseServer;Initial Catalog=$DatabaseName;Integrated Security=True;Enlist=False;Connect Timeout=5" | |
} | |
} | |
process | |
{ | |
try | |
{ | |
$connection = New-Object System.Data.SqlClient.SqlConnection($ConnectionString) | |
$connection.Open() | |
$command = New-Object system.Data.SqlClient.SqlCommand($Query, $connection) | |
$command.CommandTimeout = $CommandTimeout | |
foreach( $Parameter in $Parameters.GetEnumerator() ) | |
{ | |
$param = $command.Parameters.Add( "@$($Parameter.Key)", $Parameter.Value ) | |
} | |
$command.ExecuteNonQuery() | Out-Null | |
} | |
catch | |
{ | |
throw $_.Exception | |
} | |
finally | |
{ | |
if($connection) | |
{ | |
[System.Data.SqlClient.SqlConnection]::ClearAllPools() | |
$connection.Close() | |
$connection.Dispose() | |
} | |
} | |
} | |
end | |
{ | |
} | |
} | |
function Get-FileMetaInfo | |
{ | |
[CmdletBinding()] | |
param | |
( | |
[parameter(Mandatory=$true)][string]$FileUrl | |
) | |
begin | |
{ | |
} | |
process | |
{ | |
try | |
{ | |
$site = New-Object Microsoft.SharePoint.SPSite($FileUrl) | |
$web = $site.Openweb() | |
$file = $web.GetFile($FileUrl) | |
if( $file.Exists ) | |
{ | |
$row = Get-DataTable ` | |
-DatabaseName $site.ContentDatabase.Name ` | |
-DatabaseServer $site.ContentDatabase.Server ` | |
-Query "SELECT Id, CONVERT(nvarchar(max), MetaInfo, 1) AS 'CompressedMetaInfo' FROM [AllDocs] WHERE SiteId = @SiteId AND Id = @UniqueId" ` | |
-Parameters @{ SiteId = $site.id; UniqueId = $file.UniqueId } | |
[PSCustomObject] @{ | |
Site = $site.Url | |
Web = $web.Url | |
FileUrl = $site.MakeFullUrl($File.Url) | |
Author = $file.Author.LoginName | |
Editor = $file.ModifiedBy.LoginName | |
vti_author = $file.Properties["vti_author"] | |
vti_modifiedby = $file.Properties["vti_modifiedby"] | |
CompressedMetaInfo = $row.CompressedMetaInfo | |
DecompressedMetaInfo = (Decompress-String -String $row.CompressedMetaInfo) | |
} | |
} | |
else | |
{ | |
throw New-Object System.IO.FileNotFoundException("The file at '$fileUrl' was not found.", $FileUrl) | |
} | |
} | |
finally | |
{ | |
if( $web ) | |
{ | |
$web.Dispose() | |
} | |
if( $site ) | |
{ | |
$site.Dispose() | |
} | |
} | |
} | |
end | |
{ | |
} | |
} | |
function Set-FileMetaInfo | |
{ | |
[CmdletBinding()] | |
param | |
( | |
[parameter(Mandatory=$true)][string]$FileUrl, | |
[parameter(Mandatory=$true)][string]$CompressedMetaInfo | |
) | |
begin | |
{ | |
} | |
process | |
{ | |
try | |
{ | |
$site = New-Object Microsoft.SharePoint.SPSite($FileUrl) | |
$web = $site.Openweb() | |
$file = $web.GetFile($FileUrl) | |
if( $file.Exists ) | |
{ | |
Invoke-NonQuery ` | |
-DatabaseName $site.ContentDatabase.Name ` | |
-DatabaseServer $site.ContentDatabase.Server ` | |
-Query "UPDATE Alldocs SET MetaInfo = CONVERT(varbinary(max), '$CompressedMetaInfo', 1) WHERE SiteId = @SiteId AND Id = @UniqueId" ` | |
-Parameters @{ SiteId = $site.Id; UniqueId = $file.UniqueId } | |
} | |
else | |
{ | |
throw New-Object System.IO.FileNotFoundException("The file at '$fileUrl' was not found.", $FileUrl) | |
} | |
} | |
finally | |
{ | |
if( $web ) | |
{ | |
$web.Dispose() | |
} | |
if( $site ) | |
{ | |
$site.Dispose() | |
} | |
} | |
} | |
end | |
{ | |
} | |
} | |
Get-FileMetaInfo -FileUrl "https://sharepoint.2016.contoso.com/sites/teamsite/Shared%20Documents/Folder%201/Folder%202/Folder%203/Folder%204/ftype.txt" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment