WebAppStorage.psm1

#region Functions
#region Get-WebAppItem
function Get-WebAppItem {
    param (
        [Parameter()]
        [string]$Path,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$Username,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [securestring]$Password,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$DeploymentURL
    )

    begin
    {
        # Set the User Agent string
        $userAgent = "powershell/1.0"

        # Extract the plain text value of the password
        [pscredential]$creds = New-Object System.Management.Automation.PSCredential ($userName, $Password)
        $pass = $creds.GetNetworkCredential().Password

        # Create the Base64 value for authentication
        $base64 = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $username, $pass)))

        # Sanize DeploymentURL - Remove trailing /
        if($DeploymentURL.EndsWith("/"))
        {
            $DeploymentURL = $DeploymentURL.Substring(0, $DeploymentURL.Length - 1)
        }
    }

    process
    {
        # Set the URI
        $uri = $DeploymentURL + "/api/vfs/" + $Path

        # If the user has requested a directory, remove the trailing /
        if($uri.EndsWith('/'))
        {
            $uri = $uri.Substring(0, $uri.Length - 1)    
        }

        # Transform the uri in order to get the items of the parent directory
        $index = $uri.LastIndexOf('/')
        $name = $uri.Substring($index + 1, $uri.Length - $index - 1)
        $uri = $uri.Substring(0, $index + 1)

        # Submit the request
        try
        {
            $data = Invoke-RestMethod -Uri $uri `
                                      -Headers @{Authorization=("Basic {0}" -f $base64)} `
                                      -UserAgent $userAgent `
                                      -Method Get `
                                      -ErrorAction Stop    
        }
        catch 
        {
            Write-Error ("Failed to get the item from WebApp. " + $_.Exception.Message)                                    
        }

        $data |
            ForEach-Object{
                $relPath = $_.href.replace($DeploymentURL, "")
                $relPath = $relPath.Substring(8, $relPath.Length - 8)
                if($relPath.StartsWith("/"))
                {
                    $relPath = $relPath.Substring(1, $relPath.Length - 1)
                }
                $_ |
                    Add-Member -MemberType NoteProperty -Name RelPath -Value $relPath
            }

        Write-Output $data |
            Where-Object {$_.Name -eq $name}
    }

    end {}
}
#endregion

#region Get-WebAppChildItem
function Get-WebAppChildItem {
    param (
        [Parameter()]
        [string]$Path,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$Username,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [securestring]$Password,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$DeploymentURL
    )

    begin
    {
        # Set the User Agent string
        $userAgent = "powershell/1.0"

        # Extract the plain text value of the password
        [pscredential]$creds = New-Object System.Management.Automation.PSCredential ($userName, $Password)
        $pass = $creds.GetNetworkCredential().Password

        # Create the Base64 value for authentication
        $base64 = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $username, $pass)))

        # Sanize DeploymentURL - Remove trailing /
        if($DeploymentURL.EndsWith("/"))
        {
            $DeploymentURL = $DeploymentURL.Substring(0, $DeploymentURL.Length - 1)
        }
    }

    process
    {
        # Set the URI
        $uri = $DeploymentURL + "/api/vfs/" + $Path

        # Add the trailing / to the Path
        if(-not $uri.EndsWith('/'))
        {
            $uri += "/"
        }

        # Get the item to remove
        try 
        {
            $item = Get-WebAppItem -Username $Username `
                                   -Password $Password `
                                   -Path $Path `
                                   -DeploymentURL $DeploymentURL `
                                   -ErrorAction Stop    
        }
        catch {
            Write-Error "Failed to locate the item to be listed."
            return            
        }

        # Check if the item is a directory or not
        if($item.mime -ne "inode/directory")
        {
            Write-Output $item
            return
        }

        # Submit the request
        try
        {
            $data = Invoke-RestMethod -Uri $uri `
                                      -Headers @{Authorization=("Basic {0}" -f $base64)} `
                                      -UserAgent $userAgent `
                                      -Method Get `
                                      -ErrorAction Stop    
        }
        catch 
        {
            Write-Error ("Failed to get the list of files/folders from WebApp. " + $_.Exception.Message)                                    
        }

        $data |
            ForEach-Object{
                $relPath = $_.href.replace($DeploymentURL, "")
                $relPath = $relPath.Substring(8, $relPath.Length - 8)
                if($relPath.StartsWith("/"))
                {
                    $relPath = $relPath.Substring(1, $relPath.Length - 1)
                }
                $_ |
                    Add-Member -MemberType NoteProperty -Name RelPath -Value $relPath
            }

        Write-Output $data
    }

    end {}
}
#endregion

