Da ich inzwischen das digitale Abo der taz  habe und selbiges nicht jeden Tag manuell runterladen will oder aus dem EMailpostfach fischen, habe ich mir ein WindowsPowershell Skript dafür gebaut, welches von Windows automatisch jeden morgen als geplanter Vorgang ausgeführt wird.

Den taz_loader wollte ich nicht nehmen. Der speichert seine Einstellungen im Programmverzeichnis,was in Windows ein absolutes no-go ist. Ausserdem speichert das Passwort im Klartext. Last, but not least war das eine gute Gelegenheit, ein paar neue Powershell 2.0 Features auszuprobieren.

Hier ist das Script:

<#
.SYNOPSIS
Lädt aktuelle Ausgabe der TAZ als PDF auf den Desktop.

.DESCRIPTION
Lädt aktuelle Ausgabe der TAZ als PDF auf den Desktop.
Benutzt Benutzername und Passwort wie sie verschlüsselt im Benutzerprofil des aktuellen Windows-Benutzers
gespeichert sind.

Wenn kein Benutzername und Passwort gespeichert ist, fragt download-taz danach und speichert sie 
ab. Das Passwort wird in einer Datei verschlüsselt gespeichert, sodaß nur der Windows-Benutzer, der es 
gespeichert hat, es auf diesem Computer wieder entschlüsseln kann.

.PARAMETER Date
Datum der TAZ Ausgabe, die runtergeladen wird. Lädt die heutige Ausgabe, wenn nichts angegeben wird.

.PARAMETER NewCredentials
Gespeicherter Username und Passwort werden ignoriert, nach neuen gefragt und diese wieder abgespeichert.

.PARAMETER ShowProgress
Downloadfortschritt anzeigen. Hinweis: Funktioniert nur, wenn der Webseite die Gesamtgröße der 
Datei liefert

.PARAMETER DeleteOldIssues
Verschiebt alte Ausgaben vom Desktop in den Papierkorb

.INPUTS
Keine. Download-Taz akzeptiert keine Pipeline-Objekte 

.OUTPUTS
Keine.

.EXAMPLE
PS> download-taz
Lädt die TAZ-Ausgabe von heute als PDF auf den Desktop. 

.EXAMPLE
PS> download-taz "08/21/09"
Lädt die TAZ-Ausgabe vom 21. August 2009 als PDF auf den Desktop. 

.EXAMPLE
PS> download-taz -DeleteOldIssues 
Lädt die TAZ-Ausgabe von heute als PDF auf den Desktop und löscht alte Ausgaben.


.EXAMPLE
PS> download-taz -NewCredentials
Ignoriert gespeicherten Benutzernamen und Passwort, fragt nach neuen, speichert diese und lädt die 
heutige Ausgabe auf den Desktop.

.NOTES 
Zum Speichern von Benutzername und Passwort benutzt download-taz das Script export-pscredential 
von http://poshcode.org/?show=474

Mehr Informationen über das TAZ digital Abonnement gibt es hier: http://www.taz.de/digitaz/.digiabo
#>
[CmdletBinding(SupportsShouldProcess=$true)]
param([DateTime]$Date=(get-date),
      [switch]$newCredentials,
      [switch]$ShowProgress=$false,
      [switch]$DeleteOldIssues=$false)

# Lade export-pscredential script, welches die Funktionen export/import-pscredential zur Verfügung stellt.
. Export-pscredential.ps1

$webClient = new-object System.Net.WebClient

