Ading Exif processing

pull/1/head
Tom Peltonen 2021-08-08 09:30:15 +10:00
parent ebcce55e47
commit a2c5f23852
14 changed files with 472 additions and 153 deletions

64
Docs/Advanced.md 100644
View File

@ -0,0 +1,64 @@
# Advanced Usage
There are various options for using PeterDocs. The following sections will cover some of these.
## File Filter
The ``-FileFilter`` parameter allows selection of files tha are to be included into the archive file.
The parameter only applies to the compress function or buidling the reconciliation file.
For example:
```powershell
Compress-Peter -SourceFolder "~/Pictures/" -Secret "c0mpleX%S3cret" -FileFiletr "*.jpg"
```
will only include files with the extension ".jpg"
```powershell
Compress-Peter -SourceFolder "~/Pictures/" -Secret "c0mpleX%S3cret" -FileFiletr "IMG9*.jpg"
```
will only include files with the extension ".jpg" and starting with the characters "IMG90"
## ReconcileFile
The ``-ReconcileFile`` parameter allows specification of the reocnciliation file if you
wish to select your own name.
For example:
```powershell
Compress-Peter -SourceFolder "~/Pictures/" -Secret "c0mpleX%S3cret" -ReconcileFile "reconcile_batch2.csv"
```
will generate a reconcile file named "reconcile_batch2.csv" and place it into the 7Zip archive. Remember
to specify the reconcile file on the compare, something like this:
```powershell
Compare-Peter -RestoreFolder "c:\backup\pictures" -ReconcileFile "reconcile_batch2.csv"
```
## SecretFile
The ``-SecretFile`` parameter allows specification of the secret file if you
wish to select your own name. This parameter is only applicable with the
``-RecipientKey`` parameter
For example:
```powershell
Compress-Peter -SourceFolder "~/Pictures/" -RecipientKey "meerkat@merebox.com" -SecretFile "mypictures.key"
```
will generate a secret file named "mypictures.key". Remember to send this file to your recipient
and to specify the secret file on the expand, something like this:
```powershell
Expand-Peter -RestoreFolder "c:\backup\pictures" -RecipientKey "meerkat@merebox.com" -SecretFile "mypictures.key" -ArchiveFile "myarchive.7z"
```
## LogPath
The ``-LogPath`` parameter allows definition of the folder that will contain the
execution log.

Binary file not shown.

View File

