Liste der installierten Programme per Powershell auslesen

imageNach langer Pause hier wiedermal ein nützlicher “Powershell-Einzeiler”

Habe einen neuen Laptop bekommen, diesen natürlich neu aufgesetzt, und jetzt benötige ich eine Liste der Programm vom alten Laptop, um diese durchzusehen, zu filtern, und dann natürlich einige der Programme auch am neuen Laptop wieder zu installieren.

Screenshots aus der Systemsteuerung “Programme und Funktionen” sind etwas mühsam, und auch einem Geek nicht würdig Zwinkerndes Smiley

Daher mal schnell ein Powershell Script gebaut, wie es sich gehört, natürlich als Einzeiler:

1 get-wmiobject -class "Win32_Product" -namespace "root\CIMV2" -computername "." | format-list Vendor, Name, Caption, Description, Version, InstallDate, HelpLink

Ergebnis:

image

Oder, falls wir die Liste in Excel noch bearbeiten oder filtern möchten, senden wir das ganze gleich direkt in ein CSV File:

1 get-wmiobject -class "Win32_Product" -namespace "root\CIMV2" -computername "." | select-object Vendor, Name, Version, InstallDate, HelpLink | Export-Csv -path c:\installierte_programme.csv -UseCulture

image

Per Powershell Skript die Priorität von Prozessen verändern

Vor zwei Jahren habe ich ein WSH Skript vorgestellt, mit dem man die Priorität von Prozessen verändert.

Das Skript hat selbst in seiner Kurzform 18 Zeilen, wobei man es sicher noch kürzen könnte.

War ich früher ein großer Fan von WSH gilt meine Vorliebe heute Powershell und den wunderbaren Powershell Einzeilern die so manchem Admin das Leben erleichtern.

So läßt sich das ganze Skript in Powershell in einer Zeile zusammenfassen:

gps <ProcessName>| % { $_.PriorityClass = "High" }

Dabei ist gps die Kurzform von get-process und % die Kurzform von foreach.

Also man könnte es auch so schreiben:

Get-Process <ProcessName>| foreach { $_.PriorityClass = "High" }

Am Beispiel von Internet Explorer würde das also so aussehen:

gps iexplore | % { $_.PriorityClass = "High" }

Falls man sich die Prozess Priority Klassen nicht merken kann, einfach einen ungültigen Parameter angeben, und Powershell liefert in der Fehlermeldung die Liste der richtigen Parameter:

image

Mögliche Enumerationswerte sind "Normal, Idle, High, RealTime, BelowNormal, AboveNormal"

Windows Product Key per Powershell auslesen

GetProductKey.ps1

$map="BCDFGHJKMPQRTVWXY2346789"
$value = (get-itemproperty "HKLM:\\SOFTWARE\Microsoft\Windows NT\CurrentVersion"
).digitalproductid[0x34..0x42] 
$ProductKey = "" 
for ($i = 24; $i -ge 0; $i--
) {
  $r =
0
  for ($j = 14; $j -ge 0; $j--
) {
    $r = ($r * 256) -bxor $value[$j
]
    $value[$j] = [math]::Floor([double]($r/
24))
    $r = $r %
24
  }
  $ProductKey = $map[$r] +
$ProductKey
  if (($i % 5) -eq 0 -and $i -ne
0) {
    $ProductKey = "-" +
$ProductKey
  }
}
echo "Product Key:" $ProductKey

Powershell Einzeiler: Leistungsindex auslesen

 

get-wmiobject win32_winsat | select *score


PowerShell v2 Youtube Video Downloader

PowerShell v2 Youtube Video DownloaderThis script requires PowerShell v2 in it’s final version (as included in Windows 7 RTM).

This script will download any Youtube video in it’s best available quality as an MP4 file.

Syntax: GetYouTube Video-ID

Example: GetYouTube irp8CNj9qBI

Of course, one can also send a list of video ids to the script via a prepared text file, like so:

foreach ($video in get-content list.txt) { getyoutube.ps1 $video  } 

Here is the script GetYouTube.ps1:

