Functions/Get.ps1


function Get-LinaGlobalStats {
    <#
.SYNOPSIS
Retrieves the global stats and configuration of the current Lina server.
.DESCRIPTION
Retrieves the global stats and configuration of the current Lina server such as the server version deduplication ratio, volume stored etc
.INPUTS
None
.OUTPUTS
LinaStats Object
.EXAMPLE
Get-LinaGlobalStats
Retrieve global infos of current Lina Server
.EXAMPLE
$lina_version = (Get-LinaGlobalStats).LinaVersion
Get current installed version of Lina sofware.
#>

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

    if ($INT_LINA_API_VERSION -lt 60) {
        $request = CallAPI -Path "/info.json"
        $stats = $request
    } else {
        $request = CallAPI -Path "/ADE/info.json"
        $stats = $request
    }

    # Extract version number of Lina
    if ($stats.aln_version.IndexOf(' ') -gt 2) {
        # Extract version number
        $version_number=$stats.aln_version.Substring(0,$stats.aln_version.IndexOf(' '))
        $second_part = $stats.aln_version.Substring($stats.aln_version.IndexOf(' ')+1)
        if ($second_part.IndexOf(' ') -gt 2) {
            # version is like "5.1.10.39537 Beta (51, 3a9e34b38, Feb 2 2021 11:14:06)"
            $version_qualifier = $second_part.Substring(0,$second_part.IndexOf(' '))
            
            # Should not exist. Missing qualifier in new versions
            # Example : "5.1.10.39537 (51, 3a9e34b38, Feb 2 2021 11:14:06)"
            if ($version_qualifier -like "*(*") { $version_qualifier = "GA" }
        }else {
            # Version is like "5.1.10.39537 Beta"
            $version_qualifier = $second_part
        }
    }else {
        # Version is like "5.1.10.39537"
        $version_number=$stats.aln_version
        $version_qualifier="GA"
    }

    $LinaVersion = [version]$version_number

    if ($version_qualifier -ilike "*DEV*") {
        $LinaVersionType = "Beta"
    }elseif ($version_qualifier -ilike "*RC*") {
        $LinaVersionType = "Release Candidate"
    }elseif ($version_qualifier -ilike "*GA*") {
        $LinaVersionType = "Generally Available"
    }else {
        $LinaVersionType = "Special Version ($version_qualifier)"
    }
    <#
 <hss_version>5.2.1.12663 RC</hss_version>
                         <aln_version>5.2.1.12663 RC</aln_version>
                         <apr_version>1.7.0</apr_version>
                         <db_version>sqlite 3.28.0</db_version>
                         <hss_uuid>f27737b8-bf72-11ea-895e-43673f98433c</hss_uuid>
                         <create_date>1594031100</create_date>
                         <uptime>1623918111</uptime>
                         <time_t>1623937989</time_t>
                         <vol_read>1322777547974</vol_read>
                         <vol_transfered>0</vol_transfered>
                         <vol_written>73132294256</vol_written>
                         <vol_restored>0</vol_restored>
                         <vol_protected>56172007547</vol_protected>
                         <vol_stored>22828584752</vol_stored>
                         <vol_meta>46492939</vol_meta>
                         <disk_avail>67324133376</disk_avail>
                         <disk_use_byte>22875077691</disk_use_byte>
                         <disk_use>25.36 %</disk_use>
                         <disk_full>-1</disk_full>
                         <last_week>31538518955</last_week>
                         <nb_stream>5</nb_stream>
                         <nb_block>557448</nb_block>
                         <nb_block_unref>58250</nb_block_unref>
                         <nb_client>1</nb_client>
                         <nb_client_reached>1</nb_client_reached>
                         <nb_max_client>256</nb_max_client>
                         <nb_thread>72</nb_thread>
                         <nb_max_thread>74</nb_max_thread>
                         <net_bandwith>0.00</net_bandwith>
                         <cpr_ratio>2.33:1</cpr_ratio>
                         <dedup_ratio_real>2.45:1</dedup_ratio_real>
                         <dedup_ratio>2.79:1</dedup_ratio>
                         <cpu_usage>4</cpu_usage>
                         <ram_usage>180</ram_usage>
                         </infolist>
    #>

    $LinaStats = [PSCustomObject]@{
        PSTypeName        = 'LinaStats'
        LinaVersion       = $LinaVersion
        ServerType        = $stats.server_type
        LinaVersionType   = $LinaVersionType

        PercentDiskUse          = $stats.disk_use
        DiskFullForecastInDays  = $stats.disk_full

        DedupRatio        = $stats.dedup_ratio
        CompressionRatio  = $stats.cpr_ratio
        DiskAvailableGB   = [math]::Round(($stats.disk_avail) / (1024 * 1024 * 1024), 2)

        DiskSavingGB      = [math]::Round(($stats.vol_protected - $stats.vol_stored) / (1024 * 1024 * 1024), 1)
        VolumeProtectedGB = [math]::Round(($stats.vol_protected) / (1024 * 1024 * 1024), 1)
        VolumeRestoredGB  = [math]::Round(($stats.vol_restored) / (1024 * 1024 * 1024), 1)
        VolumeStoredGB    = [math]::Round(($stats.vol_stored) / (1024 * 1024 * 1024), 1)

        VolumeProtectedMB = [math]::Round(($stats.vol_protected) / (1024 * 1024))
        VolumeRestoredMB  = [math]::Round(($stats.vol_restored) / (1024 * 1024))
        VolumeStoredMB    = [math]::Round(($stats.vol_stored) / (1024 * 1024))

        LinaVersionFull   = $stats.aln_version
    }
    Return $LinaStats
}


function Get-LinaSystemInfos {
    <#
.SYNOPSIS
Retrieves the system informations of the current Lina server.
.DESCRIPTION
Retrieves the system informations of the current Lina server such as the Operating system details and Hardware informations
.INPUTS
None
.OUTPUTS
LinaSysteminfo Object
.EXAMPLE
Get-LinaSystemInfo
Retrieve system informations of current Lina Server
.EXAMPLE
(Get-LinaSystemInfo).PercentUsageRAM
Get current usage of RAM on the server
.EXAMPLE
(Get-LinaSystemInfo).PercentUsageCPU
Get current usage of CPU on the server
#>

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

    if ($INT_LINA_API_VERSION -lt 60) {
        $request = CallAPI -Path "/sys.xml"
        $sysinfo = ([xml]$request).sysinfo
    } else {
        $request = CallAPI -Path "/ADE/sys.json"
        $sysinfo = $request.sysinfo
    }

    $machine_type = switch ($sysinfo.vm_type) {
         1  {"Physical"; break}
         2  {"VM (VMware)"; break}
         3  {"VM (VirtualBox)"; break}
         4  {"VM (KVM)"; break}
         5  {"VM (Hyper-V)"; break}
         6  {"VM (Xen)"; break}
         7  {"VM (Windows Subsystem for Linux)"; break}
        default {"Unknown"; break}
    }

    # Avoid division by zero when there is no swap
    if ($sysinfo.swap_size -gt 0) {
        $PercentUsageSwap = [math]::Round($sysinfo.swap_free/$sysinfo.swap_size,2)*100
    }else {
        $PercentUsageSwap = 0
    }
    
    $LinaSysinfo = [PSCustomObject]@{
        PSTypeName        = 'LinaSysteminfo'
        CPUName           = $sysinfo.cpu_name
        CPUCores          = $sysinfo.cpu_count
        CPUClockMHz       = $sysinfo.cpu_clock

        MemorySizeGB      = [math]::Round($sysinfo.mem_size/1024,1)
        MemoryFreeGB      = [math]::Round($sysinfo.mem_free/1024,1)

        SwapSizeGB        = [math]::Round($sysinfo.swap_size/1024,1)
        SwapFreeGB        = [math]::Round($sysinfo.swap_free/1024,1)

        OSArchitecture    = $sysinfo.arch_name   
        OSName            = $sysinfo.os_name   
        OSRelease         = $sysinfo.os_release   
        MachineType       = $machine_type  

        PercentUsageCPU          = $sysinfo.cpu_usage
        PercentUsageRAM          = [math]::Round($sysinfo.mem_free/$sysinfo.mem_size,2)*100
        PercentUsageSwap         = $PercentUsageSwap
    }
    Return $LinaSysinfo
}

