ecspresso / KeePassUpdater

1 stars 0 forks source link

KeePassUpdater Improvements #1

Open Chefkeks opened 3 years ago

Chefkeks commented 3 years ago

Hey there,

thanks for that nice little KeePass + Plugins Powershell Update script - almost what I was looking for. Customized it a bit to fit all needs as I have several KeePass installations / portable versions flying around on my pc for several purposes (work, private, sports club, etc.) and with different needed plugins as well. Summarized:

To make it a fully portable solution a shift from registry as storage location of the plugin versions to a text or local database file inside the portable keepass folder would be necessary - so that you can carry it on an external flash drive to another computer without loosing the version info. As registry is fine for me, I won't further improve it in that way, just mentioning it.

Anyway, wanted to share an example of my improvements for Keepass update itself and a single plugin - feel free to implement some or all my improvements.

Michael 🍪✌️

function Format-KeePass {
    param(
        [Parameter(Mandatory = $true, ValueFromPipeLine = $true)]
        [String]$update_information
    )

    # Save delimiter
    $delimiter = $update_information[0]

    # Split data on new line
    $split = $update_information.split([System.Environment]::NewLine)
    if($delimiter.length -gt 1) {
        $delimiter = $split[0][0]
    }

    $formated = @{}
    # Loop each line in data
    foreach($plugin in $split) {
        # Do not parse first and last line
        if(![bool]$plugin.StartsWith($delimiter)) {
            # Save plugin name and version
            $formated[$plugin.split($delimiter)[0]] = $plugin.split($delimiter)[1]
        }
    }

    return $formated
}

function Update-Plugin {
    param(
        [Parameter(Mandatory = $true)]
        [String]$update_uri,
        [Parameter(Mandatory = $true)]
        [String]$plugin_name,
        [Parameter(Mandatory = $true)]
        [String]$author,
        [Parameter(Mandatory = $true)]
        [String]$repo,
        [Parameter(Mandatory = $true)]
        [String]$keepass_dir,
        [String]$reg_path = 'HKCU:\SOFTWARE\KeePassPluginUpdater'
    )
    # Output
    Write-Output ""
    Write-Output "Plugin: $plugin_name"

    # Plugin dir
    $plugin_dir = "$keepass_dir\plugins"

    # Get plugin version data
    $plugin_version_data = Invoke-RestMethod -Uri $update_uri | Format-KeePass

    # Test if registry key exists
    if(-not (Test-Path $reg_path)) {
        # Create key
        New-Item -Path $reg_path -ItemType Directory
    }

    # Test if registry value exists
    if(-not [bool](Get-ItemProperty -Path $reg_path -Name "$keepass_dir\$plugin_name" -ErrorAction SilentlyContinue)) {
        # Create value
        Write-Output "Plugin added to the list!"
        New-ItemProperty -Path $reg_path -Name "$keepass_dir\$plugin_name" -Value 0 -PropertyType 'String' | Out-Null
    }

    # Get value and latest version
    $plugin_installed = Get-ItemPropertyValue -Path $reg_path -Name "$keepass_dir\$plugin_name"
    $plugin_available = $plugin_version_data[$plugin_name] | Out-String

    # Output
    Write-Output "Plugin version installed: $plugin_installed"
    Write-Output "Plugin version available: $plugin_available"

    # Compare value to latest version
    if($plugin_version_data[$plugin_name] -ne $plugin_installed) {
        # Get latest release from Github
        Write-Output "Getting latest plugin release info..."
        $release = Invoke-RestMethod "https://api.github.com/repos/$author/$repo/releases/latest"

        # Extract filename keep it the same
        $filename = $release.assets.browser_download_url -replace '.+\/(\w+\.plgx)', '$1'

        # Delete old file
        if(Test-Path "$plugin_dir\$filename") {
            Write-Output "Deleting old plugin file now..."
            Remove-Item -Path "$plugin_dir\$filename"
        }

        # Download plugin file and save it in keepass folder
        Write-Output "Downloading latest plugin file..."
        Invoke-WebRequest -Uri $release.assets.browser_download_url -OutFile "$plugin_dir\$filename"

        # Update value in versions file
        Set-ItemProperty -Path $reg_path -Name "$keepass_dir\$plugin_name" -Value $plugin_version_data[$plugin_name]
    }
}

