Functions/Common.ps1

function Get-LinaHelp() {
    <#
.SYNOPSIS
Opens the HTML help of this module.
.DESCRIPTION
Opens the HTML help file located in the module folder with default browser.
.INPUTS
None
.OUTPUTS
None
.EXAMPLE
Get-LinaHelp
Opens the HTML help file located in the module folder with default browser.
#>

    $current_modulepath = Split-Path $script:MyInvocation.MyCommand.Path
    Start-Process "$current_modulepath\help.html"
}

function Disable-SslVerification {
    if (-not ([System.Management.Automation.PSTypeName]"TrustEverything").Type) {
        Add-Type -TypeDefinition  @"
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
public static class TrustEverything
{
    private static bool ValidationCallback(object sender, X509Certificate certificate, X509Chain chain,
        SslPolicyErrors sslPolicyErrors) { return true; }
    public static void SetCallback() { System.Net.ServicePointManager.ServerCertificateValidationCallback = ValidationCallback; }
    public static void UnsetCallback() { System.Net.ServicePointManager.ServerCertificateValidationCallback = null; }
}
"@

    }
    [TrustEverything]::SetCallback()
}
function Enable-SslVerification {
    if (([System.Management.Automation.PSTypeName]"TrustEverything").Type) {
        [TrustEverything]::UnsetCallback()
    }
}