function Get-LinaAgent {
    <#
.SYNOPSIS
Retrieves the agents.
.DESCRIPTION
Retrieves the agents. Returns a set of agents that correspond to the filter criteria provided
.INPUTS
None
.OUTPUTS
Array of LinaAgent Objects
.PARAMETER Name
Name of the agent(s) to retrieve. Wilcards are allowed. For example : *DESKTOP*
.PARAMETER ID
ID of the agent to retrieve. For example : 134.
.PARAMETER Tag
Exact tag of the agents to retrieve. For example : "MyTag". Must match exactly a Tag.
.EXAMPLE
Get-LinaAgent -Name "TEST"
Retrieves the agent named "TEST" and display its properties.
.EXAMPLE
$agents_desktop = Get-LinaAgent -Name "*DESKTOP*"
Retrieves all the agents with DESKTOP inside their names. The list returned can be passed to other functions.
.EXAMPLE
Get-LinaAgent | Where {$_.LastSyncTime -le ((Get-Date).AddDays(-14)) } | Select Name,Email,LastBackup,LastSyncTime | ft
Retrieves all the agents without backups since last 14 days.
.EXAMPLE
$agents_desktop = Get-LinaAgent -Tag "MyLinuxAgents"
Retrieves all the agents having a "MyLinuxAgents" tag assigned. The list returned can be passed to other functions.
.EXAMPLE
Get-LinaAgent -Name "TEST" | Remove-LinaAgent
Will get the agent named TEST then pass it to the next command for deletion.
#>

    [cmdletbinding(DefaultParameterSetName = "ByName")]
    Param(
        [Parameter(ParameterSetName = "ByName", Position = 0)]
        [ValidateNotNullOrEmpty()]
        [string]$Name,
        [Parameter(ParameterSetName = "ByID")]
        [ValidateRange(0, 2147483647)]
        [int]$ID,
        [Parameter(ParameterSetName = "ByTag")]
        [ValidateNotNullOrEmpty()]
        [string]$Tag
    )
    Write-Verbose "Getting list of agents"
    if ($global:GLOBAL_LINA_CONNECTED -eq $False) { 
        Write-Error "$($global:GLOBAL_DISCONNNECTED_MESSAGE)" 
        return
    }

    $Tenants = Get-LinaTenant
    $LinaItems = @()
    
    if ($global:INT_LINA_API_VERSION -le 50 ) {
        
        $agentstats = Get-LinaAgentStats
        # Using API 5.0 (XML and dissociated agent and AgentStats)
        $request = CallAPI -Path "/lst_clients.xml?type=agent"
        $Items = ([xml]$request).HNConfig.ClientArray.Client

        foreach ($Item in $Items) {
            <# Filtering on ID or Name (only if provided) #>
            $Name_encoded = [System.Text.Encoding]::UTF8.GetString([System.Text.Encoding]::GetEncoding(28591).GetBytes($Item.Name))
            if ((!$Name -AND !$ID) -OR ( $Item.Name -like "$Name" -OR $Name_encoded -like "$Name" -OR $Item.ID -eq $ID)) {
                $current_agentstat = $agentstats | where-object { $_.ID -eq $Item.ID }

                $CurrentLinaItem = [PSCustomObject]@{
                    PSTypeName             = 'LinaAgent'
                    Name                   = $Name_encoded
                    ID                     = $Item.ID
                    Tenant                 = $Tenants | where-object { $_.ID -eq $Item.DomainID } | Select-Object -ExpandProperty Name
                    AgentGroup             = Translate($Item.AttachementArray.Attachement.Group.Name)
                    Strategy               = Translate($Item.ServiceProfile.Name)
                    Protection             = Translate($Item.DataProfile.Name)

                    ComputerName           = $current_agentstat.ComputerName
                    System                 = $current_agentstat.System
                    SystemCategory         = TranslateSystemCategories($current_agentstat.SystemCategory)
                    AgentVersion           = $current_agentstat.AgentVersion
                    LastLogon              = $current_agentstat.LastLogon
                    Email                  = $current_agentstat.Email
                    CdpType                = $current_agentstat.CdpType
                    Alert                  = $current_agentstat.Alert

                    # LastBackup = LastSync. LastCompleted session update even when there are some errors
                    LastBackup             = $current_agentstat.LastBackup

                    LastSessionStart       = $current_agentstat.LastSessionStart
                    LastSessionComplete    = $current_agentstat.LastSessionComplete
                    LastConnection         = $current_agentstat.LastConnection
                    LastSync               = $current_agentstat.LastSync

                    LastStartedSession     = $current_agentstat.LastStartedSession
                    LastCompletedSession   = $current_agentstat.LastCompletedSession
                    LastConnectionTime     = $current_agentstat.LastConnectionTime
                    LastSyncTime           = $current_agentstat.LastSyncTime

                    TenantID               = $Item.DomainID
                    AgentGroupID           = $Item.AttachementArray.Attachement.Group.ID
                    StrategyID             = $Item.ServiceProfile.ID
                    ProtectionID           = $Item.DataProfile.ID
                    
                    StrategyInternalName   = $Item.ServiceProfile.Name
                    ProtectionInternalName = $Item.DataProfile.Name

                }
                $LinaItems += $CurrentLinaItem
            }
        }
    }
    else {
        
        # Using API 5.1+ (JSON containing agent and agentStats)
        $request = CallAPI -Path "/stats_clients.json?inactiveTime=604800"
        
        $Items = $request.ClientArray


        foreach ($Item in $Items) {
            #$Name_encoded = [System.Text.Encoding]::UTF8.GetString([System.Text.Encoding]::GetEncoding(28591).GetBytes($Item.AdminInfos.Name))
            $Name_encoded = FixEncoding($Item.AdminInfos.Name)
            <# Filtering on ID or Name (only if provided) #>
            if ((!$Name -AND !$ID -AND !$Tag) -OR ( $Item.AdminInfos.Name -like "$Name" -OR $Name_encoded -like "$Name" -OR $Item.AdminInfos.ID -eq $ID -OR $Item.Tags.Name -contains "$Tag"  )) {

                # Avoid divisions by zero
                if ($Item.AdminInfos.Quotas.MaxProtSize -gt 0) { $MaxProtSize = (($Item.ServerInfos.Backup.ProtSize) / ($Item.AdminInfos.Quotas.MaxProtSize)) * 100 } else { $MaxProtSize = $null }
                if ($Item.AdminInfos.Quotas.MaxHistSize -gt 0) { $MaxHistSize = (($Item.ServerInfos.Backup.HistSize) / ($Item.AdminInfos.Quotas.MaxHistSize)) * 100 } else { $MaxHistSize = $null }
                if ($Item.AdminInfos.Quotas.MaxProtObjs -gt 0) { $MaxProtObjs = (($Item.ServerInfos.Backup.ProtObjs) / ($Item.AdminInfos.Quotas.MaxProtObjs)) * 100 } else { $MaxProtObjs = $null }
                if ($Item.AdminInfos.Quotas.MaxHistObjs -gt 0) { $MaxHistObjs = (($Item.ServerInfos.Backup.HistObjs) / ($Item.AdminInfos.Quotas.MaxHistObjs)) * 100 } else { $MaxHistObjs = $null }
                
                # Alerts
                $alert_reasons = New-Object System.Collections.Generic.List[System.Object]
                if ($Item.ServerInfos.Alerts.Alert -eq $True) { $alert_reasons.Add("BackupAlert") } 
                if ($Item.ServerInfos.Alerts.QuotaAlert -eq $True) { $alert_reasons.Add("QuotaAlert") } 
                if ($Item.ServerInfos.Alerts.Deletion -eq $True) { $alert_reasons.Add("MarkedForDeletion") } 
                if ($Item.ServerInfos.Alerts.Inactive -eq $True) { $alert_reasons.Add("Inactive") } 
                if ($Item.ServerInfos.Alerts.ObsoleteVersion -eq $True) { $alert_reasons.Add("ObsoleteVersion") } 

                # Checking if agent is local
                if ($Item.AdminInfos.SourceID -eq "@") { $isLocal=$True } else { $isLocal = $False }
                
                # Blocking status
                if ($global:INT_LINA_API_VERSION -ge 53 ) { $AgentBlockStatus=TranslateBlockStatuses($Item.BlockListInfos.Reason) } else { $AgentBlockStatus = "" }
            
                if ($global:INT_LINA_API_VERSION -ge 60 ) { $AgentGroup=Translate ($Item.AdminInfos.AttachementArray | Where-Object { $_.Hierarchy.Name -eq 'HERIT' }).Group.Name 0 } else { $AgentGroup = Translate $Item.AdminInfos.AttachementArray.Group.Name 0 }
                

                $CurrentLinaItem = [PSCustomObject]@{
                    Name                     = $Name_encoded
                    ID                       = $Item.AdminInfos.ID
                    Tenant                   = $Tenants | where-object { $_.ID -eq $Item.AdminInfos.DomainID } | Select-Object -ExpandProperty Name
                    
                    NodeName                 = $Item.AdminInfos.NodeFriendlyName
                    AgentGroup               = $AgentGroup #Param 0 means encoding is already OK (UTF-8 because of JSON)
                    Strategy                 = Translate $Item.AdminInfos.ServiceProfile.Name 0 #Param 0 means encoding is already OK (UTF-8 because of JSON)
                    Protection               = Translate $Item.AdminInfos.DataProfile.Name 0 #Param 0 means encoding is already OK (UTF-8 because of JSON)
                    
                    Tags                     = if ($global:INT_LINA_API_VERSION -ge 60 ) { $Item.Tags.Name } else {$Null }
                    
                    ComputerName             = $Item.AgentInfos.ComputerName
                    FQDN                     = $Item.AgentInfos.Hostname
                    System                   = $Item.AgentInfos.System
                    SystemCategory           = TranslateSystemCategories($Item.AgentInfos.SystemCategory)
                    AgentVersion             = $Item.AgentInfos.AgentVersion
                    LastLogon                = $Item.AgentInfos.LastLogon
                    Email                    = $Item.AgentInfos.Email
                    CdpType                  = $Item.AgentInfos.CdpType
                    Alerts                   = $alert_reasons
                    
                    # New feature of Lina 5.3 (Block List)
                    BlockStatus              = $AgentBlockStatus
                    

                    ProtectedSizeMB          = [math]::Round(($Item.ServerInfos.Backup.ProtSize) / (1024 * 1024))
                    HistorySizeMB            = [math]::Round(($Item.ServerInfos.Backup.HistSize) / (1024 * 1024))
                    ProtectedObjects         = $Item.ServerInfos.Backup.ProtObjs
                    HistoryObjects           = $Item.ServerInfos.Backup.HistObjs

                    QuotaMaxProtSize         = $Item.AdminInfos.Quotas.MaxProtSize
                    QuotaMaxHistSize         = $Item.AdminInfos.Quotas.MaxHistSize
                    QuotaMaxProtObjs         = $Item.AdminInfos.Quotas.MaxProtObjs
                    QuotaMaxHistObjs         = $Item.AdminInfos.Quotas.MaxHistObjs

                    QuotaPercentUsedProtSize = [math]::Round($MaxProtSize)
                    QuotaPercentUsedHistSize = [math]::Round($MaxHistSize)
                    QuotaPercentUsedProtObjs = [math]::Round($MaxProtObjs)
                    QuotaPercentUsedHistObjs = [math]::Round($MaxHistObjs)
                    
                    isLocal                  = $isLocal
                    isMarkedforDeletion      = $Item.AdminInfos.Deletion

                    # LastBackup = LastSync. LastCompleted session update even when there are some errors
                    LastBackup               = HumanFriendlyTimeSpan (LinaToLocalTime $Item.NetworkInfos.SyncTime)

                    LastSessionStart         = HumanFriendlyTimeSpan (LinaToLocalTime $Item.NetworkInfos.LastStartedSession)
                    LastSessionComplete      = HumanFriendlyTimeSpan (LinaToLocalTime $Item.NetworkInfos.LastCompletedSession)
                    LastConnection           = HumanFriendlyTimeSpan (LinaToLocalTime $Item.NetworkInfos.LastConnectionTime)
                    LastSync                 = HumanFriendlyTimeSpan (LinaToLocalTime $Item.NetworkInfos.SyncTime)
                    
                    #add repli info (vivien)
                    RepliSrcName             = FixEncoding($Item.RepliInfos.RepliSrcName)
                    RepliDestName            = FixEncoding($Item.RepliInfos.RepliDestName)
                    RepliLastPIT             = HumanFriendlyTimeSpan (LinaToLocalTime $Item.RepliInfos.RepliLastPIT)
                    RepliLag                 = $Item.RepliInfos.RepliLag

                    # Same info as before but displayed as datetime instead of TimeSpan (ex: 22 days ago)
                    LastStartedSession       = LinaToLocalTime $Item.NetworkInfos.LastStartedSession
                    LastCompletedSession     = LinaToLocalTime $Item.NetworkInfos.LastCompletedSession
                    LastConnectionTime       = LinaToLocalTime $Item.NetworkInfos.LastConnectionTime
                    LastSyncTime             = LinaToLocalTime $Item.NetworkInfos.SyncTime
                    RepliLastPointInTime     = LinaToLocalTime $Item.RepliInfos.RepliLastPIT

                    TenantID                 = $Item.AdminInfos.DomainID
                    NodeID                   = $Item.AdminInfos.ServerID
                    AgentGroupID             = $Item.AdminInfos.AttachementArray.Group.ID
                    StrategyID               = $Item.AdminInfos.ServiceProfile.ID
                    ProtectionID             = $Item.AdminInfos.DataProfile.ID

                    TagsID                   = if ($global:INT_LINA_API_VERSION -ge 60 ) { $Item.Tags.ID } else {$Null }
                        
                    StrategyInternalName     = $Item.AdminInfos.ServiceProfile.Name
                    ProtectionInternalName   = $Item.AdminInfos.DataProfile.Name
                    BlockStatusValue         = $Item.BlockListInfos.Reason               
                }
                $LinaItems += $CurrentLinaItem
            }   
        }    
    
    }
    if ($LinaItems.Count -eq 0) {
        Write-Host2 "No agent found."
        Return $Null
    }
    Return $LinaItems
}

