Logeto.psm1

$UpdateInfoURL = "http://update-service.logeto.com/"
$DefaultVersion = "1.0.0"
$TerminalPackageName = "B158BDD8.LogetoTerminal"
$ServicePackageName = "Logeto.UWP.Service"

$registryFolder = "HKCU:\Software\Systemart s.r.o."
$registryFolderFallback = "HKLM:\Software\Systemart s.r.o."
$registryName = 'UpdateScope'

$AppFolder = $env:ProgramData + "\Logeto\Terminal"
$DownloadFolderPath = "\Install\Download\"

$hockeyAppPackageName = "Powershell.Installers"
$hockeyAppAppId = "219bc502d1824dd695bbc00e796e3a92"

<#
    Return Json file from the URL for set product and scop. If version parameter is set it returns Json file only if higher version was found, otherwise returns null.
#>

function Get-LogetoUpdateInfo 
{
    Param(
        [parameter(Mandatory=$true)]
        [String] $Product,

        [parameter(Mandatory=$false)]
        [String] $Scope,

        [parameter(Mandatory=$false)]
        [String] $Version
    )

    Write-Verbose "Downloading UpdateInfo"

    $fileName = $Product

    if (![String]::IsNullOrEmpty($Scope))
    {
        $fileName += "-"
        $fileName += $Scope
    }

    $fileName += ".json"

    if ([String]::IsNullOrEmpty($zipFile))
    {
        $file = [System.IO.Path]::GetTempFileName();
        try
        {
            try
            {
                Write-Verbose "URL of the file is $UpdateInfoURL$fileName" 

                (New-Object System.Net.WebClient).DownloadFile($UpdateInfoURL + $fileName, $file)
        
                $json = (Get-Content -Raw -Path $file) | ConvertFrom-Json

                Write-Verbose "Converted json $json"

                $downloadedVersion =  $json | Select -ExpandProperty Version
            
                if (![String]::IsNullOrEmpty($Version))
                {
                    if ([System.Version]$downloadedVersion -gt [System.Version]$Version)
                    {
                        Write-Verbose "UpdateInfo has been found!" 
                        return $json 
                    }
                    else
                    {
                        Write-Verbose "No newer UpdateInfo has been found!" 
                        return $null
                    }
                }

                Write-Verbose "Last UpdateInfo has been found!" 
                return $json 
            }
            finally
            {
                if (Test-Path $file)
                {
                    Remove-Item $file -Force
                }
            }
        }
        catch [Exception]
        {
            Write-Verbose "Error during finding newer version!" 
            Write-Error $_.Exception.Message
            return $null;
        }
    }

    Write-Verbose "No UpdateInfo has been found!" 
    return $null;
}

<#
    Return version of appx package
#>

Function Get-LogetoAppxVersion([String] $packageName)
{
    $packages = Get-AppxPackage | where Name -EQ $packageName

    if ($packages -ne $null)
    {
        return $packages.Version
    }

    return $DefaultVersion;
}

<#
    Return version of exe file
#>

Function Get-LogetoExeVersion([String] $pathToExe)
{
    if ([String]::IsNullOrEmpty($pathToExe) -or !(Test-Path $pathToExe))
    {
        return $DefaultVersion
    }

    return (Get-Command $pathToExe).FileVersionInfo.FileVersion
}

<#
    Return file of provided service
#>

