WarplanepornLockscreen.psm1

$configPath = Join-Path $PSScriptRoot "config.json"

function WarplanepornLockscreen {
    param (
        [string[]]$subreddits,
        [switch]$nsfw,
        [switch]$install,
        [switch]$uninstall,
        [switch]$showpic,
        [switch]$showlog,
        [switch]$list,
        [string]$add,
        [string]$remove,
        [string]$sort,
        [switch]$config,
        [switch]$save,
        [switch]$showSaved,
        [switch]$noUpdateCheck
    )

    $warplanepornPic = Join-Path $PSScriptRoot "lockscreen.jpg"

    # get config and check for update
    $configuration = Get-Config
    if (-not $noUpdateCheck) {
        if (
            (-not $configuration.nextUpdateCheck) -or
            ($configuration.nextUpdateCheck -lt [int64](Get-Date(Get-Date).ToUniversalTime() -UFormat %s))
        ) {
            Test-ForUpdate
        }
    }

    # catch weird argument combinaisons
    if ($install -and $uninstall) {
        Write-Host "Why would you want to install and uninstall??"
        Write-Host "See help : WarplanepornLockscreen -help"
        Break
    }
    if ($install -and ($list -or $add -or $remove -or $config)) {
        Write-Host "Don't ask for both installation and configuration. The former does the latter!"
        Write-Host "See help : WarplanepornLockscreen -help"
        Break
    }

    # install and uninstall options
    if ($install) {
        Install-WarplanepornLockscreen
        Break
    }

    elseif ($uninstall) {
        Uninstall-WarplanepornLockscreen
        Break
    }

    # check installed
    if ((-not (Test-Path $configPath)) -or (!$configuration.installed)) {
        Write-Host "The module was never installed, would you like to install it ? (y/n) : " -ForegroundColor Cyan -NoNewline
        if ((Read-Host).ToLower() -eq "y") {
            Install-WarplanepornLockscreen
            Break
        }
    }

    # parse options
    if (!$nsfw) { $nsfw = $configuration.nsfw }
    $sort = $sort.ToLower()
    if (-not (($sort -eq "hot") -or ($sort -eq "top") -or ($sort -eq "new") -or ($sort -eq $null) -or ($sort -eq ""))) {
        Write-Host ("Sort input {0} unknown. It should be one of top, hot and new" -f $sort) -ForegroundColor Red
        Write-Host "See help : WarplanepornLockscreen -help"
        cmd /c pause
        Break
    }
    if (!$sort) {
        if ($configuration.sort) {
            $sort = $configuration.sort
        }
        else {
            $sort = "hot"
        }
    }

    if ($showpic) {
        if (Test-Path $warplanepornPic) {
            Invoke-Item $warplanepornPic
        }
        else {
            Write-Host "No picture was found, maybe the utility was never run" -ForegroundColor Yellow
        }
    }

    if ($showlog) {
        $logfile = Join-Path $PSScriptRoot "log.txt"
        if (Test-Path $logfile) {
            Invoke-Item $logfile
        }
        else {
            Write-Host "No log was found, maybe the utility was never run" -ForegroundColor Yellow
        }
    }

    if ($list) {
        Write-Host $configuration.subreddits
    }

    if ($save) {
        $savedDir = Join-Path $PSScriptRoot "Saved"
        if (!(Test-Path $savedDir)) {
            New-Item -Path $PSScriptRoot -Name "Saved" -ItemType "directory" | Out-Null
        }
        if (Test-Path $warplanepornPic) {
            Copy-Item -Path $warplanepornPic -Destination $savedDir -Force | Out-Null
            Rename-Item -Path (Join-Path $savedDir "lockscreen.jpg") -NewName ((Get-date -Format "dd_MM_yyyy HH_mm_ss") + ".jpg") | Out-Null
            Write-Host "Successfully saved current wallpaper" -ForegroundColor Green
        }
        else {
            Write-Host "No wallpaper was found to be saved" -ForegroundColor Yellow
        }
    }

    if ($showSaved) {
        $savedDir = Join-Path $PSScriptRoot "Saved"
        if (Test-Path $savedDir) {
            Invoke-Item $savedDir
        }
        else {
            Write-Host "No wallpaper was ever saved" -ForegroundColor Yellow
        }
    }

    if ($add -and (!$config)) {
        $subreddit_add = $add.Trim()
        $ProgressPreference = 'SilentlyContinue'
        $subredditStatus = Invoke-WebRequest ("https://reddit.com/r/{0}/about.json" -f $subreddit_add) | ConvertFrom-Json
        if ($subredditStatus.data.subreddit_type -eq "public") {
            $configuration.subreddits += $subreddit_add
            $configuration | ConvertTo-Json | Out-File $configPath
            Write-Host ("Subreddit {0} was added to configuration" -f $subreddit_add) -ForegroundColor Green
        }
        else {
            Write-Host "Subreddit {0} could not be found. It could not exist or be restricted." -ForegroundColor Red
        }
    }

    if ($remove -and (!$config)) {
        if ($configuration.subreddits.count -le 1) {
            Write-Host "There is only one subreddit left in the configuration, please add a new one before removing it." -ForegroundColor Red
        }
        else {
            $subreddit_remove = $remove.Trim()
            if ($configuration.subreddits.Contains($subreddit_remove)) {
                $subreddits_remove = @()
                $configuration.subreddits | ForEach-Object {
                    if ($_ -ne $subreddit_remove) {
                        $subreddits_remove += $_
                    }
                }
                $configuration.subreddits = $subreddits_remove
                Set-Content $configPath ($configuration | ConvertTo-Json)
                Write-Host ("Subreddit {0} was removed from configuration" -f $subreddit_remove) -ForegroundColor Green
            }
            else {
                Write-Host ("Could not find subreddit {0} in the current configuration" -f $subreddit_remove) -ForegroundColor Red
            }
        }
    }

    if ($config) {
        Set-Config
    }

    if ($help) {
        Show-Help
    }

    if (!$install -and !$uninstall -and !$list -and !$config -and !$add -and !$remove -and !$showlog -and !$showpic -and !$help -and !$save -and !$showSaved) {
        if (!$subreddits) {
            if ($configuration.subreddits) {
                $subreddits = $configuration.subreddits
            }
            else {
                $subreddits = @('warplaneporn')
            }
        }

        Get-Picture -subreddits $subreddits -nsfw $nsfw -sort $sort
    }
}