function Get-LinaWaitingAgent {
<#
.SYNOPSIS
Retrieves the waiting agents.
.DESCRIPTION
Retrieves the agents waiting to be declared when auto-create is not enabled. Returns a list of agents that correspond to the filter criteria provided.
Note : Waiting agents are available in the default tenant if it is set or in all tenants if there is no default tenant.
.INPUTS
None
.OUTPUTS
Array of LinaAgent Objects
.PARAMETER ComputerName
ComputerName of the agent(s) to retrieve. Wilcards are allowed. For example : *DESKTOP*
.EXAMPLE
Get-LinaWaitingAgent -ComputerName "TEST"
Retrieves the waiting agent named "TEST" and display its properties.
.EXAMPLE
$agents_desktop = Get-LinaWaitingAgent -ComputerName "*DESKTOP*"
Retrieves all the waiting agents with DESKTOP inside their computer names. The list returned can be passed to other functions.
.EXAMPLE
$agents = Get-LinaWaitingAgent
foreach ($agent in $agents) { New-LinaAgent -Name $agent.ComputerName }
Retrieves all the waiting agents and add (declare) them in the default tenant.
#>

    [cmdletbinding(DefaultParameterSetName = "ByComputerName")]
    Param(
        [Parameter(ParameterSetName = "ByComputerName", Position = 0)]
        [ValidateNotNullOrEmpty()]
        [string]$ComputerName

    )
    Write-Verbose "Getting list of waiting agents"
    if ($global:GLOBAL_LINA_CONNECTED -eq $False) { 
        Write-Error "$($global:GLOBAL_DISCONNNECTED_MESSAGE)" 
        return
    }

    $current_tenant_id = Get-LinaCurrentTenantID 6>$null

    $DefaultTenant = Get-LinaTenant -Default
    if ($DefaultTenant.ID) {    
        # Waiting agents are only in default Tenant ifthere is one
        $void = Set-LinaCurrentTenant -ID $DefaultTenant.ID 6>$null
    }

    $LinaItems = @()

    if ($INT_LINA_API_VERSION -lt 60) {
        # Lina < 6.0
        $request = CallAPI -Path "/stats_clientswaiting.xml"
        $Items = ([xml]$request).Stats.ClientArray.Client
    } else {
        # Lina > 6.0
        $request = CallAPI -Path "/stats_clientswaiting.json"
        $Items = $request.WaitingAgents
    }


    # Go back to the current tenant after switching to default one to get waiting agents
    $void = Set-LinaCurrentTenant -ID $current_tenant_id 6>$null

        foreach ($Item in $Items) {
     
            #$Name_encoded = [System.Text.Encoding]::UTF8.GetString([System.Text.Encoding]::GetEncoding(28591).GetBytes($Item.AdminInfos.Name))
            $ComputerName_enc = FixEncoding($Item.ComputerName)
            <# Filtering on ID or Name (only if provided) #>
            if ((!$ComputerName) -OR ( $Item.ComputerName -like "$ComputerName" -OR $ComputerName_enc -like "$ComputerName")) {


                # Checking if agent is local => not sure if necessary in waiting agents
                # if ($Item.SourceID -eq "@") { $isLocal=$True } else { $isLocal = $False }
                
                $CurrentLinaItem = [PSCustomObject]@{
                    Tenant                   = $DefaultTenant.Name
                    ComputerName             = $Item.ComputerName
                    System                   = $Item.System
                    SystemCategory           = TranslateSystemCategories($Item.SystemCategory)

                    #isLocal = $isLocal
                    LastConnection           = HumanFriendlyTimeSpan (LinaToLocalTime ([Int64]$Item.LastConnection*1000))
                    
                    TenantID                 = $DefaultTenant.ID
                }
                $LinaItems += $CurrentLinaItem
            }   
        }    
    
    
    if ($LinaItems.Count -eq 0) {
        Write-Host2 "No waiting agent found."
        Return $Null
    }
    Return $LinaItems
}

function Get-LinaStrategy {
    <#
.SYNOPSIS
Retrieves the strategies.
.DESCRIPTION
Retrieves the strategies. Returns a set of strategies that correspond to the filter criteria provided.
If TenantID = 0, it is a global strategy across tenants.
.INPUTS
None
.OUTPUTS
Array of LinaStrategy Objects
.PARAMETER Name
Optional : Name of the strategy to retrieve. Wilcards are allowed. For example : *STRAT*
.PARAMETER ID
Optional : ID of the strategy to retrieve.
.EXAMPLE
Get-LinaStrategy
Retrieves all the strategies.
.EXAMPLE
Get-LinaStrategy -Name "*STRAT*"
Retrieves all the strategies with STRAT inside their names.
.EXAMPLE
Get-LinaStrategy -Name "*STRAT*" | Select-Object -Property Name,RPOInMinutes
Displays the list of strategies and their configured RPO in minutes.
#>

    [cmdletbinding(DefaultParameterSetName = "ByName")]
    Param(
        [Parameter(ParameterSetName = "ByName")]
        [ValidateNotNullOrEmpty()]
        [string]$Name,
        [Parameter(ParameterSetName = "ByID")]
        [ValidateRange(0, 2147483647)]
        [int]$ID
    )    
    Write-Verbose "Getting list of strategies"
    if ($global:GLOBAL_LINA_CONNECTED -eq $False) { 
        Write-Error "$($global:GLOBAL_DISCONNNECTED_MESSAGE)" 
        return
    }

    if ($INT_LINA_API_VERSION -lt 60) {
        # Lina < 6.0
        $request = CallAPI -Path "/lst_serviceprofiles.xml"
        $Items = ([xml]$request).HNConfig.ServiceProfileArray.ServiceProfile
    } else {
        # Lina > 6.0
        $request = CallAPI -Path "/lst_serviceprofiles.json"
        $Items = $request.ServiceProfileArray
    }


    $Tenants = LinaTenantShort
    $LinaItems = @()
    foreach ($Item in $Items) {

        if ($INT_LINA_API_VERSION -lt 60) {
            # Lina < 6.0
            $translated_name = Translate $Item.Name
        } else {
            # Lina > 6.0
            $translated_name = Translate $Item.Name 0
        }

        
        if ((!$Name -AND !$ID) -OR ( $Item.Name -like "$Name" -OR $Item.ID -eq $ID -OR $translated_name -like "$Name") ) {        
            
            if ([int]$Item.RepliTarget.ID -eq 0) {
                $replitarget = "None"
            }
            else {
                $replitarget = $Item.RepliTarget.Name
            }
            
            $CurrentLinaItem = [PSCustomObject]@{
                PSTypeName               = 'LinaStrategy'
                Name                     = $translated_name
                ID                       = $Item.ID

                Tenant                   = ($Tenants | where-object { $_.ID -eq $Item.DomainID }).Name
                RPOInMinutes             = ($Item.ParamSchedule / 60)
                RetentionInDays          = $Item.ParamDataAging
                AlertAfterDays           = ($Item.ParamAlertTime / (24 * 3600))
                ThroughputLimitKBps      = $Item.ParamThroughputLimit
                ReplicationTarget        = $replitarget
                AlwaysEncrypt            = ![bool][int]$Item.ParamEncryptionActivated
                WanMode                  = [bool][int]$Item.ParamWanActivated
                CompressionAlgo          = $Item.ParamCompression
                AllowClientRPO           = [bool][int]$Item.ParamAllowClientRPO
                AllowClientRules         = [bool][int]$Item.ParamAllowClientRules
                AllowClientPause         = [bool][int]$Item.ParamAllowClientPause
                AllowClientBoost         = [bool][int]$Item.ParamAllowClientBoost
                AllowClientNetworkParams = [bool][int]$Item.ParamAllowClientNetworkParams
                AllowWebAccess           = [bool][int]$Item.ParamAllowWebAccess
                QuotaMaxProtSizeMB       = [math]::Round(($Item.ParamQuotaMaxProtSize) / (1024 * 1024))
                QuotaMaxProtObjs         = $Item.ParamQuotaMaxProtObjs
                QuotaMaxHistSizeMB       = [math]::Round(($Item.ParamQuotaMaxHistSize) / (1024 * 1024))
                QuotaMaxHistObjs         = $Item.ParamQuotaMaxHistObjs
                ReplicationTargetID      = $Item.RepliTarget.ID
                
                TenantID                 = $Item.DomainID
                InternalName             = $Item.Name
            }
            $LinaItems += $CurrentLinaItem
        }
    }
    if ($LinaItems.Count -eq 0) {
        Write-Host2 "No strategy found."
        Return $Null
    }
    Return $LinaItems
}

function Get-LinaProtection {
    <#
    .SYNOPSIS
    Retrieves the protection policies.
    .DESCRIPTION
    Retrieves the protection policies. Returns a set of protections that correspond to the filter criteria provided.
    .INPUTS
    None
    .OUTPUTS
    Array of LinaProtection Objects
    .PARAMETER Name
    Optional : Name of the protection to retrieve. Wilcards are allowed. For example : *PROT*
    .PARAMETER ID
    Optional : ID of the protection to retrieve.
    .PARAMETER ProtectionZone
    Optional : Filter by Protection zone name used in subrules. No wildcard allowed.
    .PARAMETER ProtectionRule
    Optional : Filter by Protection rule name used in subrules. No wildcard allowed.
    .EXAMPLE
    Get-LinaProtection
    Retrieves all the protections.
    .EXAMPLE
    Get-LinaProtection -Name "*PROT*"
    Retrieves all the protection with PROT inside their names.
    .EXAMPLE
    Get-LinaProtection -ID 15
    Retrieves the protection with ID 15
    .EXAMPLE
    Get-LinaProtection -ProtectionZone "The entire computer"
    Retrieves the protection with a subrule containing the protection zone named "MyProtection"
    .EXAMPLE
    Get-LinaProtection -ProtectionRule "Protect all objects"
    Retrieves the protection with a subrule containing the protection rule named "MyProtection"
    #>

    [cmdletbinding(DefaultParameterSetName = "ByName")]
    Param(
        [Parameter(ParameterSetName = "ByName")]
        [ValidateNotNullOrEmpty()]
        [string]$Name,
        [Parameter(ParameterSetName = "ByID")]
        [ValidateRange(0, 2147483647)]
        [int]$ID,
        [Parameter(ParameterSetName = "ByProtRule")]
        [ValidateNotNullOrEmpty()]
        [string]$ProtectionRule,
        [Parameter(ParameterSetName = "ByProtZone")]
        [ValidateNotNullOrEmpty()]
        [string]$ProtectionZone           
            
    )    
    Write-Verbose "Getting list of protection policies"
    if ($global:GLOBAL_LINA_CONNECTED -eq $False) { 
        Write-Error "$($global:GLOBAL_DISCONNNECTED_MESSAGE)" 
        return
    }

    if ($INT_LINA_API_VERSION -lt 60) {
        # Lina < 6.0
        $request = CallAPI -Path "/lst_dataprofiles.xml"
        $Items = ([xml]$request).HNConfig.DataProfileArray.DataProfile
    } else {
        # Lina > 6.0
        $request = CallAPI -Path "/lst_dataprofiles.json"
        $Items = $request.DataProfileArray
    }

    $Tenants = LinaTenantShort
    $protection_rules = Get-LinaProtectionRule
    $protection_zones = Get-LinaProtectionZone       
    $LinaItems = @()

    if ($ProtectionRule) {
        $protection_rule_id_filter = ($protection_rules | Where-Object { $_.Name -eq $ProtectionRule }).ID
        if (!$protection_rule_id_filter) {
            Write-Host2 "No protection rule found with name $ProtectionRule. Please provide the exact name"
            return
        }
    }
    else {
        $protection_rule_id_filter = -1
    } 

    if ($ProtectionZone) {
        $protection_zone_id_filter = ($protection_zones | Where-Object { $_.Name -eq $ProtectionZone }).ID
        if (!$protection_zone_id_filter) {
            Write-Host2 "No protection zone found with name $ProtectionZone. Please provide the exact name"
            return
        }            
    }
    else {
        $protection_zone_id_filter = -1
    }  

    foreach ($Item in $Items) {

        if ((!$Name -AND !$ID -AND !$ProtectionRule -AND !$ProtectionZone ) -OR ( $Item.Name -like "$Name" -OR $Item.ID -eq $ID -OR (Translate($Item.Name)) -like "$Name" -OR $Item.RuleArray.RulePredef.FilterRuleID -contains $protection_rule_id_filter -OR $Item.RuleArray.RulePredef.PredefinedPathID -contains $protection_zone_id_filter) ) {        
            $CurrentLinaItem = [PSCustomObject]@{
                PSTypeName       = 'LinaProtection'
                Name             = Translate($Item.Name)
                ID               = $Item.ID

                Tenant           = ($Tenants | where-object { $_.ID -eq $Item.DomainID }).Name
                OS               = $Item.OS
                BareMetalRestore = [bool][int]$Item.BMR
                BuiltIn          = [bool]($null -eq $Item.CustomRef)
                TenantID         = $Item.DomainID
                InternalName     = $Item.Name
            }
            $i = 1
            foreach ($subrule in $Item.RuleArray.RulePredef) {
                $subrule_protection = $protection_rules | where-object { $_.ID -eq $subrule.FilterRuleID }
                $subrule_path = $protection_zones | where-object { $_.ID -eq $subrule.PredefinedPathID }

                $CurrentLinaItem | add-member -membertype noteproperty -name "Rule#$i" -value ($subrule_path.Name + " ( " + $subrule_path.Path + " ) >> " + $subrule_protection.Name)
                $i++
            }

            $LinaItems += $CurrentLinaItem
        }
    }
    if ($LinaItems.Count -eq 0) {
        Write-Host2 "No protection found."
        Return $Null
    }        
    Return $LinaItems
}
    