#region Download-WebAppFile
function Download-WebAppFile {
    param (
        [Parameter()]
        [string]$Path,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$Username,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [securestring]$Password,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$DeploymentURL,

        [Parameter(Mandatory = $false)]
        [string]$DestinationPath
    )

    begin
    {
        # Set the User Agent string
        $userAgent = "powershell/1.0"

        # Extract the plain text value of the password
        [pscredential]$creds = New-Object System.Management.Automation.PSCredential ($userName, $Password)
        $pass = $creds.GetNetworkCredential().Password

        # Create the Base64 value for authentication
        $base64 = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $username, $pass)))

        # Validate if DestinationPath exists
        try 
        {
            $dpath = Resolve-Path -Path $DestinationPath -ErrorAction Stop

            #TODO: Check if the path resolves to a file
        }
        catch
        {
            throw "Could not resolve the DestinationPath."            
        }

        # Sanize DeploymentURL - Remove trailing /
        if($DeploymentURL.EndsWith("/"))
        {
            $DeploymentURL = $DeploymentURL.Substring(0, $DeploymentURL.Length - 1)
        }
    }

    process
    {
        # Set the URI
        $uri = $DeploymentURL + "/api/vfs/" + $Path

        # Try to get the file
        try 
        {
            $file = Get-WebAppChildItem -Username $Username `
                                        -Password $Password `
                                        -Path $Path `
                                        -DeploymentURL $DeploymentURL `
                                        -ErrorAction Stop    
        }
        catch
        {
            Write-Error ("Failed to get the file from WebApp. " + $_.Exception.Message)
            return
        }

        # Check if the file is a directory
        if($file.mime -eq "inode/directory")
        {
            Write-Error ("The provided path resolves to a directory.")
            return
        }

        # Submit the request
        try
        {
            $data = Invoke-RestMethod -Uri $uri `
                                      -Headers @{Authorization=("Basic {0}" -f $base64)} `
                                      -UserAgent $userAgent `
                                      -Method Get
        }
        catch 
        {
            Write-Error ("Failed to download the file from WebApp. " + $_.Exception.Message)                                    
        }
                        
        Write-Output $data
    }

    end {}
}
#endregion

#region Upload-WebAppFile
function Upload-WebAppFile {
    param (
        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string]$Path,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$Username,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [securestring]$Password,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$DeploymentURL,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$DestinationPath
    )

    begin
    {
        # Set the User Agent string
        $userAgent = "powershell/1.0"

        # Extract the plain text value of the password
        [pscredential]$creds = New-Object System.Management.Automation.PSCredential ($userName, $Password)
        $pass = $creds.GetNetworkCredential().Password

        # Create the Base64 value for authentication
        $base64 = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $username, $pass)))

        # Sanize DeploymentURL - Remove trailing /
        if($DeploymentURL.EndsWith("/"))
        {
            $DeploymentURL = $DeploymentURL.Substring(0, $DeploymentURL.Length - 1)
        }
    }

    process
    {
        # Set the URI
        $uri = $DeploymentURL + "/api/vfs/" + $DestinationPath

        # Verify that the source file exists
        try 
        {
            $spath = (Resolve-Path -Path $Path -ErrorAction Stop).Path
        }
        catch
        {
            Write-Error "Failed to locate the file to upload."
            return    
        }

        # Check if destination path is a directory or not
        if($DestinationPath.EndsWith('/'))
        {
            # We need to add the name of the file to the uri
            $fpath = (Resolve-Path $Path).Path
            $fitem = Get-Item -Path $fpath
            $uri += $fitem.Name
        }
        else 
        {
            # Check if we are uploading to a folder and we're missing the trailing /
            try 
            {
                $dpath2 = Get-WebAppItem -Username $Username `
                                         -Password $Password `
                                         -DeploymentURL $DeploymentURL `
                                         -Path $DestinationPath `
                                         -ErrorAction Stop

                if($dpath2.mime -eq "inode/directory")
                {
                    # We need to add the name of the file to the uri
                    $fpath = (Resolve-Path $Path).Path
                    $fitem = Get-Item -Path $fpath
                    $uri = $uri + "/" + $fitem.Name
                }
            }
            catch
            {
                # There is no directory with that name, continue upload
            }
        }

        # Create the request headers
        $headers = @{
            'Authorization' = "Basic {0}" -f $base64
            'If-Match' = "*"
        }

        # Submit the request
        try
        {
            $data = Invoke-RestMethod -Uri $uri `
                                      -Headers $headers `
                                      -UserAgent $userAgent `
                                      -Method Put `
                                      -InFile $spath `
                                      -ErrorAction Stop    
        }
        catch 
        {
            Write-Error ("Failed to upload the file to the WebApp. " + $_.Exception.Message)                                    
        }

        $data
    }

    end {}
}
#endregion