Import-Module bitstransfer
$ErrorActionPreference = "SilentlyContinue"
$v=$args[0]
#$v="irp8CNj9qBI"
#Grab Youtube Page
$s=(New-Object System.Net.WebClient).DownloadString("http://www.youtube.com/watch?v=" + $v)
#extract token
$t=$s | % {$_.substring($_.IndexOf("`"t`": `"")+6,44)}
#extract title
$r="<title\b[^>]*>YouTube - (.*?)</title>" ; $f = $s | ?{ $_ -match $r } |  %{ $_ -match $r | out-null ; $matches[1] }
#Try downloading 720p version of video
$u = "http://www.youtube.com/get_video?fmt=22&video_id=" + $v + "&t=" + $t
$o = $Env:Userprofile + "\Videos\" + $f + "(HQ).mp4"
Start-BitsTransfer –source $u -destination $o
#Try downloading regular mp4 version of video, if HQ version failed
if (!$?) {
$u = "http://www.youtube.com/get_video?fmt=18&video_id=" + $v + "&t=" + $t
$o = $Env:Userprofile + "\Videos\" + $f + ".mp4"
Start-BitsTransfer –source $u -destination $o
}

Powershell Einzeiler: Anzahl der Prozesse, Programme oder Fenster

get-process
gibt mir eine Liste aller Prozesse

([Object[]](Get-Process)).Count
gibt mir die Anzahl aller Prozesse

Eine Liste aller Fenster – d.h. Prozesse die auch über ein Fenster verfügen bekomme ich so:

get-process | where {$_.mainWindowTitle} | format-table id, name, mainwindowtitle -autosize

Und so bekomme die Anzahl der offenen Fenster:

([Object[]](Get-Process  | where {$_.mainWindowTitle})).Count

Powershell Einzeiler: Einzelnen, internen SMTP Server zur Exchange Konfiguration hinzufügen

$a = (Get-TransportConfig).InternalSMTPServers ; $a += "192.168.10.5" ; Set-TransportConfig -InternalSMTPServers $a

“Wie kriegt man einen Browser ohne Browser?” - Windows 7 ohne IE8

In Europa will Microsoft ja Windows 7 ohne Internet Explorer ausliefern, und immer wieder kommt die Frage “Ja wie kann ich denn einen Browser ‘runterladen ohne Browser?”

Also erstens denke ich, dass man den IE8 als optionales Update über Windows Update bekommen wird, zweitens denke ich, dass jeder OEM einen Browser nachrüsten wird, bevor er Windows 7 ausliefert und drittens gibt’s ja noch Powershell v2 ;-)

Einfach Powershell öffnen und diesen handlichen Einzeiler eintippen:

Import-Module bitstransfer ; Start-BitsTransfer –source "http://hyperion.zih.tu-dresden.de/moz/firefox/releases/3.0.11/win32/de/Firefox%20Setup%203.0.11.exe" -destination "C:\Users\Public\Downloads\"

oder

Import-Module bitstransfer ; Start-BitsTransfer -source "http://download.microsoft.com/download/3/D/C/3DC5DC1B-2B60-487A-BAE2-732662BC0886/IE8-WindowsVista-x86-DEU.exe" -destination "C:\Users\Public\Downloads\"

;-) 

Okay das war jetzt nicht 100% ernst gemeint, aber wieso nicht, gegebenenfalls noch die Download URL ausbessern (Sprache, Architektur etc.)

Apropos – Das Powershell v2 Modul Filetransfer, dass ich schon in meinem Beitrag “Podcatcher mit Powershell v2 & BITS” beschrieben hatte, wurde umbenannt in Bitstransfer und verwendet eine deutlich andere Syntax als in früheren Betaversionen von Powershell v2.

Windows Services (Dienste) mittels Powershell starten

Habe mir in Windows Powershell einen netten Einzeiler zurechtgelegt, der mir automatisch alle Dienste startet, die eigentlich automatisch starten sollten, aber (aus welchem Grund auch immer) nicht laufen.
Auf einigen meiner Server kommt es immer mal wieder vor, das Services nicht laufen, die eigentlich laufen sollen. Mit diesem Powershell Skript ist das Problem schnell behoben:

Get-WmiObject Win32_Service | where {$_.StartMode -eq "Auto"} | where {$_.State -eq "Stopped"} | Start-Service

Podcatcher mit Powershell v2 & BITS