function Is-Portable-Used {
    param(
        [Parameter(Mandatory = $true)]
        [String]$portable
    )
    # Portable Keepass
    if($portable -eq $true) {
        $keepass_dir = $PSScriptRoot

    # Installed Keepass
    } else {
        $keepass_dir = "${env:ProgramFiles(x86)}\KeePass Password Safe 2"
    }

    return $keepass_dir
}
# Output
Write-Output "Keepass Updater started!"
Write-Output ""

# Portable?
$portable = $true

# Portable or installed version
$keepass_dir = Is-Portable-Used $portable
Write-Output "Keepass Directory: $keepass_dir"

# Get Keepass version installed and avalable
$keepass_exe = "$keepass_dir\KeePass.exe"
$installed = (Get-ItemProperty $keepass_exe).VersionInfo.ProductVersion -replace '.0',''
$latest = (Invoke-RestMethod -Uri 'https://www.dominik-reichl.de/update/version2x.txt.gz' | Format-KeePass)['KeePass']

# Output
Write-Output "Keepass Version installed: $installed"
Write-Output "Keepass Version available: $latest"
Write-Output "Closing Keepass now..."
Write-Output ""

# Stop KeePass
Start-Process -FilePath $keepass_exe -ArgumentList '--exit-all'

if($installed -lt $latest) {
    # Use users temp folder
    $keepass_tmp = $env:TEMP

    # Portable Keepass
    if($portable -eq $true) {
        # Download
        Write-Output "Downloading Keepass Portable $latest"
        Invoke-WebRequest -Uri "https://sourceforge.net/projects/keepass/files/KeePass%202.x/$latest/KeePass-$latest.zip/download" -UserAgent [Microsoft.PowerShell.Commands.PSUserAgent]::FireFox -OutFile "$keepass_tmp\KeePass-$latest.zip"

        # Extract
        Write-Output "Extracting Keepass Portable $latest now..."
        Expand-Archive -LiteralPath "$keepass_tmp\KeePass-$latest.zip" -DestinationPath $keepass_dir -Force

        # Delete download
        Remove-Item -Path "$keepass_tmp\KeePass-$latest.zip"

    # Installed Keepass
    } else {
        # Download
        Write-Output "Downloading Keepass Installer $latest"
        Invoke-WebRequest -Uri "https://sourceforge.net/projects/keepass/files/KeePass%202.x/$latest/KeePass-$latest-Setup.exe/download" -UserAgent [Microsoft.PowerShell.Commands.PSUserAgent]::FireFox -OutFile "$keepass_tmp\KeePass-$latest.exe"

        # Install
        Write-Output "Installing Keepass $latest now..."
        Start-Process -FilePath "$keepass_tmp\KeePass-$latest.exe" -ArgumentList '/SILENT' -Wait

        # Delete download
        Remove-Item -Path "$keepass_tmp\KeePass-$latest.exe"
    }

    # Verify Keepass update
    $updated = (Get-ItemProperty $keepass_exe).VersionInfo.ProductVersion -replace '.0',''
    if ($updated -eq $latest) {
        Write-Output "Successfully updated Keepass to $latest!"
    } else {
        # Save foreground color
        $fc = $host.UI.RawUI.ForegroundColor

        # Set new color
        $host.UI.RawUI.ForegroundColor = 'red'

        # Output
        Write-Output "Error! Something went wrong updating Keepass!"

        # Restore the original color
        $host.UI.RawUI.ForegroundColor = $fc
    }
}

# Plugin updates
Write-Output "Checking for plugin updates now..."

$parameters = @{
    update_uri  = 'https://raw.githubusercontent.com/rookiestyle/keepassotp/master/version.info'
    plugin_name = 'KeePassOTP'
    author      = 'Rookiestyle'
    repo        = 'KeePassOTP'
    keepass_dir = $keepass_dir
}

Update-Plugin @parameters

# Output
Write-Output ""
Write-Output "Keepass Updater finished!"
Write-Output ""

# Start KeePass again
Read-Host -Prompt "Press Enter to start Keepass"
Start-Process -FilePath $keepass_exe
ecspresso commented 3 years ago

Thanks for this, I haven't had the time to look into it yet but I will try to take the time soon.

To make it a fully portable solution a shift from registry as storage location of the plugin versions to a text or local database file inside the portable keepass folder would be necessary - so that you can carry it on an external flash drive to another computer without loosing the version info.

Should be possible to implement a switch of some kind or do a fallback, look for a file first and registry second.