function Get-LinaProtectionZone {
    <#
            .SYNOPSIS
            Retrieves the protection zones.
            .DESCRIPTION
            Retrieves the protection zones. Returns a set of protection zones that correspond to the filter criteria provided.
            .INPUTS
            None
            .OUTPUTS
            Array of LinaProtectionZone Objects
            .PARAMETER Name
            Optional : Name of the protection zone to retrieve. Wilcards are allowed. For example : *Mac*
            .PARAMETER ID
            Optional : ID of the protection zone to retrieve.
            .PARAMETER Path
            Optional : Return protection containing this exact path. Wildcards character (*) will be considered only as stars.
            .PARAMETER MatchingPath
            Optional : Return protection matching this path. Wilcards are allowed.
            .EXAMPLE
            Get-LinaProtectionZone
            Retrieves all the protection zones.
            .EXAMPLE
            Get-LinaProtectionZone -Name "*Mac*"
            Retrieves all the protection zones with Mac inside their names.
            .EXAMPLE
            Get-LinaProtectionZone -Path "\*\Users\*\"
            Retrieves the protection zones matching this exact path.
            .EXAMPLE
            Get-LinaProtectionZone -MatchingPath "\*\Users\*\"
            Retrieves all the protection zones matching this path (stars are considered as wildcards)
            #>

    [cmdletbinding(DefaultParameterSetName = "ByName")]
    Param(
        [Parameter(ParameterSetName = "ByName")]
        [ValidateNotNullOrEmpty()]
        [string]$Name,
        [Parameter(ParameterSetName = "ByID")]
        [ValidateRange(0, 2147483647)]
        [int]$ID,
        [Parameter(ParameterSetName = "ByPath")]
        [ValidateNotNullOrEmpty()]
        [string]$Path,
        [Parameter(ParameterSetName = "ByMatchingPath")]
        [ValidateNotNullOrEmpty()]
        [string]$MatchingPath                   
    )    
    Write-Verbose "Getting list of protection zones"
    if ($global:GLOBAL_LINA_CONNECTED -eq $False) { 
        Write-Error "$($global:GLOBAL_DISCONNNECTED_MESSAGE)" 
        return
    }

    if ($INT_LINA_API_VERSION -lt 60) {
        # Lina < 6.0
        $request = CallAPI -Path "/lst_predefinedpaths.xml"
        $Items = ([xml]$request).HNConfig.PredefinedPathArray.PredefinedPath
    } else {
        # Lina > 6.0
        $request = CallAPI -Path "/lst_predefinedpaths.json"
        $Items = $request.PredefinedPathArray
    }


    $Tenants = LinaTenantShort
    $LinaItems = @()
    foreach ($Item in $Items) {

        if ((!$Name -AND !$ID -AND !$Path -AND !$MatchingPath) -OR ( $Item.Name -like "$Name" -OR $Item.ID -eq $ID -OR (Translate($Item.Name)) -like "$Name" -OR $Item.Path -eq "$Path" -OR $Item.Path -like "$MatchingPath" ) ) {        
            $CurrentLinaItem = [PSCustomObject]@{
                PSTypeName = 'LinaProtectionZone'
                Name       = Translate($Item.Name)
                ID         = $Item.ID
        
                Tenant     = ($Tenants | where-object { $_.ID -eq $Item.DomainID }).Name
                OS         = $Item.OS
                Path       = $Item.Path
                BuiltIn    = [bool]($null -eq $Item.CustomRef)

                TenantID   = $Item.DomainID
    
            }
            $LinaItems += $CurrentLinaItem
        }
    }
    if ($LinaItems.Count -eq 0) {
        Write-Host2 "No protection zone found."
        Return $Null
    }        
    Return $LinaItems
}

function Get-LinaFileCategory {
    <#
        .SYNOPSIS
        Retrieves the file categories.
        .DESCRIPTION
        Retrieves the file categories. Returns a set of file categories that correspond to the filter criteria provided.
        .INPUTS
        None
        .OUTPUTS
        Array of LinaFileCategory Objects
        .PARAMETER Name
        Optional : Name of the file category to retrieve. Wilcards are allowed. For example : *FILECAT*
        .PARAMETER ID
        Optional : ID of the file category to retrieve.
        .EXAMPLE
        Get-LinaFileCategory
        Retrieves all the file categories.
        .EXAMPLE
        Get-LinaFileCategory -Name "*Text*"
        Retrieves all the file categories with Text inside their names.
        #>

    [cmdletbinding(DefaultParameterSetName = "ByName")]
    Param(
        [Parameter(ParameterSetName = "ByName")]
        [ValidateNotNullOrEmpty()]
        [string]$Name,
        [Parameter(ParameterSetName = "ByID")]
        [ValidateRange(0, 2147483647)]
        [int]$ID
    )    
    Write-Verbose "Getting list of file categories"
    if ($global:GLOBAL_LINA_CONNECTED -eq $False) { 
        Write-Error "$($global:GLOBAL_DISCONNNECTED_MESSAGE)" 
        return
    }

    if ($INT_LINA_API_VERSION -lt 60) {
        # Lina < 6.0
        $request = CallAPI -Path "/lst_filetypes.xml"
        $Items = ([xml]$request).HNConfig.FileTypeArray.FileType
    } else {
        # Lina > 6.0
        $request = CallAPI -Path "/lst_filetypes.json"
        $Items = $request.FileTypeArray
    }

    $Tenants = LinaTenantShort
    $LinaItems = @()
    foreach ($Item in $Items) {
        if ((!$Name -AND !$ID) -OR ( $Item.Name -like "$Name" -OR $Item.ID -eq $ID -OR (Translate($Item.Name)) -like "$Name" ) ) {        
            $CurrentLinaItem = [PSCustomObject]@{
                PSTypeName     = 'LinaFileCategory'
                Name           = Translate($Item.Name)
                ID             = $Item.ID
    
                Tenant         = ($Tenants | where-object { $_.ID -eq $Item.DomainID }).Name
                FileExtensions = if ($INT_LINA_API_VERSION -lt 60) { $Item.ExtArray.Ext } else { $Item.ExtArray } 
                BuiltIn        = [bool]($null -eq $Item.CustomRef)
    
                TenantID       = $Item.DomainID

            }
            $LinaItems += $CurrentLinaItem
        }
    }
    if ($LinaItems.Count -eq 0) {
        Write-Host2 "No file category found."
        Return $Null
    }        
    Return $LinaItems
}

function TranslateBehaviors ($to_translate) {
    switch ( $to_translate ) {
        "INCL" { $result = "Include / No Agent modification allowed" }
        "EXCL" { $result = "Exclude / No Agent modification allowed" }
        "ADVINCL" { $result = "Include / Allow agents modifications" }
        "ADVEXCL" { $result = "Exclude / Allow agents modifications" }
    }
    return $result
}

function Get-LinaProtectionRule {
    <#
            .SYNOPSIS
            Retrieves the protection rules.
            .DESCRIPTION
            Retrieves the protection rules. Returns a set of file categories that correspond to the filter criteria provided.
            .INPUTS
            None
            .OUTPUTS
            Array of LinaProtectionRules Objects
            .PARAMETER Name
            Optional : Name of the protection rule to retrieve. Wilcards are allowed. For example : *FILECAT*
            .PARAMETER ID
            Optional : ID of the protection rule to retrieve.
            .EXAMPLE
            Get-LinaProtectionRule
            Retrieves all the protection rules.
            .EXAMPLE
            Get-LinaProtectionRule -Name "*Office*"
            Retrieves all the protection rules with Office inside their names.
            #>

    [cmdletbinding(DefaultParameterSetName = "ByName")]
    Param(
        [Parameter(ParameterSetName = "ByName")]
        [ValidateNotNullOrEmpty()]
        [string]$Name,
        [Parameter(ParameterSetName = "ByID")]
        [ValidateRange(0, 2147483647)]
        [int]$ID
    )    
    Write-Verbose "Getting list of protection rules"
    if ($global:GLOBAL_LINA_CONNECTED -eq $False) { 
        Write-Error "$($global:GLOBAL_DISCONNNECTED_MESSAGE)" 
        return
    }

    if ($INT_LINA_API_VERSION -lt 60) {
        # Lina < 6.0
        $request = CallAPI -Path "/lst_filters.xml"
        $Items = ([xml]$request).HNConfig.FilterRuleArray.FilterRule
    } else {
        # Lina > 6.0
        $request = CallAPI -Path "/lst_filters.json"
        $Items = $request.FilterRuleArray
    }

    $Tenants = LinaTenantShort
    $FileCategories = Get-LinaFileCategory
    $LinaItems = @()
    foreach ($Item in $Items) {
        if ((!$Name -AND !$ID) -OR ( $Item.Name -like "$Name" -OR $Item.ID -eq $ID -OR (Translate($Item.Name)) -like "$Name" ) ) {        

                        
            $CurrentLinaItem = [PSCustomObject]@{
                PSTypeName      = 'LinaProtectionRule'
                Name            = Translate($Item.Name)
                ID              = $Item.ID
                Tenant          = ($Tenants | where-object { $_.ID -eq $Item.DomainID }).Name
                DefaultBehavior = TranslateBehaviors($Item.DefaultBehavior)
                BuiltIn         = [bool]($null -eq $Item.CustomRef)
                TenantID        = $Item.DomainID
    
            }
            $i = 1
            foreach ($subrule in $Item.SubRuleArray.SubRule) {
                $filecat_name = ($FileCategories | where-object { $_.ID -eq $subrule.FileType.ID }).Name
                $rule_behavior = TranslateBehaviors($subrule.Behavior)

                $CurrentLinaItem | add-member -membertype noteproperty -name "Rule#$i" -value ($rule_behavior + ' : ' + $filecat_name)
                $i++
            }

            $LinaItems += $CurrentLinaItem
        }
    }
    if ($LinaItems.Count -eq 0) {
        Write-Host2 "No protection rule found."
        Return $Null
    }        
    Return $LinaItems
}