try
{
    $CredentialsPath = join-path ([System.Environment]::GetFolderPath("ApplicationData")) "Download-TAZ\credentials.txt"

    $newCredentials = -not (test-path $CredentialsPath)

    if ($newCredentials)
    {
        if ($PSCmdLet.ShouldProcess("Benutzername/Password","Abfragen und speichern"))
        {
            $c = get-credential
            [System.IO.Directory]::CreateDirectory((split-path $CredentialsPath -parent))
            export-pscredential -Credential $c -Path $CredentialsPath
        }
        else
        {
            return
        }
    }
    else
    {
        $c = import-pscredential -Path $CredentialsPath
    }

    $webclient.Credentials = $c

    $ProgressAction = {
        write-progress -Activity "Download TAZ" -Status "Runterladen" -PercentComplete $Eventargs.ProgressPercentage
    }

    if ($ShowProgress)
    {
        # Lösche alte Event-Subscription bevor neuer Eventhandler registriert wird.
        get-eventsubscriber | where { $_.EventName -eq "DownloadProgressChanged"} | foreach-object { unregister-event -SubscriptionID $_.SubscriptionID }
        Register-objectevent -EventName "DownloadProgressChanged" -InputObject $webclient -Action $ProgressAction | Out-Null
    }
    
    # Da die Download-Funktion asynchron aufgerufen wird, warten wir auf das DownloadFileCompleted event
    get-eventsubscriber | where { $_.EventName -eq "DownloadFileCompleted"} | foreach-object { unregister-event -SubscriptionID $_.SubscriptionID }
    Register-objectevent -EventName "DownloadFileCompleted" -InputObject $webclient -SourceIdentifier "DownloadTAZ.Completed" | Out-Null

    $url = "http://www.taz.de/taz/abo/get.php?f={0:yyyy_MM_dd}.pdf" -f $Date

    # Zielpfad für das runtergeladene PDF
    $LocalPath = [System.Environment]::GetFolderPath("Desktop")
    $filename = "TAZ_{0:yyyy_MM_dd}.pdf" -f $Date
    $localfilename = join-path $LocalPath $filename

    if ($DeleteOldIssues)
    {
        ls (join-path $LocalPath "\*") -include "TAZ_????_??_??.pdf" -exclude $Filename | foreach-object {
            if ($PSCmdlet.ShouldProcess("$_", "In den Papierkorb verschieben"))
            {
                $shell = new-object -comobject "Shell.Application"
                $item = $shell.Namespace(0).ParseName($_.Fullname)
                $item.InvokeVerb("delete")
            }
        }
    }
    
    if (test-path $localfilename)
    {
        # Angeforderte TAZ-Ausgabe schon vorhanden. Nichts zu tun
        return
    }
    
    if ($PSCmdlet.ShouldProcess("$url", "Nach $localfilename runterladen"))
    {
        # Sicherstellen, das kein altes Event in der Wartschlange steckt
        remove-event -SourceIdentifier "DownloadTAZ.Completed" -erroraction SilentlyContinue

        # Download asynchron starten. Das erlaubt uns, auf das DownloadProgress Event zu reagieren
        $Webclient.DownloadFileAsync($url, $localfilename)
        
        # Warten bis Download fertig ist. Danach Event aus der Warteschlange löschen
        wait-event -SourceId "DownloadTAZ.Completed" | Out-Null
        remove-event -SourceId "DownloadTAZ.Completed"
        $webclient = $null
    }
}
finally
{
    # Aufräumen
    if ($webClient)
    {
        $webClient.CancelAsync()
    }
    
    get-eventsubscriber | where { $_.EventName -eq "DownloadProgressChanged"} | foreach-object { unregister-event -SubscriptionID $_.SubscriptionID -whatif:$false -confirm:$false }
    get-eventsubscriber | where { $_.EventName -eq "DownloadFileCompleted"} | foreach-object { unregister-event -SubscriptionID $_.SubscriptionID -whatif:$false -confirm:$false }
}

Leider unterstützt die Sourcecode-Anzeige von wordpress.com die Powershell-Syntax nicht, sodass die Farbdarstellung des Codes eher suboptimal  ist.

Das download-taz Script läuft nur mit Powershell v2.0. Neu war für mich in diesem Script die Verwendung von Events. Man sieht in den Zeilen 94 bis 103, wie zunächst ein Scriptblock definiert wird, der dann via register-objectevent als Eventhandler für das DownloadProgressChanged-Event des Webclient-Objekts registriert wird. Die Idee war, beim Runterladen den Fortschritt mit Hilfe des write-progress CmdLets anzuzeigen. Als ich es fertig hatte, musste ich leider feststellen, das der TAZ-Webserver die Dateigröße nicht mitliefert, die aber für die prozentuale Anzeige von write-progress nötig ist. Ich hätte mir den Aufwand also sparen können. Immerhin hab ich gelernt, wie man Powershell-Events im Zusammenhang mit .NET Objekten verwendet.