function Show-Help {
    Write-Host "Usage: WarplanepornLockscreen [-subreddits sub1,sub2...] [-nsfw] [-sort top|hot|new]"
    Write-Host "Refresh lock screen wallpaper from the subreddits specified or from those in the config"
    Write-Host ""
    Write-Host "Other usages"
    Write-Host "=============="
    Write-Host "WarplanepornLockscreen [-uninstall]"
    Write-Host "WarplanepornLockscreen [-config]"
    Write-Host "WarplanepornLockscreen [-list]"
    Write-Host "WarplanepornLockscreen [-add subreddit]"
    Write-Host "WarplanepornLockscreen [-remove subreddit]"
    Write-Host "WarplanepornLockscreen [-saveCurrent]"
    Write-Host "WarplanepornLockscreen [-showSaved]"
    Write-Host "WarplanepornLockscreen [-showPic]"
    Write-Host "WarplanepornLockscreen [-showlog]"
}

function Get-Config {
    $configuration = @{}

    if (Test-Path $configPath) {
        (Get-Content -Raw -Path $configPath | ConvertFrom-Json).psobject.Properties | ForEach-Object {
            $configuration[$_.Name] = $_.Value
        }
    }

    return $configuration
}

function Set-Config {
    param(
        [switch]$ExecuteAfter,
        [switch]$install
    )

    $ProgressPreference = 'SilentlyContinue'

    $configuration = Get-Config

    if ($install) {
        $configuration.installed = $true
    }


    # ask nsfw
    $nsfw = (Read-Host "Do you want to include nsfw posts in the potential wallpapers ? (y/n) (default no)").ToLower()
    $configuration.nsfw = ($nsfw -eq "y")

    # ask sort
    while ($true) {
        $sort = (Read-Host "How to sort posts ? (top|hot|new) (leave empty for hot)")
        if ($sort -eq "") {
            $configuration.sort = "hot"
            Break
        }
        elseif (($sort -eq "top") -or ($sort -eq "hot") -or ($sort -eq "new")) {
            $configuration.sort = $sort
            Break
        }
        Write-Host "I didn't get that..."
    }

    # ask subreddits
    Write-Host "What subreddits would you like to see wallpapers from ?" -ForegroundColor Yellow
    Write-Host "Enter one without the r/ prefix, press enter then the next one. Leave a line empty to confirm." -ForegroundColor Yellow
    Write-Host "Just press ENTER for default (warplaneporn)" -ForegroundColor Yellow

    $inSubIdx = 1
    $subreddits = @()
    while ($true) {
        $subreddit = (Read-Host ("Subreddit {0}" -f $inSubIdx)).Trim()
        if ($subreddit -eq "") { Break }

        Write-Host ("Checking subreddit {0}..." -f $subreddit) -ForegroundColor DarkYellow
        $subredditStatus = Invoke-WebRequest ("https://reddit.com/r/{0}/about.json" -f $subreddit) | ConvertFrom-Json
        if ($subredditStatus.data.subreddit_type -eq "public") {
            $subreddits += $subreddit
            $inSubIdx += 1
        }
        else {
            Write-Host ("Subreddit {0} could not be found. It could not exist or be restricted." -f $subreddit) -ForegroundColor Red
        }
    }
    if (!$subreddits) {
        $subreddits = @("warplaneporn")
    }
    $configuration.subreddits = $subreddits

    # write to file
    if (Test-Path $configPath) { Remove-Item $configPath | Out-Null }
    $configuration | ConvertTo-Json | Out-File $configPath

    # execute utility if asked to
    if ($ExecuteAfter) {
        Write-Host ""
        WarplanepornLockscreen
    }
    else {
        Write-Host "Configuration saved" -ForegroundColor Green
    }
}