function CallAPI() {
    [cmdletbinding()]
    Param(
        [Parameter(Mandatory = $True, Position = 0)]
        [ValidateNotNullOrEmpty()]
        [string]$Path,
        [Parameter(Mandatory = $False, Position = 1)]
        [ValidateNotNullOrEmpty()]
        [string]$ContentType,
        [Parameter(Mandatory = $False, Position = 2)]
        [ValidateNotNullOrEmpty()]
        [string]$Body,                  
        [Parameter()]
        [switch]$FirstConnection,
        [Parameter(Mandatory = $False)]
        [ValidateNotNullOrEmpty()]
        [string]$Method
    )


    if ($global:GLOBAL_LINA_APIKEY) {
        Write-Verbose "Using API Key $($global:GLOBAL_LINA_APIKEY)"
        $Headers = @{"X-API-KEY"="$global:GLOBAL_LINA_APIKEY"}  
    } else {
        $Headers=@{}
    }

    # Translating Path for version >=6.0
    if ($global:INT_LINA_API_VERSION -ge 60) {
        if ($Path -eq "/info.json") {
            $Path = "/ADE/info.json"
        }
    }

    if ($Path -like "*.json*") {
        $ContentType = "application/json; charset=utf-8"
    } else {
        # most content is XML so this is default
        $ContentType = "application/xml; charset=utf-8"
    }

    if (!$Method) {
        $Method ="POST"
    }

    if ($FirstConnection) {
        if ( $GLOBAL_IGNORE_CERTIFICATES ) {
            # Disable Certificate checking
            if ($PSVersionTable.PSVersion.Major -lt 6) {
                # Pre-PowerShell 6.0
                # Disabled keep-alive (PowerShell for Windows Only) to fix issue : A connection that was expected to be kept alive was closed by the server
                Disable-SslVerification
                $request = Invoke-RestMethod -Uri $GLOBAL_LINA_SERVER$Path -SessionVariable Currentsession -DisableKeepAlive -Headers $Headers -ErrorAction SilentlyContinue
            }
            else {
                # PowerShell Core (>= 6.0)
                $request = Invoke-RestMethod -Uri $GLOBAL_LINA_SERVER$Path -SessionVariable Currentsession -SkipCertificateCheck -Headers $Headers -ErrorAction SilentlyContinue
            }
        }
        else {
            # Enable certificate checking
            if ($PSVersionTable.PSVersion.Major -lt 6) {
                # Pre-PowerShell 6.0
                # Disabled keep-alive (PowerShell for Windows Only) to fix issue : A connection that was expected to be kept alive was closed by the server
                $request = Invoke-RestMethod -Uri $GLOBAL_LINA_SERVER$Path -SessionVariable Currentsession -DisableKeepAlive -Headers $Headers -ErrorAction SilentlyContinue
            }
            else {
                # PowerShell Core (>= 6.0)
                $request = Invoke-RestMethod -Uri $GLOBAL_LINA_SERVER$Path -SessionVariable Currentsession -Headers $Headers -ErrorAction SilentlyContinue
            }
        }

        Set-Variable -name LoggedSession -Scope global -Value $Currentsession

    }
    else {
        # Next connections reuse the Session Cookie set globally
        if ($PSVersionTable.PSVersion.Major -ge 6 -AND $GLOBAL_IGNORE_CERTIFICATES) {
            <# Disable Certificate checking for WebRequest (PowerShell >= 6.0) #>
            $request = Invoke-RestMethod -Uri $GLOBAL_LINA_SERVER$Path -ContentType $ContentType -Method $Method -Body $Body -WebSession $LoggedSession -Headers $Headers -SkipCertificateCheck
        }
        else {
            # Same request if enabled or not. Checking is disabled globally on PowerShell < 6.0
            # Disabled keep-alive (PowerShell for Windows Only) to fix issue : A connection that was expected to be kept alive was closed by the server
            $request = Invoke-RestMethod -Uri $GLOBAL_LINA_SERVER$Path -ContentType $ContentType -Method $Method -Body $Body -WebSession $LoggedSession -Headers $Headers -DisableKeepAlive
        }
    }
    if ($global:LINA_DEBUG_MODE -AND $Path -notlike "*locale/*.js*") { 
        if ($Path -like "*.json*") {
            $req_return = $request | ConvertTo-Json
        }elseif ($Path -like "*login.*") {
            $req_return=$request
        }else {
            $req_return = ([xml]$request).OuterXml
        }
        Write-Host "Call API $Path : $request $req_return"
        Write-Host "Body : $Body"
    }

    if ($global:LINA_DOC_MODE -AND $Path -notlike "*locale/*.js*") { 
        if ($Path -like "*.json*") {
            $req_return = $request | ConvertTo-Json
        }elseif ($Path -like "*mng_*.html*") {
            $req_return = "[$request]"
        }else {
            $type = $request.GetType().FullName
            if ($type -like "*XmlDocument*") {
                $req_return = ([xml]$request).OuterXml
            }else {
                $req_return = $request
            }
                
        }
        
        if ($Path -match "\?") { 
            $query_vars = $Path.Substring($Path.IndexOf('?')).Split('#')[0] 
            $url_path = $Path.Substring(0,$Path.IndexOf('?'))
        }else {
            $query_vars = ""
            $url_path = $Path
        }
        
        # Get Parent function Name
        $callStack = Get-PSCallStack
        $stackcount= $callStack.Count
        # Stack = 3 means the main function
        #if ($callStack.Count -eq 3 -AND $global:LINA_DOC_URL_DONE -notcontains "$url_path") {
        $ParentFunctionName = $callStack[1].FunctionName -Replace "<Process>",""
        $function_description = (Get-Command $ParentFunctionName | get-Help -full | Select-Object -Property "Synopsis").Synopsis
        # Write-Host2 "WARNING : Call Stack count : $stackcount - Function is $ParentFunctionName"
        # When runned in VS Code , Stack should be 3 / if run directly stack should be 4.
        if ($stackcount -eq 4) {
            
            Write-Host "================================================="
            Write-Host "POWERSHELL FUNCTION : $ParentFunctionName"
            Write-Host "STACK # : $stackcount"
            Write-Host "DESCRIPTION : $function_description"
            Write-Host "REST REQUEST : $Method $Path"
            Write-Host "HEADERS : Content-Type: $ContentType"
            Write-Host "PATH : $url_path"
            Write-Host "QUERY : $query_vars"
            Write-Host "RESPONSE : $req_return"
            CreateHTMLDocForFunction "$function_description" "POST" "$url_path" "$ContentType" "$query_vars" "$Body" "$req_return"
        }
    }  
    Return $request
}

function InitializeHTMLDoc() {
    $Global:doc_function_id = 0
    $version = [version]$global:LINA_VERSION
    $short_version = [string]$version.Major+"."+[string]$version.Minor+"."+[string]$version.Build
    $html = @"
    <!doctype html>
    <html lang='en'>
    <head>
        <!-- Required meta tags -->
        <meta charset='utf-8'>
        <meta name='viewport' content='width=device-width, initial-scale=1, shrink-to-fit=no'><!-- Bootstrap CSS -->
        <link rel='stylesheet' href='css/bootstrap.min.css'>
        <title>Lina REST API</title>
        <script src='js/jquery-3.4.1.min.js'></script>
        <script src='js/popper.min.js'></script>
        <script src='js/bootstrap.min.js'></script>
    </head>
    <body>
        <center>
            <h1 class='display-4'>Lina Rest API v$short_version</h1>
        </center>
        <center>
        <h3>Requests</h3>
        </center>
        <div class="list-group container">
"@


        Remove-item ".\index.html" -ErrorAction SilentlyContinue
        Add-Content -Path ".\index.html" -Encoding "UTF8" -Value $html
        #New-Item -Path . -Name "index.html" -ItemType "file" -Value $html
}