Schon öfter benutzt, aber nach wie vor sehr nützlich ist die automatisch aus den Kommentaren generierte Hilfe. Die lässt sich einfach mit

download-taz –?

oder

get-help download-taz

anzeigen.

Das Speichern von Benutzername und Passwort hatte ich erst angefangen, auf Basis der .NET SecureString-Klasse selber zu implementieren, hab dann aber genau diese Funktion hier gefunden. SecureString verschlüsselt den Inhalt sodaß nur der aktuelle Windows-Benutzer auf dem aktuell verwendeten Computer ihn entschlüsseln kann. Das so verschlüsselte Passwort wird in einer Datei gespeichert, die im Benutzerprofil-Bereich des aktuellen Windows-Benutzers liegt. Damit könnte zwar immer noch ein Schadprogramm, welches mit meinen Benutzerrechten läuft, die Datei lesen und das Passwort verschlüsseln, aber besser als ein Klartext-Passwort ist das allemal. Und wenn ein Schadprogramm auf meinem Rechner laufen kann, das ist das Digiztaz-Passwort das kleinste Problem.

Jetzt benutze ich Vista schon seit der ersten Vorabversion, die offiziell zu bekommen war (und bin inzwischen auf Win7 gewechselt), aber dieses Feature kannte ich noch nicht:

Wenn man den sich gerade einschaltenden Screensaver z.B. durch eine Mausbewegung sofort wieder ausschaltet und das zweimal hintereinander macht, verdoppelt Windows automatisch den Timeout. Beispiel:

  1. Screensaver schaltet sich nach 5 Minuten ein.
  2. Man bewegt die Maus oder drückt eine Taste um in sofort nach dem Einschalten wieder loszuwerden, vielleicht weil man gerade was längeres liest.
  3. Nach weiteren 5 Minuten schaltet er sich wieder ein und man schließt ihn sofort wieder.
  4. Jetzt schaltet Windows ihn erst nach der doppelten Zeit (hier 10 Minuten) ein.
  5. Schließt man ihn beim nächsten Einschalten (nach 10 Minuten) wieder sofort, verdoppelt sich der Timeout auf 20 Minuten

Nett. Laut Raymond Chen lässt sich das ausschalten. In Windows 7 fehlt die von ihm erwähnte Option “Adaptive Display” in den Energiespar-Einstellungen. Die entsprechende Gruppenrichtlinie gibt es allerdings.

Heute konnte man für wenige Minuten Windows 7 Home Premium für 49,95€ vorbestellen. Hätte es zwar zeitlich geschafft, aber ist leider wenig attraktiv. Ich schätze es sehr, das die Schrifterkennung meines Tablet-PCs sich von Deutsch auf Englisch umschalten lässt. Das geht aber nur bei Windows 7 Ultimate. Bitlocker fehlt bei Home Premium auch.

zu Hause fürs Mediacenter reicht Home Premium auch nicht, weil man sich nicht per Remote Desktop einloggen kann, was hin und wieder mal nützlich ist zum administrieren.

Nicht mal die vergünstigte Home Premium für 49,95€ und ein späteres “Anytime Upgrade” auf Ultimate lohnt. Da ist die Systembuilder-Version von Windows 7 Ultimate günstiger, geht man davon aus, das der Preis der OEM-Version in etwa dem Vista-Preis entspricht.

Schade.

File in use

28. Juni 2009

Das ist nett. Anstatt, wie in älteren Windows-Versionen nur zu sagen, das eine Datei in Benutzung ist, sagt Windows 7 auch, wer den Finger drauf hat:

image

Praktisch. Da braucht man nicht mehr umständlich mit dem Process-Explorer nachschauen, wer der Übeltäter ist.