Powershell Check for Updates

#requires -version 3.0
 
#### Check for updates
#### Version 1.36
#### Last updated 2017-02-07
#### The canonical version of this script is at http://tallguyracing.com/wiki/doku.php?id=powershell_check_for_updates
 
$appsToCheck = @{
          'Dummy'            = 'Dummy'
        ; 'ThisScript'       = '1.31'
        ; 'Anki'             = '2.0.36'
        ; 'ConEmu'           = '160619'
        ; 'Paint.NET'        = '4.0.9'
#       ; 'Rockbox'          = '3.13'
#       ; 'DokuWiki'         = '2013-05-10'
        ; 'Notepad++'        = '6.9.2'         # Hopefully, this can be reverted to a fully automatic check in the future.
#        ; 'Notepad++'        = 'Notepad++\notepad++.exe'
        ; 'GIMP'             = 'GIMP 2\bin\gimp-2.8.exe'
        ; '7zip'             = '7-Zip\7z.exe'
        ; 'WinMerge'         = 'WinMerge\WinMergeU.exe'
#       ; 'Foobar2000'       = 'foobar2000\foobar2000.exe'
        ; 'Fiddler'          = 'Fiddler2\fiddler.exe'
        ; 'Process Explorer' = 'C:\tools\sysinternals\procexp.exe'
        ; 'Process Monitor'  = 'C:\tools\sysinternals\procmon.exe'
#        ; 'VLC'              = 'VideoLAN\VLC\vlc.exe'
        ; 'FileZilla'        = 'FileZilla FTP Client\filezilla.exe'
#        ; 'ImageMagick'      = 'ImageMagick\magick.exe'
#        ; 'TortoiseSVN'      = 'TortoiseSVN\bin\TortoiseSVN.dll'
        ; 'WinDirStat'       = 'WinDirStat\windirstat.exe'
#        ; 'LibreOffice'      = 'LibreOffice 5\program\soffice.exe'
#        ; 'VirtualBox'       = 'Oracle\VirtualBox\VBoxSVC.exe'
#        ; 'Tixati'           = 'tixati\tixati.exe'
#        ; 'VeraCrypt'        = 'VeraCrypt\VeraCrypt.exe'
#        ; 'IrfanView'        = 'IrfanView\i_view32.exe'
}
 
function WebRequest ($url) {
    [net.httpwebrequest]$httpwebrequest = [net.webrequest]::create($url)
    [net.httpWebResponse]$httpwebresponse = $httpwebrequest.getResponse()
 
    if (!$httpwebresponse) {
        return $null
    }
 
    $reader = new-object IO.StreamReader($httpwebresponse.getResponseStream())
    $WSResponse = $reader.ReadToEnd()
    $reader.Close()
 
    return $WSResponse
}
 
function WebRequest ($url) {
    [net.httpwebrequest]$httpwebrequest = [net.webrequest]::create($url)
    [net.httpWebResponse]$httpwebresponse = $httpwebrequest.getResponse()
 
    if (!$httpwebresponse) {
        return $null
    }
 
    $reader = new-object IO.StreamReader($httpwebresponse.getResponseStream())
    $WSResponse = $reader.ReadToEnd()
    $reader.Close()
 
    return $WSResponse
}
 
function GetVersionNumberFromWeb ($appName, $url, $regexPattern)
{
    $response = WebRequest $url
 
    if (!$response) {
        Write-Host "FAILED: ${appName}: Could not get the web page." -ForegroundColor Red
        return
    }
 
    Out-File -filepath (Join-path $env:temp "$appName.html") -inputobject $response
 
    if ($regexPattern.EndsWith('/s')) {
        # Use a bit of a hack to get singleline mode.
        $regexPattern = $regexPattern.Substring(0, $regexPattern.Length - 2)
        $response = $response.Replace("`n", " ")
    }
 
    $versionNumberMatches = ([regex]$regexPattern).matches($response)
 
    if ($versionNumberMatches.Count -eq 0) {
        Write-Host "FAILED: ${appName}: Could not get version number from the web page. Nothing matched the regex." -ForegroundColor Red
        return
    }
    if ($versionNumberMatches.Count -ne 1) {
        Write-Host "FAILED: ${appName}: Could not get version number from the web page. The regex had more than one match." -ForegroundColor Red
        return
    }
    if (! $versionNumberMatches[0].Success) {
        Write-Host "FAILED: ${appName}: Could not get version number from the web page. The regex match was not successful." -ForegroundColor Red
        return
    }
    if (! $versionNumberMatches[0].Groups["versionNumber"].Success) {
        Write-Host "FAILED: ${appName}: Could not get version number from the web page. The regex group was not successful." -ForegroundColor Red
        return
    }
 
    $latestVersionNumber = $versionNumberMatches[0].Groups["versionNumber"].Value
    Write-Host "${appName}: The latest version number is $LatestVersionNumber."
 
    return $latestVersionNumber
}
 