function CreateHTMLDocForErrors() {
    $html = "</div><br/><center><h3>Description Of Usual Server Status Responses:</h3></center>
    <div class='list-group container'>"


    foreach ($error_code in $global:INT_API_ERRORCODES) {
        $html+=" <div class='list-group-item list-group-item-action'>
        <div class='row'>
            <div class='col-sm-1'><span class='badge badge-info'>$($error_code[0])</span></div>
            <div class='col-sm-6'><b>$($error_code[1])</b></div>
            <div class='col-sm-5'>$($error_code[2])</div>
        </div>
    </div>"

    }
    $html += "</div>"
    Add-Content -Path ".\index.html" -Encoding "UTF8" -Value $html
}

function CloseHTMLDoc() {
$html = @"
    <script>
        function myFunction(url) { if (document.getElementById(url)) { etat = document.getElementById(url).style.display; if (etat == 'none') { document.getElementById(url).style.display = 'inline'; } else { document.getElementById(url).style.display = 'none'; } } if (document.getElementById(url + '.result')) { result = document.getElementById(url + '.result').style.display; if (result == 'none') { document.getElementById(url + '.result').style.display = 'inline'; } else { document.getElementById(url + '.result').style.display = 'none'; } } if (document.getElementById(url + '.sample')) { result = document.getElementById(url + '.sample').style.display; if (result == 'none') { document.getElementById(url + '.sample').style.display = 'inline'; } else { document.getElementById(url + '.sample').style.display = 'none'; } } }
    </script>
    </body>
    </html>
"@

Add-Content -Path ".\index.html" -Encoding "UTF8" -Value $html
}

function CreateHTMLDocForFunction($description, $verb, $url, $contenttype, $query, $body, $response) {
    Write-Host2 "WARNING : Documenting URL $url"
    
    if ($body -eq "") { $verb = "GET" }
    $response= [System.Web.HttpUtility]::HtmlEncode($response)
    $body= [System.Web.HttpUtility]::HtmlEncode($body)
    
    
    if ($query -like "?user=superadmin&password=*") {
        $query="?user=superadmin&password=YourPassword"
    }
    
    
    if ($verb -eq "POST") {
        $style1="badge-danger"
    }else {
        $style1="badge-primary"
    }

    $Contenttype="ContentType: $Contenttype"

    # For those without dedicated commandlet
    $description = switch -wildcard ($url) {
        "*check_session.xml" {"Get current session details (current view / language) "; break}
        "*lst_globals.xml" {"Get tenant global configuration"; break}
        "*attach_prof_to_ug.json" {"Attach a User Profile to a User Group."; break}
        "*list_prof_ug_relation.json" {"Retrieves the relations between User Profiles and User Groups."; break}
        "*list_user_ug_relation.json" {"Retrieves the relations between Users and User Groups."; break}
         default{$description; break}
    }

    if ($global:LINA_DOC_URL_DONE -notcontains "$description") {
        # Not documented yet
        $global:LINA_DOC_URL_DONE += "$description"

    $Global:doc_function_id +=1
    $html = @"
<div class='list-group-item list-group-item-action'>
    <div class='row'>
        <div class='col-sm-1'><span class='badge $($style1)'>$($verb)</span></div>
        <div class='col-sm'>
            <h5><b>$($url)</b></h5>
        </div>
        <div class='col-sm-5'>$($description)</div>
        <div class='col-sm-1'>
            <button class='btn btn-link dropdown-toggle' type='button' id='dropdownMenuButton'
                data-toggle='dropdown' aria-haspopup='true' aria-expanded='false'
                onclick='myFunction("function_id_$($Global:doc_function_id)")'></button>
        </div>
    </div>
    <div id='function_id_$($Global:doc_function_id)' class='row' style='display: none;'>
        <div class='col-sm'>
            <h5><b>Example:</b></h5>
            <span class='badge $($style1)'>$($verb)</span> https://hostname:8181$($url)
        </div>
        <div class='col-sm'>
            <br><br><b>Headers</b>
            <div class='row'>
                <div class='col-sm'><pre><code>
$($contenttype)</code>
                </pre><br></div>
            </div>
        </div>
        <div class='col-sm'>
            <br><br><b>Query String</b>
            <div class='row'>
                <div class='col-sm'><pre><code>
$($query)</code>
                </pre><br></div>
            </div>
        </div>
        <div class='col-sm'>
            <br><br><b>Body</b>
            <div class='row'>
                <div class='col-sm'><pre><code>
$($body)</code>
                </pre><br></div>
            </div>
        </div>
        <div class='col-sm'>
            <br><br><b>Response</b>
            <div class='row'>
                <div class='col-sm'><pre><code>
$($response)</code>
                </pre><br></div>
            </div>
        </div>
    </div>
</div>
"@


Add-Content -Path ".\index.html" -Encoding "UTF8" -Value $html

}
}