Vor kurzem habe ich einen Podcatcher mit Powershell und BITS (“Background Intelligent Transfer Service”) geschrieben, für den man aber zusätzlich noch bitsadmin.exe und bget.cmd benötigt.
Da bitsadmin.exe eher mühsam und bget.cmd zwar nett aber etwas buggy ist, bin ich gleich auf die schon damals angeführte Möglichkeit umgestiegen, Powershell v2 (CTP 3) zu verwenden – damit braucht man nämlich keine zusätzlichen Tools mehr.

Das ganze funktioniert bisher recht robust – ich möchte nur noch eine (visuelle) Statusanzeige dazu programmieren.

param ([switch] $schedule)
#Set-PSDebug -Trace 1
Import-Module FileTransfer -Verbose

$today = get-date
$yesterday = $today.AddDays(-1)
$today = Get-Date -date $today -uformat "%Y%m%d"
$yesterday = Get-Date -date $yesterday -uformat "%Y%m%d"
$today = $today.tostring()
$yesterday = $yesterday.tostring()

if ($schedule) {
$taskname = $myinvocation.mycommand.definition -replace '\W','_'
schtasks /create /ru PIVO9\Christian /rl HÖCHSTE /sc MINUTE /MO 5 /tn "$taskname" /tr "%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell -c $($myinvocation.mycommand.definition)"
return;
}

pushd (split-path -parent $myinvocation.mycommand.definition)

$feed=[xml](New-Object System.Net.WebClient).DownloadString("http://***.*****.net/mythfeed/feed.php")
foreach($i in $feed.rss.channel.item) {
    $url = New-Object System.Uri($i.enclosure.url)
    $absURL = $url.AbsoluteUri   
    $filename = "C:\Users\Public\Videos\ElskeTV\" + $url.Segments[-1]
    If ((Test-Path $filename) -eq $FALSE) 
    {
echo ----------------------------------------------------------------------------------------------------
echo $url.Segments[-1]

$joblist = Get-FileTransfer
$jobexist = $false

foreach ($i in $joblist) {

foreach ($j in $i.filelist) {
if ($j.IsTransferComplete) { Complete-FileTransfer $i }
if ($j.RemoteName -eq $absURL) {
echo "Job wurde gefunden..."
if ($j.BytesTotal -gt 0) { "{0:P1}" -f  ($j.BytesTransferred / $j.BytesTotal) }
$i.JobState
$i.CreationTime
$jobexist = $true
if ($j.RemoteName -match $today) {$i | Set-FileTransfer -Priority "FOREGROUND" }
if ($j.RemoteName -match $yesterday) {$i | Set-FileTransfer -Priority "FOREGROUND" }
}
}}

if ($jobexist -eq $false) {
New-FileTransfer -ServerFileName $absURL -ClientFileNamePrefix C:\users\Public\videos\ElskeTV\ -Displayname $url.Segments[-1] -Asynchronous
Echo "Neuer Download-Job wurde hinzugefügt!"
}

    }
}

echo ----------------------------------------------------------------------------------------------------

popd

#Fortschritt in Prozent der einzelnen Downloads
#$joblist = Get-FileTransfer ; foreach ($i in $joblist) { foreach ($j in $i.filelist) { if ($j.BytesTotal -gt 0) { "{0:P1}" -f  ($j.BytesTransferred / $j.BytesTotal) }}}

#Summe aller übertragenen Bytes aller Downloadjobs
#$joblist = Get-FileTransfer ; $total = 0 ; foreach ($i in $joblist) { foreach ($j in $i.filelist) { if ($j.BytesTotal -gt 0) {$total = $total + $j.BytesTransferred}}} ; $total ; get-date

#Downloadjobs auflisten, die in den letzten 10 Minuten erstellt wurden
#get-FileTransfer | Where {$(Get-Date).AddMinutes(-10) -lt $_.CreationTime}

#Dateien auflisten mit einem bestimmten Datum im Dateinamen
#$joblist = Get-FileTransfer ; foreach ($i in $joblist) { foreach ($j in $i.filelist) { if ($j.RemoteName -match "20090418") {$i | fl * } }}

#Priority hochsetzen für Downloads mit einem bestimmten Datum im Dateinamen
#$joblist = Get-FileTransfer ; foreach ($i in $joblist) { foreach ($j in $i.filelist) { if ($j.RemoteName -match "20090418") {$i | Set-FileTransfer -Priority "FOREGROUND" } }}

 

 