function TranslatePermissions($permissions, $perm_category) {

    $perms_filtered = $permissions | Where-Object { $_ -like "*$perm_category" } | ForEach-Object { $_ -replace "$perm_category", "" }

    $short_perms = ""
    ForEach ($perm in $perms_filtered) {
        $short_perms += $perm + ","
    }
    # Trimming last character ,
    $short_perms = $short_perms -replace ",$"

    Switch ($perm_category) {
        { 'UserProfiles', 'Users', 'Replications', 'AlnAgent', 'AlnStrategy', 'AlnProtection', 'AlnProtectionRule', 'AlnProtectionZone', 'AlnFileCategory', 'Domains' -contains $_ } {
            if ($short_perms -eq "View,Create,Delete,Set") {
                Return "Full"
            }
            else {
                Return $short_perms
            }
        }
        { 'Alerts', 'Services', 'BMR', 'License', 'Smtp', 'Ldap', 'Config' -contains $_ } {
            if ($short_perms -eq "View,Set") {
                Return "Full"
            }
            else {
                Return $short_perms
            }
        }
        { 'Support', 'Files', 'Smtp', 'Ldap', 'Config' -contains $_ } {
            if ($short_perms -eq "View") {
                Return "Full"
            }
            else {
                Return $short_perms
            }
        }
        { 'Snapshot' -contains $_ } {
            if ($short_perms -eq "View,Create,Delete") {
                Return "Full"
            }
            else {
                Return $short_perms
            }
        }
        { 'Tunables' -contains $_ } {
            if ($short_perms -eq "View,Set,ViewAdvanced") {
                Return "Full"
            }
            else {
                Return $short_perms
            }
        }
        { 'Statistics' -contains $_ } {
            if ($short_perms -eq "View,Recompute") {
                Return "Full"
            }
            else {
                Return $short_perms
            }
        }
        { 'AlnDefaultConfiguration', 'AlnGlobalDefaultConfiguration' -contains $_ } {
            if ($short_perms -eq "Set") {
                Return "Full"
            }
            else {
                Return $short_perms
            }
        }
        { 'Service' -contains $_ } {
            if ($short_perms -eq "Restart") {
                Return "Full"
            }
            else {
                Return $short_perms
            }
        }
        Default {
            Return $short_perms
        }
    }
    if ($short_perms -like "*View,Create,Delete,Set*" -OR $short_perms -like "*ViewAll,Create,Delete,Set*") {
        Return "ALL"
    }
    Return $short_perms
}

function Get-LinaUserProfile {
    <#
    .SYNOPSIS
    Retrieves the user profiles
    .DESCRIPTION
    Retrieves the user profiles.
    By default only show Lina permissions (corresponds to Lina tab in Web interface).
    Use the -ShowAllPermissions to also displays Users tab and Server Tab permissions.
    "Full" means that the user have all the possible permissions in the category.
    Note : Requires PowerShell 5.0 or greater.
    .INPUTS
    None
    .OUTPUTS
    Array of LinaUserProfile Objects
    .PARAMETER Name
    Optional : Name of the user profile to retrieve. Wilcards are allowed. For example : *UserProfile*
    .PARAMETER ID
    Optional : ID of the user profile to retrieve.
    .PARAMETER Tenant
    Optional : Tenant of the user profile to retrieve.
    .EXAMPLE
    Get-LinaUserProfile
    Retrieves all the user profiles and shows only Lina permissions
    .EXAMPLE
    Get-LinaUserProfile -ShowAllPermissions
    Retrieves all the user profiles and shows all permissions Lina + Users + Server
    .EXAMPLE
    Get-LinaUserProfile -Name "MyProfile"
    Retrieves MyProfile Lina permissions
    .EXAMPLE
    Get-LinaUserProfile -Tenant "MyTenant" | Where {$_.BuiltIn -eq $False -AND $_.Name -ne "Default"}
    Retrieves all custom profiles from Tenant MyTenant
    #>

    [cmdletbinding(DefaultParameterSetName = "ByName")]
    Param(
        [Parameter(ParameterSetName = "ByName")]
        [ValidateNotNullOrEmpty()]
        [string]$Name,
        [Parameter(ParameterSetName = "ByID")]
        [ValidateRange(0, 2147483647)]
        [int]$ID,
        [Parameter(Mandatory = $False)]
        [ValidateNotNullOrEmpty()]
        [string]$Tenant,
        [Parameter(Mandatory = $False)]
        [Switch]$ShowAllPermissions
    )    
    Write-Verbose "Getting list of user profiles"
    if ($global:GLOBAL_LINA_CONNECTED -eq $False) { 
        Write-Error "$($global:GLOBAL_DISCONNNECTED_MESSAGE)" 
        return
    }

    if ($PSVersionTable.PSVersion.Major -lt 5) {
        Write-Host2 "ERROR : This cmdlet requires PowerShell 5.0 or greater in order to work"
        Return 
    }

 
    $request = CallAPI -Path "/ADM/list_profile.json"
    $Items = $request.profiles
    $Tenants = LinaTenantShort
    $LinaItems = @()
        
    # Filter on Tenant
    if ($Tenant) {
        $domain = $Tenants | Where-Object { $_.Name -eq $Tenant }
        $domain_id = [int]$domain.ID
    }

    foreach ($Item in $Items) {
        # Fixing name of default Administrator Profile
        if ($Item.name -eq "Admin" -AND $Item.custom -eq $False) {
            $translated_name = FixEncoding(Translate "USER_PROFILE_NAME_ADMIN")
        }
        else {
            # Only one item is builtin, all others are custom
            $translated_name = FixEncoding($Item.name)
        }

        
        # Filtering specific accounts SuperAdmin and Guest
        if ((!$Name -AND !$ID -AND $Item.name -notin ("Guest")) -OR ( $Item.name -like "$Name" -OR $Item.id -eq $ID -OR $translated_name -like "$Name") ) {        
                
            # When Roles are negatives, means we have to use highest value less this one (+1 but i don't know why ..)
            # Negative values are used by default profiles only
            if ($Item.roles1 -lt 0) {
                # Signed int with negative value
                $roles1_value = $ROLES1_MAX + $Item.roles1 + 1
            }elseif ($Item.roles1 -gt $ROLES1_MAX )  {
                # Value is higher than max => it is an unsigned value. Converting it to signed value
                $roles1_value = $ROLES1_MAX + 1 + ( $Item.roles1  + [Math]::Pow(2, 31) ) % [Math]::Pow(2, 32) - [Math]::Pow(2, 31) 
            }else {
                # Value is withing range (should be signed int)
                $roles1_value = $Item.roles1
            }

            if ($Item.roles2 -lt 0) {
                $roles2_value = $ROLES2_MAX + $Item.roles2 + 1
            }
            elseif ($Item.roles2 -gt $ROLES2_MAX )  {
                $roles2_value = $ROLES2_MAX + 1 + ( $Item.roles2  + [Math]::Pow(2, 31) ) % [Math]::Pow(2, 32) - [Math]::Pow(2, 31) 
            }
            else {
                $roles2_value = $Item.roles2
            }

            if ($Item.roles3 -lt 0) {
                $roles3_value = $ROLES3_MAX + $Item.roles3 + 1
            }
            elseif ($Item.roles3 -gt $ROLES3_MAX )  {
                $roles3_value = $ROLES3_MAX + 1 + ( $Item.roles3  + [Math]::Pow(2, 31) ) % [Math]::Pow(2, 32) - [Math]::Pow(2, 31) 
            }            
            else {
                $roles3_value = $Item.roles3
            }

            if ($Item.roles4 -lt 0) {
                # Getting negative (signed) values
                $roles4_value = $ROLES4_MAX + $Item.roles4 + 1
            }
            elseif ($Item.roles4 -gt $ROLES4_MAX )  {
                # Getting positive (unsigned) values
                $roles4_value = $ROLES4_MAX + 1 + ( $Item.roles4  + [Math]::Pow(2, 31) ) % [Math]::Pow(2, 32) - [Math]::Pow(2, 31) 
            }            
            else {
                # Getting positive (signed) values
                $roles4_value = $Item.roles4
            }

            $roles1_list = [ROLES1_LIST]$roles1_value -split ", "
            $roles2_list = [ROLES2_LIST]$roles2_value -split ", "
            $roles3_list = [ROLES3_LIST]$roles3_value -split ", "
            $roles4_list = [ROLES4_LIST]$roles4_value -split ", "

            $CurrentLinaItem = [PSCustomObject]@{
                PSTypeName                 = 'LinaUserProfile'
                Name                       = $translated_name
                ID                         = $Item.id
                Tenant                     = ($Tenants | where-object { $_.ID -eq $Item.domain_id }).Name
                # Because is not in use in the Web interface
                #Comment = $Item.comment

                Lina_Agents                = TranslatePermissions $roles3_list "AlnAgent"
                Lina_Strategies            = TranslatePermissions $roles3_list "AlnStrategy"
                Lina_Protections           = TranslatePermissions $roles3_list "AlnProtection"
                Lina_ProtectionRules       = TranslatePermissions $roles3_list "AlnProtectionRule"
                Lina_ProtectionZones       = TranslatePermissions $roles4_list "AlnProtectionZone"
                Lina_FileCategories        = TranslatePermissions $roles4_list "AlnFileCategory"
                Lina_Preferences           = TranslatePermissions $roles4_list "AlnDefaultConfiguration"
                Lina_GlobalDefaultSettings = TranslatePermissions $roles4_list "AlnGlobalDefaultConfiguration"
                # Lina_CrossRestore = TranslatePermissions $roles1_list "CrossResto"
                # Lina_BMRRestore = TranslatePermissions $roles1_list "BMRResto"
                    
            }

            if ($ShowAllPermissions) {
                $CurrentLinaItem | add-member -membertype noteproperty -name "Users_Users"          -value ( TranslatePermissions $roles1_list "Users" )
                $CurrentLinaItem | add-member -membertype noteproperty -name "Users_UserProfiles"   -value ( TranslatePermissions $roles1_list "UserProfiles" )
                $CurrentLinaItem | add-member -membertype noteproperty -name "Users_SMTP"           -value ( TranslatePermissions $roles2_list "Smtp" )
                $CurrentLinaItem | add-member -membertype noteproperty -name "Users_LDAP"           -value ( TranslatePermissions $roles2_list "Ldap" )
                    
                $CurrentLinaItem | add-member -membertype noteproperty -name "Server_Replications"       -value ( TranslatePermissions $roles1_list "Replications" )
                $CurrentLinaItem | add-member -membertype noteproperty -name "Server_Snapshot"           -value ( TranslatePermissions $roles2_list "Snapshot" )
                $CurrentLinaItem | add-member -membertype noteproperty -name "Server_Configuration"      -value ( TranslatePermissions $roles2_list "Config" )
                $CurrentLinaItem | add-member -membertype noteproperty -name "Server_Alerts"             -value ( TranslatePermissions $roles1_list "Alerts" )
                $CurrentLinaItem | add-member -membertype noteproperty -name "Server_Services"           -value ( TranslatePermissions $roles1_list "Services" )
                $CurrentLinaItem | add-member -membertype noteproperty -name "Server_License"            -value ( TranslatePermissions $roles2_list "License" )
                $CurrentLinaItem | add-member -membertype noteproperty -name "Server_Maintenance"        -value ( TranslatePermissions $roles1_list "Support" )
                $CurrentLinaItem | add-member -membertype noteproperty -name "Server_Files"              -value ( TranslatePermissions $roles2_list "Files" )
                $CurrentLinaItem | add-member -membertype noteproperty -name "Server_FineTuning"         -value ( TranslatePermissions $roles2_list "Tunables" )
                $CurrentLinaItem | add-member -membertype noteproperty -name "Server_Statistics"         -value ( TranslatePermissions $roles2_list "Statistics" )
                $CurrentLinaItem | add-member -membertype noteproperty -name "Server_ServiceRestart"     -value ( TranslatePermissions $roles2_list "Service" )
                #Streams = TranslatePermissions $roles2_list "Streams"
            }
            $CurrentLinaItem | add-member -membertype noteproperty -name "BuiltIn" -value ( [bool](!$Item.custom) )
            $CurrentLinaItem | add-member -membertype noteproperty -name "UUID" -value ( $Item.uuid )
            $CurrentLinaItem | add-member -membertype noteproperty -name "InternalRoles1" -value ( $roles1_value )
            $CurrentLinaItem | add-member -membertype noteproperty -name "InternalRoles2" -value ( $roles2_value )
            $CurrentLinaItem | add-member -membertype noteproperty -name "InternalRoles3" -value ( $roles3_value )
            $CurrentLinaItem | add-member -membertype noteproperty -name "InternalRoles4" -value ( $roles4_value )
            $CurrentLinaItem | add-member -membertype noteproperty -name "TenantID" -value ( $Item.domain_id )
            $CurrentLinaItem | add-member -membertype noteproperty -name "InternalName" -value ( $Item.name )

            # Filtering User profiles by tenant when tenant is provided
            if (!$Tenant -OR $CurrentLinaItem.TenantID -eq $domain_id) {
                $LinaItems += $CurrentLinaItem
            }
        }
    }
    if ($LinaItems.Count -eq 0) {
        Write-Host2 "No user profile found."
        return $null
    }
    Return $LinaItems
}