function LinaToLocalTime ($lina_time = 0) {
    <# Lina Times are UTC based ? Not GMT ?#>
    if (!$lina_time -OR $lina_time -eq 0 ) {
        Return $null
    }

    $date = (Get-Date 01.01.1970) + ([System.TimeSpan]::fromseconds($lina_time / 1000))
    $oFromTimeZone = [System.TimeZoneInfo]::FindSystemTimeZoneById("UTC")
    $oToTimeZone = [System.TimeZoneInfo]::FindSystemTimeZoneById([System.TimeZoneInfo]::Local.Id)
    $utc = [System.TimeZoneInfo]::ConvertTimeToUtc($date, $oFromTimeZone)
    $newTime = [System.TimeZoneInfo]::ConvertTime($utc, $oToTimeZone)
    return $newTime
}

function HumanFriendlyTimespan ($last_backup_time = 0) { 
    if (!$last_backup_time -OR $last_backup_time -eq 0 ) {
        Return $null
    }

    if ((Get-Date $last_backup_time -format "yyyy") -eq 1970) {
        return "Never"
    }

    $since = New-TimeSpan -Start $last_backup_time
    $since_d = [math]::Round($since.TotalDays)
    $since_h = [math]::Round($since.TotalHours)
    $since_m = [math]::Round($since.TotalMinutes)
    $since_s = [math]::Round($since.TotalSeconds)
    
    if ($since_d -ge 1) {
        if ($since_d -gt 1) { $plural = "s" }
        return "$since_d day$plural ago"
    }
    elseif ($since_h -ge 1) {
        if ($since_h -gt 1) { $plural = "s" }
        return "$since_h hour$plural ago"
    }
    elseif ($since_m -ge 1) {
        if ($since_m -gt 1) { $plural = "s" }
        return "$since_m minute$plural ago"
    }
    else {
        if ($since_s -gt 1) { $plural = "s" }
        return "$since_s second$plural ago"
    }
}

function HumanFriendlyTimespanFuture ($expiration_date = 0) { 
    if (!$expiration_date -OR $expiration_date -eq 0 ) {
        Return "Never"
    }

    $expire = New-TimeSpan -End $expiration_date
    $expire_d = [math]::Round($expire.TotalDays)
    $expire_h = [math]::Round($expire.TotalHours)
    $expire_m = [math]::Round($expire.TotalMinutes)
    $expire_s = [math]::Round($expire.TotalSeconds)
    
    if ($expire_d -ge 1) {
        if ($expire_d -gt 1) { $plural = "s" }
        return "$expire_d day$plural"
    }
    elseif ($expire_h -ge 1) {
        if ($expire_h -gt 1) { $plural = "s" }
        return "$expire_h hour$plural"
    }
    elseif ($expire_m -ge 1) {
        if ($expire_m -gt 1) { $plural = "s" }
        return "$expire_m minute$plural"
    }
    else {
        if ($expire_s -gt 1) { $plural = "s" }
        return "$expire_s second$plural"
    }
}

function TranslateErrorCode() {
    Param(
        [Parameter(Mandatory = $True, Position = 0)]
        [string]$LinaError,
        [Parameter(Mandatory = $True, Position = 1)]
        [ValidateNotNullOrEmpty()]
        [string]$FailedAction
    )

    $ERROR_CODES = @{"0" = "OK"; "1" = "Undefined ID"; "2" = "Bad syntax (XML)"; "3" = "Cannot delete referenced object"; "4" = "Name already exists"; "5" = "Undefined referenced ID"; "6" = "Bad parameter"; "7" = "Cannot modify immutable object"; "8" = "Bad path syntax / path too long"; "9" = "Bad extension syntax / extension too long"; "10" = "Bad Name syntax / name too long"; "11" = "Buffer too short (internal)"; "12" = "Unexpected XML tag"; "13" = "Permission error, no admin session found"; "101" = "Conversion error (internal)"; "102" = "Duplicate ID (internal)"; "103" = "XML error (internal)"; "104" = "XML data error (internal)"; "105" = "Memory allocation error (internal)"; "107" = "Bad data header (internal)"; "108" = "Bad command (internal)"; "199" = "Generic internal"; "999" = "Unidentified error" }
    $error_clean = $LinaError.Replace("[", "").Replace("]", "").Trim()
    $translated_error = $ERROR_CODES[$error_clean]

    if ($translated_error) {
        $found_error = $translated_error
    }
    else {
        $found_error = "Unknown Error # $translated_error"
    }
    Write-Host2 "ERROR : an error occurred trying to $FailedAction : $found_error / Error $LinaError"
}