#region Upload-WebAppDirectory
function Upload-WebAppDirectory
{
    [cmdletBinding()]
    param
    (
        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string]$Path,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$Username,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [securestring]$Password,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$DeploymentURL,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$DestinationPath
    )

    # Test if Path exists
    try
    {
        $p = (Resolve-Path -Path $Path -ErrorAction Stop).Path
    }
    catch
    {
        Write-Error "Failed to located directory $Path"
        return
    }

    # Check if Path is a directory
    # TODO

    # Create a list of items to upload
    $items = Get-ChildItem -Path $p -Force

    # Create the directory structure
    $items |
        ForEach-Object{
            # Get the full path to the directory
            $fpath = $_.FullName

            # Get the relative path to the root directory
            $rpath = $_.FullName.Substring($p.Length + 1, $_.FullName.Length - $p.Length - 1)

            # Form the destination path
            $drpath = $rpath.Replace('\', '/')



            # Get the path on the website
            if($drpath.Contains('/'))
            {
                $wdpath = $drpath.Substring(0, $drpath.LastIndexOf('/'))
            }
            else
            {
                $wdpath = $drpath
            }


            # Add the parent path from the function
            if($DestinationPath.EndsWith('/'))
            {
                $wdpath = $DestinationPath + $wdpath
            }
            else
            {
                $wdpath = $DestinationPath + '/' + $wdpath
            }

            if($_.PSIsContainer -eq $false)
            {
                # Processing files
                Write-Verbose "Processing file $rpath"

                # Form the path to the destination directory
                $wddpath = $wdpath.Substring(0, $wdpath.LastIndexOf('/'))
                if(-Not $wddpath.EndsWith('/'))
                {
                    $wddpath += "/"
                }

                # Upload the file
                Upload-WebAppFile -Username $Username `
                                  -Password $Password `
                                  -DeploymentURL $DeploymentUrl `
                                  -Path $_.FullName `
                                  -DestinationPath $wddpath
            }
            else
            {
                # Processing directories
                Write-Verbose "Processing directory $rpath"

                # Add the trailing /
                $drpath += "/"

                # Create the directory on the website
                # No need to create the directory since it is created automatically if needed
                <#
                New-WebAppDirectory -Username $Username `
                                    -Password $Password `
                                    -DeploymentURL $DeploymentUrl `
                                    -Name $_.Name `
                                    -Path $wdpath
                #>


                # Process the contents of the directory
                Upload-WebAppDirectory -Path $_.FullName -DestinationPath $wdpath
            }
        }
}
#endregion