function Test-ForUpdate {
    $configuration = Get-Config
    $configuration.nextUpdateCheck = [int64](Get-Date(Get-Date).ToUniversalTime() -UFormat %s) + 86400

    if (Test-Path $configPath) {
        Remove-Item $configPath | Out-Null
    }
    $configuration | ConvertTo-Json | Out-File $configPath

    $localVersion = (Get-Module -Name WarplanepornLockscreen).Version
    $remoteVersion = (Find-Module -Name WarplanepornLockscreen -Repository PSGallery).Version

    if ($remoteVersion -gt $localVersion) {
        Write-Host "New version available. Would you like to update (y/n)? " -ForegroundColor Yellow -NoNewline
        if ((Read-Host).ToLower() -eq 'y') {
            Update-Module -Name WarplanepornLockscreen

            $newVersionDirectory = Join-Path (Split-Path -Parent $PSScriptRoot) $remoteVersion
            if (Test-Path $newVersionDirectory) {
                Copy-Item -Force -Path (Join-Path $PSScriptRoot "config.json") -Destination $newVersionDirectory | Out-Null
                Write-Host "Successfully updated WarplanepornLockscreen to version $remoteVersion." -ForegroundColor Green
            }
            else {
                Write-Host "Successfully updated WarplanepornLockscreen to version $remoteVersion " -NoNewline
                Write-Host "but could not copy current configuration to the new version..." -ForegroundColor Yellow
            }

            Write-Host "Please relaunch WarplanepornLockscreen to continue."
            Exit
        }
    }
}