function InitTranslations() {
    Param(
        [Parameter(Mandatory = $True, Position = 0)]
        [ValidateNotNullOrEmpty()]
        [string]$Lang
    )   
    $translations = @{ }
    
    # Init old translations (lina < 5.3) (add them to new if values does not exists)

    # Do not use CallApi to avoid issues with POST method not authorized
    $Path = "/Admin/locale/$lang.js"

    if ($PSVersionTable.PSVersion.Major -lt 6) {
        # Pre-PowerShell 6.0
        # Disabled keep-alive (PowerShell for Windows Only) to fix issue : A connection that was expected to be kept alive was closed by the server
        $request = Invoke-RestMethod -Uri $GLOBAL_LINA_SERVER$Path -Method "GET" -DisableKeepAlive -ErrorAction SilentlyContinue -SkipHttpErrorCheck
    }
    else {
        # PowerShell Core (>= 6.0)
        $request = Invoke-RestMethod -Uri $GLOBAL_LINA_SERVER$Path -Method "GET" -DisableKeepAlive -SkipCertificateCheck -ErrorAction SilentlyContinue -SkipHttpErrorCheck
    }
    $temp_utf = FixEncoding($request.ToString())
    <#
    if ($PSVersionTable.PSVersion.Major -ge 6) {
        $temp_utf = $request.ToString()
    }else {
        $temp_utf = [System.Text.Encoding]::UTF8.GetString([System.Text.Encoding]::GetEncoding(28591).GetBytes($request.ToString()))
    }
    #>


    $temp_split = $temp_utf -split '\n'
    $lines = ($temp_split | Select-String -Pattern "SERVER_NAME_", "UNCAT_AGENTS_LABEL", "USER_PROFILE_NAME_","USER_GROUP_NAME_","AGENT_VIEW_BY_AGENT_GROUP_LABEL" ).line

    foreach ($line in $lines) {
        $splitted = $line -split ': "'
        $key = $splitted[0].Trim().Replace('SERVER_NAME_', '').Replace("__", "_")
        $value = $splitted[1].Replace('",', '').Trim()
        try {
            $translations.add($key, $value)
        } catch { } finally {}
    }

    
    # Init new translations
    $Path = "/locale/messages.$lang.xlf"
    try {
        if ($PSVersionTable.PSVersion.Major -lt 6) {
            # Pre-PowerShell 6.0
            # Disabled keep-alive (PowerShell for Windows Only) to fix issue : A connection that was expected to be kept alive was closed by the server
            $request2 = Invoke-RestMethod -Uri $GLOBAL_LINA_SERVER$Path -Method "GET" -DisableKeepAlive -ErrorAction SilentlyContinue -ErrorVariable oErr
        }else {
            # PowerShell Core (>= 6.0)
            $request2 = Invoke-RestMethod -Uri $GLOBAL_LINA_SERVER$Path -Method "GET" -DisableKeepAlive -SkipCertificateCheck -ErrorAction SilentlyContinue -ErrorVariable oErr
        }
    }catch {
        $RestError = $_
    }

    if (!$RestError) {
        # Clean XML because angular creates an invalid one
        [xml]$d = [System.Text.Encoding]::UTF8.GetString([System.Text.Encoding]::GetEncoding("ISO-8859-1").GetBytes($request2)) -replace "<p>|</p>|<div>|</div> </|</div>|<br>|<br/>|<b>|</b>|<u>|</u>|<hr>|</hr>|<li>|</li>|",""
        $g = $d.xliff.file.body.'trans-unit' | group-object id
        if($g){
            $g.group | Where-Object { ($_.ID -ceq $_.ID.ToUpper()) -AND ($_.ID -match '^.*(_LABEL|SERVER_NAME_|UNCAT_AGENTS_LABEL|USER_PROFILE_NAME_|USER_GROUP_NAME_|AGENT_VIEW_BY_AGENT_GROUP_LABEL).*$') } | ForEach-Object {
                $text_id=[string]$_.ID.Trim().Replace('SERVER_NAME_', '').Replace("__", "_").Replace("NO_GROUP_INHERITANCE_LABEL", "UNCAT_AGENTS_LABEL")
                $text_trad=([string]$_.target.Innerxml).Replace('",', '').Trim()
                # Add text to translation only if not already present
                if ($translations.ContainsKey("$text_id")) {
                    Write-Debug "Key present : $text_id"
                }else {
                    $translations.add($text_id,$text_trad)
                }
            }
        }
    }

    Set-Variable -name LINA_TRANSLATIONS -Scope global -Value $translations
}