#region Remove-WebAppItem
function Remove-WebAppItem {
    param (
        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string]$Path,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$Username,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [securestring]$Password,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$DeploymentURL,

        [Parameter(Mandatory = $false)]
        [switch]$Recurse = $false
    )

    begin
    {
        # Set the User Agent string
        $userAgent = "powershell/1.0"

        # Extract the plain text value of the password
        [pscredential]$creds = New-Object System.Management.Automation.PSCredential ($userName, $Password)
        $pass = $creds.GetNetworkCredential().Password

        # Create the Base64 value for authentication
        $base64 = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $username, $pass)))

        # Sanize DeploymentURL - Remove trailing /
        if($DeploymentURL.EndsWith("/"))
        {
            $DeploymentURL = $DeploymentURL.Substring(0, $DeploymentURL.Length - 1)
        }
    }

    process
    {
        # Set the URI
        $uri = $DeploymentURL + "/api/vfs/" + $Path

        # Create the request headers
        $headers = @{
            'Authorization' = "Basic {0}" -f $base64
            'If-Match' = "*"
        }

        # Get the item to remove
        try 
        {
            $item = Get-WebAppItem -Username $Username `
                                   -Password $Password `
                                   -Path $Path `
                                   -DeploymentURL $DeploymentURL `
                                   -ErrorAction Stop    
        }
        catch {
            Write-Error "Failed to locate the item to be removed."
            return            
        }

        # Check if the item is a directory
        if($item.mime -eq "inode/directory")
        {
            # Get the number of items in it
            $initems = @(Get-WebAppChildItem -Username $Username `
                                           -Password $Password `
                                           -DeploymentURL $DeploymentURL `
                                           -Path $Path)

            if($initems.Count -gt 0)
            {
                if($Recurse -eq $false)
                {
                    Write-Error "Directory not empty."
                    return
                }
                else
                {
                    # Remove the files and folders
                    foreach($i in $initems)
                    {
                        # Remove the file
                        if($i.mime -ne "inode/directory")
                        {
                            try 
                            {
                                Remove-WebAppItem -Username $Username `
                                                  -Password $Password `
                                                  -DeploymentURL $DeploymentURL `
                                                  -Path ($Path + "/" + $i.name) `
                                                  -ErrorAction Stop
                            }
                            catch {
                                Write-Error "Failed to remove item $($i.name)."                                
                            }
                        }
                        else
                        {
                            # Recursively remove the subdirectory
                            try 
                            {
                                Remove-WebAppItem -Username $Username `
                                                  -Password $Password `
                                                  -DeploymentURL $DeploymentURL `
                                                  -Path ($Path + "/" + $i.Name) `
                                                  -Recurse `
                                                  -ErrorAction Stop
                            }
                            catch {
                                Write-Error "Failed to remove item $($i.Name)."
                            }                            
                        }
                    }                   
                }
            }
        }

        # Submit the request
        try
        {
            $data = Invoke-RestMethod -Uri $uri `
                                      -Headers $headers `
                                      -UserAgent $userAgent `
                                      -Method Delete `
                                      -ErrorAction Stop    
        }
        catch 
        {
            Write-Error ("Failed to delete the file from the WebApp. " + $_.Exception.Message)                                    
        }
    }

    end {}
}
#endregion

#region New-WebAppDirectory
function New-WebAppDirectory {
    param (
        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string]$Path,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$Username,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [securestring]$Password,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$DeploymentURL,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$Name
    )

    begin
    {
        # Set the User Agent string
        $userAgent = "powershell/1.0"

        # Extract the plain text value of the password
        [pscredential]$creds = New-Object System.Management.Automation.PSCredential ($userName, $Password)
        $pass = $creds.GetNetworkCredential().Password

        # Create the Base64 value for authentication
        $base64 = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $username, $pass)))

        # Sanize DeploymentURL - Remove trailing /
        if($DeploymentURL.EndsWith("/"))
        {
            $DeploymentURL = $DeploymentURL.Substring(0, $DeploymentURL.Length - 1)
        }
    }

    process
    {
        # Set the URI
        $uri = $DeploymentURL + "/api/vfs/" + $Path + "/" + $Name + "/"

        # Verify that the path to create the directory in exists and is a directory
        #TODO

        # Create the request headers
        $headers = @{
            'Authorization' = "Basic {0}" -f $base64
            'If-Match' = "*"
        }

        # Submit the request
        try
        {
            $data = Invoke-RestMethod -Uri $uri `
                                      -Headers $headers `
                                      -UserAgent $userAgent `
                                      -Method Put `
                                      -ErrorAction Stop    
        }
        catch 
        {
            Write-Error ("Failed to create the directory in the WebApp. " + $_.Exception.Message)                                    
        }
    }

    end {}
}
#endregion

#endregion

#region Exports
Export-ModuleMember -Function "Get-WebAppItem"
Export-ModuleMember -Function "Get-WebAppChildItem"
Export-ModuleMember -Function "Download-WebAppFile"
Export-ModuleMember -Function "Upload-WebAppFile"
Export-ModuleMember -Function "Upload-WebAppDirectory"
Export-ModuleMember -Function "Remove-WebAppItem"
Export-ModuleMember -Function "New-WebAppDirectory"
#endregion