function Get-Picture {
    # get image from given subreddit and check dimensions
    param (
        [string[]]$subreddits,
        [string]$sort,
        [bool]$nsfw
    )
    Write-Host ("List of configured subreddits : {0}" -f [string]$subreddits)

    $ProgressPreference = 'SilentlyContinue'
    $templockscreenImagePath = Join-Path $PSScriptRoot "lockscreen_temp.jpg"
    $lockscreenImagePath = Join-Path $PSScriptRoot "lockscreen.jpg"

    $ShuffledSubreddits = $subreddits | Sort-Object { Get-Random }
    $notfound = 1
    $subIdx = 0
    while (($subIdx -lt $ShuffledSubreddits.count) -and $notfound) {
        $Subreddit = @($ShuffledSubreddits)[$subIdx]
        Write-Log ("Will choose image from subreddit {0}, sorting by {1}" -f $Subreddit, $sort);

        $request = 'https://reddit.com/r/{0}/{1}.json?limit=10' -f $Subreddit, $sort
        $jsonRequest = Invoke-WebRequest $request | ConvertFrom-Json
        $posts = $jsonRequest.data.children

        $posts | ForEach-Object {
            if (($_.data.post_hint -eq "image") -and ((-not $_.data.over_18) -or $nsfw) -and $notfound) {
                Write-Log ("Downloading image at {0}" -f $_.data.url);
                (New-Object System.Net.WebClient).DownloadFile($_.data.url, $templockscreenImagePath)

                [void][System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
                $Image = [System.Drawing.Image]::FromFile($templockscreenImagePath)
                $imagewidth = $Image.width
                $imageheight = $Image.height
                $Image.Dispose();
                if (($imagewidth -ge 1000) -and (($imagewidth / $imageheight) -ge 1)) {
                    $notfound = 0
                    if (Test-Path $lockscreenImagePath) { Remove-Item $lockscreenImagePath -Force }
                    Rename-Item $templockscreenImagePath "lockscreen.jpg"
                }
                elseif ($imagewidth -le 1000) {
                    Write-Log ("Image is too small (width : {0})" -f $imagewidth);
                }
                else {
                    Write-Log ("Image is too disproportionate (width/height ratio : {0})" -f ($imagewidth / $imageheight));
                }
            }
        }

        if ($notfound) {
            Write-Log "No images could be found...";
        }
        else {
            Write-Log "Setting lockscreen background";
            Set-LockscreenWallpaper -LockScreenImageValue $lockscreenImagePath;
        }

        $subIdx += 1
    }
}

function Set-LockscreenWallpaper {
    param(
        [string]$LockScreenImageValue
    )

    $RegKeyPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\PersonalizationCSP"
    $LockScreenPath = "LockScreenImagePath"
    $LockScreenStatus = "LockScreenImageStatus"
    $LockScreenUrl = "LockScreenImageUrl"
    $StatusValue = "1"

    if (([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
        if (!(Test-Path $RegKeyPath)) {
            New-Item -Path $RegKeyPath -Force | Out-Null

            New-ItemProperty -Path $RegKeyPath -Name $LockScreenStatus -Value $StatusValue -PropertyType DWORD -Force | Out-Null
            New-ItemProperty -Path $RegKeyPath -Name $LockScreenPath -Value $LockScreenImageValue -PropertyType STRING -Force | Out-Null
            New-ItemProperty -Path $RegKeyPath -Name $LockScreenUrl -Value $LockScreenImageValue -PropertyType STRING -Force | Out-Null
        }
        else {
            New-ItemProperty -Path $RegKeyPath -Name $LockScreenStatus -Value $value -PropertyType DWORD -Force | Out-Null
            New-ItemProperty -Path $RegKeyPath -Name $LockScreenPath -Value $LockScreenImageValue -PropertyType STRING -Force | Out-Null
            New-ItemProperty -Path $RegKeyPath -Name $LockScreenUrl -Value $LockScreenImageValue -PropertyType STRING -Force | Out-Null
        }

    }
    else {
        Write-Log ("Error: not running as admin, cannot set the registry");
    }
}

function Install-WarplanepornLockscreen {
    # check to see if user is admin
    if (([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {

        # get PS version (Core/Desktop)
        if ($PSVersionTable.PSEdition -eq "Core") { $PSExecutable = "pwsh.exe" }
        else { $PSExecutable = "powershell.exe" }

        # Create a task scheduler event
        $argument = "-WindowStyle Hidden -ExecutionPolicy Bypass -command `"WarplanepornLockscreen -noUpdateCheck`""
        $action = New-ScheduledTaskAction -id "WarplanepornLockscreen" -execute $PSExecutable -Argument $argument
        $settings = New-ScheduledTaskSettingsSet -StartWhenAvailable -RunOnlyIfNetworkAvailable -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -DontStopOnIdleEnd
        $trigger = New-ScheduledTaskTrigger -Daily -At 1am
        Write-Host "`nThe task that is about to be created needs an administrator password to work" -ForegroundColor Cyan
        $Credentials = Test-Credential
        if ($Credentials) {
            Write-Host "Username and password are correct" -ForegroundColor Green
            Write-Host "Unregistering any existing scheduled task" -ForegroundColor DarkYellow
            Unregister-ScheduledTask -TaskName "WarplanepornLockscreen" -ErrorAction SilentlyContinue -Confirm:$false
            Write-Host "Registering new task" -ForegroundColor DarkYellow
            Register-ScheduledTask `
                -TaskName "WarplanepornLockscreen" `
                -User $Credentials[0] `
                -Action $action `
                -Settings $settings `
                -Trigger $trigger -RunLevel Highest `
                -Password $Credentials[1] `
                -taskPath "\WarplanepornLockscreen\" | Out-Null
        }

        if ($? -and (Get-ScheduledTask -TaskName "WarplanepornLockscreen" -ErrorAction SilentlyContinue)) {
            Write-Log "WarplanepornLockscreen is installed" -colour "Green"
            Write-Host ""
        }

        # run user config
        Set-Config -ExecuteAfter -install

        # check execution policy
        $LMExecutionPolicy = (Get-ExecutionPolicy -Scope LocalMachine)
        $CUExecutionPolicy = (Get-ExecutionPolicy -Scope CurrentUser)
        $UnrestrictedExecutionPolicy = "Unrestricted"
        $UndefinedExecutionPolicy = "Undefined"
        if (-not ((($LMExecutionPolicy -eq $UnrestrictedExecutionPolicy) -and ($CUExecutionPolicy -eq $UndefinedExecutionPolicy)) -or ($CUExecutionPolicy -eq $UnrestrictedExecutionPolicy))) {
            Write-Host "`nWARNING: ExecutionPolicy is not unrestricted. The task will work but you will not be able to refresh the wallpaper manually" -ForegroundColor Red
            Write-Host "To enable execution of scripts, run " -NoNewline
            Write-Host "Set-ExecutionPolicy Unrestricted" -BackgroundColor DarkYellow -ForegroundColor Black -NoNewline
            Write-Host " as admin"
            Write-Host "See https://github.com/viccol961/warplaneporn-lockscreen#executionpolicy"
            Write-Host "See https://go.microsoft.com/fwlink/?LinkID=135170"
        }
    }
    else {
        Write-Host "You need run this script as an Admin to install it" -ForegroundColor Red
        exit
    }
}

function Uninstall-WarplanepornLockscreen {
    if (([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
        # remove registry key
        $RegKeyPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\PersonalizationCSP"
        Remove-Item -Path $RegKeyPath -Force -Recurse | Out-Null;
        if (Test-Path $RegKeyPath) {
            Write-Host "Failed to remove registry key" -ForegroundColor Red
        }
        else {
            Write-Host "Removed registry key" -ForegroundColor Green
        }

        # unregister task
        Unregister-ScheduledTask -TaskName "WarplanepornLockscreen" -ErrorAction SilentlyContinue -Confirm:$false | Out-Null;
        Get-ScheduledTask -TaskName "WarplanepornLockscreen" -ErrorAction SilentlyContinue -OutVariable task
        if ($task) {
            Write-Host "Failed to unregister task" -ForegroundColor Red
        }
        else {
            Write-Host "Unregistered task" -ForegroundColor Green
        }

        # remove ps module
        Remove-Item  $PSScriptRoot -Recurse -ErrorAction SilentlyContinue -Force | Out-Null;
        if (Test-Path $PSScriptRoot) {
            Write-Host "Could not automatically remove PowerShell module" -ForegroundColor Red
            Write-host "You may want to manually remove the module. Just delete the WarplanepornLockscreen folder." -ForegroundColor Cyan
            Start-Sleep 1
            Invoke-Item (Split-Path -Parent $PSScriptRoot);
        }
        else {
            Write-Host "Uninstalled module" -ForegroundColor Green
        }
    }
    else {
        Write-host "You need to run this script as admin to uninstall" -ForegroundColor Red
    }

}
function Test-Credential {
    # check password, allowing multiple attemps
    $retryPassword = $true;
    $usernameCorrect = $false;
    Add-Type -AssemblyName System.DirectoryServices.AccountManagement
    $DS = New-Object System.DirectoryServices.AccountManagement.PrincipalContext('machine', $env:COMPUTERNAME)

    while ((!$usernameCorrect) -and $retryPassword) {
        $username = Read-Host "Enter administrator username "
        $securePassword = Read-Host "Enter password for that user " -AsSecureString
        $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($securePassword)
        $password = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
        if ($DS.ValidateCredentials($username, $password)) {
            return ($username, $password)
        }
        else {
            Write-Warning "Username and/or password incorrect";
            $retryPassword = ((Read-Host "Try again (y/n) ?").ToLower() -eq "y")
        }
        if (!$retryPassword) {
            Write-Host "WARNING: The task creation will fail without administator password." -ForegroundColor Red
            Write-Host "You will still be able to run the utility to manually refresh the lock screen wallpaper`n" -ForegroundColor Red
            return $false
        }
        Start-Sleep -s 1
    }
}

function Write-Log {
    param (
        [string]$Msg,
        [string]$colour = "White"
    )

    $logfile = Join-Path $PSScriptRoot "log.txt"

    if (($null -ne $logfile)) {
        $date = Get-date -Format "dd/MM/yyyy HH:mm:ss"
        if (!(Test-Path $logfile)) { Set-Content $logfile "WarplanepornLockscreen log" }
        if ((get-item $logfile).length -gt 64kb) {
            $oldlog = (Get-Content $logfile)[-40..-1]
            Set-Content $logfile ("WarplanepornLockscreen log -- Trimmed {0}" -f $date)
            Add-Content $logfile $oldlog
        }
        Add-Content $logfile ("" + $date + "-> " + $msg)
    }

    Write-Host $Msg -ForegroundColor $colour
}

Set-Alias -Name WPP-LS -Value WarplanepornLockscreen
Export-ModuleMember -Alias "WPP-LS"