@ -31,11 +31,12 @@
$global:default_reconcileFile = "##protect_transfer_reconcile_files##.csv" $global:default_reconcileFile = "##protect_transfer_reconcile_files##.csv"
$global:default_exifFile = "##peter_exif##.csv"
$global:LogPathName = "" $global:LogPathName = ""
$global:MetadataPathName = Join-Path -Path ".\" -ChildPath ".peter-metadata"
function Open-Log { function Open-Log {
$dateTimeStart = Get-Date -f "yyyy-MM-dd HH:mm:ss" $dateTimeStart = Get-Date -f "yyyy-MM-dd HH:mm:ss"
Write-Log "***********************************************************************************" Write-Log "***********************************************************************************"
Write-Log "* Start of processing: [$dateTimeStart]" Write-Log "* Start of processing: [$dateTimeStart]"
@ -49,7 +50,7 @@ function Write-Log {
) )
$date = Get-Date -f "yyyy-MM-dd" $date = Get-Date -f "yyyy-MM-dd"
if (($null -eq $global:LogPathName) -or ($global:LogPathName -eq "")) if (($null -eq $global:LogPathName) -or ($global:LogPathName -eq ""))
{ {
$global:LogPathName = Join-Path -Path ".\" -ChildPath "Logs" $global:LogPathName = Join-Path -Path ".\" -ChildPath "Logs"
@ -100,7 +101,8 @@ param(
function Test-PasswordQuality function Test-PasswordQuality
{ {
Param( Param(
[Parameter(Mandatory)][String] $TestPassword [Parameter(Mandatory)]
[String] $TestPassword
) )
$qualityMatch = $true $qualityMatch = $true
@ -142,20 +144,20 @@ function Get-ConvenientFileSize
) )
if ($totalFileSize -ge 1000000000000) { if ($totalFileSize -ge 1TB) {
$totalRightLabel = "TB" $totalRightLabel = "TB"
$totalFileXbytes = [math]::Round(($size / 1000000000000), 2) $totalFileXbytes = [math]::Round(($size / 1TB), 2)
} else { } else {
if ($totalFileSize -ge 1000000000) { if ($totalFileSize -ge 1GB) {
$totalRightLabel = "GB" $totalRightLabel = "GB"
$totalFileXbytes = [math]::Round(($size / 1000000000), 2) $totalFileXbytes = [math]::Round(($size / 1GB), 2)
} else { } else {
if ($totalFileSize -ge 1000000) { if ($totalFileSize -ge 1MB) {
$totalRightLabel = "MB" $totalRightLabel = "MB"
$totalFileXbytes = [math]::Round(($size / 1000000), 2) $totalFileXbytes = [math]::Round(($size / 1MB), 2)
} else { } else {
$totalRightLabel = "KB" $totalRightLabel = "KB"
$totalFileXbytes = [math]::Round(($size / 1000), 2) $totalFileXbytes = [math]::Round(($size / 1KB), 2)
} }
} }
} }
@ -230,11 +232,11 @@ function Get-ConvenientFileSize
.Example .Example
# Create a reconcile file for folder "C:\sourcefiles\" # Create a reconcile file for folder "C:\sourcefiles\"
Build-PeterReconcile -SourceFolder "C:\sourcefiles\" -ReconcileFile ".\myreconcile.csv" New-PeterReconcile -SourceFolder "C:\sourcefiles\" -ReconcileFile ".\myreconcile.csv"
#> #>
function Build-PeterReconcile function New-PeterReconcile
{ {
Param( Param(
[Parameter(Mandatory)][String] $SourceFolder, [Parameter(Mandatory)][String] $SourceFolder,
@ -256,7 +258,7 @@ Param(
if ($Feedback) { if ($Feedback) {
Open-Log Open-Log
Write-Log "Function 'Build-PeterReconcile' parameters follow" Write-Log "Function 'New-PeterReconcile' parameters follow"
Write-Log "Parameter: SourceFolder Value: $SourceFolder " Write-Log "Parameter: SourceFolder Value: $SourceFolder "
Write-Log "Parameter: ReconcileFile Value: $ReconcileFile " Write-Log "Parameter: ReconcileFile Value: $ReconcileFile "
Write-Log "Parameter: RootFolder Value: $RootFolder " Write-Log "Parameter: RootFolder Value: $RootFolder "
@ -292,7 +294,16 @@ Param(
Write-Log "Generating reconciliation file '$ReconcileFile'" Write-Log "Generating reconciliation file '$ReconcileFile'"
Write-Host "Generating reconciliation file '$ReconcileFile'" Write-Host "Generating reconciliation file '$ReconcileFile'"
if ($IncludeExif) {
if (!(Test-Path -Path $global:MetadataPathName )) {
$null = New-Item -Path $global:MetadataPathName -ItemType Directory
}
$ExifFile = Join-Path -Path $global:MetadataPathName -ChildPath $global:default_exifFile
Write-Log "Generating Exif file '$ExifFile'"
Set-Content -Path $ExifFile -Value '"FullName","Author","Title","Subject","Comments","DateTaken","ISO","FNumber"'
}
$totalFileCount = 0 $totalFileCount = 0
$totalFileSize = 0 $totalFileSize = 0
@ -307,7 +318,7 @@ Param(
Write-Progress -Activity "Creating reconciliation entries in file $ReconcileFile" -Status "Start" Write-Progress -Activity "Creating reconciliation entries in file $ReconcileFile" -Status "Start"
Set-Content -Path $ReconcileFile -Value '"FullName","LastWriteTime","CreationTime","LastAccessTime","Length","Hash","ParentFolder","Object","Attributes","Extension"' Set-Content -Encoding utf8 -Path $ReconcileFile -Value '"FullName","LastWriteTime","CreationTime","LastAccessTime","Length","Hash","ParentFolder","Object","Attributes","Extension"'
if ($SourceFolder.StartsWith("@")) { if ($SourceFolder.StartsWith("@")) {
Write-Log "Using @ file '$($SourceFolder.Substring(1))'" Write-Log "Using @ file '$($SourceFolder.Substring(1))'"
@ -341,13 +352,18 @@ Param(
} }
$record = '"'+$_.FullName.Replace($RootFolder, "")+'","'+$_.LastWriteTime.ToString("yyyy-MM-ddTHH:mm:ss")+'"' $record = '"'+$_.FullName.Replace($RootFolder, "")+'","'+$_.LastWriteTime.ToString("yyyy-MM-ddTHH:mm:ss")+'"'
$record = $record + ',"'+$_.CreationTime.ToString("yyyy-MM-ddTHH:mm:ss")+'","'+$_.LastAccessTime.ToString("yyyy-MM-ddTHH:mm:ss")+'"' $record = $record + ',"'+$_.CreationTime.ToString("yyyy-MM-ddTHH:mm:ss")+'","'+$_.LastAccessTime.ToString("yyyy-MM-ddTHH:mm:ss")+'"'
$record = $record + ','+$_.Length+',"'+$sourceHash+'","'+ $_.Directory + '","' + $_.Name + '","' + $_.Attributes+'","'+$_.Extension+'"' $record = $record + ','+$_.Length+',"'+$sourceHash+'"'
$record = $record + ',"'+ $_.Directory + '","' + $_.Name + '","' + $_.Attributes+'","'+$_.Extension+'"'
if ($IncludeExif) {
$null = Get-ImageFileContents -ImageFile $($_.FullName)
}
Add-Content -Path $ReconcileFile -Value $record Add-Content -Path $ReconcileFile -Value $record
if ($IncludeExif) {
$exifData = Get-ImageFileContents -ImageFile $($_.FullName)
$exifRecord = '"' + $_.FullName + '","' + $exifData.Author + '","' + $exifData.Title + '","' + $exifData.Subject + '","' + $exifData.Comments + '"'
$exifRecord = $exifRecord + ',"' + $exifData.DateTaken + '","' + $exifData.Iso + '","' + $exifData.FNumber + '"'
Add-Content -Path $ExifFile -Value $exifRecord
}
} }
} }
@ -378,12 +394,16 @@ Param(
$record = $record + ',"'+$_.CreationTime.ToString("yyyy-MM-ddTHH:mm:ss")+'","'+$_.LastAccessTime.ToString("yyyy-MM-ddTHH:mm:ss")+'"' $record = $record + ',"'+$_.CreationTime.ToString("yyyy-MM-ddTHH:mm:ss")+'","'+$_.LastAccessTime.ToString("yyyy-MM-ddTHH:mm:ss")+'"'
$record = $record + ','+$_.Length+',"'+$sourceHash+'","'+ $_.Directory + '","' + $_.Name + '","' + $_.Attributes+'","'+$_.Extension+'"' $record = $record + ','+$_.Length+',"'+$sourceHash+'","'+ $_.Directory + '","' + $_.Name + '","' + $_.Attributes+'","'+$_.Extension+'"'
Add-Content -Path $ReconcileFile -Value $record
if ($IncludeExif) { if ($IncludeExif) {
$null = Get-ImageFileContents -ImageFile $($_.FullName) $exifData = Get-ImageFileContents -ImageFile $($_.FullName)
$exifRecord = '"' + $_.FullName + '","' + $exifData.Author + '","' + $exifData.Title + '","' + $exifData.Subject + '","' + $exifData.Comments + '"'
$exifRecord = $exifRecord + ',"' + $exifData.DateTaken + '","' + $exifData.Iso + '","' + $exifData.FNumber + '"'
Add-Content -Path $ExifFile -Value $exifRecord
} }
Add-Content -Path $ReconcileFile -Value $record
} }
} }
@ -571,6 +591,7 @@ Param(
[String] $FileFilter ="*", [String] $FileFilter ="*",
[String] $SecretFile, [String] $SecretFile,
[switch] $ExcludeHash, [switch] $ExcludeHash,
[switch] $IncludeExif,
[String] $RootFolder, [String] $RootFolder,
[String] $LogPath [String] $LogPath
@ -596,6 +617,7 @@ Param(
Write-Log "Parameter: FileFilter Value: $FileFilter " Write-Log "Parameter: FileFilter Value: $FileFilter "
Write-Log "Parameter: SecretFile Value: $SecretFile " Write-Log "Parameter: SecretFile Value: $SecretFile "
Write-Log "Parameter: ExcludeHash Value: $ExcludeHash " Write-Log "Parameter: ExcludeHash Value: $ExcludeHash "
Write-Log "Parameter: IncludeExif Value: $IncludeExif "
Write-Log "Parameter: LogPath Value: $LogPath " Write-Log "Parameter: LogPath Value: $LogPath "
Write-Log "" Write-Log ""
@ -668,7 +690,10 @@ Param(
if ($ReconcileFile -eq "") if ($ReconcileFile -eq "")
{ {
$ReconcileFile = $default_reconcileFile if (!(Test-Path -Path $global:MetadataPathName)) {
$null = New-Item -Path $global:MetadataPathName -ItemType Directory
}
$ReconcileFile = Join-Path -Path $global:MetadataPathName -ChildPath $default_reconcileFile
} }
if ($FileFilter -eq "") if ($FileFilter -eq "")
@ -727,9 +752,9 @@ Param(
[int] $archiveFileCount = $archiveInfo.FilesCount [int] $archiveFileCount = $archiveInfo.FilesCount
if ($ExcludeHash) { if ($ExcludeHash) {
Build-PeterReconcile -ReconcileFile $ReconcileFile -SourceFolder $SourceFolder -FileFilter $FileFilter -RootFolder $rootFolder -ExcludeHash -ProcessFileCount $archiveFileCount New-PeterReconcile -ReconcileFile $ReconcileFile -SourceFolder $SourceFolder -FileFilter $FileFilter -RootFolder $rootFolder -ExcludeHash -ProcessFileCount $archiveFileCount -IncludeExif
} else { } else {
Build-PeterReconcile -ReconcileFile $ReconcileFile -SourceFolder $SourceFolder -FileFilter $FileFilter -RootFolder $rootFolder -ProcessFileCount $archiveFileCount New-PeterReconcile -ReconcileFile $ReconcileFile -SourceFolder $SourceFolder -FileFilter $FileFilter -RootFolder $rootFolder -ProcessFileCount $archiveFileCount -IncludeExif
} }
If (!(Test-Path -Path $ReconcileFile )) { If (!(Test-Path -Path $ReconcileFile )) {
Write-Log "Reconcile file '$ReconcileFile' was not created. See any previous errors" Write-Log "Reconcile file '$ReconcileFile' was not created. See any previous errors"
@ -738,11 +763,11 @@ Param(
return return
} }
Write-Log "Add reconcile file '$ReconcileFile' to file '$ArchiveFile'" Write-Log "Add folder '$global:MetadataPathName' to file '$ArchiveFile'"
$fullReconcileName = (Get-Item $ReconcileFile).FullName $fullMetadatName = (Get-Item $global:MetadataPathName).FullName
$fullZipName = (Get-Item $ArchiveFile).FullName $fullZipName = (Get-Item $ArchiveFile).FullName
Compress-7Zip -Path $fullReconcileName -ArchiveFileName $fullZipName -Format SevenZip -Append -Password $secret -EncryptFilenames Compress-7Zip -Path $fullMetadatName -ArchiveFileName $fullZipName -PreserveDirectoryRoot -Format SevenZip -Append -Password $secret -EncryptFilenames
Remove-Item $fullReconcileName #Remove-Item $fullMetadatName -Recurse
Write-Log "Archive file '$ArchiveFile' created from folder '$SourceFolder'" Write-Log "Archive file '$ArchiveFile' created from folder '$SourceFolder'"
Write-Host "Archive file '$ArchiveFile' created from folder '$SourceFolder'" -ForegroundColor Green Write-Host "Archive file '$ArchiveFile' created from folder '$SourceFolder'" -ForegroundColor Green
@ -1506,6 +1531,7 @@ Param(
[Parameter(Mandatory)][String] $RestoreFolder, [Parameter(Mandatory)][String] $RestoreFolder,
[String] $ReconcileFile, [String] $ReconcileFile,
[String] $RootFolder, [String] $RootFolder,
[Switch] $ExcludeHash,
[Switch] $ExtendedCheck, [Switch] $ExtendedCheck,
[String] $LogPath [String] $LogPath
) )
@ -1534,7 +1560,7 @@ Param(
if ($ReconcileFile -eq "") if ($ReconcileFile -eq "")
{ {
$ReconcileFile = Join-Path -Path $RestoreFolder -ChildPath $default_reconcileFile $ReconcileFile = Join-Path -Path (Join-Path -Path $RestoreFolder -ChildPath $global:MetadataPathName) -ChildPath $default_reconcileFile
Write-Log "Using default reconciliation file '$ReconcileFile'" Write-Log "Using default reconciliation file '$ReconcileFile'"
Write-Host "Using default reconciliation file '$ReconcileFile'" Write-Host "Using default reconciliation file '$ReconcileFile'"
If (!(Test-Path -Path $ReconcileFile )) { If (!(Test-Path -Path $ReconcileFile )) {
@ -1572,21 +1598,24 @@ Param(
$restoreFileName = $(Join-Path -Path $RestoreFolder -ChildPath $_.FullName) $restoreFileName = $(Join-Path -Path $RestoreFolder -ChildPath $_.FullName)
} }
If (Test-Path -Path $restoreFileName ) { If (Test-Path -Path $restoreFileName ) {
if ($_.Hash -ne "") { if (!($ExcludeHash)) {
$targetHash= (Get-FileHash -Path $restoreFileName).Hash if ($_.Hash -ne "") {
if ($_.Hash -ne $targetHash) { $targetHash= (Get-FileHash -Path $restoreFileName).Hash
$errorCount = $errorCount + 1 if ($_.Hash -ne $targetHash) {
Write-Log "Hash mismatch for file '$restoreFileName' with target value $targetHash" $errorCount = $errorCount + 1
Write-Log "Hash mismatch for file '$restoreFileName' with target value $targetHash"
}
} else {
$missingHash = $true
} }
} else {
$missingHash = $true
} }
if ((Get-Item -Path $restoreFileName).CreationTime.ToString("yyyy-MM-ddTHH:mm:ss") -ne $_.CreationTime) {
Write-Log "Creation mismatch for file '$restoreFileName' with target value $((Get-Item -Path $restoreFileName).CreationTime.ToString("yyyy-MM-ddTHH:mm:ss"))" if ((Get-Item -Path $restoreFileName).LastWriteTime.ToString("yyyy-MM-ddTHH:mm:ss") -ne $_.LastWriteTime) {
Write-Log "LastWrite mismatch for file '$restoreFileName' with target value $((Get-Item -Path $restoreFileName).LastWriteTime.ToString("yyyy-MM-ddTHH:mm:ss")) expected $($_.LastWriteTime)"
$errorCreateCount = $errorCreateCount + 1 $errorCreateCount = $errorCreateCount + 1
$dateTimeValue = [Datetime]::ParseExact($_.CreationTime, 'yyyy-MM-ddTHH:mm:ss', $null) $dateTimeValue = [Datetime]::ParseExact($_.LastWriteTime, 'yyyy-MM-ddTHH:mm:ss', $null)
$fileValue = (Get-Item -Path $restoreFileName).CreationTime $fileValue = (Get-Item -Path $restoreFileName).LastWriteTime
$diff = ($dateTimeValue - $fileValue).Seconds $diff = ($dateTimeValue - $fileValue).Seconds
# Allow +/- 2 second discrepancy # Allow +/- 2 second discrepancy
if (($diff.Seconds -lt -2) -or ($diff.Seconds -gt 2)) { if (($diff.Seconds -lt -2) -or ($diff.Seconds -gt 2)) {
@ -1595,11 +1624,25 @@ Param(
} }
if ((Get-Item -Path $restoreFileName).Length -ne $_.Length) { if ((Get-Item -Path $restoreFileName).Length -ne $_.Length) {
$errorCount = $errorCount + 1 $errorCount = $errorCount + 1
Write-Log "Length mismatch for file '$restoreFileName' with target value $(Get-Item -Path $restoreFileName).Length)" Write-Log "Length mismatch for file '$restoreFileName' with target value $(Get-Item -Path $restoreFileName).Length) expected $($_.Length)"
} }
# Note that last / write access time is not checked by default as it will commonly be changed after restore # Note that last / write access time is not checked by default as it will commonly be changed after restore
if ($extendedCheck) { if ($extendedCheck) {
if ((Get-Item -Path $restoreFileName).CreationTime.ToString("yyyy-MM-ddTHH:mm:ss") -ne $_.CreationTime) {
Write-Log "Creation mismatch for file '$restoreFileName' with target value $((Get-Item -Path $restoreFileName).CreationTime.ToString("yyyy-MM-ddTHH:mm:ss")) expected $($_.CreationTime)"
$errorCreateCount = $errorCreateCount + 1
$dateTimeValue = [Datetime]::ParseExact($_.CreationTime, 'yyyy-MM-ddTHH:mm:ss', $null)
$fileValue = (Get-Item -Path $restoreFileName).CreationTime
$diff = ($dateTimeValue - $fileValue).Seconds
# Allow +/- 2 second discrepancy
if (($diff.Seconds -lt -2) -or ($diff.Seconds -gt 2)) {
$errorCount = $errorCount + 1
}
}
if ((Get-Item -Path $restoreFileName).LastAccessTime.ToString("yyyy-MM-ddTHH:mm:ss") -ne $_.LastAccessTime) { if ((Get-Item -Path $restoreFileName).LastAccessTime.ToString("yyyy-MM-ddTHH:mm:ss") -ne $_.LastAccessTime) {
$errorCount = $errorCount + 1 $errorCount = $errorCount + 1
Write-Log "Last access mismatch for file '$restoreFileName' with target value $((Get-Item -Path $restoreFileName).LastAccessTime.ToString("yyyy-MM-ddTHH:mm:ss"))" Write-Log "Last access mismatch for file '$restoreFileName' with target value $((Get-Item -Path $restoreFileName).LastAccessTime.ToString("yyyy-MM-ddTHH:mm:ss"))"

View File

@ -1,62 +1,282 @@
Function Get-ExifContents { Add-Type -AssemblyName System.Drawing
param(
$ImageStream, function Get-ExifContents {
[int] $ExifCode param(
) [Parameter(Mandatory)]
$ImageStream,
[Parameter(Mandatory)]
[int] $ExifCode,
[Switch] $Numeric,
[int] $Size = 0,
[int] $Parts = 1
)
Try { Try {
if (-not $ImageStream.PropertyIdList.Contains($ExifTagCode)) $list_id = $ImageStream.PropertyIdList
{ if ($list_id.IndexOf($ExifCode) -eq -1) {
$Value = "<empty>" if ($Numeric) {
$Value = 0
} else {
$Value = ""
}
} else { } else {
if ($Numeric -and $Size -eq 0) {
$Size = 8
}
$PropertyItem = $ImageStream.GetPropertyItem($ExifCode) $PropertyItem = $ImageStream.GetPropertyItem($ExifCode)
$valueBytes = $PropertyItem.Value if ($null -eq $PropertyItem) {
$Value = [System.Text.Encoding]::ASCII.GetString($valueBytes) if ($Numeric) {
$Value = 0
} else {
$Value = ""
}
} else {
$valueBytes = $PropertyItem.Value
if ($null -eq $valueBytes) {
if ($Numeric) {
$Value = 0
} else {
$Value = ""
}
} else {
if ($Numeric) {
$Value = MakeNumber -Num $valueBytes -Size $Size -Parts $Parts
} else {
$value = ""
0..($valueBytes.Length-1) | ForEach-Object {
if ($valueBytes[$_] -ne 0) {
$value = $value + [System.Text.Encoding]::ASCII.GetString($valueBytes[$_])
}
}
if ($null -ne $value -and $Size -gt 0 -and $Size -lt $Value.Length) {
$Value = $Value.Substring(0,$Size)
}
}
}
}
} }
} }
Catch{ Catch{
$Value = "<empty>" if ($Numeric) {
Write-Host "Type $($valueBytes.GetType())"
Write-Host "Error in exif: $_"
} else {
Write-Host "Error in exif: $_"
}
$Value = ""
} }
return $Value return $Value
} }
Function Get-ImageFileContents {
param(
[String] $ImageFile
)
function Get-ByteMultiplier {
param(
[Parameter(Mandatory)]
[int] $Factor
)
$byteMultiplier = 1
1..$Factor | ForEach-Object {
$byteMultiplier = $byteMultiplier * 256
}
return $byteMultiplier
}
function MakeNumber {
param(
[Parameter(Mandatory)]
[byte[]] $Num,
[int] $Size,
[int] $Parts = 1
)
if ($null -eq $Num) {
return "<null>"
}
if ($Num.Length -eq $Size -and $Parts -eq 1) {
if ($Size -eq 1) {
return ($Num[0])
}
if ($Size -eq 2) {
return ( $Num[0] + 256 * $Num[1] )
}
}
# GPS cords
if ($Num.Length -eq 24 -and $Parts -eq 3) {
$First =$Num[0] + (Get-ByteMultiplier 1) * $Num[1] + (Get-ByteMultiplier 2) * $Num[2] + (Get-ByteMultiplier 3) * $Num[3] ;
$Second=$Num[8] + (Get-ByteMultiplier 1) * $Num[9] + (Get-ByteMultiplier 2) * $Num[10] + (Get-ByteMultiplier 3) * $Num[11] ;
$Third=$Num[16] + 256 * $Num[17] + 65536 * $Num[18] + 16777216 * $Num[19] ;
return @($first, $second, $third)
}
# Shutter
if ($Num.Length -eq 8 -and $Parts -eq 2) {
$First =$Num[0] + (Get-ByteMultiplier 1) * $Num[1] + (Get-ByteMultiplier 2) * $Num[2] + (Get-ByteMultiplier 3) * $Num[3] ;
$Second=$Num[4] + (Get-ByteMultiplier 1) * $Num[5] + (Get-ByteMultiplier 2) * $Num[6] + (Get-ByteMultiplier 3) * $Num[7] ;
if ($first -gt 2147483648) {
$first = $first - (Get-ByteMultiplier 4)
}
if ($Second -gt 2147483648) {
$Second= $Second - (Get-ByteMultiplier 4)
}
if ($Second -eq 0) {
$Second= 1
}
if (($first -eq 1) -and ($Second -ne 1)) {
$first = "1"
}
return @($first, $second)
}
$First =$Num[0] + (Get-ByteMultiplier 1) * $Num[1] + (Get-ByteMultiplier 2) * $Num[2] + (Get-ByteMultiplier 3) * $Num[3] ;
return $first
}
function Get-ImageFileContents {
param(
[Parameter(Mandatory)]
[String] $ImageFile
)
if (!(Test-Path -Path $ImageFile)) {
Write-Host "File not found: $ImageFile"
break
}
Try { Try {
$fullPath = (Resolve-Path $ImageFile).Path
$fs = [System.IO.File]::OpenRead($fullPath)
$image = [System.Drawing.Image]::FromStream($fs, $false, $false)
$maker = Get-ExifContents -ImageStream $image -ExifCode 271 $fullPath = (Resolve-Path $ImageFile).Path
$model = Get-ExifContents -ImageStream $image -ExifCode 272
$version = Get-ExifContents -ImageStream $image -ExifCode 305 $fileStreamArgs = @($fullPath
$dateTime = Get-ExifContents -ImageStream $image -ExifCode 306 [System.IO.FileMode]::Open
$latRef = Get-ExifContents -ImageStream $image -ExifCode 1 [System.IO.FileAccess]::Read
$longRef = Get-ExifContents -ImageStream $image -ExifCode 3 [System.IO.FileShare]::Read
1024,
[System.IO.FileOptions]::SequentialScan
)
$fs = New-Object System.IO.FileStream -ArgumentList $fileStreamArgs
$image = [System.Drawing.Image]::FromStream($fs)
$val = Get-ExifContents -ImageStream $image -ExifCode 37378 -Numeric -Size 8 -Parts 2
if ($null -eq $val -or $val -eq "") {
$Aperture = ""
} else {
if ($val.Length -eq 2) {
$Aperture = "$($val[0]/$val[1])"
} else {
$Aperture = $val[0]
}
}
# Flash
$val = Get-ExifContents -ImageStream $image -ExifCode 37385 -Numeric -Size 2
if (($val % 2) -eq 1){
$Flash = $true
} else {
$Flash = $false
}
# Shutterspeed
$val = Get-ExifContents -ImageStream $image -ExifCode 33434 -Numeric -Size 8 -Parts 2
if ($null -eq $val -or $val -eq "") {
$Shutterspeed = ""
} else {
if ($val.Length -eq 2) {
$Shutterspeed = "$($val[0])/$($val[1])"
} else {
$Shutterspeed = $val[0]
}
}
# Latitude
$val = Get-ExifContents -ImageStream $image -ExifCode 2 -Numeric -Size 24 -Parts 3
if ($null -eq $val -or $val -eq "") {
$Latitude = ""
} else {
if ($val.Length -eq 3) {
$Latitude = "$($val[0]).$($val[1]).$($val[2])"
} else {
$Latitude = $val[0]
}
}
# Longitude
$val = Get-ExifContents -ImageStream $image -ExifCode 4 -Numeric -Size 24 -Parts 3
if ($null -eq $val -or $val -eq "") {
$Longitude = ""
} else {
if ($val.Length -eq 3) {
$Longitude = "$($val[0]).$($val[1]).$($val[2])"
} else {
$Longitude = $val[0]
}
}
$ExifData = [PSCustomObject][ordered]@{ $ExifData = [PSCustomObject][ordered]@{
File = $ImageFile File = $ImageFile
CameraMaker = $maker
CameraModel = $model DateTaken = Get-ExifContents -ImageStream $image -ExifCode 36867 -Size 19
SoftwareVersion = $version DateDigitized = Get-ExifContents -ImageStream $image -ExifCode 36868 -Size 19
DateTaken = $dateTime DateModified = Get-ExifContents -ImageStream $image -ExifCode 306 -Size 19
Author = Get-ExifContents -ImageStream $image -ExifCode 40093
Title = Get-ExifContents -ImageStream $image -ExifCode 40091 #270
Subject = Get-ExifContents -ImageStream $image -ExifCode 40095
Comments = Get-ExifContents -ImageStream $image -ExifCode 40092 #37510
Keywords = Get-ExifContents -ImageStream $image -ExifCode 40094
Artist = Get-ExifContents -ImageStream $image -ExifCode 315
Copyright = Get-ExifContents -ImageStream $image -ExifCode 33432
Height = Get-ExifContents -ImageStream $image -ExifCode 40963 -Numeric
Width = Get-ExifContents -ImageStream $image -ExifCode 40962 -Numeric
PixelX = Get-ExifContents -ImageStream $image -ExifCode 40962 -Numeric -Size 8
PixelY = Get-ExifContents -ImageStream $image -ExifCode 40963 -Numeric -Size 8
ResolutionX = Get-ExifContents -ImageStream $image -ExifCode 282 -Numeric
ResolutionY = Get-ExifContents -ImageStream $image -ExifCode 283 -Numeric
CameraMaker = Get-ExifContents -ImageStream $image -ExifCode 271
CameraModel = Get-ExifContents -ImageStream $image -ExifCode 272
CameraLabel = Get-ExifContents -ImageStream $image -ExifCode 51105
SoftwareVersion = Get-ExifContents -ImageStream $image -ExifCode 305
LatitudeRef = Get-ExifContents -ImageStream $image -ExifCode 1
Latitude = $Latitude
LongitudeRef = Get-ExifContents -ImageStream $image -ExifCode 3
Longitude = $Longitude
ExifVersion = Get-ExifContents -ImageStream $image -ExifCode 36864
Flash = $Flash
Iso = Get-ExifContents -ImageStream $image -ExifCode 34855 -Numeric
FocalLength = Get-ExifContents -ImageStream $image -ExifCode 37386 -Numeric -Size 2
ShutterSpeed = $Shutterspeed
Aperture = $Aperture
FNumber = Get-ExifContents -ImageStream $image -ExifCode 33437 -Numeric -Size 4
} }
$image.dispose() $image.dispose()
$fs.Close() $fs.Close()
Write-Host " File '$($ExifData.File)' and maker '$($ExifData.CameraMaker)' "
return $ExifData return $ExifData
} }
Catch { Catch {
Write-Host "Error: $_"
Write-Error "Error Opening '$ImageFile'" Write-Error "Error Opening '$ImageFile'"
if ($image) { if ($image) {
$image.dispose() $image.dispose()
@ -64,8 +284,9 @@ param(
if ($fs) { if ($fs) {
$fs.close() $fs.close()
} }
break
return $null return $null
} }
} }
#Get-ImageFileContents -ImageFile "E:\tom\Projects\powershell\ptrFiles\assets\03.jpg"

View File

@ -45,8 +45,9 @@
.Parameter Action .Parameter Action
Action to perform, which can be: Action to perform, which can be:
- Install : Install 7Zip4PowerShell and other modules
- Pack : Archive the contents of a folder(s) - Pack : Archive the contents of a folder(s)
- Put : Send the archive to AWS S3 or Backblaze
- Get : Receive the archive from AWS S3 or Backblaze
- Unpack : Unpack the archive, but no reconfile is performed - Unpack : Unpack the archive, but no reconfile is performed
- Reconcile : Reconcile the contents in the restored folder - Reconcile : Reconcile the contents in the restored folder
- ReconcileFile : Generate reconfile file. The pack process does this. - ReconcileFile : Generate reconfile file. The pack process does this.
@ -194,75 +195,92 @@
#> #>
[CmdletBinding()]
param ( param (
[Parameter(Mandatory)][String] $Action, [Parameter(Mandatory)]
[Parameter(Mandatory)][String] $Path, [Alias("Task")]
[String] $Action,
[Parameter(Mandatory)]
[Alias("Directory","DirectoryPath","Folder","FolderPath")]
[String] $Path,
[String] $RecipientKey, [String] $RecipientKey,
[Alias("Password")]
[String] $SecretKey, [String] $SecretKey,
[Alias("CompressFile")]
[String] $ArchiveFile, [String] $ArchiveFile,
[String] $RootFolder, [String] $RootFolder,
[String] $FileFilter, [String] $FileFilter,
[String] $ReconcileFile, [String] $ReconcileFile,
[String] $SecretFile, [String] $SecretFile,
[Alias("Profile", "Username")]
[String] $CloudProfile, [String] $CloudProfile,
[switch] $ExcludeHash, [switch] $ExcludeHash,
[String] $LogPath
[switch] $IncludeExif,
[String] $LogPath = ""
) )
Import-Module .\PeterDocs Import-Module .\PeterDocs
$actioned = $false $actioned = $false
if ($action -eq "Install") {
$actioned = $true
if ($cloudProfile -eq "") {
Install-Module -Name 7Zip4Powershell -Scope CurrentUser
Install-Module -Name AWS.Tools.Installer -Scope CurrentUser
Install-Module -Name AWS.Tools.S3 -Scope CurrentUser
Install-Module -Name Meerkat.PeterDocs -Scope CurrentUser
} else {
Install-Module -Name 7Zip4Powershell -Scope $cloudProfile
Install-Module -Name AWS.Tools.Installer -Scope $cloudProfile
Install-Module -Name AWS.Tools.S3 -Scope $cloudProfile
Install-Module -Name Meerkat.PeterDocs -Scope $cloudProfile
}
}
if ($action -eq "Pack") { if ($action -eq "Pack") {
$actioned = $true $actioned = $true
Compress-Peter -TransferFolder $path -Secret $secret -ArchiveFile $archiveFile -ReconcileFile $reconcileFile -RootFolder $rootFolder -FileFilter $fileFilter if ($ExcludeHash) {
Compress-Peter -ExcludeHash -SourceFolder $path -SecretKey $SecretKey -ArchiveFile $archiveFile -ReconcileFile $reconcileFile -RootFolder $rootFolder -FileFilter $fileFilter -LogPath $LogPath -IncludeExif
} else {
Compress-Peter -SourceFolder $path -SecretKey $SecretKey -ArchiveFile $archiveFile -ReconcileFile $reconcileFile -RootFolder $rootFolder -FileFilter $fileFilter -LogPath $LogPath -IncludeExif
}
} }
if ($action -eq "Put") { if ($action -eq "Put") {
$actioned = $true $actioned = $true
Send-Peter -ArchiveFile $archiveFile -TargetPath $path -SecretFile $secretFile -TargetProfile $cloudProfile Send-Peter -ArchiveFile $archiveFile -TargetPath $path -SecretFile $secretFile -TargetProfile $cloudProfile -LogPath $LogPath
} }
if ($action -eq "Get") { if ($action -eq "Get") {
$actioned = $true $actioned = $true
Receive-Peter -ArchiveFile $archiveFile -SourcePath $path -SecretFile $secretFile -SourceProfile $cloudProfile Receive-Peter -ArchiveFile $archiveFile -SourcePath $path -SecretFile $secretFile -SourceProfile $cloudProfile -LogPath $LogPath
} }
if ($action -eq "Unpack") { if ($action -eq "Unpack") {
$actioned = $true $actioned = $true
Expand-Peter -RestoreFolder $path -Secret $secret -ArchiveFile $ArchiveFile Expand-Peter -RestoreFolder $path -SecretKey $secretKey -SecretFile $secretFile -ArchiveFile $ArchiveFile -LogPath $LogPath
} }
if ($action -eq "ReconcileFile") { if ($action -eq "ReconcileFile") {
$actioned = $true $actioned = $true
Build-PeterReconcile -ReconcileFile $reconcileFile -FolderName $path -Feedback -RootFolder $rootFolder -FileFilter $fileFilter if ($ExcludeHash) {
New-PeterReconcile -ExcludeHash -ReconcileFile $reconcileFile -SourceFolder $path -Feedback -RootFolder $rootFolder -FileFilter $fileFilter -LogPath LogPath
} else {
New-PeterReconcile -ReconcileFile $reconcileFile -SourceFolder $path -Feedback -RootFolder $rootFolder -FileFilter $fileFilter -LogPath LogPath -IncludeExif
}
} }
if ($action -eq "Reconcile") { if ($action -eq "Reconcile") {
$actioned = $true $actioned = $true
Compare-Peter -ReconcileFile $reconcileFile -RestoreFolder $path -RootFolder $rootFolder if ($ExcludeHash) {
Compare-Peter -ExcludeHash -ReconcileFile $reconcileFile -RestoreFolder $path -RootFolder $rootFolder -LogPath $LogPath
} else {
Compare-Peter -ReconcileFile $reconcileFile -RestoreFolder $path -RootFolder $rootFolder -LogPath $LogPath
}
} }
if ($action -eq "ArchiveInformation") { if ($action -eq "ArchiveInformation") {
@ -286,50 +304,6 @@ Import-Module .\PeterDocs
} }
if ($action -eq "MakeCert") {
$actioned = $true
if (($RecipientKey -eq "") -and ($SecretKey -eq "")) {
Write-Host "Recipient Key Name required to create a standard certificate" -ForegroundColor Red
return
}
if ($Path -ne "Cert:\CurrentUser\My") {
Write-Host "The -Path value needs to be 'Cert:\CurrentUser\My'" -ForegroundColor Red
return
}
Write-Host "Making a file encryption certificate"
New-SelfSignedCertificate -Subject $RecipientKey -KeyFriendlyName $RecipientKey -DnsName $RecipientKey -CertStoreLocation $Path -KeyUsage KeyEncipherment,DataEncipherment, KeyAgreement -Type DocumentEncryptionCert
}
if ($action -eq "ListCert") {
$actioned = $true
if ($Path -ne "Cert:\CurrentUser\My") {
Write-Host "The -Path value needs to be 'Cert:\CurrentUser\My'" -ForegroundColor Red
return
}
Write-Host "Listing encryption certificates"
if ($RecipientKey -eq "")
{
Get-Childitem -Path $Path -DocumentEncryptionCert
} else {
Write-Host ""
Write-Host " PSParentPath: Microsoft.PowerShell.Security\Certificate::$Path"
Write-Host ""
Write-Host "Thumbprint Subject"
Write-Host "---------- -------"
Get-Childitem -Path $Path -DocumentEncryptionCert | ForEach-Object {
if ($_.Subject -eq ("CN=$RecipientKey"))
{
Write-Host "$($_.Thumbprint) $($_.Subject)"
}
}
}
}
if (!($actioned)) if (!($actioned))
{ {
Write-Host "Unknown action '$action'. No processing performed" -ForegroundColor Red Write-Host "Unknown action '$action'. No processing performed" -ForegroundColor Red
@ -340,7 +314,6 @@ Import-Module .\PeterDocs
Write-Host " Unpack : Unpack folder contents from secure 7Zip file" Write-Host " Unpack : Unpack folder contents from secure 7Zip file"
Write-Host " Reconcile : Reconcile files in unpack folder with list of packed files" Write-Host " Reconcile : Reconcile files in unpack folder with list of packed files"
Write-Host " ReconcileFile : Generate a reconcile file without packing" Write-Host " ReconcileFile : Generate a reconcile file without packing"
Write-Host " Install : Install required packages"
Write-Host " ArchiveInformation : Fetch archive information from archive file" Write-Host " ArchiveInformation : Fetch archive information from archive file"
Write-Host "" Write-Host ""
@ -348,4 +321,3 @@ Import-Module .\PeterDocs
Write-Host " Get-Help .\ptrDocs.ps1" Write-Host " Get-Help .\ptrDocs.ps1"
} }

View File

@ -38,6 +38,23 @@ resolution of digital cameras.
## Usage ## Usage
Some basic commands in sequence are demonstrated below:
```powershell
# Create the archive file
Compress-Peter -SourceFolder "~/Pictures/" -Secret "c0mpleX%S3cret"
# Send the archive to S3
Send-Peter -ArchiveFile "PETERDOCS_20210625_1245.7z" -TargetPath "s3://bucketname/pathpeter/PETERDOCS_20210625_1245.7z"
# Fetch the archive from S3
Receive-Peter -ArchiveFile "myarchive.7z" -SourcePath "s3://bucketname/pathpeter/PETERDOCS_20210625_1245.7z"
# Expand the archive
Expand-Peter -RestoreFolder "c:\backup\pictures" -Secret "c0mpleX%S3cret" -ArchiveFile "myarchive.7z"
# Compare the restored files
Compare-Peter -RestoreFolder "c:\backup\pictures"
```
The above commands are using the default settings for certain options
Packages source folder contents into a 7ZIP file, adding a reconciliation Packages source folder contents into a 7ZIP file, adding a reconciliation
file to the 7ZIP file and then encrypting the contents. Send file to the 7ZIP file and then encrypting the contents. Send
@ -78,10 +95,12 @@ will add text content to the same log file. The default log name takes the form
You will need to install the PeterDocs module from the PowerShell gallery or You will need to install the PeterDocs module from the PowerShell gallery or
via local file NuGet package file if Internet access is limited. via local file NuGet package file if Internet access is limited.
See the [Advanced Usage](Docs/Advanced.md) for more advanced options.
## Further Reading ## Further Reading
[Design](Design.md) [Design](Docs/Design.md)
[Install](Install.md) [Install](Docs/Install.md)
[Compress](Compress.md) [Compress](Docs/Compress.md)