Message Size Limits in Exchange 2007 setzen

In Exchange Server 2007 können bzw. müssen Nachrichtengrößen an verschiedenen Stellen gesetzt werden.

Einerseits in der Transport-Config, andererseits auf den Send- und Receive Konnektoren.

In Powershell kann ich sämtliche aktuellen Limits so abfragen:

get-transportconfig | select identity,maxreceivesize,maxsendsize
get-receiveconnector | select identity,maxmessagesize
get-sendconnector | select identity,maxmessagesize
get-mailbox | select name,maxreceivesize,maxsendsize
get-mailcontact | select name,maxreceivesize,maxsendsize
get-mailPublicFolder | select name,maxreceivesize,maxsendsize
get-distributiongroup | select name,maxreceivesize,maxsendsize

So setze ich das Sende Limit für einen bestimmten Sendconnector:

set-sendconnector –identity "Name des Send Connectors" -maxmessagesize 50MB

image

So setze ich es für alle Send-Konnektoren (Vorsicht! Ist das wirklich erwünscht?)

get-sendconnector | set-sendconnector -maxmessagesize 50MB

image

 

Genau das gleiche kann ich mit den Receive Konnektoren machen:

set-receiveconnector –identity "Name des Receive Connectors" -maxmessagesize 50MB

oder für alle:

get-receiveconnector | set-receiveconnector -maxmessagesize 50MB

image

Und jetzt noch für die Transport Config:

get-transportconfig | set-transportconfig -maxreceivesize 50MB -maxsendsize 50MB

image

 

Weiters gibt es User-Limits, die man aber explizit gesetzt haben muss (Standard ist “Unlimited”)

Man kann alle weiteren Limits auf Mailboxen, Kontakten, Öffentlichen Ordern und Verteilerlisten überprüfen:

get-mailbox | select name,maxreceivesize,maxsendsize
get-mailcontact | select name,maxreceivesize,maxsendsize
get-mailPublicFolder | select name,maxreceivesize,maxsendsize
get-distributiongroup | select name,maxreceivesize,maxsendsize

Dann kann man natürlich mit den entsprechenden Befehlen die Limits wieder ändern, ich zeige es jetzt hier nur am Beispiel einer Mailbox:

Für eine bestimmte Mailbox:

get-mailbox -Identity ChristianHaberl | set-mailbox -maxreceivesize 50MB -maxsendsize 50MB

oder für alle Mailboxen:

get-mailbox | set-mailbox -maxreceivesize 50MB -maxsendsize 50MB

oder alle Mailbox Limits entfernen:

get-mailbox | set-mailbox -maxreceivesize unlimited -maxsendsize unlimited

Podcatcher mittels Powershell und BITS

Das BITS (“Background Intelligent Transfer Service”) ist ein Windows Dienst der sich um den Download von (größeren) Dateien ressourcen und netzwerkschonend im Hintergrund kümmert. Da er als Service läuft und auch Resumes unterstützt, kann auch ein Reboot einem mit BITS initiierten Download nichts anhaben. Ursprünglich nur für Windows Updates gedacht, kann man BITS mit dem Tool bitsadmin.exe auch für andere/eigene Downloads nutzen.
Bitsadmin.exe bekommt man bei älteren Windows Versionen über die Windows Support Tools, ab Windows Vista ist es bereits enthalten.
Da bitsadmin.exe nicht besonders userfreundlich ist, verwende ich bget.cmd zur Steuerung von BITS download jobs.
Ich habe einige Video Podcasts (mit US Fernsehsendungen), die ich regelmäßig herunterladen möchte, und mit allen verfügbaren Podcatchern für Windows (inkl. iTunes) bin ich mehr als unzufrieden. Daher lag die Idee nahe, einen eigenen Podcatcher mit Powershell zu schreiben.

Nichts leichter als das!

Man nehme:

  • Powershell 1.0
  • bitsadmin.exe
  • bget.cmd (hier)

podcatcher.ps1

$feed=[xml](New-Object System.Net.WebClient).DownloadString("http://podcast.msnbc.com/audio/podcast/MSNBC-NN-NETCAST-M4V.xml")
foreach($i in $feed.rss.channel.item) {
    $url = New-Object System.Uri($i.enclosure.url)
   
$absURL = $url.AbsoluteUri   
    $filename = "D:\Videos\" + $url.Segments[-1]
    If ((Test-Path $filename) -eq $FALSE) 
    {
    bget """$absURL""" """$filename""" –b
    }
}