function TranslateSystemCategories($ToTranslate = 0) {
    if (!$ToTranslate -OR $ToTranslate -eq 0 ) {
        return $null
    }
    $system_categories = @{"256" = "Windows"; "512" = "Linux"; "768" = "macOS"; "272" = "Windows Server"; "528" = "Linux Server"; "784" = "macOS Server" }
    $translated = $system_categories[$ToTranslate]
    if ($translated) {
        return $translated
    }
    else {
        return $ToTranslate
    }
}

function TranslateBlockStatuses($ToTranslate) {
    if ($Null -eq $ToTranslate ) {
        # Null status = Normal (0)
        $ToTranslate = 0
    }
    $translated = Translate($INT_BLOCK_STATUS[[int]$ToTranslate])
    if ($translated) {
        return $translated
    }else {
        return $ToTranslate
    }
}

function TranslateBlockStatusesIntName($ToTranslate) {
    if ($Null -eq $ToTranslate ) {
        # Null status = Normal (0)
        $ToTranslate = 0
    }
    $translated = Translate($global:INT_BLOCK_STATUS_NAME[[int]$ToTranslate])
    if ($translated) {
        return $translated
    }else {
        return $ToTranslate
    }
}



function Translate($ToTranslate = 0, $FixEncoding = 1) {
    # FixEncoding = 0 is used for JSON returns that ara already in UTF-8 and do not need fixing
    if (!$ToTranslate -OR $ToTranslate -eq 0 ) {
        return $null
    }

    # Translating an array of items => returns array of item translated
    if ($ToTranslate.Count -gt 1 ) {
        $words_translated=@()
        foreach ( $word in $ToTranslate ) {
            $words_translated+=(Translate $word $FixEncoding)
        }
        return $words_translated
    }
    
    $ToTranslate = $ToTranslate.TrimStart("_").TrimEnd("_")

    if ($ToTranslate -eq "UNCAT") { $ToTranslate = "UNCAT_AGENTS_LABEL"; }
    $translated = $LINA_TRANSLATIONS[$ToTranslate.Replace("__", "_")]

    if ($translated) {
        return $translated
    }
    else {
        # No translation available. Use original text.
        if ($ToTranslate -AND $FixEncoding -eq 1) {
            return [System.Text.Encoding]::UTF8.GetString([System.Text.Encoding]::GetEncoding(28591).GetBytes($ToTranslate))
            #return FixEncoding($ToTranslate)
        }
        elseif ($ToTranslate -AND $FixEncoding -eq 0) {
            return FixEncoding($ToTranslate)
        }
        else {
            return $null
        }
    }
}

function FixEncoding($text) {
    if ($PSVersionTable.PSVersion.Major -ge 6) {
        Return $text
    }
    else {
        Return [System.Text.Encoding]::UTF8.GetString([System.Text.Encoding]::GetEncoding(28591).GetBytes("$text"))
    }
    
}

