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'

$LogetoFolder = $env:ProgramData + "\Logeto"
$DownloadFolderPath = "\Download\"

$HockeyAppPackageName = "Powershell.Installers"
$HockeyAppAppId = "219bc502d1824dd695bbc00e796e3a92"

<#
    Return Json file from the URL for set product and scope. 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-LogetoProgress "Searching for new version." "Get-LogetoUpdateInfo"

    $fileName = $Product

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

    $fileName += ".json"

    $file = [System.IO.Path]::GetTempFileName();
    try
    {
        try
        {
            Write-LogetoDebug "URL of the UpdateInfo file is $UpdateInfoURL$fileName" 

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

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

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

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

<#
    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        
    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
}
<#
    Return the version of an appx package
#>

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

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

    return $DefaultVersion;
}

<#
    Return the 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 path to the file of service
#>

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

    return $null
}

<#
    Create and return custom Logeto exception
#>

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
}

<#
    Return Logeto scope value from system registry
#>

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
}

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

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

<#
    Install any Logeto product
#>

function Download-LogetoProduct
{
        Param(
        [parameter(Mandatory=$true)]
        $UpdateInfo,

        [parameter(Mandatory=$true)]
        [ValidateSet('logeto-terminal', 'logeto-terminal-service')]
        $ProductName
    )

    $folder = $LogetoFolder + $DownloadFolderPath + $ProductName

    try
    {       
        [System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}

        $packageUrl =  $UpdateInfo | Select -ExpandProperty PackageUrl

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

<#
    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) $ProductName
    }
    elseif ($ProductName -eq 'logeto-terminal-service')
    {
        Install-LogetoTerminalService (Get-LogetoTerminalServiceUpdateInfo) $ProductName
    }
}

<#
    Install logeto-terminal product
#>

function Install-LogetoTerminal {

    Param(
        [parameter(Mandatory=$true)]
        $UpdateInfo,
        [parameter(Mandatory=$false)]
        $ProductName = 'logeto-terminal'
    )

    Set-TerminalInstallerPhase1

    Download-LogetoProduct $UpdateInfo $ProductName

    Install $LogetoFolder + $DownloadFolderPath + $ProductName
}

<#
    Install logeto-terminal-service product
#>

function Install-LogetoTerminalService {

    Param(
        [parameter(Mandatory=$true)]
        $UpdateInfo,
        [parameter(Mandatory=$false)]
        $ProductName = 'logeto-terminal-service'
    )

    Download-LogetoProduct $UpdateInfo $ProductName

    InstallService $LogetoFolder + $DownloadFolderPath + $ProductName
}

<#
    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')
    {

    }
}

<#
    Extended Write-Host for installer purposes
#>

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
    }
}

<#
    Extended Write-Debug for installer purposes
#>

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

<#
    Extract zip file to dstination directory
#>

Function Invoke-LogetoExtract($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)
} 

<#
    Send error report to HockeyApp
#>

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

    $bodyWriter = New-Object System.IO.StreamWriter $body

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

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

        $bodyWriter.WriteLine($description)
    }

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

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

    $bodyWriter.WriteLine($message)
    $bodyWriter.WriteLine('')


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

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

        if ((Get-Member -InputObject $errorRecord -Name InvocationInfo) -ne $null)
        {
            if ((Get-Member -InputObject $errorRecord.InvocationInfo -Name PositionMessage) -ne $null)
            {
                $bodyWriter.WriteLine("`nInvocation Info Position Message:")
                $bodyWriter.WriteLine($errorRecord.InvocationInfo.PositionMessage.ToString())
            }
        }
    }
    $bodyWriter.WriteLine("--$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'
    }
}