Zuerst hole ich mir das XML File des RSS Feed herunter. Dann loope ich durch alle items, und lasse mir die enclosure URLs zurückgeben. Als Objekt vom Typ System.Uri kann ich hier unter anderem auf die Parameter AbsoluteUri (ganze URL, bereits urlencoded, falls das im Feed nicht der Fall war) und segments[-1], welcher mir nur den Dateinamen zurückgibt, zugreifen.
Schließlich stelle ich noch sicher, dass das File im Zielpfad nicht existiert, und dann übergebe ich URL, Filename und den Batch Parameter “-b” an bget.
BITS erstellt das File im Zielordner erst, wenn es fertig heruntergeladen ist. Das ist auch praktisch im Vergleich zu anderen Podcatchern, so landen keine unfertigen Videos im Video-Ordner.
BITS und bget kümmern sich auch von selbst darum, dass ein File dass bereits heruntergeladen wird, nicht nochmal heruntergeladen wird (anhand der URL).
So muß ich podcatcher.ps1 nur mehr in der Aufgabenplanung regelmäßig ausführen, und ich habe einen perfekten podcatcher der absolut robust ist und mir nicht mehr ein Verzeichnis mit halbfertig heruntergeladenen Video Files hinterläßt, so wie seine Vorgänger.

Übrigens: Schön, wieder mal sagen zu können “In Windows 7 wird alles besser” – Genauer gesagt in PowerShell 2.0, das in Windows 7 und Windows Server 2008 R2 bereits enthalten sein wird. Da brauchen wir dann nämlich weder bitsadmin.exe noch bget.cmd sondern können BITS komplett mittels Powershell Cmdlets (z.B. 'New-FileTransfer') steuern. Mehr dazu hier -  die Syntax ist aber noch nicht ganz final. Wenn Powershell 2.0 final ist, werde ich dieses Skript umschreiben.

Meine Exchange 2007 IPBlocklist-Provider

Hier meine Exchange 2007 IPBlocklist-Provider (DNSBL/RBL) Liste, wie ich sie über die letzten 2 Jahre durch ständiges überwachen, testen und fein-tunen herausbekommen habe. Listen mit zu vielen False-Positives habe ich nur ausgeblendet (-Enabled $false).
Um nachzusehen ob und auf welchen DNSBL Listen eine Adresse ist, eignet sich recht gut dieser Dienst: http://www.kloth.net/services/dnsbl.php
Mit dem Powershell Skript Get-AntispamTopRBLProviders.ps1 kann man den Erfolg der einzelnen RBL Anbieter testen, wobei hier natürlich die Priorität (Reihenfolge) der einzelnen Anbieter das Ergebnis verfälscht:
 image

Hier meine Konfiguration als Powershell Skript, geeignet zum Importieren auf weitere Server:

Add-IPBlockListProvider -Name:'Spamhaus' -LookupDomain:sbl.spamhaus.org -Priority:1 -AnyMatch $True -Enabled $False
Add-IPBlockListProvider -Name:'Spamcop' -LookupDomain:bl.spamcop.net -Priority:2 -AnyMatch $True -Enabled $True
Add-IPBlockListProvider -Name:'SORBS' -LookupDomain:dnsbl.sorbs.net -Priority:4 -AnyMatch $True -Enabled $False
Add-IPBlockListProvider -Name:'UCEprotect Level 1' -LookupDomain:dnsbl-1.uceprotect.net -Priority:5 -AnyMatch $True -Enabled $False
Add-IPBlockListProvider -Name:'NJABL Combined' -LookupDomain:combined.njabl.org -Priority:6 -AnyMatch $True -Enabled $False
Add-IPBlockListProvider -Name:'NJABL' -LookupDomain:dnsbl.njabl.org -IPAddressesMatch '127.0.0.2','127.0.0.4','127.0.0.5','127.0.0.6','127.0.0.8','127.0.0.9' -Priority:7 -AnyMatch $False -Enabled $True
Add-IPBlockListProvider -Name:'no-more-funn.moensted.dk' -LookupDomain:no-more-funn.moensted.dk -Priority:9 -AnyMatch $True -Enabled $False
Add-IPBlockListProvider -Name:'tqmcube.com' -LookupDomain:dnsbl.tqmcube.com -Priority:10 -AnyMatch $True -Enabled $False
Add-IPBlockListProvider -Name:'l2.apews.dnsbl.uceprotect.net' -LookupDomain:l2.apews.dnsbl.uceprotect.net -Priority:11 -AnyMatch $True -Enabled $False
Add-IPBlockListProvider -Name:'Manitu.net' -LookupDomain:ix.dnsbl.manitu.net -Priority:12 -AnyMatch $True -Enabled $True
Add-IPBlockListProvider -Name:'cbl.abuseat.org' -LookupDomain:cbl.abuseat.org -Priority:13 -AnyMatch $True -Enabled $True