function LinaTimestamp {
    return [math]::Round((New-TimeSpan -Start (Get-Date "01/01/1970") -End (Get-Date)).TotalSeconds * 1000)
}
function Connect-LinaServer {
    <#
.SYNOPSIS
Connects to an Atempo Lina server.
.DESCRIPTION
Connects to an Atempo Lina server 5.0+ using superadmin credentials.
Select your locale using the -Locale switch. Locale is used only to display the default elements (strategies, protections etc) with localized names.
Please note : there is no support for multiple connections to different or same server.
By default certificate checking is not enabled. If you want to enable it use $GLOBAL_IGNORE_CERTIFICATES= $False
.INPUTS
None
.OUTPUTS
None
.PARAMETER Server
Specify the URL of the Lina server you want to connect to.
You need to specify the protocol and ports. For example : https://10.0.0.1:8181 or http://10.0.0.1:8181
.PARAMETER Credential
Use PowerShell Secure credentials
.PARAMETER User
Specify the user name you want to use for authenticating with the server. It must be superadmin.
.PARAMETER Password
Specify the password
.PARAMETER ApiKey
Specify an API Key instead of login/password
.PARAMETER Locale
Specify the language that will be used for default elements names.
Optional. Default value is English.
Possible values are : "en","fr","es","de"
.PARAMETER Tenant
Select a specific tenant for actions (listing, creation will be limited to this tenant)
Optional. Default value is -1 (All tenant / Global view)
.EXAMPLE
Connect-LinaServer -Server "https://mylinaserver.domain.com:8181" -User "superadmin" -Password "mypassword"
Connection to mylinaserver.domain.com using default locale and global view (no tenant).
.EXAMPLE
Connect-LinaServer -Server "https://mylinaserver.domain.com:8181" -User "superadmin" -Password "mypassword" -Locale "fr" -Tenant "MyTenant"
Connection to mylinaserver.domain.com using french language and filtered on the tenant MyTenant.
.EXAMPLE
$user = "superadmin"
$pwd = ConvertTo-SecureString "mypassword" -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential($user,$pwd)
Connect-LinaServer -Server "https://mylinaserver.domain.com:8181" -Credential $cred -Locale "en"
Connection using PowerShell Credentials with login / password
.EXAMPLE
Connect-LinaServer -Server "https://mylina6server.domain.com:8181" -ApiKey "6e4abc4-4085-44a6-85ee-1b886e00f6db" -Locale "en"
Connection using API Key
.EXAMPLE
$user = "X-API-KEY"
$apikey = ConvertTo-SecureString "6e4abc4-4085-44a6-85ee-1b886e00f6db" -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential($user,$apikey)
Connect-LinaServer -Server "https://mylina6server.domain.com:8181" -Credential $cred -Locale "en"
Connection using PowerShell Credentials with API Key
#>

    [cmdletbinding()]
    Param(
        [Parameter(Mandatory = $True, Position = 0)]
        [ValidateNotNullOrEmpty()]
        [string]$Server,
        [Parameter(Mandatory = $True,ParameterSetName = "ByApiKey",Position = 1)]
        [ValidateNotNullOrEmpty()]
        [string]$ApiKey,
        [Parameter(Mandatory = $True,ParameterSetName = "ByCredential",Position = 1)]
        [ValidateNotNullOrEmpty()]
        [pscredential]$Credential,
        [Parameter(Mandatory = $True,ParameterSetName = "ByUserPassword", Position = 1)]
        [ValidateNotNullOrEmpty()]
        [string]$User,
        [Parameter(Mandatory = $True,ParameterSetName = "ByUserPassword", Position = 2)]
        [ValidateNotNullOrEmpty()]
        [string]$Password,
        [Parameter(Mandatory = $false, Position = 3)]
        [ValidateSet("en", "fr", "es", "de")] 
        [string]$Locale = "en",
        [Parameter(Mandatory = $false, Position = 4)]
        [ValidateNotNullOrEmpty()]
        [string]$Tenant
    )
    if ($global:LINA_DEBUG_MODE -eq $true) { $PSDefaultParameterValues['*:Verbose'] = $true }
    if ($global:LINA_DOC_MODE -eq $true) {  Write-Host2 "WARNING : Documentation mode is enabled" }

    if ($Credential) { 
        $User=$Credential.UserName
        if ($User -ilike "*X-API-KEY*") {
            # Credentials is an API Key
            $ApiKey=Unmarshal($Credential.Password)
            $passenc = "empty"
        } else {
            # Crendetials is a password
            $passenc = prt($Credential.Password)
        }
    }else {
        $passenc = [System.Web.HttpUtility]::UrlEncode($Password)
    }
    Set-Variable -name GLOBAL_LINA_SERVER -Scope global -Value $Server


    if ($ApiKey) {
        Write-Host "Connecting to Lina server $GLOBAL_LINA_SERVER using API Key : " -NoNewline
        Set-Variable -name GLOBAL_LINA_APIKEY -Scope global -Value $ApiKey
        $global:INT_LINA_API_VERSION=60
        $request = CallAPI -Path "/ADE/server.json"
    } else {
        $userenc = [System.Web.HttpUtility]::UrlEncode($User)

        Write-Host "Connecting to Lina server $GLOBAL_LINA_SERVER : " -NoNewline

        try {
            # Lina < 6.0
            $request = CallAPI -Path "/ADE/login.html?user=$userenc&password=$passenc" -FirstConnection
        } catch {
            $exception = $_.Exception
            if ( $_.Exception.Response.StatusCode.value__ -eq 404 ) {
                # Lina >= 6.0
                # We know we are at least in 6.0
                $global:INT_LINA_API_VERSION=60
                try {
                    $request = CallAPI -Path "/ADE/login.json?user=$userenc&password=$passenc" -FirstConnection
                }catch {
                    $statuscode=$_.Exception.Response.StatusCode.value__
                    $statusdesc=$_.Exception.Response.ReasonPhrase
                    Write-Error "Could not connect : Error $statuscode / $statusdesc"
                    return                          
                }    
            }
        }
    }

    Clear-Variable "passenc"

    if ([string]$request -like "*OK*") {
        Set-Variable -name GLOBAL_LINA_CONNECTED -Scope global -Value $True
        Set-Variable -name GLOBAL_LINA_SERVERTYPE -Scope global -Value ((Get-LinaGlobalStats).ServerType) 

        $LINA_VERSION = (Get-LinaGlobalStats).LinaVersion

        if ($LINA_VERSION.Major -ge 5) {

            # API version is 52 for version 5.2.x / 61 for version 6.1.x
            if ($LINA_VERSION.Minor -ge 0) {
                Set-Variable -name INT_LINA_API_VERSION -Scope global -Value (($LINA_VERSION.Major*10)+$LINA_VERSION.Minor)
            } else {
                Set-Variable -name INT_LINA_API_VERSION -Scope global -Value ($LINA_VERSION.Major*10)
            }

            InitTranslations($Locale)
            
            Write-Host2 "Successfully connected (Lina version $LINA_VERSION)"
            Write-Verbose "API version used $global:INT_LINA_API_VERSION"
            

            if ($global:LINA_DOC_MODE -eq $true) { InitializeHTMLDoc }
        } else {
            Write-Error "cannot connect to server with Lina version below 5.0 (current version : $LINA_VERSION)"
            Set-Variable -name GLOBAL_LINA_CONNECTED -Scope global -Value $False
            return
        }
    }
    else {
        $request | Format-List
        Write-Error "An error occurred trying to connect : $request"
        Set-Variable -name GLOBAL_LINA_CONNECTED -Scope global -Value $False
        return
    }    
}