function CheckLocalFile ($appName, $url, $regexPattern, $localFilename, $extraFormatting)
{
    if (Test-Path $localFilename) {
        $fullLocalFilename = $localFilename
    } elseif (Test-Path "C:\Program Files (x86)\$localFilename") {
        $fullLocalFilename = "C:\Program Files (x86)\$localFilename"
    } elseif (Test-Path "C:\Program Files\$localFilename") {
        $fullLocalFilename = "C:\Program Files\$localFilename"
    } else {
        Write-Host "FAILED: The filename ${localFilename} or any of its variants does not exist." -ForegroundColor Red
        return
    }
 
    $appInfo = Get-Command $fullLocalFilename
    $currentVersionNumber = $appInfo.FileVersionInfo.ProductVersion
 
    if ($extraFormatting.Contains(';CommaToFullStop;'))
    {
        $currentVersionNumber = $currentVersionNumber.Replace(',', '.')
    }
    if ($extraFormatting.Contains(';RemoveSpaces;'))
    {
        $currentVersionNumber = $currentVersionNumber.Replace(' ', '')
    }
    if ($extraFormatting.Contains(';TrimTrailingZero;') -and $currentVersionNumber.EndsWith('0'))
    {
        $currentVersionNumber = $currentVersionNumber.Substring(0, $currentVersionNumber.Length - 1)
    }
    if ($extraFormatting.Contains(';TrimTrailingDotZero;') -and $currentVersionNumber.EndsWith('.0'))
    {
        $currentVersionNumber = $currentVersionNumber.Substring(0, $currentVersionNumber.Length - 2)
    }
    while ($extraFormatting.Contains(';TrimTrailingDotZeros;') -and $currentVersionNumber.EndsWith('.0'))
    {
        $currentVersionNumber = $currentVersionNumber.Substring(0, $currentVersionNumber.Length - 2)
    }
    if ($extraFormatting.Contains(';FirstThreeDigitsOnly;'))
    {
        if (! ($currentVersionNumber -match "\d+(?:\.\d+){2}"))
        {
            Write-Host "FAILED: ${appName}: Could not get the first three digits of the version number from '$currentVersionNumber'." -ForegroundColor Red
            return
        }
        $currentVersionNumber = $Matches.Values
    }
 
    CheckCurrentVersionNumber $appName $url $regexPattern $currentVersionNumber
}
 
function CheckCurrentVersionNumber ($appName, $url, $regexPattern, $currentVersionNumber)
{
    $latestVersionNumber = GetVersionNumberFromWeb $appName $url $regexPattern
    if (! $latestVersionNumber) {
        return
    } 
 
    Write-Host "${appName}: The current version number is $currentVersionNumber."
 
    if ($currentVersionNumber -ne $latestVersionNumber) {
        Write-Host "${appName}: Update required!" -ForegroundColor Green
    } else {
        Write-Host "${appName}: No update required."
    }
}
 
