Digitales taz Abo via Powershell
22. August 2009
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.
SupportsShouldProcess=$true
27. Juni 2009
Mit der neuen Windows Powershell Version 2.0 kann man sehr einfach die Standardparameter -whatif und -confirm in eigenen Funktionen benutzen:
|
001
002 003 004 005 006 007 008 009 010 |
function do-something
{ [CmdletBinding(supportsShouldProcess=$true)] param($p) if ($pscmdlet.shouldProcess("$p", "do-something")) |
Mit der Deklaration in Zeile 3 akzeptiert die Funktion automatisch –whatif und –confirm:
PS> do-something "dangerous" -whatif
What if: Performing operation "do-something" on Target "dangerous".
PS> do-something "dangerous" -confirm
Confirm
Are you sure you want to perform this action?
Performing operation "do-something" on Target "dangerous".
[Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "Y"): y
do-something dangerous
Sehr praktisch.