function Get-LinaUser {
    <#
            .SYNOPSIS
            Retrieves the users.
            .DESCRIPTION
            Retrieves the users.
            .INPUTS
            None
            .OUTPUTS
            Array of LinaUser Objects
            .PARAMETER Name
            Optional : Name of the user to retrieve. Wilcards are allowed. For example : *Jon*
            .PARAMETER ID
            Optional : ID of the user to retrieve.
            .EXAMPLE
            Get-LinaUser
            Retrieves all the users.
            .EXAMPLE
            Get-LinaUser -Name "*Jon*"
            Retrieves all the users with Jon inside their names.
            #>

    [cmdletbinding(DefaultParameterSetName = "ByName")]
    Param(
        [Parameter(ParameterSetName = "ByName")]
        [ValidateNotNullOrEmpty()]
        [string]$Name,
        [Parameter(ParameterSetName = "ByID")]
        [ValidateRange(0, 2147483647)]
        [int]$ID                 
    )    
    Write-Verbose "Getting list of users"
    if ($global:GLOBAL_LINA_CONNECTED -eq $False) { 
        Write-Error "$($global:GLOBAL_DISCONNNECTED_MESSAGE)" 
        return
    }

    $request = CallAPI -Path "/ADM/user.json"
    $Items = $request.users
    $request2 = CallAPI -Path "/ADM/list_user_ug_relation.json"
    $User_to_UserGroup = $request2.relations
    $Tenants = LinaTenantShort
    $UserGroups = Get-LinaUserGroup
    $LinaItems = @()
    foreach ($Item in $Items) {
        if ((!$Name -AND !$ID -AND $Item.name -notin ("superadmin", "guest")) -OR ( $Item.name -like "$Name" -OR $Item.id -eq $ID -OR (FixEncoding($Item.name)) -like "$Name" ) ) {        
            $UserGroupID= ($User_to_UserGroup | where-object {$_.user_id -eq $Item.id}).ug_id
            $UserGroupObject = $UserGroups | where-object { $_.ID -eq $UserGroupID }
            $CurrentLinaItem = [PSCustomObject]@{
                PSTypeName  = 'LinaUser'
                Name        = FixEncoding($Item.name)
                ID          = $Item.id
                Tenant      = ($Tenants | where-object { $_.ID -eq $Item.domain_id }).Name
                UserGroup   = $UserGroupObject.Name
                UserProfile = $UserGroupObject.UserProfile
                Disabled    = [bool]($Item.disabled)
                # Have to check what is type
                Type        = $global:INT_USER_TYPES[$Item.type]
                BuiltIn     = [bool](!$Item.custom)
                TenantID    = $Item.domain_id
                UserGroupID = $UserGroupObject.ID
                UserProfileID = $UserGroupObject.UserProfileID
            }
            $LinaItems += $CurrentLinaItem
        }
    }
    if ($LinaItems.Count -eq 0) {
        Write-Host2 "No user found."
        Return $Null
    }        
    Return $LinaItems
}

function Get-LinaUserGroup {
    <#
            .SYNOPSIS
            Retrieves the user groups.
            .DESCRIPTION
            Retrieves the user groups.
            .INPUTS
            None
            .OUTPUTS
            Array of LinaUserGroup Objects
            .PARAMETER Name
            Optional : Name of the user group to retrieve. Wilcards are allowed. For example : *Admin*
            .PARAMETER ID
            Optional : ID of the user group to retrieve.
            .EXAMPLE
            Get-LinaUserGroup
            Retrieves all the user groups.
            .EXAMPLE
            Get-LinaUserGroup -Name "*Admin*"
            Retrieves all the user groups with Admin inside their names.
            #>

    [cmdletbinding(DefaultParameterSetName = "ByName")]
    Param(
        [Parameter(ParameterSetName = "ByName")]
        [ValidateNotNullOrEmpty()]
        [string]$Name,
        [Parameter(ParameterSetName = "ByID")]
        [ValidateRange(0, 2147483647)]
        [int]$ID                 
    )    
    Write-Verbose "Getting list of user groups"
    if ($global:GLOBAL_LINA_CONNECTED -eq $False) { 
        Write-Error "$($global:GLOBAL_DISCONNNECTED_MESSAGE)" 
        return
    }

    $request = CallAPI -Path "/ADM/list_user_group.json"
    $Items = $request.user_groups
    $request2 = CallAPI -Path "/ADM/list_prof_ug_relation.json"
    $UserGroup_to_UserProfile = $request2.relations
    $Tenants = LinaTenantShort
    $UserProfiles = Get-LinaUserProfile
    $LinaItems = @()
    foreach ($Item in $Items) {
        if ($Item.custom) {
            $ug_name=FixEncoding($Item.name)
        }else {
            $name_upper=$Item.name.ToUpper()
            $ug_name=Translate("USER_GROUP_NAME_$name_upper")
        }
         
        if ((!$Name -AND !$ID -AND $Item.name -notin ("SuperAdmin", "Guest")) -OR ( $Item.name -like "$Name" -OR $Item.id -eq $ID -OR $ug_name -like "$Name" ) ) {        
            $UserProfileID= ($UserGroup_to_UserProfile | where-object {$_.ug_id -eq $Item.id}).prof_id
            $CurrentLinaItem = [PSCustomObject]@{
                PSTypeName = 'LinaUserGroup'
                Name          = $ug_name
                ID            = $Item.id
                Tenant        = ($Tenants | where-object { $_.ID -eq $Item.domain_id }).Name
                UserProfile   = ($UserProfiles | where-object { $_.ID -eq $UserProfileID }).Name
                BuiltIn       = [bool](!$Item.custom)
                TenantID      = $Item.domain_id
                UserProfileID = $UserProfileID
                InternalName  = $Item.name
            }
            $LinaItems += $CurrentLinaItem
        }
    }
    if ($LinaItems.Count -eq 0) {
        Write-Host2 "No user group found."
        Return $Null
    }        
    Return $LinaItems
}

function Get-LinaTenant {
    <#
.SYNOPSIS
Retrieves the tenants (entities).
.DESCRIPTION
Retrieves the tenants (entities). Returns a set of tenants that correspond to the filter criteria provided.
.INPUTS
None
.OUTPUTS
Array of LinaTenant Objects
.PARAMETER Name
Optional : Name of the tenant to retrieve. Wilcards are allowed. For example : *Tenant*
.PARAMETER ID
Optional : ID of the tenant to retrieve.
.PARAMETER Default
Get the default tenant if any
.EXAMPLE
Get-LinaTenant
Retrieve all tenants
.EXAMPLE
Get-LinaTenant -Name "BaasCustomer1"
Retrieve Tenant named BaasCustomer1
.EXAMPLE
Get-LinaTenant -ID 2
Retrieve Tenant with ID 2
.EXAMPLE
Get-LinaTenant -Default
Retrieve the default tenant (if any)
#>

    [cmdletbinding(DefaultParameterSetName = "ByName")]
    Param(
        [Parameter(Mandatory = $false, Position = 0, ParameterSetName = "ByName")]
        [ValidateNotNullOrEmpty()]
        [string]$Name,
        [Parameter(Mandatory = $True, ParameterSetName = "ByID")]
        [ValidateRange(0, 2147483647)]
        [int]$ID,
        [Parameter(Mandatory = $True, ParameterSetName = "Default")]
        [switch]$Default
    )

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

    if ($INT_LINA_API_VERSION -lt 60) {
        # Lina < 6.0
        $request = CallAPI -Path "/ADE/check_session.xml"
        $request_type = [string]$request.GetType()
        if ( $request_type -like "*Xml*") {
            $current_tenant_id = [int](([xml]$request).session.effective_domain_id)
        }else {
            Write-Host2 "ERROR : Type is not XML. Found $request_type instead."
            $request | Format-List
            exit
        }
    } else {
        # Lina > 6.0
        $request = CallAPI -Path "/ADE/check_session.json"
        $current_tenant_id = [int]($request.effective_domain_id)
    }


    
    if ($current_tenant_id -gt 1) {
        Write-Host2 "WARNING : to list tenants you should be logged with superadmin privileges and be in the global view (using Set-LinaCurrentTenant -All). Current tenant ID is : $current_tenant_id"
        return ""
    }

    Write-Verbose "Getting list of tenants"

    $request = CallAPI -Path "/ADM/list_domain.json"
    $Items = $request.domains
  
    if ($INT_LINA_API_VERSION -lt 60) {
        # Lina < 6.0
        $request2 = CallAPI -Path "/lst_globals.xml?domain_id=0"
        $prefs_global = ([xml]$request2).HNConfig.Globals
        $prefs_tenants = ([xml]$request2).HNConfig.Globals.DomainGlobalsArray.DomainGlobals
    } else {
        # Lina > 6.0
        $request2 = CallAPI -Path "/lst_globals.json?domain_id=0"
        $prefs_global = $request2
        $prefs_tenants = $request2.DomainGlobalsArray
    }



    
    $cache_strategies = Get-LinaStrategy
    $cache_protections = Get-LinaProtection


    $LinaItems = @()
    foreach ($Item in $Items) {
        $name_encoded = FixEncoding($Item.name)
        if ((!$Name -AND !$ID) -OR $Item.name -like "$Name" -OR $name_encoded -like "$Name" -OR $Item.id -eq $ID ) {
            if ($prefs_tenants.DomainID -contains [int]$Item.id) {
                $DefaultStrategyID = ($prefs_tenants | where-object { $_.DomainID -eq [int]$Item.id }).WinDefaultServiceProfileID
                $WindowsDefaultProtectionID = ($prefs_tenants | where-object { $_.DomainID -eq [int]$Item.id }).WinDefaultDataProfileID
                $MacDefaultProtectionID = ($prefs_tenants | where-object { $_.DomainID -eq [int]$Item.id }).MacDefaultDataProfileID
                $LinuxDefaultProtectionID = ($prefs_tenants | where-object { $_.DomainID -eq [int]$Item.id }).LinuxDefaultDataProfileID
            }
            else {
                $DefaultStrategyID = ($prefs_tenants | where-object { $_.DomainID -eq [int]$Item.id }).WinDefaultServiceProfileID
                $WindowsDefaultProtectionID = ($prefs_tenants | where-object { $_.DomainID -eq [int]$Item.id }).WinDefaultDataProfileID
                $MacDefaultProtectionID = ($prefs_tenants | where-object { $_.DomainID -eq [int]$Item.id }).MacDefaultDataProfileID
                $LinuxDefaultProtectionID = ($prefs_tenants | where-object { $_.DomainID -eq [int]$Item.id }).LinuxDefaultDataProfileID
            }
            # These ones are always shared across tenants
            $AutoCreateClients = [bool][int]$prefs_global.AutoCreateClients

            $CurrentLinaItem = [PSCustomObject]@{
                PSTypeName                 = 'LinaTenant'
                Name                       = $name_encoded
                ID                         = $Item.id

                # Had to reencode to UTF-8 because of a bug or maybe to bad return type
                Comment                    = FixEncoding($Item.comment)
                IsDefault                  = [bool][int]$Item.def_domain

                AutoCreateClients          = $AutoCreateClients
                WindowsDefaultProtection   = ($cache_protections | Where-Object { $WindowsDefaultProtectionID -eq $_.ID }).Name
                MacDefaultProtection       = ($cache_protections | Where-Object { $MacDefaultProtectionID -eq $_.ID }).Name
                LinuxDefaultProtection     = ($cache_protections | Where-Object { $LinuxDefaultProtectionID -eq $_.ID }).Name
                DefaultStrategy            = ($cache_strategies | Where-Object { $DefaultStrategyID -eq $_.ID }).Name
                
                # Need to check if this one is always global or not
                # WebRestoreEnabled = $tenant_config.WebRestoreEnabled

                WindowsDefaultProtectionID = $WindowsDefaultProtectionID
                MacDefaultProtectionID     = $MacDefaultProtectionID
                LinuxDefaultProtectionID   = $LinuxDefaultProtectionID   
                DefaultStrategyID          = $DefaultStrategyID

                UUID                       = $Item.uuid
            }
            $LinaItems += $CurrentLinaItem
        }
        
    }

    if ($LinaItems.Count -eq 0) {
        Write-Host2 "No tenant found."
        Return $null
    }
    
    if ($Default) {
        Return ($LinaItems | Where-Object { $_.IsDefault })
    }
    else {
        Return $LinaItems
    }
}

