1 #Requires -Version 5.0
2 Set-StrictMode -Version Latest
3 
4 <#
5     .SYNOPSIS
6     Updates openHAB to the latest version.
7     .DESCRIPTION
8     The Update-openHAB function performs the necessary tasks to update openHAB.
9     .PARAMETER OHDirectory
10     The directory where openHAB is installed (default: current directory).
11     .PARAMETER OHVersion
12     The version to upgrade to.
13     .PARAMETER Snapshot
14     DEPRECATED - Upgrade to a snapshot version ($true) or a release version ($false) (default: $false)
15     DEPRECATED - Please specify "-snapshot" in the OHVersion instead (ex: "3.0.0-SNAPSHOT")
16     .PARAMETER AutoConfirm
17     Automatically confirm update (used for headless mode)
18     .EXAMPLE
19     Update the openHAB distribution in the current directory to the current stable version
20     Update-openHAB
21     .EXAMPLE
22     Update the openHAB distribution in the C:\oh-snapshot directory to the next snapshot version
23     Update-openHAB -OHDirectory C:\oh-snapshot -OHVersion 3.0.0-SNAPSHOT
24 #>
25 
26 ###########################################################################
27 # NOTE: changes in this script should be reflected in update.sh as well
28 ###########################################################################
29 
Update-openHAB()30 Function Update-openHAB() {
31     [CmdletBinding()]
32     param(
33         [Parameter(ValueFromPipeline = $True)]
34         [string]$OHDirectory = ".",
35         [Parameter(ValueFromPipeline = $True)]
36         [string]$OHVersion,
37         [Parameter(ValueFromPipeline = $True)]
38         [boolean]$Snapshot = $false,
39         [Parameter(ValueFromPipeline = $True)]
40         [boolean]$AutoConfirm = $false,
41         [Parameter(ValueFromPipeline = $True)]
42         [boolean]$SkipNew = $false,          # sssh - secret switch ;)
43         [Parameter(ValueFromPipeline = $True)]
44         [boolean]$KeepUpdateScript = $false  # sssh - secret switch ;)
45     )
46 
47     # Downloads the URL into a file showing a progress meter.  Any error will be thrown to the caller
48 
49     function DownloadFiles() {
50         param(
51             [Parameter(Mandatory = $True)]
52             [string] $URL,
53             [Parameter(Mandatory = $True)]
54             [string] $OutputFile
55         )
56 
57         # Create the request
58         [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12;
59         $uri = New-Object "System.Uri" "$URL"
60         $request = [System.Net.HttpWebRequest]::Create($uri)
61         $request.set_Timeout(15000)
62 
63         #Get the response (along with the total size)
64         $response = $request.GetResponse()
65         $totalLength = [System.Math]::Floor($response.get_ContentLength()/1024)
66 
67         try {
68             # Gets the response stream and setup the buffer
69             $responseStream = $response.GetResponseStream()
70             $targetStream = New-Object -TypeName System.IO.FileStream -ArgumentList $Outputfile, Create
71             $buffer = new-object byte[] 10KB
72 
73             # Save console settings
74             $startTop = [System.Console]::CursorTop
75             $startVisibility = [System.Console]::CursorVisible
76             $startColor = [System.Console]::ForegroundColor
77 
78             # Process each chunk into the output file (updating the progress meter along the way)
79             try {
80                 [System.Console]::CursorVisible = $False
81                 [System.Console]::ForegroundColor = "Blue"
82                 $count = $responseStream.Read($buffer,0,$buffer.length)
83                 $downloadedBytes = $count
84                 while ($count -gt 0)
85                 {
86                     $bytes = [System.Math]::Floor($downloadedBytes/1024)
87                     $perc = [System.Math]::Floor(($bytes / $totalLength) * 100)
88                     [System.Console]::CursorLeft = 0
89                     [System.Console]::CursorTop = $startTop
90                     [System.Console]::Write("Downloaded {0}K of {1}K [{2}%]", $bytes, $totalLength, $perc)
91                     $targetStream.Write($buffer, 0, $count)
92                     $count = $responseStream.Read($buffer,0,$buffer.length)
93                     $downloadedBytes = $downloadedBytes + $count
94                 }
95                 Write-Host "`nFinished Download"
96             } finally {
97                 # Set the console settings back
98                 [System.Console]::CursorVisible = $startVisibility
99                 [System.Console]::ForegroundColor = $startColor
100             }
101         } finally {
102             # Cleanup resources
103             if ($targetStream) {
104                 $targetStream.Flush()
105                 $targetStream.Close()
106                 $targetStream.Dispose()
107             }
108             if ($responseStream) {
109                 $responseStream.Dispose()
110             }
111         }
112     }
113 
114     # This function 'normalizes' the version number - creates left 0 padded segments "0000.0000.0000" that can be compared against
115     function NormalizeVersionNumber() {
116         param(
117             [Parameter(Mandatory = $True)]
118             [string] $VersionNumber
119         )
120 
121         $parts = $VersionNumber.Split(".")
122         if ($parts.Length -eq 2) {
123             $parts += "0"
124         }
125         if ($parts.Length -ne 3) {
126             throw "$VersionNumber is not formatted correctly (d.d.d)"
127         }
128 
129         $rc = "";
130         $parts | ForEach-Object {
131             $rc += $_.PadLeft(5 - $_.Length, '0')
132             $rc += "."
133         }
134         return $rc.Substring(0, $rc.Length - 1)
135     }
136 
137     # This function will process a command from the upgrade.lst file (called from ProcessVersionChange)
138     function ProcessCommand() {
139         param(
140             [Parameter(Mandatory = $True)]
141             [string] $Line
142         )
143 
144         # Use of global variables (not passed in)
145         $Line = $Line.Replace("`$OPENHAB_USERDATA", $OHUserData)
146         $Line = $Line.Replace("`$OPENHAB_CONF", $OHConf)
147         $Line = $Line.Replace("`$OPENHAB_HOME", $OHDirectory)
148         $Line = $Line.Replace("`$OPENHAB_RUNTIME", $OHRuntime)
149 
150         # Split the line into it's distinct parts
151         $parts = $Line.Split(";")
152 
153         # blank line - simply return
154         if ($parts.length -eq 0) {
155             return;
156         }
157 
158         # If default - rename an item to "x.bak" (assumes a new version of this file will be added by the upgrade process)
159         if ($parts[0] -eq "DEFAULT") {
160             if ($parts.length -le 1) {
161                 Write-Host -ForegroundColor Red "Badly formatted: $Line"
162             }
163             else {
164                 try {
165                     Rename-Item -Path $parts[1] "$parts[1].bak" -ErrorAction Stop
166                     Write-Host -ForegroundColor Cyan "$($parts[1]) renamed to $($parts[1]).bak"
167                 }
168                 catch {
169                     Write-Host -ForegroundColor Yellow "Could not rename $($parts[1]) to $($parts[1]).bak"
170                 }
171             }
172         }
173 
174         # Deletes an item
175         ElseIf ($parts[0] -eq "DELETEDIR" -or $parts[0] -eq "DELETE") {
176             if ($parts.length -le 1) {
177                 Write-Host -ForegroundColor Red "Badly formatted: $Line"
178             }
179             else {
180                 try {
181                     if ($parts[0] -eq "DELETEDIR") {
182                         DeleteIfExists $parts[1] $True
183                     } else {
184                         DeleteIfExists $parts[1]
185                     }
186                     Write-Host -ForegroundColor Cyan "Deleted $($parts[1])"
187                 }
188                 catch {
189                     Write-Host -ForegroundColor Yellow "Could not delete $($parts[1])"
190 
191                 }
192             }
193         }
194 
195         # Moves an item
196         ElseIf ($parts[0] -eq "MOVE") {
197             if ($parts.length -le 2) {
198                 Write-Host -ForegroundColor Red "Badly formatted: $Line"
199             }
200             else {
201                 try {
202                     Move-Item -Path $parts[1] -Destination $parts[2] -ErrorAction Stop
203                     Write-Host -ForegroundColor Cyan "Moved $($parts[1]) to $($parts[2])"
204                 }
205                 catch {
206                     Write-Host -ForegroundColor Yellow "Could not move $($parts[1]) to $($parts[2])"
207                 }
208             }
209         }
210 
211         # Replaces text in a file
212         ElseIf ($parts[0] -eq "REPLACE") {
213             if ($parts.length -le 3) {
214                 Write-Host -ForegroundColor Red "Badly formatted: $Line"
215             }
216             else {
217                 try {
218                     (Get-Content $parts[3]).replace($parts[1], $parts[2]) | Set-Content $parts[3]
219                     Write-Host -ForegroundColor Cyan "Replaced string $($parts[1]) to $($parts[2]) in file $($parts[3])"
220                 }
221                 catch {
222                     Write-Host -ForegroundColor Yellow "Could not replace string $($parts[1]) to $($parts[2]) in file $($parts[3])"
223                 }
224             }
225         }
226 
227         # Shows a note (console message with a green label)
228         ElseIf ($parts[0] -eq "NOTE") {
229             if ($parts.length -le 1) {
230                 Write-Host -ForegroundColor Red "Badly formatted: $Line"
231             }
232             else {
233                 Write-Host -ForegroundColor Green "Note: " -NoNewLine
234                 Write-Host $parts[1]
235             }
236         }
237         # Shows a note (console message with a red label)
238         ElseIf ($parts[0] -eq "ALERT") {
239             if ($parts.length -le 1) {
240                 Write-Host -ForegroundColor Red "Badly formatted: $Line"
241             }
242             else {
243                 Write-Host -ForegroundColor Red "Warning: " -NoNewLine
244                 Write-Host $parts[1]
245             }
246         }
247         Else {
248             Write-Host -ForegroundColor Red "Unknown command: $Line"
249         }
250     }
251 
252     # Processes the update.lst file for the specific section, and version (going from x.x.x to x.x.x).
253     # A boolean is returned indicating whether we found any commands or not
254     function ProcessVersionChange() {
255         param(
256             [Parameter(Mandatory = $True)]
257             [string] $FileName,
258             [Parameter(Mandatory = $True)]
259             [string] $Section,
260             [Parameter(Mandatory = $True)]
261             [string] $VersionMsg,
262             [Parameter(Mandatory = $True)]
263             [string] $OldVersion,
264             [Parameter(Mandatory = $True)]
265             [string] $NewVersion
266         )
267 
268         # Flags used
269         $InSection = $false
270         $InNewVersion = $false
271 
272         # Normalize our lower/upper versions
273         $NormalizedOldVersion = NormalizeVersionNumber $OldVersion
274         $NormalizedNewVersion = NormalizeVersionNumber $NewVersion
275 
276         # FoundSomething is true if we did some action (and is returned to caller)
277         $FoundSomething = $False
278 
279         # Loops through the content of the file...
280         Get-Content $FileName -ErrorAction Stop | ForEach-Object {
281             # Skip blank lines
282             if ($_ -ne "") {
283                 # If it's OUR section - flip the switch
284                 if ($_ -match "\[\[$Section\]\]") {
285                     $InSection = $True
286                 }
287                 # If it's not OUR section - flip it false
288                 ElseIf ($_ -match "\[\[.*\]\]") {
289                     $InSection = $false
290                     $InNewVersion = $false
291                 }
292                 # If its a version number section
293                 ElseIf ($_ -match "\[\d\.*\d\.*\d\]") {
294                     # Determine if we are in a section and the version number is greater than our lower bound but less than or equal to our upper bound
295                     if ($InSection) {
296                         $NormalizedSectionVersion = NormalizeVersionNumber $_.Substring(1, $_.length - 2)
297                         $InNewVersion = ($NormalizedSectionVersion -gt $NormalizedOldVersion) -and ($NormalizedSectionVersion -le $NormalizedNewVersion)
298                         if ($InNewVersion -and $InSection) {
299                             # If so, show that we are processing this section
300                             Write-Host ""
301                             Write-Host -ForegroundColor Cyan "$VersionMsg $_ :"
302                         }
303                     }
304                 }
305                 else {
306                     if ($InSection -and $InNewVersion) {
307                         # Woohoo - found a command to process
308                         $FoundSomething = $True
309                         ProcessCommand $_
310                     }
311                 }
312             }
313         }
314 
315         return $FoundSomething
316     }
317 
318     # Force reimport of common functions (in case of upgrading the script)
319     Import-Module $PSScriptRoot\common.psm1 -Force
320 
321     # Write out startup message
322     Write-Host ""
323     BoxMessage "openHAB update script" Magenta
324     Write-Host ""
325 
326     # Check for admin (commented out - don't think we need it)
327     # CheckForAdmin
328 
329     # Check for openhab running
330     CheckOpenHABRunning
331 
332     # Check if service is installed, stop and delete it
333     Write-Host -ForegroundColor Cyan "Checking whether a service exists"
334     try {
335         $service = Get-Service 'openHAB%' -ErrorAction Ignore
336         if ($service) {
337             # Stop and delete the service
338             Write-Host -ForegroundColor Cyan "Stopping the service"
339             Stop-Service $service.Name -Force -ErrorAction Stop
340             Write-Host -ForegroundColor Cyan "Deleting the service"
341             Remove-Service $service.Name -ErrorAction Stop
342         }
343     }
344     catch {
345         exit PrintAndReturn "Could not stop/delete the openHAB windows server - please do that manually and try again" $_
346     }
347 
348 
349     # Find the proper directory root directory
350     Write-Host -ForegroundColor Cyan "Checking the specified openHAB directory"
351     $OHDirectory = GetOpenHABRoot $OHDirectory
352     if ($OHDirectory -eq "") {
353         exit PrintAndReturn "Could not find the openHAB directory! Make sure you are in the openHAB directory or specify the -OHDirectory parameter!"
354     }
355 
356     # Get the various 'other' directories
357     $OHConf = GetOpenHABDirectory "OPENHAB_CONF" "$OHDirectory\conf"
358     $OHUserData = GetOpenHABDirectory "OPENHAB_USERDATA" "$OHDirectory\userdata"
359     $OHRuntime = GetOpenHABDirectory "OPENHAB_RUNTIME" "$OHDirectory\runtime"
360     $OHAddons = GetOpenHABDirectory "OPENHAB_ADDONS" "$OHDirectory\addons"
361 
362     # Validate that all the directories exist (and are directories)
363     if (-NOT (Test-Path -Path $OHConf -PathType Container)) {
364         exit PrintAndReturn "Configuration directory does not exist:  $OHConf"
365     }
366 
367     if (-NOT (Test-Path -Path $OHUserData -PathType Container)) {
368         exit PrintAndReturn "Userdata directory does not exist:  $OHUserData"
369     }
370 
371     if (-NOT (Test-Path -Path $OHRuntime -PathType Container)) {
372         exit PrintAndReturn "Runtime directory does not exist:  $OHRuntime"
373     }
374 
375     if (-NOT (Test-Path -Path $OHAddons -PathType Container)) {
376         exit PrintAndReturn "Addons directory does not exist:  $OHAddons"
377     }
378 
379     # Tell the user what we are processing
380     Write-Host -ForegroundColor Yellow "Using $OHConf as conf folder"
381     Write-Host -ForegroundColor Yellow "Using $OHUserData as userdata folder"
382     Write-Host -ForegroundColor Yellow "Using $OHRuntime as runtime folder"
383     Write-Host -ForegroundColor Yellow "Using $OHAddons as addons folder"
384 
385     # Get current openHAB version
386     $CurrentVersion = GetOpenHABVersion $OHUserData
387     if ($CurrentVersion -eq "") {
388         exit PrintAndReturn "Can't get the current openhab version from $OHUserData\etc\version.properties"
389     }
390 
391     # Get the current version (d.d.d)
392     #    If stable, simply use d.d.d
393     #    If snapshot, strip the "-snapshot" part (d.d.d-snapshort)
394     #    If milestone, strip the milestone part (d.d.d.milestone)
395     $parts = $CurrentVersion.Split(".")
396     if (($parts.Length -lt 3) -or ($parts.Length -gt 4)) {
397        exit PrintAndReturn "Current openhab version from $OHUserData\etc\version.properties is malformed.  Should be either d.d.d (for stable) or d.d.d-snapshot (for snapshot) or d.d.d.d (for milestones) - malformed version: $CurrentVersion"
398     }
399 
400     if ($parts[2].EndsWith("-SNAPSHOT", "CurrentCultureIgnoreCase")) {
401         $CurrentVersion = $parts[0] + "." + $parts[1] + "." + $parts[2].Substring(0, $parts[2].Length - "-SNAPSHOT".Length);
402     }
403     else {
404         $CurrentVersion = $parts[0] + "." + $parts[1] + "." + $parts[2]
405     }
406     Write-Host -ForegroundColor Yellow "The current version is $CurrentVersion"
407 
408     # If the OHVersion parameter was not specified,
409     #    If the current version is snapshot  - make OHVersion the same snapshot
410     #    If the current version is stable    - make OHVersion the next minor upgrade (current version 3.0.0 would make our OHversion 3.1.0)
411     #    If the current version is milestone - make OHVersion the stable version (3.1.0.M6 becomes 3.1.0)
412     if (-Not $OHVersion) {
413         if ($parts.Length -eq 3) {
414             if ($parts[2].EndsWith("-SNAPSHOT", "CurrentCultureIgnoreCase")) {
415                 $OHVersion = $parts[0] + "." + $parts[1] + "." + $parts[2]
416             } else {
417                 $OHVersion = $parts[0] + "." + ([int]$parts[1] + 1) + "." + $parts[2]
418             }
419         } elseif ($parts.Length -eq 4) {
420             $OHVersion = $parts[0] + "." + $parts[1] + "." + $parts[2]
421         }
422         else {
423             exit PrintAndReturn "The current version $CurrentVersion was not formatted correctly (d.d.d)"
424         }
425     }
426 
427     # If snapshot was defined, add "-snapshot" to the OHVersion if not already present
428     if ($Snapshot -eq $true) {
429         BoxMessage "-SNAPSHOT is deprecated - please put '-snapshot' in OHVersion instead (ex: 3.1.0-snapshot)" Magenta
430         if (-Not $OHVersion.EndsWith("-SNAPSHOT", "CurrentCultureIgnoreCase")) {
431             $OHVersion = $OHVersion + "-SNAPSHOT"
432         }
433     }
434 
435     # Split up the OHVersion to validate
436     $parts = $OHVersion.Split(".")
437 
438     # If only "x.y" - make "x.y.0"
439     if ($parts.Length -eq 2) {
440         $parts += "0"
441     }
442 
443     # Valid versions:
444     #    Stable:    "3.x.y"
445     #    Snapshot:  "3.x.y-SNAPSHOT"
446     #    Milestone: "3.x.y.Mz"
447     if (($parts.Length -lt 3) -or ($parts.Length -gt 4)) {
448         exit PrintAndReturn "The specified OH version $OHVersion was not formatted correctly (d.d.d[.d])"
449     }
450 
451     $Snapshot = $False
452     $Milestone = ""
453     if ($parts[2].EndsWith("-SNAPSHOT", "CurrentCultureIgnoreCase")) {
454         $Snapshot = $True
455         $parts[2] = $parts[2].Substring(0, $parts[2].Length - "-SNAPSHOT".Length);
456     } elseif ($parts.Length -eq 4) {
457         $Milestone = $parts[3]
458     }
459     $OHVersion = $parts[0] + "." + $parts[1] + "." + $parts[2]
460 
461     # Recreate the name - should be standardized now and is used for messages and downloads
462     if ($Snapshot -eq $True) {
463         $OHVersionName =  "$OHVersion-SNAPSHOT"
464     } elseif ($Milestone -ne "") {
465         $OHVersionName = $OHVersion + "." + $Milestone
466     } else {
467         $OHVersionName = $OHVersion
468     }
469 
470     # Get the current directory (so we can switch back to it at the end)
471     try {
472         $StartDir = Get-Location -ErrorAction Stop
473     }
474     catch {
475         exit PrintAndReturn "Can't retrieve the current location - exiting" $_
476     }
477 
478     # Set the current directory to our OH root directory
479     Write-Host -ForegroundColor Cyan "Changing location to $OHDirectory"
480     try {
481         Set-Location -Path $OHDirectory
482     }
483     catch {
484         exit PrintAndReturn "Could not change location to $OHDirectory - exiting" $_
485     }
486 
487     # Setup where our temporary locations will be
488     $TempDir = "$(GetOpenHABTempDirectory)"
489     $TempDistributionZip = "$TempDir\openhab-$OHVersion.zip";
490     $TempDistribution = "$TempDir\update"
491 
492     # Create the proper download URLs
493     if ($Snapshot) {
494         $DownloadLocation="https://ci.openhab.org/job/openHAB3-Distribution/lastSuccessfulBuild/artifact/distributions/openhab/target/openhab-$OHVersionName.zip"
495         $AddonsDownloadLocation="https://ci.openhab.org/job/openHAB3-Distribution/lastSuccessfulBuild/artifact/distributions/openhab-addons/target/openhab-addons-$OHVersionName.kar"
496     }
497     elseif ($Milestone -ne "") {
498         $DownloadLocation="https://www.openhab.org/download/milestones/org/openhab/distro/openhab/$OHVersionName/openhab-$OHVersionName.zip"
499         $AddonsDownloadLocation="https://www.openhab.org/download/milestones/org/openhab/distro/openhab-addons/$OHVersionName/openhab-addons-$OHVersionName.kar"
500     }
501     else {
502         $DownloadLocation = "https://www.openhab.org/download/releases/org/openhab/distro/openhab/$OHVersionName/openhab-$OHVersionName.zip"
503         $AddonsDownloadLocation = "https://www.openhab.org/download/releases/org/openhab/distro/openhab-addons/$OHVersionName/openhab-addons-$OHVersionName.kar"
504     }
505 
506     # If we are not in SkipNew (or SkipNew and the temporary distribution file/folders have not been created yet):
507     #   1. Delete and recreate the temporary distribution directory if it exists
508     #   2. Download the distribution to the temp directory
509     #   3. Expand the distribution to the temp directory
510     #   4. Copy the update.ps1/common.psm1 files to the temp distribution if KeepUpdateScript is true (dev purposes only)
511     if (($SkipNew -eq $False) -or
512             (($SkipNew -eq $True) -and -NOT
513                 ((Test-Path -Path $TempDistributionZip -PathType Leaf) -and (Test-Path -Path $TempDistribution -PathType Container))
514             )
515         ) {
516         ########### STEP 1 - Delete and recreate the temporary distribution directory if it exists
517         try {
518             DeleteIfExists $TempDir $True
519         }
520         catch {
521             # Do nothing here - probably a file lock issue
522         }
523 
524         try {
525             Write-Host -ForegroundColor Cyan "Creating temporary update directory $TempDir"
526             CreateDirectory $TempDir
527         }
528         catch {
529             exit PrintAndReturn "Error creating temporary update directory $TempDir - exiting" $_
530         }
531 
532         ########### STEP 2 - download the distribution
533         try {
534             Write-Host -ForegroundColor Cyan "Downloading the openHAB $OHVersionName distribution to $TempDistributionZip"
535             DownloadFiles $DownloadLocation $TempDistributionZip
536         }
537         catch {
538             if ([int]$_.Exception.InnerException.Response.StatusCode -eq 404) {
539                 exit PrintAndReturn "Download of $OHVersionName failed because it's not a valid version" $_
540             } else {
541                 exit PrintAndReturn "Download of $DownloadLocation failed" $_
542             }
543         }
544 
545         ########### STEP 3 - Expand the archive
546         try {
547             Write-Host -ForegroundColor Cyan "Extracting the archive ($TempDistributionZip) to $TempDistribution"
548             Expand-Archive -Path $TempDistributionZip -DestinationPath $TempDistribution -Force -ErrorAction Stop
549         } catch {
550             exit PrintAndReturn "Unzipping of $TempDistributionZip to $TempDistribution failed." $_
551         }
552 
553         ########### STEP 4 - Copy the update/common over if we are keeping the update scripts
554         if ($KeepUpdateScript) {
555             try {
556                 Write-Host -ForegroundColor Cyan "Keeping commons.psm1 and update.ps1 by copying to $TempDistribution\runtime\bin"
557                 Copy-Item -Path "$OHRuntime\bin\common.psm1" -Destination "$TempDistribution\runtime\bin\common.psm1" -Force
558                 Copy-Item -Path "$OHRuntime\bin\update.ps1" -Destination "$TempDistribution\runtime\bin\update.ps1" -Force
559             } catch {
560                 Write-Error $_
561                 Write-Host -ForegroundColor Magenta "Could not copy the common.psm1 and update.ps1 to $TempDistribution\runtime\bin"
562                 # Don't bother with AutoConfirm here since this is special debugging logic to begin with
563                 $confirmation = Read-Host "Okay to Continue? [y/N]"
564                 if ($confirmation -ne 'y') {
565                     exit PrintAndReturn "Cancelling update"
566                 }
567             }
568         }
569     }
570 
571     # If not SkipNew - check to see if the new distribution has an update.ps1 (which is likely)
572     # and then execute it (exiting with it's result)
573     if ($SkipNew -eq $False) {
574         $newUpdate = Join-Path $TempDistribution "\runtime\bin\update.ps1"
575 
576         If (Test-Path $newUpdate) {
577             Write-Host ""
578             BoxMessage "New update.ps1 was found - executing it instead (found in $newUpdate)" Magenta
579             Write-Host ""
580             try {
581                 # go back to our original directory so the new update script does it as well
582                 Set-Location -Path $StartDir  -ErrorAction Continue
583                 . $newUpdate
584                 exit Update-openHAB -OHDirectory $OHDirectory -OHVersion $OHVersionName -AutoConfirm $AutoConfirm -SkipNew $true -KeepUpdateScript $KeepUpdateScript
585             } catch {
586                 exit PrintAndReturn "Execution of new update.ps1 failed - please execute it yourself (found in $newUpdate)" $_
587             }
588         }
589     }
590 
591     # Do the following questions after the update.ps1 check to make sure this question isn't asked twice!
592 
593     # Are we resinstalling the current version (as long as it's not a snapshot)
594     if ($OHVersion -eq $CurrentVersion -and $Snapshot -eq $False) {
595         if ($AutoConfirm) {
596             Write-Host -ForegroundColor Magenta "Current version is equal to specified version ($OHVersionName).  ***REINSTALLING*** $OHVersionName instead (rather than upgrading)."
597         } else {
598             Write-Host -ForegroundColor Magenta "Current version is equal to specified version ($OHVersionName).  If you continue, you will REINSTALL $OHVersionName rather than upgrade."
599             $confirmation = Read-Host "Okay to Continue? [y/N]"
600             if ($confirmation -ne 'y') {
601                 exit PrintAndReturn "Cancelling update"
602             }
603         }
604         Write-Host -ForegroundColor Yellow "REINSTALLING" -NoNewline -BackgroundColor Blue
605         Write-Host -ForegroundColor Yellow " version $OHVersionName"
606     } else {
607 
608         # Are we trying to downgrade the distribution (yikes!)
609         if ((NormalizeVersionNumber $OHVersion) -lt (NormalizeVersionNumber $CurrentVersion)) {
610             # Don't use autoconfirm on a downgrade warning
611             BoxMessage "You are attempting to downgrade from $CurrentVersion to $OHVersionName !!!" Red
612             Write-Host -ForegroundColor Magenta "This script is not meant to downgrade and the results will be unpredictable"
613             $confirmation = Read-Host "Okay to Continue? [y/N]"
614             if ($confirmation -ne 'y') {
615                 exit PrintAndReturn "Cancelling update"
616             }
617             Write-Host -ForegroundColor Yellow "DOWNGRADING" -NoNewline -BackgroundColor Red
618             Write-Host -ForegroundColor Yellow " to version $OHVersionName"
619         } else {
620             Write-Host -ForegroundColor Yellow "Upgrading to version $OHVersionName"
621         }
622     }
623 
624 
625     # Crete the temporary backup locations to the current distribution
626     $TempBackupDir = "$TempDir\backup-$CurrentVersion"
627     $TempBackupDirHome = $TempBackupDir + "\home"
628     $TempBackupDirRuntime = $TempBackupDir + "\runtime"
629     $TempBackupDirUserData = $TempBackupDir + "\userdata"
630     $TempBackupDirConf = $TempBackupDir + "\conf"
631 
632     # Backup the current distribution to those locations
633     Write-Host ""
634     Write-Host -ForegroundColor Cyan "Making a backup of your distribution to $TempBackupDir"
635     try {
636         Write-Host -ForegroundColor Cyan "Creating backup directories in $TempBackupDir"
637         DeleteIfExists $TempBackupDir $True
638 
639         Write-Host -ForegroundColor Cyan "Copying directory conf, userdata and runtime to $TempBackupDirConf"
640         Copy-Item -Path $OHConf, $OHUserData, $OHRuntime -Destination $TempBackupDir -Recurse -Force -ErrorAction Stop
641 
642         Write-Host -ForegroundColor Cyan "Copying files from $OHDirectory to $TempBackupDirHome"
643         Get-ChildItem $OHDirectory -File -ErrorAction Stop | Copy-Item -Destination $TempBackupDirHome -Force -ErrorAction Stop
644 
645     } catch {
646         exit PrintAndReturn "Could not backup existing distribution to $TempBackupDir" $_
647     }
648 
649     # Alright - we are ready to being the update process.  This will be wrapped in a
650     # try/catch/finally to restore our current distribution on error and to cleanup
651     # the temporary files when finished
652     try {
653 
654         # If our update.lst exists, process the notes (ie MSG section) and the PRE section
655         $updateLst = Join-Path $TempDistribution "\runtime\bin\update.lst"
656 
657         if (Test-Path $updateLst) {
658             Write-Host ""
659             Write-Host -ForegroundColor Cyan "The script will attempt to update openHAB to version $OHVersionName"
660             Write-Host -ForegroundColor Cyan "Please read the following " -NoNewLine
661             Write-Host -ForegroundColor Green "notes" -NoNewLine
662             Write-Host -ForegroundColor Cyan " and " -NoNewLine
663             Write-Host -ForegroundColor Red "warnings"
664             $NotesFound = $False
665             try {
666                 $NotesFound = ProcessVersionChange $updateLst "MSG" "Important notes for version" $CurrentVersion $OHVersion
667             } catch {
668                 # PrintAndReturn since there have been no file changes yet
669                 exit PrintAndReturn "Could not process 'MSG' of $updateLst" $_
670             }
671 
672             if ($NotesFound) {
673                 if (-Not $AutoConfirm) {
674                     $confirmation = Read-Host "Okay to Continue? [y/N]"
675                     if ($confirmation -ne 'y') {
676                         exit PrintAndReturn "Cancelling update"
677                     }
678                 }
679             } else {
680                 Write-Host -ForegroundColor Blue "No notes found for version $OHVersionName"
681             }
682 
683             try {
684                 Write-Host ""
685                 Write-Host -ForegroundColor Cyan "Execute 'PRE' instructions for version $OHVersionName"
686                 if (-NOT (ProcessVersionChange $updateLst "PRE" "Performing pre-update tasks for version" $CurrentVersion $OHVersion)) {
687                     Write-Host -ForegroundColor Blue "No 'PRE' instructions found for version $OHVersionName"
688                 }
689             } catch {
690                 return PrintAndThrow "Could not process 'PRE' of $updateLst" $_
691             }
692             Write-Host ""
693         }
694 
695 
696         # Delete current userdata files
697         # Update openHAB
698         #   1. First remove all file in runtime (they will all be replaced)
699         #   2. Remove all the userdata\etc files listed in userdata_sysfiles.lst (they may be replaced)
700         #   3. Remove the cache/tmp directories
701         #   4. Then copy all files from our new distribution WITHOUT overwriting anything
702         #      (by removals in 1 & 2 - that means we will replace those)
703         #
704 
705         ############## STEP 1 - remove runtime
706         try {
707             Write-Host -ForegroundColor Cyan "Deleting current runtime ($OHRuntime)"
708             DeleteIfExists $OHRuntime $True
709         } catch {
710             return PrintAndThrow "Could not delete current runtime ($OHRuntime)" $_
711         }
712 
713         ############## STEP 2 - remove userdata\etc files in userdata_sysfiles.lst
714         $updateSysFilesLst = "$TempDistribution\runtime\bin\userdata_sysfiles.lst"
715         if (Test-Path $updateSysFilesLst) {
716             Write-Host -ForegroundColor Cyan "Deleting current files in userdata that should not persist"
717             foreach ($FileName in Get-Content $updateSysFilesLst) {
718                 $fileToDelete = "$OHUserData\etc\$FileName"
719                 try {
720                     if (Test-Path -Path $fileToDelete) {
721                         DeleteIfExists $fileToDelete
722                         Write-Host -ForegroundColor Cyan "Deleted $FileName from $OHUserData\etc"
723                     }
724                 } catch {
725                     Write-Error $_
726                     Write-Host "Could not delete $fileToDelete.  File is no longer needed and should be manually deleted."
727                 }
728             }
729         }
730 
731         ############## STEP 3 - remove cache/tmp directories
732         try {
733             Write-Host -ForegroundColor Cyan "Removing $OHUserData\cache"
734             DeleteIfExists "$OHUserData\cache" $True
735         } catch {
736             return PrintAndThrow "Could not delete the $OHUserData\cache directory" $_
737         }
738 
739         try {
740             Write-Host -ForegroundColor Cyan "Removing $OHUserData\tmp"
741             DeleteIfExists "$OHUserData\tmp" $True
742         } catch {
743             return PrintAndThrow "Could not delete the $OHUserData\tmp directory" $_
744         }
745 
746         ############## STEP 4 - copy files from temporary to distribution WITHOUT replacement
747         Write-Host -ForegroundColor Cyan "Copying $TempDistribution to $OHDirectory without overwriting existing ones"
748         try {
749             Get-ChildItem -Path $TempDistribution -Recurse -ErrorAction Stop | ForEach-Object {
750                 $relPath = GetRelativePath $TempDistribution $_.FullName
751 
752                 if ($relPath.StartsWith(".\addons")) {
753                     $localPath = Join-Path $OHAddons $relPath.Substring(".\addons".Length)
754                 } elseif ($relPath.StartsWith(".\conf")) {
755                     $localPath = Join-Path $OHConf $relPath.Substring(".\conf".Length)
756                 } elseif ($relPath.StartsWith(".\userdata")) {
757                     $localPath = Join-Path $OHUserData $relPath.Substring(".\userdata".Length)
758                 } elseif ($relPath.StartsWith(".\runtime")) {
759                     $localPath = Join-Path $OHRuntime $relPath.Substring(".\runtime".Length)
760                 } else {
761                     $localPath = Join-Path $OHDirectory $relPath
762                 }
763                 if (-Not (Test-Path $localPath)) {
764                     if (Test-Path $localPath -PathType Container) {
765                         CreateDirectory $localPath
766                     } else {
767                         Copy-Item -Path $_.FullName -Destination $localPath -ErrorAction Stop
768                     }
769                 }
770             }
771         } catch {
772             return PrintAndThrow "Error occurred copying $TempDistribution to $OHDirectory" $_
773         }
774 
775         # If we have an update.lst - process the "POST" section
776         if (Test-Path $updateLst) {
777             Write-Host ""
778             try {
779                 Write-Host -ForegroundColor Cyan "Execute 'POST' instructions for version $OHVersionName"
780                 if (-NOT (ProcessVersionChange $updateLst "POST" "Performing post-update tasks for version" $CurrentVersion $OHVersion)) {
781                     Write-Host -ForegroundColor Blue "No 'POST' instructions found for version $OHVersionName"
782                 }
783             } catch {
784                 return PrintAndThrow "Could not process 'POST' of $updateLst" $_
785             }
786         }
787         Write-Host ""
788 
789 
790         # If there's an existing addons file, we need to replace it with the correct version.
791         try {
792             $AddonsFile = "$OHAddons\openhab-addons-$OHVersionName.kar"
793             if (Test-Path -Path $AddonsFile) {
794                 Write-Host "Found an openHAB addons file, replacing with new version"
795                 DeleteIfExists $AddonsFile
796                 DownloadFiles $AddonsDownloadLocation "$OHAddons\openhab-addons-$OHVersionName.kar"
797             }
798         } catch {
799             return PrintAndThrow "Could not replace the $AddonsFile" $_
800         }
801 
802 
803         # Hop for joy - we did it!
804         Write-Host -ForegroundColor Green "openHAB updated to version $OHVersionName!"
805         Write-Host -ForegroundColor Green "Run start.bat to launch it."
806         Write-Host -ForegroundColor Green "Check https://www.openhab.org/docs/installation/windows.html"
807         Write-Host -ForegroundColor Green "for instructions on re-installing the Windows Service if desired"
808     }
809     catch {
810 
811         # Some issue happened - we need to copy the old distribution back
812         BoxMessage "Restoring your distribution from $TempBackupDir" Yellow
813         try {
814             Write-Host -ForegroundColor Cyan "Removing existing files in $OHDirectory"
815             Get-ChildItem $OHDirectory -File -ErrorAction SilentlyContinue | Remove-Item -Force -ErrorAction SilentlyContinue
816             Write-Host -ForegroundColor Cyan "Copying backup files from $TempBackupDirHome to $OHDirectory"
817             Get-ChildItem $TempBackupDirHome -file -ErrorAction Stop | Copy-Item -Destination $OHDirectory -Force -ErrorAction Stop
818 
819             Write-Host -ForegroundColor Cyan "Removing the directory $OHConf"
820             Remove-Item "$OHConf\*" -Recurse -Force -ErrorAction SilentlyContinue
821             Write-Host -ForegroundColor Cyan "Copying backup directory $TempBackupDirConf to $OHConf"
822             Copy-Item -Path "$TempBackupDirConf\*" -Destination $OHConf -Recurse -Force -ErrorAction Stop
823 
824             Write-Host -ForegroundColor Cyan "Removing the directory $OHUserData"
825             Remove-Item "$OHUserData\*" -Recurse -Force -ErrorAction SilentlyContinue
826             Write-Host -ForegroundColor Cyan "Copying backup directory $TempBackupDirUserData to $OHUserData"
827             Copy-Item -Path "$TempBackupDirUserData\*" -Destination $OHUserData -Recurse -Force -ErrorAction Stop
828 
829             Write-Host -ForegroundColor Cyan "Removing the directory $OHRuntime"
830             Remove-Item "$OHRuntime\*" -Recurse -Force -ErrorAction SilentlyContinue
831             Write-Host -ForegroundColor Cyan "Copying backup directory $TempBackupDirRuntime to $OHRuntime"
832             Copy-Item -Path "$TempBackupDirRuntime\*" -Destination $OHRuntime -Recurse -Force -ErrorAction Stop
833         } catch {
834             Write-Host -ForegroundColor Cyan "Restoration was unsuccessful - you may want to restore from $TempBackupDir yourself"
835             Write-Error $_
836         }
837         exit -1
838     }
839     finally {
840         # And we are done...
841         Write-Host ""
842 
843         # If our temp backup directory exists - ask if we should remove it
844         # TODO - maybe only do this if an error occurred
845         try {
846             if (Test-Path $TempBackupDir) {
847                 if ($AutoConfirm) {
848                     Write-Host -ForegroundColor Cyan "Removing temporary distribution backup $TempBackupDir"
849                     DeleteIfExists $TempBackupDir $True
850                 } else {
851                     Write-Host -ForegroundColor Cyan "Your prior distribution is in $TempBackupDir"
852                     $confirmation = Read-Host "Should it be deleted? [y/N]"
853                     if ($confirmation -eq 'y') {
854                         Write-Host -ForegroundColor Cyan "Removing temporary distribution backup $TempBackupDir"
855                         DeleteIfExists $TempBackupDir $True
856                     }
857                 }
858             }
859         }
860         catch {
861             Write-Host -ForegroundColor Red "Could not delete $TempBackupDir - delete it manually"
862         }
863 
864         try {
865             # If the backup directory doesn't exist - delete the tempdir directory (and it's parent)
866             # (may exist if they answer "N" to the above question)
867             if (-NOT (Test-Path $TempBackupDir)) {
868                 try {
869                     Write-Host -ForegroundColor Cyan "Removing temporary directory $TempDir"
870                     DeleteIfExists $TempDir $True
871                 }
872                 catch {
873                     Write-Host -ForegroundColor Red "Could not delete $TempDir - delete it manually"
874                 }
875             }
876         }
877         catch {
878             Write-Host -ForegroundColor Red "Could not delete $parent - delete it manually"
879         }
880 
881         # FINALLY - set our location back to where we began
882         Write-Host -ForegroundColor Cyan "Setting location back to $StartDir"
883         Set-Location -Path $StartDir  -ErrorAction Continue
884     }
885 }
886