diff --git a/Docs/Advanced.md b/Docs/Advanced.md new file mode 100644 index 0000000..8f6271e --- /dev/null +++ b/Docs/Advanced.md @@ -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. diff --git a/Compress.md b/Docs/Compress.md similarity index 100% rename from Compress.md rename to Docs/Compress.md diff --git a/Design.md b/Docs/Design.md similarity index 100% rename from Design.md rename to Docs/Design.md diff --git a/Encryption.md b/Docs/Encryption.md similarity index 100% rename from Encryption.md rename to Docs/Encryption.md diff --git a/Expand.md b/Docs/Expand.md similarity index 100% rename from Expand.md rename to Docs/Expand.md diff --git a/Install.md b/Docs/Install.md similarity index 100% rename from Install.md rename to Docs/Install.md diff --git a/ReceiveArchive.md b/Docs/ReceiveArchive.md similarity index 100% rename from ReceiveArchive.md rename to Docs/ReceiveArchive.md diff --git a/Reconcile.md b/Docs/Reconcile.md similarity index 100% rename from Reconcile.md rename to Docs/Reconcile.md diff --git a/SendArchive.md b/Docs/SendArchive.md similarity index 100% rename from SendArchive.md rename to Docs/SendArchive.md diff --git a/PeterDocs/PeterDocs.psd1 b/PeterDocs/PeterDocs.psd1 index d784c74..51db17c 100644 Binary files a/PeterDocs/PeterDocs.psd1 and b/PeterDocs/PeterDocs.psd1 differ diff --git a/PeterDocs/PeterDocs.psm1 b/PeterDocs/PeterDocs.psm1 index bc792a1..a8c193b 100644 --- a/PeterDocs/PeterDocs.psm1 +++ b/PeterDocs/PeterDocs.psm1 @@ -31,11 +31,12 @@ $global:default_reconcileFile = "##protect_transfer_reconcile_files##.csv" +$global:default_exifFile = "##peter_exif##.csv" $global:LogPathName = "" - +$global:MetadataPathName = Join-Path -Path ".\" -ChildPath ".peter-metadata" function Open-Log { - + $dateTimeStart = Get-Date -f "yyyy-MM-dd HH:mm:ss" Write-Log "***********************************************************************************" Write-Log "* Start of processing: [$dateTimeStart]" @@ -49,7 +50,7 @@ function Write-Log { ) $date = Get-Date -f "yyyy-MM-dd" - + if (($null -eq $global:LogPathName) -or ($global:LogPathName -eq "")) { $global:LogPathName = Join-Path -Path ".\" -ChildPath "Logs" @@ -100,7 +101,8 @@ param( function Test-PasswordQuality { Param( - [Parameter(Mandatory)][String] $TestPassword + [Parameter(Mandatory)] + [String] $TestPassword ) $qualityMatch = $true @@ -142,20 +144,20 @@ function Get-ConvenientFileSize ) - if ($totalFileSize -ge 1000000000000) { + if ($totalFileSize -ge 1TB) { $totalRightLabel = "TB" - $totalFileXbytes = [math]::Round(($size / 1000000000000), 2) + $totalFileXbytes = [math]::Round(($size / 1TB), 2) } else { - if ($totalFileSize -ge 1000000000) { + if ($totalFileSize -ge 1GB) { $totalRightLabel = "GB" - $totalFileXbytes = [math]::Round(($size / 1000000000), 2) + $totalFileXbytes = [math]::Round(($size / 1GB), 2) } else { - if ($totalFileSize -ge 1000000) { + if ($totalFileSize -ge 1MB) { $totalRightLabel = "MB" - $totalFileXbytes = [math]::Round(($size / 1000000), 2) + $totalFileXbytes = [math]::Round(($size / 1MB), 2) } else { $totalRightLabel = "KB" - $totalFileXbytes = [math]::Round(($size / 1000), 2) + $totalFileXbytes = [math]::Round(($size / 1KB), 2) } } } @@ -230,11 +232,11 @@ function Get-ConvenientFileSize .Example # 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( [Parameter(Mandatory)][String] $SourceFolder, @@ -256,7 +258,7 @@ Param( if ($Feedback) { 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: ReconcileFile Value: $ReconcileFile " Write-Log "Parameter: RootFolder Value: $RootFolder " @@ -292,7 +294,16 @@ Param( Write-Log "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 $totalFileSize = 0 @@ -307,7 +318,7 @@ Param( 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("@")) { Write-Log "Using @ file '$($SourceFolder.Substring(1))'" @@ -341,13 +352,18 @@ Param( } $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 + ','+$_.Length+',"'+$sourceHash+'","'+ $_.Directory + '","' + $_.Name + '","' + $_.Attributes+'","'+$_.Extension+'"' - - if ($IncludeExif) { - $null = Get-ImageFileContents -ImageFile $($_.FullName) - } + $record = $record + ','+$_.Length+',"'+$sourceHash+'"' + $record = $record + ',"'+ $_.Directory + '","' + $_.Name + '","' + $_.Attributes+'","'+$_.Extension+'"' 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 + ','+$_.Length+',"'+$sourceHash+'","'+ $_.Directory + '","' + $_.Name + '","' + $_.Attributes+'","'+$_.Extension+'"' + Add-Content -Path $ReconcileFile -Value $record + 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] $SecretFile, [switch] $ExcludeHash, + [switch] $IncludeExif, [String] $RootFolder, [String] $LogPath @@ -596,6 +617,7 @@ Param( Write-Log "Parameter: FileFilter Value: $FileFilter " Write-Log "Parameter: SecretFile Value: $SecretFile " Write-Log "Parameter: ExcludeHash Value: $ExcludeHash " + Write-Log "Parameter: IncludeExif Value: $IncludeExif " Write-Log "Parameter: LogPath Value: $LogPath " Write-Log "" @@ -668,7 +690,10 @@ Param( 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 "") @@ -727,9 +752,9 @@ Param( [int] $archiveFileCount = $archiveInfo.FilesCount 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 { - 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 )) { Write-Log "Reconcile file '$ReconcileFile' was not created. See any previous errors" @@ -738,11 +763,11 @@ Param( return } - Write-Log "Add reconcile file '$ReconcileFile' to file '$ArchiveFile'" - $fullReconcileName = (Get-Item $ReconcileFile).FullName + Write-Log "Add folder '$global:MetadataPathName' to file '$ArchiveFile'" + $fullMetadatName = (Get-Item $global:MetadataPathName).FullName $fullZipName = (Get-Item $ArchiveFile).FullName - Compress-7Zip -Path $fullReconcileName -ArchiveFileName $fullZipName -Format SevenZip -Append -Password $secret -EncryptFilenames - Remove-Item $fullReconcileName + Compress-7Zip -Path $fullMetadatName -ArchiveFileName $fullZipName -PreserveDirectoryRoot -Format SevenZip -Append -Password $secret -EncryptFilenames + #Remove-Item $fullMetadatName -Recurse Write-Log "Archive file '$ArchiveFile' created from folder '$SourceFolder'" Write-Host "Archive file '$ArchiveFile' created from folder '$SourceFolder'" -ForegroundColor Green @@ -1506,6 +1531,7 @@ Param( [Parameter(Mandatory)][String] $RestoreFolder, [String] $ReconcileFile, [String] $RootFolder, + [Switch] $ExcludeHash, [Switch] $ExtendedCheck, [String] $LogPath ) @@ -1534,7 +1560,7 @@ Param( 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-Host "Using default reconciliation file '$ReconcileFile'" If (!(Test-Path -Path $ReconcileFile )) { @@ -1572,21 +1598,24 @@ Param( $restoreFileName = $(Join-Path -Path $RestoreFolder -ChildPath $_.FullName) } If (Test-Path -Path $restoreFileName ) { - if ($_.Hash -ne "") { - $targetHash= (Get-FileHash -Path $restoreFileName).Hash - if ($_.Hash -ne $targetHash) { - $errorCount = $errorCount + 1 - Write-Log "Hash mismatch for file '$restoreFileName' with target value $targetHash" + if (!($ExcludeHash)) { + if ($_.Hash -ne "") { + $targetHash= (Get-FileHash -Path $restoreFileName).Hash + if ($_.Hash -ne $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 - $dateTimeValue = [Datetime]::ParseExact($_.CreationTime, 'yyyy-MM-ddTHH:mm:ss', $null) - $fileValue = (Get-Item -Path $restoreFileName).CreationTime + $dateTimeValue = [Datetime]::ParseExact($_.LastWriteTime, 'yyyy-MM-ddTHH:mm:ss', $null) + $fileValue = (Get-Item -Path $restoreFileName).LastWriteTime $diff = ($dateTimeValue - $fileValue).Seconds # Allow +/- 2 second discrepancy if (($diff.Seconds -lt -2) -or ($diff.Seconds -gt 2)) { @@ -1595,11 +1624,25 @@ Param( } if ((Get-Item -Path $restoreFileName).Length -ne $_.Length) { $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 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) { $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"))" diff --git a/PeterDocs/PeterExif.ps1 b/PeterDocs/PeterExif.ps1 index ff8f363..aaac7c9 100644 --- a/PeterDocs/PeterExif.ps1 +++ b/PeterDocs/PeterExif.ps1 @@ -1,62 +1,282 @@ -Function Get-ExifContents { -param( - $ImageStream, - [int] $ExifCode -) +Add-Type -AssemblyName System.Drawing + +function Get-ExifContents { + param( + [Parameter(Mandatory)] + $ImageStream, + [Parameter(Mandatory)] + [int] $ExifCode, + [Switch] $Numeric, + [int] $Size = 0, + [int] $Parts = 1 + ) Try { - if (-not $ImageStream.PropertyIdList.Contains($ExifTagCode)) - { - $Value = "" + $list_id = $ImageStream.PropertyIdList + if ($list_id.IndexOf($ExifCode) -eq -1) { + if ($Numeric) { + $Value = 0 + } else { + $Value = "" + } } else { + + if ($Numeric -and $Size -eq 0) { + $Size = 8 + } + $PropertyItem = $ImageStream.GetPropertyItem($ExifCode) - $valueBytes = $PropertyItem.Value - $Value = [System.Text.Encoding]::ASCII.GetString($valueBytes) + if ($null -eq $PropertyItem) { + 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{ - $Value = "" + if ($Numeric) { + Write-Host "Type $($valueBytes.GetType())" + Write-Host "Error in exif: $_" + } else { + Write-Host "Error in exif: $_" + } + $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 "" + } + + 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 { - $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 - $model = Get-ExifContents -ImageStream $image -ExifCode 272 - $version = Get-ExifContents -ImageStream $image -ExifCode 305 - $dateTime = Get-ExifContents -ImageStream $image -ExifCode 306 - $latRef = Get-ExifContents -ImageStream $image -ExifCode 1 - $longRef = Get-ExifContents -ImageStream $image -ExifCode 3 - + $fullPath = (Resolve-Path $ImageFile).Path + + $fileStreamArgs = @($fullPath + [System.IO.FileMode]::Open + [System.IO.FileAccess]::Read + [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]@{ File = $ImageFile - CameraMaker = $maker - CameraModel = $model - SoftwareVersion = $version - DateTaken = $dateTime + + DateTaken = Get-ExifContents -ImageStream $image -ExifCode 36867 -Size 19 + DateDigitized = Get-ExifContents -ImageStream $image -ExifCode 36868 -Size 19 + 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() $fs.Close() - Write-Host " File '$($ExifData.File)' and maker '$($ExifData.CameraMaker)' " - return $ExifData } Catch { + Write-Host "Error: $_" Write-Error "Error Opening '$ImageFile'" if ($image) { $image.dispose() @@ -64,8 +284,9 @@ param( if ($fs) { $fs.close() } + break return $null } } - \ No newline at end of file +#Get-ImageFileContents -ImageFile "E:\tom\Projects\powershell\ptrFiles\assets\03.jpg" diff --git a/ptrDocs.ps1 b/PeterTask.ps1 similarity index 77% rename from ptrDocs.ps1 rename to PeterTask.ps1 index 12dfa0e..29ae730 100644 --- a/ptrDocs.ps1 +++ b/PeterTask.ps1 @@ -45,8 +45,9 @@ .Parameter Action Action to perform, which can be: - - Install : Install 7Zip4PowerShell and other modules - 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 - Reconcile : Reconcile the contents in the restored folder - ReconcileFile : Generate reconfile file. The pack process does this. @@ -194,75 +195,92 @@ #> +[CmdletBinding()] param ( - [Parameter(Mandatory)][String] $Action, - [Parameter(Mandatory)][String] $Path, + [Parameter(Mandatory)] + [Alias("Task")] + [String] $Action, + + [Parameter(Mandatory)] + [Alias("Directory","DirectoryPath","Folder","FolderPath")] + [String] $Path, + [String] $RecipientKey, + + [Alias("Password")] [String] $SecretKey, + + [Alias("CompressFile")] [String] $ArchiveFile, + [String] $RootFolder, + [String] $FileFilter, + [String] $ReconcileFile, + [String] $SecretFile, + + [Alias("Profile", "Username")] [String] $CloudProfile, + [switch] $ExcludeHash, - [String] $LogPath + + [switch] $IncludeExif, + + [String] $LogPath = "" ) Import-Module .\PeterDocs - $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") { $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") { $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") { $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") { $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") { $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") { $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") { @@ -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)) { 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 " Reconcile : Reconcile files in unpack folder with list of packed files" 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 "" @@ -348,4 +321,3 @@ Import-Module .\PeterDocs Write-Host " Get-Help .\ptrDocs.ps1" } - diff --git a/README.md b/README.md index 13da793..acf5a5e 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,23 @@ resolution of digital cameras. ## 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 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 via local file NuGet package file if Internet access is limited. +See the [Advanced Usage](Docs/Advanced.md) for more advanced options. + ## Further Reading -[Design](Design.md) +[Design](Docs/Design.md) -[Install](Install.md) +[Install](Docs/Install.md) -[Compress](Compress.md) +[Compress](Docs/Compress.md)