function LinaTenantShort {
    <#
    .SYNOPSIS
    Retrieves the tenants (entities).
    .DESCRIPTION
    Retrieves the tenants (entities). Returns a set of tenants that correspond to the filter criteria provided.
    .INPUTS
    None
    .OUTPUTS
    Array of LinaTenant Objects
    .PARAMETER Name
    Optional : Name of the tenant to retrieve. Wilcards are allowed. For example : *Tenant*
    .PARAMETER ID
    Optional : ID of the tenant to retrieve.
    .PARAMETER Default
    Get the default tenant if any
    .EXAMPLE
    Get-LinaTenant
    Retrieve all tenants
    .EXAMPLE
    Get-LinaTenant -Name "BaasCustomer1"
    Retrieve Tenant named BaasCustomer1
    .EXAMPLE
    Get-LinaTenant -ID 2
    Retrieve Tenant with ID 2
    .EXAMPLE
    Get-LinaTenant -Default
    Retrieve the default tenant (if any)
    #>

    [cmdletbinding(DefaultParameterSetName = "ByName")]
    Param(
        [Parameter(Mandatory = $false, Position = 0, ParameterSetName = "ByName")]
        [ValidateNotNullOrEmpty()]
        [string]$Name,
        [Parameter(Mandatory = $True, ParameterSetName = "ByID")]
        [ValidateRange(-1, 2147483647)]
        [int]$ID,
        [Parameter(Mandatory = $True, ParameterSetName = "Default")]
        [switch]$Default
    
    )
    Write-Verbose "Getting list of tenants (short)"
    if ($global:GLOBAL_LINA_CONNECTED -eq $False) { 
        Write-Error "$($global:GLOBAL_DISCONNNECTED_MESSAGE)" 
        return
    }    
    $request = CallAPI -Path "/ADM/list_domain.json"
    $Items = $request.domains
    $LinaItems = @()
    foreach ($Item in $Items) {
        $name_encoded = FixEncoding($Item.name)
        if ((!$Name -AND !$ID) -OR $Item.name -like "$Name" -OR $name_encoded -like "$Name" -OR $Item.id -eq $ID ) {
    
            $CurrentLinaItem = [PSCustomObject]@{
                PSTypeName = 'LinaTenant'
                Name       = FixEncoding($Item.name)
                ID         = $Item.id
    
                # Had to reencode to UTF-8 because of a bug or maybe to bad return type
                Comment    = FixEncoding($Item.comment)
                IsDefault  = [bool][int]$Item.def_domain
    
                UUID       = $Item.uuid
            }
            $LinaItems += $CurrentLinaItem
        }
            
    }
    if ($LinaItems.Count -eq 0) {
        Write-Host2 "No tenant found."
        Return $Null
    }
    if ($Default) {
        Return ($LinaItems | Where-Object { $_.IsDefault })
    }
    else {
        Return $LinaItems
    }
}

function Get-LinaAgentStats {
    <#
.SYNOPSIS
Retrieves the agents statistics and advanced information.
.DESCRIPTION
Retrieves the agents statistics and advanced information. Statistics are only available for agent that have been online at least one time.
.INPUTS
None
.OUTPUTS
Array of LinaAgentStats Objects
.PARAMETER Name
Name of the agent(s) to retrieve. Wilcards are allowed. For example : *DESKTOP*
.PARAMETER ID
ID of the agent to retrieve. For example : 134.
.EXAMPLE
Get-LinaAgentStats
Retrieve all agent stats
.EXAMPLE
Get-LinaAgentStats | where {$_.LastLogon -like "*mike*"}
Retrieve agent info on agents where mike is logged in.
.EXAMPLE
Get-LinaAgentStats | where {$_.LastCompletedSession -le (Get-Date).AddDays(-7)}
Retrieve agent info on agents with no completed backups in last 7 days
#>

    [cmdletbinding(DefaultParameterSetName = "ByName")]
    Param(
        [Parameter(ValueFromPipeline)]
        [pscustomobject]$lina_agent,
        [Parameter(ParameterSetName = "ByName")]
        [ValidateNotNullOrEmpty()]
        [string]$Name,
        [Parameter(ParameterSetName = "ByID")]
        [ValidateRange(0, 2147483647)]
        [int]$ID
    )
    if ($global:INT_LINA_API_VERSION -ge 51) {
        Write-Host2 "WARNING : Get-LinaAgentStats is deprecated with Lina 5.1, use Get-LinaAgent instead. Commands have been merged"
        return 
    }
    Write-Verbose "Getting agents Statistics (only available if has been online)"
    if ($global:GLOBAL_LINA_CONNECTED -eq $False) { 
        Write-Error "$($global:GLOBAL_DISCONNNECTED_MESSAGE)" 
        return
    }

    $request = CallAPI -Path "/stats_clients.xml"
    $Items = ([xml]$request).Stats.ClientArray.Client
    $LinaItems = @()
    foreach ($Item in $Items) {
        <# Filtering on ID or Name (only if provided) #>
        if ((!$Name -AND !$ID -AND !$lina_agent) -OR ($Item.AdminName -like "$Name" -OR $Item.AdminID -eq $ID -OR $lina_agent.ID -eq $Item.AdminID ) ) {
            $CurrentLinaItem = [PSCustomObject]@{
                PSTypeName             = 'LinaAgentStats'
                Name                   = $Item.AdminName
                ID                     = $Item.AdminID

                ComputerName           = $Item.ComputerName
                System                 = $Item.System
                SystemCategory         = TranslateSystemCategories($Item.SystemCategory)
                AgentVersion           = $Item.AgentVersion
                Strategy               = Translate($Item.AdminServiceProfileName)
                Protection             = Translate($Item.AdminDataProfileName)
                LastLogon              = $Item.LastLogon
                Email                  = $Item.Email
                CdpType                = $Item.CdpType
                Alert                  = $Item.Alert
                
                LastBackup             = HumanFriendlyTimeSpan (LinaToLocalTime $Item.SyncTime)

                LastSessionStart       = HumanFriendlyTimeSpan (LinaToLocalTime $Item.LastStartedSession)
                LastSessionComplete    = HumanFriendlyTimeSpan (LinaToLocalTime $Item.LastCompletedSession)
                LastConnection         = HumanFriendlyTimeSpan (LinaToLocalTime $Item.LastConnectionTime)
                LastSync               = HumanFriendlyTimeSpan (LinaToLocalTime $Item.SyncTime)

                LastStartedSession     = LinaToLocalTime $Item.LastStartedSession
                LastCompletedSession   = LinaToLocalTime $Item.LastCompletedSession
                LastConnectionTime     = LinaToLocalTime $Item.LastConnectionTime
                LastSyncTime           = LinaToLocalTime $Item.SyncTime
                
                StrategyInternalName   = $Item.AdminServiceProfileName
                ProtectionInternalName = $Item.AdminDataProfileName
            }
            $LinaItems += $CurrentLinaItem
        }
        
    }
    if ($LinaItems.Count -eq 0) {
        Write-Verbose "No agent statistics found."
        Return $Null
    }        
    Return $LinaItems
}

function Get-LinaAgentGroup {
    <#
.SYNOPSIS
Retrieves the agent groups
.DESCRIPTION
Retrieves the agent groups (Main view of agents)
.INPUTS
None
.OUTPUTS
Array of LinaAgentGroup Objects
.PARAMETER Name
Name of the Group to retrieve. Wilcards are allowed. For example : *UNCATEGOR*
.PARAMETER ID
ID of the group retrieve. For example : 4.
.PARAMETER Tenant
Name of the Tenant hosting the groups
Can also be used in conjunction with -Name to filter by name AND Tenant.
Can be useful if multiple groups have the same name in multiple tenants.
.EXAMPLE
Get-LinaAgentGroup
Retrieve all agent groups
.EXAMPLE
Get-LinaAgentGroup -ID 131
Retrieve agent group by it ID. Will return a unique object.
.EXAMPLE
Get-LinaAgentGroup -Name "Group1"
Retrieve agent group by its Name. Wilcards are allowed.
Warning : result may not be unique even without using wildcards. Group names may not be unique across tenants.
.EXAMPLE
Get-LinaAgentGroup -Tenant "MyTenant"
Retrieve agent groups of the Tenant MyTenant
.EXAMPLE
Get-LinaAgentGroup -Name "Group1" -Tenant "BaasCustomer1"
Filter agent group by name and by Tenant MyTenant. If not using wildcards in name, return will be unique.
#>

    [cmdletbinding(DefaultParameterSetName = 'ByNothing')]
    Param(
        [Parameter(Mandatory = $true, ParameterSetName = "ByName")]
        [Parameter(Mandatory = $True, ParameterSetName = "ByNameAndTenant")]
        [Parameter(Mandatory = $false, ParameterSetName = "ByNothing")]
        [ValidateNotNullOrEmpty()]
        [string]$Name,
        [Parameter(Mandatory = $true, ParameterSetName = "ByID")]
        [ValidateRange(0, 2147483647)]
        [int]$ID,
        [Parameter(Mandatory = $True, ParameterSetName = "ByTenant")]
        [Parameter(Mandatory = $True, ParameterSetName = "ByNameAndTenant")]
        [Parameter(Mandatory = $false, ParameterSetName = "ByNothing")]
        [ValidateNotNullOrEmpty()]
        [string]$Tenant
    )

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

    if ($Tenant) {
        $TenantID = (LinaTenantShort -Name $Tenant).ID
        Write-Verbose "Getting agent groups for Tenant $Tenant (ID $TenantID)"
    }
    else {
        Write-Verbose "Getting agent groups"
    }
        
    if ($INT_LINA_API_VERSION -lt 60) {
        # Lina < 6.0
        $request = CallAPI -Path "/lst_hierarchies.xml"
        $Items = ([xml]$request).HNConfig.HierarchyArray.Hierarchy.GroupArray.Group
    } else {
        # Lina > 6.0 => taking only Agent group used for inheritance, not legacy groups
        $request = CallAPI -Path "/lst_hierarchies.json"
        $Items = ($request.HierarchyArray | Where-Object { $_.Name -eq 'HERIT' } ).GroupArray
    }


    $Tenants = LinaTenantShort
    $LinaItems = @()

    if ($INT_LINA_API_VERSION -lt 60) {
    # Lina < 6.0

        foreach ($Item in $Items) {
            # PreFiltering By Tenant (Groups in tenant 0 are for all Tenants, example : Unsorted agents)
            if (!$TenantID -OR $Item.DomainID -eq $TenantID -OR $Item.DomainID -eq 0 ) {
                <# Filtering on ID or Name (only if provided) #>
                $name_translation = Translate($Item.Name) 
                
                $View = $Item.ParentNode.ParentNode.Name
                # Default view name should be translated
                if ($View -eq "DEF") {
                    $ViewTranslated = Translate "AGENT_VIEW_BY_AGENT_GROUP_LABEL"
                }else {
                    $ViewTranslated = Translate $view
                }

                if ((!$Name -AND !$ID) -OR ( $name_translation -like "$Name" -OR $Item.ID -eq $ID) ) {
                    $CurrentLinaItem = [PSCustomObject]@{
                        PSTypeName   = 'LinaAgentGroup'
                        Name         = $name_translation
                        ID           = $Item.ID
                        View         = $ViewTranslated
                        Strategy     = Translate($Item.ServiceProfile.Name)
                        Protection   = Translate($Item.DataProfile.Name)
                        Tenant       = $Tenants | where-object { $_.ID -eq $Item.DomainID } | Select-Object -ExpandProperty Name
                        NbAgents     = $Item.NbRefs 

                        InternalName = $Item.Name
                        TenantID     = $Item.DomainID
                        ViewID       = $Item.ParentNode.ParentNode.ID
                        StrategyInternalID   = $Item.ServiceProfile.ID
                        ProtectionInternalID = $Item.DataProfile.ID
                    }
                    $LinaItems += $CurrentLinaItem
                }
            }
                
        }
    } else {

        foreach ($Item in $Items) {
            # PreFiltering By Tenant (Groups in tenant 0 are for all Tenants, example : Unsorted agents)
            if (!$TenantID -OR $Item.DomainID -eq $TenantID -OR $Item.DomainID -eq 0 ) {
                <# Filtering on ID or Name (only if provided) #>
                $name_translation = Translate $Item.Name 0 
                
                if ((!$Name -AND !$ID) -OR ( $name_translation -like "$Name" -OR $Item.ID -eq $ID) ) {
                    $CurrentLinaItem = [PSCustomObject]@{
                        PSTypeName   = 'LinaAgentGroup'
                        Name         = $name_translation
                        ID           = $Item.ID
                        Strategy     = Translate($Item.ServiceProfile.Name)
                        Protection   = Translate($Item.DataProfile.Name)
                        Tenant       = $Tenants | where-object { $_.ID -eq $Item.DomainID } | Select-Object -ExpandProperty Name
                        NbAgents     = $Item.NbRefs 

                        InternalName = $Item.Name
                        TenantID     = $Item.DomainID
                        StrategyInternalID   = $Item.ServiceProfile.ID
                        ProtectionInternalID = $Item.DataProfile.ID
                    }
                    $LinaItems += $CurrentLinaItem
                }
            }
                
        }

    }
    if ($LinaItems.Count -eq 0) {
        Write-Host2 "No agent group found."
        Return $Null
    }            
    Return $LinaItems
}
 
