Skip to content

Instantly share code, notes, and snippets.

@joerodgers
Last active October 16, 2019 19:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save joerodgers/33460b8849c0b241f5fa3854997ccff1 to your computer and use it in GitHub Desktop.
Save joerodgers/33460b8849c0b241f5fa3854997ccff1 to your computer and use it in GitHub Desktop.
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