Anbieter für geblockte IP-Adressen IP Block List Providers

EDIT 8.7.2009:

Natürlich reicht es, die 4 Listen, die ich tatsächlich aktiviert habe (“-Enabled $True” - grün) einzutragen, die anderen, deaktivierten IP Block List Providers habe ich nur zu Testzwecken in meiner Konfiguration.

Ob die Reihenfolge ideal ist, lässt sich schwer testen, da der zweit-, dritt- und viertgereihte IP Blocklist Provider natürlich nicht dran kommt, wenn schon der erstgereihte einen Match liefert.

Spamhaus stehe ich übrigens sehr kritisch gegenüber seitdem sie nic.at geblockt haben als “Strafe” dafür, dass nic.at eine Domain nicht sperren wollte, von der gespammt wurde (was weder nic.at’s Aufgabe ist, noch es eine rechtliche Handhabe gäbe)

Andere Listen, wie dnsbl-2.uceprotect.net und dnsbl-3.uceprotect.net oder dnsbl.sorbs.net/127.0.0.10 sowie blackholes.five-ten-sg.com würde ich gerne zusätzlich verwenden allerdings nicht “absolut” – sondern nur in Kombination mit anderen Anti-spam Filtern wie dem Content Filter. Würde man diese Listen absolut verwenden, käme es massenhaft zu False Positives da diese Listen entweder ganze Netzwerke listen, oder alle dynamischen IP Adressen (was man keinesfalls blocken darf, denn es ist grundsätzlich nicht verboten auf dynamischen IP Adressen Mail Server zu betreiben, selbst wenn vermutlich 99% der Leute die sowas tun, Spammer sind.

Ich bin gerade auf der Suche nach einem C# Developer der mir hilft einen Exchange Transport Agent zu schreiben, der genau das tut, nämlich nach dem Content Filtering bestimmte DNSBL Listen auswerten und bei einem Match den SCL Wert erhöhen.

Facebook

Letzte Tweets

Twitter Mai 26, 23:39
Gelbe Engel! Danke!

Twitter Mai 18, 21:34
Finland was terrible. #Eurovision

Twitter Mai 17, 12:49
just blogged Ist das Surface Pro teuer? – Ein Fact-Check: Gestern wurde angekündigt, dass das Surface Pro in D... http://t.co/PhW7qzhkUq

Folge mir auf Twitter!

Über den Autor

MCTS

Christian Haberl Christian Haberl ist seit mehr als 10 Jahren als EDV Berater, Vortragender und Trainer tätig. Er kann sich nicht für ein Spezialgebiet entscheiden, drum heißt dieser Blog auch "Kraut & Rüben Blog" - Unter seine Interessen fallen Web-Entwicklung auf ASP.NET Basis, Information Worker & Productivity Technologien (Windows, Office), Server (Windows Server, Small Business, Virtualisierung, Exchange), Scripting, Spam Filtering/Security und Digital Home. Christian Haberl ist auch einer der führenden Produktspezialisten für Windows Media Center und Windows Home Server und ist Direktor des ClubDigitalHome.
Im Jahr 2008 hat Christian Haberl über 200 Vorträge und Schulungen durchgeführt.
Im Frühjahr 2009 wurde Christian Haberl von Microsoft Österreich zum "Influencer" ernannt, weiters wurde er Microsoft Certified Technology Specialist / Microsoft Certified Connected Home Integrator sowie Microsoft Certified Consumer Sales Specialist.

Month List

Netzwerk Management

Bandwidth

RecentComments

Comment RSS