function Disconnect-LinaServer {
    <#
.SYNOPSIS
Disconnects from an Atempo Lina server.
.DESCRIPTION
Disconnects current session.
.INPUTS
None
.OUTPUTS
None
.EXAMPLE
Disconnect-LinaServer
Disconnects current session.
#>

    if ($global:GLOBAL_LINA_CONNECTED -eq $False) { 
        Write-Error "$($global:GLOBAL_DISCONNNECTED_MESSAGE)" 
        return
    }

    Write-Host "Disconnecting from Lina server $GLOBAL_LINA_SERVER : " -NoNewline
    if ($global:GLOBAL_LINA_APIKEY) {
        Remove-Variable -Scope global -Name "GLOBAL_LINA_APIKEY"
        Write-Host2 "Successfully disconnected using API Key"
    }else {
        $request = CallAPI -Path "/ADE/logout.json"
        if ([string]$request -like "*- OK*" -OR [string]$request -like "*ASM_OK*" ) {
            Write-Host2 "Successfully disconnected."
        }
        else {
            TranslateErrorCode -LinaError $request -FailedAction "Disconnecting from server"
        }
        if ($global:LINA_DOC_MODE -eq $true) { 
            CreateHTMLDocForErrors
            CloseHTMLDoc 
        }
    }
    Set-Variable -name GLOBAL_LINA_CONNECTED -Scope global -Value $False
}


Function prt($value){
    $unmr = Unmarshal($value)
    return [System.Web.HttpUtility]::UrlEncode($unmr)
}

Function Unmarshal($value)
{
    [System.IntPtr] $bstr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($value);
    try{[System.Runtime.InteropServices.Marshal]::PtrToStringBSTR($bstr);}
    finally{[System.Runtime.InteropServices.Marshal]::FreeBSTR($bstr);}
    
}


# Write to console intelligently with colors
Function Write-Host2 ( $message , $type) {
    Switch -Wildcard ($message) {
        "*SUCCESS*"  { $color = "Green"  }
        "WARNING*"   { $color = "Yellow" }
        "ERROR*"     { $color = "Red"    }
        "INFO*"      { $color = "Blue"    }
        Default      { $color = "White"  }
    }
    # If type is forced , using type
    Switch -Wildcard ($type) {
        "*SUCCES*"  { $color = "Green"  }
        "*WARN*"    { $color = "Yellow" }
        "*ERR*"     { $color = "Red"    }
        "*INFO*"    { $color = "Blue"  }
    }
    Write-Host $message -ForegroundColor $color
}