function Get-LinaCurrentTenantID {
    <#
.SYNOPSIS
Get the current tenant
.DESCRIPTION
Get the current tenant. If current tenant is the Global View (no tenant selected), it will return -1.
.INPUTS
None
.OUTPUTS
Current tenant ID
.EXAMPLE
$current_tenant_id = Get-LinaCurrentTenantID
Get the current tenant ID.
#>

    Write-Verbose "Getting current tenant"

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

    $request = CallAPI -Path "/ADE/check_session.xml"
    $current_tenant_id = [int](([xml]$request).session.effective_domain_id)
    if ($current_tenant_id -eq -1 -OR $current_tenant_id -eq 1) {
        # Notes on tenant numbers : -1 and 0 => Global View, 1 = Root (first level tenant invisible/ contains superadmin and guest). >1 : Second level tenants
        Write-Host2 "No current tenant selected (ID = -1 or 1). Global view on all tenants."
        Return -1
    }
    elseif ($current_tenant_id -gt 1) {
        $current_tenant = LinaTenantShort -ID $current_tenant_id
        $current_tenant_name = $current_tenant.Name 
        Write-Host2 "Current tenant is $current_tenant_name (ID $current_tenant_id)"
        Return $current_tenant_id
    }
    else {
        TranslateErrorCode -LinaError $request -FailedAction "get current tenant"
        Return $null
    }
}

function Get-LinaCrossRestoreToken {
<#
.SYNOPSIS
Retrieves the cross restore tokens
.DESCRIPTION
Retrieves the cross restore tokens. Returns a set of tokens that correspond to the filter criteria provided
.INPUTS
None
.OUTPUTS
Array of LinaCrossRestoreToken Objects
.PARAMETER ID
Optional : ID of the cross restore token to retrieve. For example : 134.
.PARAMETER Code
Optional Code of the cross restore token to retrieve. Wilcards are allowed. For example : 1T7ZF8O56V3JXMKE.
.PARAMETER Tenant
Optional : Get tokens for specific tenant only
.EXAMPLE
Get-LinaCrossRestoreToken
Get the complete list of cross restore tokens for all tenants
.EXAMPLE
Get-LinaCrossRestoreToken -Code "1T7ZF8O56V3JXMKE"
Get cross restore token(s) matching this code.
.EXAMPLE
(Get-LinaCrossRestoreToken -Code "1T7ZF8O56V3JXMKE").AgentNames
Get list of agent names configured in this token
.EXAMPLE
Get-LinaCrossRestoreToken -ID 12
Get cross restore token with ID 12
.EXAMPLE
Get-LinaCrossRestoreToken -Tenant "MyTenant"
Get the list of cross restore tokens for tenant MyTenant
#>

[cmdletbinding(DefaultParameterSetName = "ByCode")]
Param(
    [Parameter(ParameterSetName = "ByCode", Position = 0)]
    [ValidateNotNullOrEmpty()]
    [string]$Code,
    [Parameter(ParameterSetName = "ByID")]
    [ValidateRange(0, 2147483647)]
    [int]$ID,
    [Parameter(Mandatory = $false, Position = 1)]
    [ValidateNotNullOrEmpty()]
    [string]$Tenant      
)
    Write-Verbose "Getting cross restore token"

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

    if ($global:INT_LINA_API_VERSION -lt 52 ) {
        Write-Error "This feature is only available on Lina 5.2+"
        return
    }

    $current_tenant_id = Get-LinaCurrentTenantID 6>$null

    if (!$Tenant) {
        $tenant_list = LinaTenantShort 6>$null
    }else {
        $tenant_list = LinaTenantShort -Name $Tenant 6>$null
        if ( !$tenant_list) {
            Write-Host2 "ERROR : No tenant found with name $Tenant. Please input the exact name of the Tenant"
            Return
        }
    }

    $LinaItems = @()

    # Cross restore tokens are listed by tenants by default
    foreach ($lina_tenant in $tenant_list) {

        $void = Set-LinaCurrentTenant -ID $lina_tenant.ID 6>$null

        $request = CallAPI -Path "/security_cross_listtokens.json"
        $Items = $request.tokens


        foreach ($Item in $Items) {

            if ( (!$ID -AND !$Code) -OR ( $Item.code -like "$Code" -OR $Item.id -eq $ID) ) {
                
                if ($Item.expire -eq 0) {
                    $token_expiration_date = "None"
                }else {
                    $token_expiration_date = LinaToLocalTime $Item.expire
                }

                if ($Item.lastlist -eq 0) {
                    $token_lastuse_date = "None"
                }else {
                    $token_lastuse_date = LinaToLocalTime $Item.lastlist
                }            

                $CurrentLinaItem = [PSCustomObject]@{
                    PSTypeName          = 'LinaCrossRestoreToken'
                    ID                  = $Item.id
                    Tenant              = $lina_tenant.Name
                    Code                = $Item.code
                    ExpiresIn           = HumanFriendlyTimespanFuture (LinaToLocalTime $Item.expire)
                    ExpirationDate      = $token_expiration_date
                    AgentNames          = $Item.agents.Name
                    NumberUse           = $Item.nblist
                    LastUseDate         = $token_lastuse_date
                    AgentDetails        = $Item.agents
                    TenantID            = $lina_tenant.ID
        
                }
                $LinaItems += $CurrentLinaItem
            }
        }

    }

    # Setting back the current tenant to what it was before the Get command
    $void = Set-LinaCurrentTenant -ID $current_tenant_id 6>$null

    if ($LinaItems.Count -eq 0) {
        Write-Host2 "No cross restore token found."
        Return $Null
    }        
    Return $LinaItems

}

function Get-LinaNode {
    <#
            .SYNOPSIS
            Retrieves the server nodes
            .DESCRIPTION
            Retrieves the server nodes
            .INPUTS
            None
            .OUTPUTS
            Array of LinaNode Objects
            .PARAMETER Name
            Optional : Name of the Lina server node to retrieve. Wilcards are allowed. For example : *node1*
            .PARAMETER ID
            Optional : ID of the Lina server node to retrieve.
            .EXAMPLE
            Get-LinaNode
            Retrieves all the Lina server nodes.
            .EXAMPLE
            Get-LinaNode -Name "*node1*"
            Retrieves all the lina server nodes containing "node1" in their names.
            #>

    [cmdletbinding(DefaultParameterSetName = "ByName")]
    Param(
        [Parameter(ParameterSetName = "ByName")]
        [ValidateNotNullOrEmpty()]
        [string]$Name,
        [Parameter(ParameterSetName = "ByID")]
        [ValidateRange(0, 2147483647)]
        [int]$ID                 
    )


    Write-Verbose "Getting list of nodes"
    if ($global:GLOBAL_LINA_CONNECTED -eq $False) { 
        Write-Error "$($global:GLOBAL_DISCONNNECTED_MESSAGE)" 
        return
    }

    if ($INT_LINA_API_VERSION -lt 60) {
        Write-Host2 "ERROR : Get-LinaNode requires a Lina Server with version 6.x or Later"
        return
    }

    if ($GLOBAL_LINA_SERVERTYPE -ne "master") {
        Write-Host2 "ERROR : Get-LinaNode requires a Lina Server operating in master node."
        return
    }

    $request = CallAPI -Path "/ADE/node/list.json"
    $Items = $request.nodes

    $LinaItems = @()
    foreach ($Item in $Items) {
         
        if ((!$Name -AND !$ID -AND $Item.friendly_name) -OR ( $Item.friendly_name -like "$Name" -OR $Item.id -eq $ID ) ) {        
            $CurrentLinaItem = [PSCustomObject]@{
                PSTypeName = 'LinaNode'
                Name          = $Item.friendly_name
                ID            = $Item.uuid
                HostName      = $Item.name

                NbAgents      = $Item.nb_agent
                Port          = $Item.port
                AccessInfo    = $Item.access_info
                Version       = $Item.version
                OS            = $Item.os
                HSSPort       = $Item.hss_port
                AdminPort     = $Item.admin_port
                
                FreeSpaceGB   = [math]::Round(($item.free_space_volume) / (1024 * 1024 * 1024), 2)
                
                LastIP        = $Item.last_ip
                LastSeen      = HumanFriendlyTimeSpan (LinaToLocalTime ([Int64]$Item.last_seen*1000))
                LastSeenDate  = LinaToLocalTime ([Int64]$Item.last_seen*1000)
                
                IsOffline       = [bool]$Item.offline
                internalID    = $Item.id

            }
            $LinaItems += $CurrentLinaItem
        }
    }
    if ($LinaItems.Count -eq 0) {
        Write-Host2 "No node found."
        Return $Null
    }        
    Return $LinaItems
}