Function Get-LogetoServicePath([String] $serviceName)
{
    $service = (Get-WmiObject win32_service | ?{$_.Name -eq $serviceName})
    if ($service)
    {
        return $service.PathName.Replace("`"","")
    }

    return $null
}

<#
    Test if registrz key/name combination exists.
#>

Function Test-LogetoRegistryValue($regkey, $name)
{
    Get-ItemProperty $regkey $name -ErrorAction SilentlyContinue | Out-Null
    $?
}

<#
    Return Loget scope value
#>

function Get-LogetoScope
{
    if (Test-LogetoRegistryValue $registryFolder $registryName)
    {
        $scope = (Get-ItemProperty -Path $registryFolder -Name $registryName)."$registryName"
    }
    elseif (Test-LogetoRegistryValue $registryFolderFallback $registryName)
    {
        $scope = (Get-ItemProperty -Path $registryFolderFallback -Name $registryName)."$registryName"
    }

    return $scope
}

<#
    Return Json file for Terminal appx, if there is no newer version null is returned.
#>

function Get-LogetoTerminalUpdateInfo 
{
    $productPrefix = "logeto-terminal"
    $scope = Get-LogetoScope
    $appxVersion = Get-LogetoAppxVersion $TerminalPackageName        
    Write-Host "Prefix $productPrefix"
    Write-Host "Scope $scope"
    Write-Host "Version $appxVersion"
    return Get-LogetoUpdateInfo -Product $productPrefix -Scope $scope -Version $appxVersion
}

<#
    Return Json file for Terminal Service, if there is no newer version null is returned.
#>

function Get-LogetoTerminalServiceUpdateInfo 
{
    $productPrefix = "logeto-terminal-service"
    $scope = Get-LogetoScope
    $serviceVersion = Get-LogetoExeVersion (Get-LogetoServicePath $ServicePackageName)
    return Get-LogetoUpdateInfo -Product $productPrefix -Scope $scope -Version $serviceVersion
}

<#
    Install any Logeto product
#>

function Install-LogetoProduct
{
    Param(
        [parameter(Mandatory=$true)]
        [ValidateSet('logeto-terminal', 'logeto-terminal-service')]
        $productName
    )

    if ($productName -eq 'logeto-terminal')
    {
        Install-LogetoTerminal (Get-LogetoTerminalUpdateInfo)
    }
    elseif ($productName -eq 'logeto-terminal-service')
    {
        Install-LogetoTerminalService (Get-LogetoTerminalServiceUpdateInfo)
    }
}

<#
    Install-LogetoTerminal Function
#>

function Install-LogetoTerminal {

    Param(
        [parameter(Mandatory=$true)]
        $UpdateInfo
    )

    $downloadPath = $AppFolder + $DownloadFolderPath + "logeto-terminal"
    Download-LogetoUpdate $UpdateInfo $AppFolder + $downloadPath
    Install $downloadPath
}

<#
    Install-LogetoTerminalService Function
#>

function Install-LogetoTerminalService {

    Param(
        [parameter(Mandatory=$true)]
        $UpdateInfo
    )

    Write-Verbose "The module should be now installed! Install-LogetoTerminalService"
}

<#
    Uninstall any Logeto product
#>

function UnInstall-LogetoProduct
{
    Param(
        [parameter(Mandatory=$true)]
        [ValidateSet('logeto-terminal', 'logeto-terminal-service')]
        $productName
    )

    if ($productName -eq 'logeto-terminal')
    {
        
    }
    elseif ($productName -eq 'logeto-terminal-service')
    {

    }
}

Function Get-LogetoCustomException($exceptionText, $i18n, $errorcode, $repeatable)
{
    $MyException = New-Object System.Exception -ArgumentList ($exceptionText);
    $MyException.Data.Add("i18n", $i18n)
    $MyException.Data.Add("errorcode", $errorcode)
    $MyException.Data.Add("repeatable", $repeatable)
    return $MyException
}

Function Write-LogetoProgress([String] $text, [String] $code)
{
    if (Get-Command -CommandType Function -Name "Write-InstallerProgress" -ErrorAction SilentlyContinue)
    {
       Write-InstallerProgress $text $code
    } 
    else
    {
       Write-Host $text
    }
}

Function Write-LogetoDebug([String] $text)
{
    if (Get-Command -CommandType Function -Name "Write-InstallerDebug" -ErrorAction SilentlyContinue)
    {
       Write-InstallerDebug $text
    } 
    else
    {
       Write-Host $text
    }
}

Function Download-LogetoUpdate($updateInfo, $path)
{
    try
    {       
        [System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}

        if (!$updateInfo)
        {
            Write-LogetoDebug 'Update info not found' 
        }
        else
        {            
            Write-LogetoDebug "Update info found" 

            $packageUrl =  $UpdateInfo | Select -ExpandProperty PackageUrl

            $zipFile = [System.IO.Path]::GetTempFileName();       
            try
            {
                (New-Object System.Net.WebClient).DownloadFile($serviceDownloadUrl + $packageUrl + ".zip", $zipFile)
            
                Extract $zipFile $path
            }
            finally
            {
                if (Test-Path $zipFile)
                {
                    Remove-Item $zipFile -Force
                }
            }
    
            Write-LogetoDebug "Download finished." 
        }
    }
    catch
    {
        throw CreateCustomException "Downloading the newest version failed with reason: $_.Exception.Message" "ScriptSystemartInstallerDownloadFailed" $resultExceptionThrown 1
    }
}

Function Extract($sourceFile, $destionationDir)
{
    if (Test-Path $destionationDir)
    {
        Remove-Item $destionationDir -Recurse -Force
    }

    if (!(Test-Path $destionationDir))
    {
        New-Item -ItemType Directory -Force -Path $destionationDir | Out-Null
    }

    Add-Type -Assembly System.IO.Compression.FileSystem
    [System.IO.Compression.ZipFile]::ExtractToDirectory($sourceFile, $destionationDir)
} 

Function Install([String] $path)
{
    $pathToManifest = Join-Path $path $manifestName
    $manifest = Get-Content $pathToManifest | ConvertFrom-Json

    if ($manifest.Description -and $manifest.ScriptName)
    {
        Write-LogetoDebug "Found installation manifest for `"$($manifest.Description)`"" 
        $installScript = Join-Path $path $manifest.ScriptName
        Write-LogetoDebug "Running script `"$($manifest.ScriptName)`"" 

        Write-LogetoProgress "Downloading the newest version" "ScriptSystemartInstallerInstalling"

        &$installScript -action install
        return
    }
}

Function Uninstall([String] $path)
{
    $pathToManifest = Join-Path $path $manifestName
    $manifest = Get-Content $pathToManifest | ConvertFrom-Json

    if ($manifest.Description -and $manifest.ScriptName)
    {
        Write-LogetoDebug "Found installation manifest for `"$($manifest.Description)`"" 
        $installScript = Join-Path $path $manifest.ScriptName
        Write-LogetoDebug "Running uninstall script `"$($manifest.ScriptName)`"" 

        Write-LogetoProgress "Uninstalling the latest version" "ScriptSystemartInstallerUninstalling"

        &$installScript -action uninstall
        return
    }
}

Function Configure([String] $path)
{
    $pathToManifest = Join-Path $path $manifestName
    $manifest = Get-Content $pathToManifest | ConvertFrom-Json

    if ($manifest.Description -and $manifest.ScriptName)
    {
        Write-LogetoDebug "Found configuration manifest for `"$($manifest.Description)`""
        $installScript = Join-Path $path $manifest.ScriptName
        Write-LogetoDebug "Running script `"$($manifest.ScriptName)`"" 

        Write-LogetoProgress "�Downloading the newest version" "ScriptSystemartInstallerConfiguring"

        &$installScript -action configure
        return
    }
}

<#
    Send error report to HockeyApp
#>

Function Send-ReportToHockeyApp([String] $message, [System.Management.Automation.ErrorRecord] $errorRecord, [String] $description)
{
    Write-LogetoDebug $message
    Write-LogetoDebug $errorRecord
    
    $body = New-Object System.IO.MemoryStream

    $boundary = [Guid]::NewGuid().ToString().Replace('-','')

    if (!([String]::IsNullOrEmpty($description)))
    {
        WriteLine $body "--$boundary"
        
        WriteLine $body 'Content-Disposition: form-data; name="description"; filename="description.txt"' 
        WriteLine $body 'Content-Type:application/octet-stream'
        WriteLine $body ''

        WriteLine $body $description
    }

    WriteLine $body "--$boundary"
    
    WriteLine $body 'Content-Disposition: form-data; name="log"; filename="log.txt"' 
    WriteLine $body 'Content-Type:application/octet-stream'
    WriteLine $body ''

    WriteLine $body "Package: $hockeyAppPackageName"
    WriteLine $body 'Version: 1.0.0.0'
    WriteLine $body ('OS: ' + (Get-WmiObject Win32_OperatingSystem).Version)
    WriteLine $body ('Manufacturer: ' + (Get-WmiObject -Class Win32_ComputerSystem).Manufacturer)
    WriteLine $body ('Model: '  + (Get-WmiObject -Class Win32_ComputerSystem).Model)
    WriteLine $body ('Date: ' + (Get-Date -format o))
    WriteLine $body ('CrashReporter Key: ' + (Get-WmiObject Win32_ComputerSystemProduct).UUID)
    WriteLine $body ''

    WriteLine $body $message
    WriteLine $body ''


    if ($errorRecord)
    {
        WriteLine $body 'Exception Stack:'
        
        if ($errorRecord.Exception)
        {
            WriteLine $body $ErrorRecord.Exception.ToString()
            # Make it show up as the first row on the HockeyApp page
            WriteLine $body "at $($ErrorRecord.Exception.ToString())()"
        }

        if ((Get-Member -InputObject $errorRecord -Name ScriptStackTrace) -ne $null)
        {
            WriteLine $body "`nScript Stack Trace:"
            WriteLine $body $errorRecord.ScriptStackTrace.ToString()
        }

        if ((Get-Member -InputObject $errorRecord -Name InvocationInfo) -ne $null)
        {
            if ((Get-Member -InputObject $errorRecord.InvocationInfo -Name PositionMessage) -ne $null)
            {
                WriteLine $body "`nInvocation Info Position Message:"
                WriteLine $body $errorRecord.InvocationInfo.PositionMessage.ToString()
            }
        }
    }
    WriteLine $body "--$boundary--"

    Write-LogetoDebug 'Sending report' 
    try
    {
        $uri = "https://rink.hockeyapp.net/api/2/apps/$hockeyAppAppId/crashes/upload"
        Invoke-RestMethod -ContentType "multipart/form-data; boundary=$boundary" -Uri $uri -TimeoutSec 120 -Method POST -Body $body.ToArray()
        Write-LogetoDebug 'Report sent' 
    }
    catch
    {
        Write-LogetoDebug 'Report not sent'
    }
}