function CheckAppCurrentVersionNumber ($appName)
{
    switch ($appName)
    {
        'Dummy' { }
        #### Semi-automatic checks ####
        'ThisScript'
        {
            CheckCurrentVersionNumber 'ThisScript' 'http://tallguyracing.com/wiki/doku.php?id=powershell_check_for_updates' `
                '>#### Version (?<versionNumber>\d+\.\d+)<' `
                $appsToCheck['ThisScript']
        }
        'Anki'
        {
            CheckCurrentVersionNumber 'Anki' 'http://ankisrs.net/' `
                '>Download Anki for Windows Vista/7/8/10 \((?<versionNumber>\d+(?:\.\d+){2})\)<' `
                $appsToCheck['Anki']
        }
        'ConEmu'
        {
            CheckCurrentVersionNumber 'ConEmu' 'http://conemu.github.io/version.ini' `
                '\[ConEmu_Preview_2\]\s*version=(?<versionNumber>\w+)' `
                $appsToCheck['ConEmu']
        }
        'Paint.NET'
        {
            CheckCurrentVersionNumber 'Paint.NET' 'http://www.getpaint.net/index.html' `
                '>paint\.net\s*(?<versionNumber>\d+(?:\.\d+){2})<' `
                $appsToCheck['Paint.NET']
        }
        'Rockbox'
        {
            CheckCurrentVersionNumber 'Rockbox' 'http://www.rockbox.org/download/' `
                '<h1>Rockbox (?<versionNumber>\d+(?:\.\d+){1,2}) Download</h1>' `
                $appsToCheck['Rockbox']
        }
        'DokuWiki'
        {
            CheckCurrentVersionNumber 'DokuWiki' 'http://download.dokuwiki.org/' `
                '<a href="src/dokuwiki/dokuwiki-stable\.tgz">\(direct link\)</a>\s*<span class="hint">(?<versionNumber>\d{4}-\d{2}-\d{2}).+?</span>' `
                $appsToCheck['DokuWiki']
        }
        'Notepad++'
        {
            CheckCurrentVersionNumber 'Notepad++' 'http://notepad-plus-plus.org/' `
                '>Current Version:\s*(?:<\w+>\s*)*(?<versionNumber>\d+(?:\.\d+){1,3})<' `
                $appsToCheck['Notepad++']
        }
        'Inkscape'
        {
            CheckCurrentVersionNumber 'Inkscape' 'http://inkscape.org/en/download/windows' `
                '>Latest stable version: Inkscape ?(?<versionNumber>\d+(?:\.\d+){1,2})<' `
                $appsToCheck['Inkscape']
        }
        'Hugin'
        {
            CheckCurrentVersionNumber 'Hugin' 'http://sourceforge.net/projects/hugin/' `
                '>hugin-(?<versionNumber>\d+(?:\.\d+){2})\.tar\.bz2<' `
                $appsToCheck['Hugin']
        }
        'Stellarium'
        {
            CheckCurrentVersionNumber 'Stellarium' 'http://stellarium.org/' `
                '>latest version is (?<versionNumber>\d+(?:\.\d+){2})<' `
                $appsToCheck['Stellarium']
        }
        'Gmvault'
        {
            CheckCurrentVersionNumber 'Gmvault' 'http://gmvault.org/' `
                'Download\s+Gmvault<small>\s*\((?<versionNumber>\d+(?:\.\d+){1,2})\s*\)\s*</small>' `
                $appsToCheck['Gmvault']
        }
 
        #### Fully-automatic checks ####
        'GIMP'
        {
            CheckLocalFile 'GIMP' 'http://www.gimp.org/downloads/' `
                '>The current stable release of GIMP is <(b|strong)>(?<versionNumber>\d+(?:\.\d+){2})<\/(b|strong)>' `
                $appsToCheck['GIMP'] ''
        }
        '7zip'
        {
            CheckLocalFile '7zip' 'http://www.7-zip.org/' `
                'Download 7-Zip (?<versionNumber>\d+(?:\.\d+){1,2}) \(\d+-\d+-\d+\) for Windows' `
                $appsToCheck['7zip'] ''
        }
        'WinMerge'
        {
            CheckLocalFile 'WinMerge' 'http://winmerge.org/' `
                'href="http://downloads.sourceforge.net/winmerge/WinMerge-(?<versionNumber>\d+(?:\.\d+){2})-Setup.exe"' `
                $appsToCheck['WinMerge'] ''
        }
        'Foobar2000'
        {
            CheckLocalFile 'Foobar2000' 'http://www.foobar2000.org/download' `
                'href="/getfile/[^/]+/foobar2000_v(?<versionNumber>\d+(?:\.\d+){1,2}).exe">foobar2000 v\d+(?:\.\d+){1,2}' `
                $appsToCheck['Foobar2000'] `
                ';TrimTrailingDotZero;'
        }
        'Fiddler'
        {
            CheckLocalFile 'Fiddler' 'https://chocolatey.org/packages/fiddler4' `
                '<title>\s*Chocolatey\s+Gallery\s*\|\s*Fiddler\s*(?<versionNumber>\d+(?:\.\d+){2})' `
                $appsToCheck['Fiddler'] `
                ';FirstThreeDigitsOnly;'
        }
        'Process Explorer'
        {
            CheckLocalFile 'Process Explorer' 'http://technet.microsoft.com/en-US/sysinternals/bb896653' `
                '<h1>Process Explorer v(?<versionNumber>\d+\.\d+)</h1>' `
                $appsToCheck['Process Explorer'] ''
        }
        'Process Monitor'
        {
            CheckLocalFile 'Process Monitor' 'http://technet.microsoft.com/en-US/sysinternals/bb896645' `
                '<h1>Process Monitor v(?<versionNumber>\d+\.\d+)</h1>' `
                $appsToCheck['Process Monitor'] ';TrimTrailingZero;'
        }
#       Next time the version changes, try using this rather than the Semi-automatic check.
#        'Notepad++'
#        {
#            CheckLocalFile 'Notepad++' 'http://notepad-plus-plus.org/download/' `
#                '<title>Notepad\+\+ v(?<versionNumber>\d+(?:\.\d+){1,2}) - Current Version</title>' `
#                $appsToCheck['Notepad++'] ''
#        }
        'VLC'
        {
            CheckLocalFile 'VLC' 'http://videolan.org/vlc/' `
                'href=''//get.videolan.org/vlc/(?:\d+(?:\.\d+){2})/win32/vlc-(?<versionNumber>\d+(?:\.\d+){2})-win32.exe''>' `
                $appsToCheck['VLC'] `
                ';CommaToFullStop;TrimTrailingDotZero;'
        }
        'FileZilla'
        {
            CheckLocalFile 'FileZilla' 'http://filezilla-project.org/download.php?type=client' `
                '>The latest stable version of FileZilla Client is (?<versionNumber>\d+(?:\.\d+){2,3})<' `
                $appsToCheck['FileZilla'] `
                ';CommaToFullStop;RemoveSpaces;TrimTrailingDotZero;'
        }
        'ImageMagick'
        {
            CheckLocalFile 'ImageMagick' 'http://www.imagemagick.org/script/binary-releases.php' `
                '>ImageMagick-(?<versionNumber>\d+(?:\.\d+){2})(?:-\d+)?-Q16-x64-dll\.exe<' `
                $appsToCheck['ImageMagick'] ''
        }
        'TortoiseSVN'
        {
            CheckLocalFile 'TortoiseSVN' 'http://tortoisesvn.net/downloads.html' `
                '>The current version is (?<versionNumber>\d+(?:\.\d+){2})<' `
                $appsToCheck['TortoiseSVN'] `
                ';FirstThreeDigitsOnly;'
        }
        'WinDirStat'
        {
            CheckLocalFile 'WinDirStat' 'http://windirstat.info/' `
                '>Latest version: (?<versionNumber>\d+(?:\.\d+){2})<' `
                $appsToCheck['WinDirStat'] `
                ';FirstThreeDigitsOnly;'
        }
        'WinDirStat'
        {
            CheckLocalFile 'WinDirStat' 'http://windirstat.info/' `
                '>Latest version: (?<versionNumber>\d+(?:\.\d+){2})<' `
                $appsToCheck['WinDirStat'] `
                ';FirstThreeDigitsOnly;'
        }
        'Expresso'
        {
            CheckLocalFile 'Expresso' 'http://www.ultrapico.com/ExpressoDownload.htm' `
                '\(Version (?<versionNumber>\d+(?:\.\d+){2}) - ' `
                $appsToCheck['Expresso'] `
                ';FirstThreeDigitsOnly;'
        }
        'LibreOffice'
        {
            CheckLocalFile 'LibreOffice' 'http://www.libreoffice.org/download/libreoffice-fresh/' `
                '>Download Version (?<versionNumber>\d+(?:\.\d+){2})<' `
                $appsToCheck['LibreOffice'] `
                ';FirstThreeDigitsOnly;'
        }
        'VirtualBox'
        {
            CheckLocalFile 'VirtualBox' 'http://www.virtualbox.org/wiki/Downloads' `
                '\>\s*VirtualBox\s*(?<versionNumber>\d+(?:\.\d+){1,2})\s*platform\s*packages\s*\<' `
                $appsToCheck['VirtualBox'] `
                ';FirstThreeDigitsOnly;'
        }
        'Tixati'
        {
            CheckLocalFile 'Tixati' 'http://www.tixati.com/download/' `
                '>Download Tixati v(?<versionNumber>\d+\.\d+)<' `
                $appsToCheck['Tixati'] ''
        }
        'VeraCrypt'
        {
            CheckLocalFile 'VeraCrypt' 'https://veracrypt.codeplex.com/wikipage?title=Downloads' `
                '>Latest Stable Release - (?<versionNumber>\d+\.\d+)' `
                $appsToCheck['VeraCrypt'] ''
        }
        'IrfanView'
        {
            CheckLocalFile 'IfranView' 'http://www.irfanview.com/checkversion.php' `
                'Current IrfanView version is: <b>(?<versionNumber>\d+\.\d+)' `
                $appsToCheck['IrfanView'] ''
        }
        'Deluge'
        {
            CheckLocalFile 'Deluge' 'http://deluge-torrent.org/' `
                '\>\s*(?<versionNumber>\d+(?:\.\d+)+)\s*\<' `
                $appsToCheck['Deluge'] `
                ';FirstThreeDigitsOnly;'
        }
        default
        {
            Write-Host "FAILED: ${appName}: Unknown app." -ForegroundColor Red
        }
    }
}
 
cls
foreach ($appName in $AppsToCheck.Keys) { CheckAppCurrentVersionNumber ($appName) }
#CheckAppCurrentVersionNumber 'Fiddler'