Windows Home Server 2011 auf dem Lenovo D400

22 09 2011

Ich habe sehr gute Erfahrungen mit meinem Lenovo D400 Windows Home Server gemacht, der bei mir als NAS und Backup-System dient. Als NAS leistet er gute Dienste, weil er Daten mit über 50MB/s anliefern kann und sich vor allem mit dem Add-In LightsOut automatisch in den Stand-By begibt, wenn kein Rechner an ist und auch wieder aufwacht, wenn es was zu tun gibt. 30W rund um die Uhr mitlaufen lassen ist nicht so nach meinem Geschmack.

Viel besser aber ist die automatische Datensicherung meiner Windows-Rechner. Täglich wird im Hintergrund eine Datensicherung gemacht, die sogar einstellbar weit in die Vergangenheit zurückversioniert. Versehentlich irgendwelche wichtigen Daten gelöscht, fällt aber erst zwei Monate später auf? Kein Problem, einfach eine der älteren Sicherungen öffnen und die Daten wieder rausfischen. Das ganze ist sogar recht Datensparsam, weil identische Daten nur einmal gespeichert werden. Fünf Rechner mit Windows 7 mit je 20 Versionierungsständen sichern die identischen Systemdateien also nur einmal und nicht hundertfach. Das ganze geht ohne irgendwelches Zutun der User, genau so, wie man sich das wünscht. Die tägliche Sicherung bremst den Rechner auch nicht übermäßig aus, man muss keine Programme beenden und sie dauert meist nur wenige Minuten. So ists recht.

Der erste Windows Home Server unterstützte ein auf den ersten Blick cooles Feature namens Drive Extender, bei dem die Freigaben automatisch auf alle angeschlossenen Festplatten verteilt wurden. Praktisch, weil man dann beliebig Festplatten nachstecken konnte und die Freigaben einfach mitwuchsen. Man konnte sogar pro Freigabe anweisen, dass die Daten auf mindestens zwei Platten vorhanden sein sollen, falls mal eine ausfällt. Der Nachfolger Windows Home Server 2011 hat dieses Feature einfach gestrichen, was einerseits ärgerlich ist, andererseits aber einen guten Grund hat: Stimmt mit dem Drive Extender irgendwas nicht, wird die Sache haarig. Genau das ist uns bei einem Home Server passiert (nicht bei meinem zu Hause), wir mussten also die Daten manuell (an einem anderen Rechner) von den beiden Platten zusammenkopieren und den Server komplett neu aufsetzen. Dabei sind etliche Daten abhanden gekommen, die wir auf dem System abgelegt hatten, nachdem der Drive-Extender-Dienst nicht mehr gestartet werden konnte. Ärgerlich, aber immerhin ist uns der Defekt früh genug aufgefallen. Da der Drive Extender offenbar sogar Microsoft zu mulmig ist und wir Ärger damit hatten, haben wir die Gelegenheit ergriffen, auf dem Lenovo D400 direkt mal den neuen WHS 2011 aufzuspielen. Die Lizenz kostet nur noch 40€, das ist ein überschaubares Investment.

In einer der letzten c't-Ausgaben gab es eine Anleitung, wie man den Acer easyStore H340 auf den WHS 2011 migriert, die man vollständig analog auch für den Lenovo D400 anwenden kann (er ist mit den H340 weitgehend baugleich). Man findet aber auch im Netz genug Anleitungen, die den Vorgang erläutern. Kurz gesagt: Ein USB-Stick mit diskpart bootbar machen, den Inhalt der Windows Home Server 2011 DVD da drauf kopieren, eine cfg.ini-Datei mit bestimmten Einstellungen für die bedienungslose Installation darauf ablegen, geleerte Platte in den untersten Slot stecken, den Stick in einen der hinteren USB-Slots stecken, Server anschalten, eine Stunde dem lustigen Blinken zusehen, fertig. Alternativ kann man für 15€ ein Slotblech mit VGA-Buchse bei eBay kaufen und die Installation interaktiv durchziehen, das Ergebnis ist identisch, denn auch dabei kann man die später unabänderliche Arbeitsgruppe "WORKGROUP" nicht ändern (habe ich danach an meinem eigenen Lenovo D400 ausprobiert). Damit muss man also leben.

Nach der Installation muss man den neuen Home-Server-Connector auf allen Clients aufspielen und eine neue (verbilligte) LightsOut-Lizenz muss auch her), alles nicht grundlegend neu. Erwähnenswert ist, dass der Ersatz für den Drive Extender in meinen Augen viel besser ist: Der Home Server wendet die Clientsicherungstechnik einfach auch auf sich selbst an und macht zweimal täglich eine Datensicherung von wählbaren Ordnern auf die zweite Platte, die wahlweise auch eine externe Platte sein darf. So hat man als Benefit auch eine Versionierung der gesicherten Freigaben, was ein Vorteil gegenüber der kaputten Situation ist, dass Freigaben auf dem Server jede versehentliche Löschung gleich auf alle Platten verteilt.

Leider fällt einem schnell auf, dass der Stand-By-Modus nicht funktioniert. Eine kurze Recherche später hatte ich die Lösung: Man muss die ganzen Intel-Treiber installieren, dann darf man endlich einen Stand-By auslösen. Leider wacht der Server immer sofort wieder auf, also noch weiter suchen: Es fehlt noch ein Treiber für die LED-Ansteuerung. Also mit folgenden Treibern läuft bei mir auch der Stand-By mit dem Lenovo D400, man braucht dazu:

  • Den Intel Grafiktreiber für den 945er Chipsatz (bei mir Version 15.12.75.4.64.1930 für Windows 7 64-Bit : "Treiber für Intel® Grafik-Media-Beschleuniger für Windows* 7 64")
  • Den Intel ICH7 RST-Treiber (bei mir Version 10.1.0.1008 vom 15.12.2010 für Windows Server 2008 R2 Standard: "Intel® Rapid-Storage-Technik")
  • Das Intel INF-Update-Utility (bei mir Version 9.2.0.1030 vom 21.04.2011: "INF-Update-Utility - Hauptsächlich für Intel® 6er, 5er, 4er, 3er und 900er Chipsätze")
  • Den Marvell Yukon 88E8071 Treiber für Windows Server 2008 R2 64 Bit (bei mir Version 11.41.3.3 vom 17.02.2011)
  • Den LED-Treiber von einem großartigen engagierten Entwickler, den man in diesem Forum bekommt. Wichtig ist, nach der Installation in der Konfigurationsdatei den Hardware-Erkennungsmodus auf den Acer H340 zu zwingen, sonst wird der D400 nicht erkannt. Readme lesen!

Etwas über 50€ und ein Arbeitstag später hat man also einen aktuellen Windows Home Server ohne fehleranfälligen Drive Extender auf der alten und sehr günstigen Hardware. Wer unbedingt die Drive-Extender-Features haben will, kann eins der beiden verfügbaren Add-Ons benutzen, aber ehrlich gesagt ist mir das zu gefährlich und ich komme mit dem aktuellen Modus super klar: Schlicht, stabil und von überschaubarer Komplexität, beim Thema Storage mache ich keine Experimente mit reingefummelten Tricksereien.

P.S. Die versionierten Clientsicherungen gehen bei der Umstellung natürliuch flöten und die Daten in den Freigaben muss man auch in irgendeiner Form sichern und später wieder aufspielen. Ich hatte es bei meinem eigenen Server leicht, denn alles passte auf die Systemplatte, so dass ich die nach dem Entfernen der zweiten Platte im alten Home Server einfach rausziehen und mit allen Daten drauf beiseite legen konnte. Nach der Installation auf den nun freien zweiten Platte (im untersten Slot) konnte ich die Daten einfach umkopieren und die alte Systemplatte als Sicherungsplatte einrichten.


HTML5-Elemente auch im IE ohne JavaScript und trotzdem valide

20 12 2010

Ha, na sowas. Da kommt jemand mit einer relativ eleganten Möglichkeit um die Ecke, dem IE die neuen HTML5-Elemente auch ohne JavaScript bekannt zu machen. Er benutzt dafür einen XML-Namespace und benutzt dann so Sachen wie html5:section statt section. Das ist geradezu genial und funktioniert angeblich sogar zuverlässig, validiert aber in erster Linie nicht, wenn man sich nicht mit XHTML5 ganz anderen Ärger ins Haus holen will. Daneben ist es auch komisch, wenn der Autor so ein HTML schreiben muss, nur um auf den IE Rücksicht zu nehmen. Wie ließe sich das also noch verbessern?

Mir kam sofort die naheliegende Idee, das mit einem serverseitigen Ausgabefilter nur an die IEs auszuliefern. Die Voraussetzung wird nicht jedes CMS einfach so mitmachen, aber falls doch, ermöglicht einem das, die HTML5-Elemente jetzt zu benutzen. Wie genau soll das funktionieren? Wenn man einen serverseitigen DOM/HTML-Parser benutzt, ist es relativ leicht, aber das sollte in der CMS-Landschaft die große Ausnahme sein. Also braucht man ein paar simple RegExes:

  • <html muss im HTML-Output durch <html xmlns="http://www.w3.org/1999/xhtml" xmlns:html5="http://www.w3.org/1999/xhtml" ersetzt werden. Wenn man gründlich arbeitet, prüft man vorher, ob das HTML-Element nicht schon die passenden Namespaces trägt.
  • <(/?)(abbr|article|aside|audio|canvas|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video) muss im HTML-Code durch <$1html5:$2 ersetzt werden, das sind die öffnenden und schließenden Tags.
  • (abbr|article|aside|audio|canvas|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video) muss im CSS durch html5\:$1 ersetzt werden.

Das schöne an dieser Lösung ist, dass man Serverseitig recht einfach einen IE erkennen kann und ihm die gefilterten Quellen anbietet. Dass der Code für den IE nicht validiert, halte ich für verschmerzbar. Falls man versehentlich einen Browser erwischt, der sich nur als IE ausgibt, klappt zudem trotzdem alles. Und das wichtigste in meinen Augen ist, dass man die CSS Spezifität nicht verändert und sich auf diesem Weg keine Seiteneffekte ins Boot holt. Gut so.

Der einzige Nachteil, den ich momentan sehe, ist die Voraussetzung eines finalen Output-Filters. Viele CMSe buffern den HTML-Output sowieso und bieten einen Hook an, an den man sich einfach dranhängt, in dem Fall hat man natürlich leichtes Spiel. Etwas schwieriger ist das bei CMSen, die sowas nicht haben und vor allem bei statischen CSS-Dateien, in dem Fall muss man irgendwie einen Filter in den Workflow frickeln, was stark von der eingesetzen Serverumgebung abhängt und nicht einfach generalisiert lösbar sein wird. Wenn man aber sowieso schon ein CSS-Framework wie Turbine einsetzt, lässt sich das prima dort lösen, vielleicht gar mit einem HTML5-Enabler Plugin. Ansonsten wäre das mal ein guter Anlass, so ein Framework einzusetzen.

Damit löst man aber noch immer nicht das Cache-Problem: Man muss externe Proxy-Caches aushebeln und benötigt zudem plötzlich zwei lokale Caches, die von der Cache-Logik unterschieden werden müssen. Da wird es dann diffizil und man verzichtet vielleicht lieber auf eine Browser-Unterscheidung, macht den Output-Filter immer an und scheißt auf den Validator. Ist ja auch eine Lösung und man muss weiterhin nicht selbst mit den Namespaces hantieren.


Nur halb so schlimm: Eigene Short-URLs

05 09 2009

Vor einiger Zeit habe ich gefordert, dass größere und oft bei Twitter verlinkte Seiten eigene kurze URLs für ihren Content anbieten. Neulich erst habe ich mir dann Gedanken gemacht, wie man Short-URLs und so verüberflüssigen kann. Auf diese große Lösung mag ich nicht warten, aber zumindest bei der kleinen Lösung kann ich mit gutem Beispiel voran gehen. Also habe ich gerade schnell ein kurzes Script geschrieben, das kurze URLs für mein Blog in die vollen URLs umsetzt. Zum Beispiel lautet für diesen Eintrag die kurze URL http://spackblog.de/668, was mit 23 Zeichen deutlich kürzer ist als die verdammt lange volle URL und vor allem kürzer als 30 Zeichen, ab denen Twitter ungefragt mit bit.ly verkürzt.

Ich hatte eine Weile überlegt, diese Funktionalität als Serendipity-PlugIn zu veröffentlichen, aber das habe ich aus verschiedenen Gründen nicht gemacht. Ein Grund ist, dass mich ein undankbarer Vollspacko im S9Y-Forum angesaugt und als Schnösel bezeichnet hat, als ich mein letztes PlugIn dort vorgestellt habe. Ich will da gar nicht genauer drauf eingehen, Folge ist jedenfalls, dass meine Lust auf die extra Arbeit für ein S9Y-PlugIn dadurch doch sehr gesunken ist. Ein weiterer Grund ist, dass der generierte Link irgendwo im Template und noch mal im Header positioniert werden muss, damit es Sinn macht. Das kann ein PlugIn nicht flexibel alleine regeln. Man könnte den Link allenfalls dort hinpacken, wo momentan auch die Tags sind, aber das gefällt sicher nicht jedem. An die .htaccess des Blogs, oder wenn dieses in einem Unterverzeichnis steckt auch an die .htaccess der Domain muss man auch noch ran. Spricht alles gegen ein PlugIn. Sowas gehört in meinen Augen sowieso in den S9Y-Core.

Das Script funktioniert super simpel. Alles fängt mit einer Anweisung in der .htaccess an, die alles, was nach kurz-URL aussieht an das Script weiterreicht:

RewriteEngine On
RewriteBase /
RewriteRule ^(e|a|c)?([0-9]+)/?$ s9y_shorturl.php?type=$1&id=$2 [NC,L]

Man kann schon sehen, dass das Script auch kurze URLs für Kategorien und Autoren unterstützt. Der spannende Teil im Script selber ist die Datenbankabfrage, die die kurze URL aus der Datenbank ausliest:


// open a database-connection
$dbh = new PDO('mysql:host=' . $db_server . ';dbname=' . $db_database, $db_user, $db_pass);

// prepare the statement
$stmt = $dbh->prepare('SELECT permalink
  FROM s9y_permalinks
  WHERE type = :type
    AND entry_id = :entry_id
  LIMIT 1');

// execute the statement
$stmt->execute(array(':type' => $type, ':entry_id' => $entry_id));
  
// read the returned urlpart
$urlpart = $stmt->fetchColumn();

// close the database-connection
$dbh = null;

// redirect the user with a 301 status code (moved permanently)
header('Location: ' $blog_base_url . $urlpart, true, 301);
exit;

Der Rest vom Script besteht aus Fehlerbehandlung und Variablen-Vorbereitung, die ich hier mal weg lasse. Der ganze Code findet sich im Nachtrag zum Download. Damit ist der erste Teil schon fertig, der die kurzen URLs auflöst. Für die kurzen URLs werden einfach die von Serendipity vergebenen IDs genutzt, der Parameter type kann übrigens 'entry', 'category' oder 'author' sein. S9Y führt eine eigene Tabelle für die Zuordnung von IDs und URLs, die man nur abfragen muss.

Der zweite Teil ist leider Handarbeit im Template. Man muss dazu an der Stelle, wo der Link auftauchen soll folgenden Code in der entries.tpl platzieren: <a href="http://SHORTURL_DOMAIN/{$entry.id}">http://SHORTURL_DOMAIN/{$entry.id}</a>. SHORTURL_DOMAIN muss natürlich durch die jeweilige URL ersetzt werden, in meinem Fall ist das tatsächlich eine andere Domain als die, auf der das Blog läuft. Ich habe zusätzlich noch in der index.tpl im Headerbereich im {if $entry.id} Block einen Short-URl Autodiscovery Link eingefügt, wie er hier beschrieben ist.

Das ganze arbeitet extrem simpel und macht genau das, was es soll: Wenn jemand meinen Blog bei Twitter und Co. verlinken will, kann jetzt meine 23 stellige Kurz-URL hernehmen und sich so den Scheiß mit den verkürzten URLs sparen, bei denen man nicht weiß, wohin sie eigentlich linken. Alle Seiten sollten solche eigenen Kurz-URLs haben, dann wäre das mit Twitter nur noch halb so schlimm.

Nachtrag 24.02.2011: Weil eine Anfrage nach dem vollen Code kam, habe ich den mal hübsch gemacht und zum Download bereit gestellt. Das Script selber hat außer PHP 5.1 mit PDO keine Abhängigkeiten und stört auch andere Scripte nicht, solange sich die Rewrite-Regeln in der .htaccess vertragen. Wer ganz sicher gehen will, betreibt das alleine auf einer (Sub-)Domain. Viel Spaß damit. Ach ja, Lizenz ist cc-by-sa, das sollte genug Freiheiten lassen.


jQuery Stolperstein: hover() und slideUp()/slideDown()/animate()

21 06 2009

Gelegentlich möchte ein CSS Dowpdown-Menü um eine kleine Animation erweitert werden, die bei der Gelegenheit auch den Suckerfish für den IE6 übernimmt. Natürlich macht man sowas mit jQuery, wenn man das sowieso eingebunden hat, in etwa so (natürlich alles innerhalb von $(document).ready()):

$('#mainMenu>li').hover(
  function(){
    $(this).find('>ul').slideDown(150);
  },
  function(){
    $(this).find('>ul').slideUp(50);
  }
);

Nun kommt es dabei immer wieder zu den selben Problemen:

Fährt man mit der Maus schnell mehrmals über einen Punkt, werden alle nötigen Animationen in eine Warteschlange gepackt und nach und nach gemütlich abgearbeitet. Das lässt sich noch leicht und logisch beheben, indem man jeweils mit einem stop() vor slideDown() und SlideUp() die Animation erst einmal stoppt, bevor man das Gegenstück ausführt. Das gleiche passiert auch bei animate() und vergleichbaren Sachen und lässt sich da auf die gleiche Weise beheben. Soweit kein Problem und nach ein paar Sekunden Google-Recherche gefunden.

Ein weiteres Problem ist das Handling der Animation bei der ersten Berührung mit der Maus: Scheinbar wird das jQuery hover Event erst bei der zweiten Berührung genutzt, bei der ersten klappt das Menü ganz normal mit der im CSS definierten Methode aus. Auch hier hilft eine Google-Recherche schnell weiter und bringt die Lösung frisch auf den Tisch: Vor der ersten Berührung (also zweckmäßiger Weise in $(document).ready()) müssen die UL-Elemente der Untermenüs mit einem beherzten Aufruf von hide() versteckt werden, auch wenn sie durch das CSS eigentlich schon versteckt sind. OK, das muss man wissen und kommt nicht von alleine drauf.

Perfide ist das dritte Problem bei Animationen mit den Ausmaßen der Untermenüs, also slideDown()/slideUp() und animate() in Kombination mit height() oder width(): Nutzt man diese innerhalb der hover() Funktion und verlässt das Element noch während die Animation läuft, speichert jQuery den zu diesem Zeitpunkt aktuellen Wert der animierten Abmessungen zwischen und animiert fortan nur noch bis zu diesem Maximalwert. Verlässt man den Menüpunkt also sofort wieder, wird das Menü bis zum Seitenreload nie wieder erscheinen, denn es ist ja nur noch etwa einen Pixel hoch und wird entsprechend auch nur bis zu einem Pixel animiert. Hier habe ich keine Lösung bei Google gefunden, weil ich nicht wusste, wonach ich suchen sollte. Also habe ich mich mit dem Firebug auf die Lauer gelegt und das Problem in der beschriebenen Form analysieren können. Ich weiß nicht, ob das ein Bug ist oder volle Absicht, aber ich kann mir nicht wirklich eine Situation vorstellen, wo dieses Verhalten gewünscht wäre. Die Lösung dafür liegt aber auf der Hand: Ein unscheinbares height('auto') vor der ausklappenden Animation behebt das Problem sehr zuverlässig.

So sieht nun also das komplette Menüscript aus, das sich endlich so verhält, wie man es auch erwartet:

$('#mainMenu>li>ul').hide(); // needed to prevent CSS-only behaviour on first contact
$('#mainMenu>li').hover(
  function() {
    $(this).find('>ul').stop().height('auto').slideDown(150); // the height('auto') prevents the menu from memorizing an incorrect height-value forever when leaving the menu while the animation is running
  },
  function() {
    $(this).find('>ul').stop().slideUp(50);
  }
);

An anderer Stelle half mir height('auto') aber nicht weiter, was also tun? Die Lösung lag zunächst auch hier auf der Hand: Beim Laden der Seite müssen die initialen Werte des Elements in einer Variablen gespeichert werden, zu denen die Animation dann jeweils zurückkehren kann. Das schien mir immens unelegant, weil ich nicht den globalen Scope mit solcherlei Variablen vollmüllen wollte. Mein Bruder brachte dann den entscheidenden Tipp: Man kann die Werte prima als Attribute des Elements im DOM ablegen. Beliebige Attribute sind in HTML zwar nicht erlaubt, aber wenn das Dokument erst mal ins DOM eingelesen wurde, sind die Limitierungen von HTML völlig gleichgültig. Kurz gesagt: Ist das Ding im DOM, ist es kein HTML mehr. Die Denke muss man sich erst mal klar machen. Gut, also schreibe ich die Werte in $(document).ready() fix als Attribute ins DOM und alles wird gut. Doch da hatte ich nicht mit der strengen, aber korrekten Auslegung des ready()-Events in Opera gerechnet: Dort stehen zu diesem Zeitpunkt die gewünschten Werte nicht zur Verfügung, weil es sofort gefeuert wird, sobald das DOM fertig eingelesen wurde, aber eben noch bevor die Engine irgendetwas rendern konnte. Abmessungen sind also nicht bekannt. Die Lösung lautet hier $(window).load(). Dieses Event wird gefeuert, sobald die Seite gerendert wurde, mithin also auch alle Abmessungen bereit stehen. So sind die Events spezifiziert, aber Opera scheint der einzige Browser zu sein, der sie auch so handhabt. Die komplette Lösung für dieses Problem lautet also in etwa so:

$(window).load(function(){
  $("#elementId").attr('origHeight', $("#elementId").height());
});

Ausgelesen wird auf die gleiche Weise, also $("#elementId").attr('origHeight').

Nachtrag 05.07.2010: Wo ich den alten Beitrag gerade noch mal lese, fällt mir auf, dass Doku lesen oft hilft. Der letzte Punkt wird natürlich viel eleganter gelöst, wenn man einfach die data()-Methode von jQuery benutzt. Also wie immer: Augen auf und Hirn an.

Nachtrag 19.07.2011: In den Kommentaren wurde ich darauf hingewiesen, dass ein .stop(true, true) die genannten Probleme löst. Fantastisch. Ist das neu dazu gekommen oder bin ich nur zu blöd, Dokumentationen zu lösen?

Nachtrag 04.11.2011: jQuery 1.7 nimmt sich zumindest bei toggling animations des Problems an, dass ein Abbruch auf halber Höhe weitere Klappvorgänge nur noch bis dort hin durchführt. Ich zitiere aus dem Release-Post zu jQuery 1.7:

In previous versions of jQuery, toggling animations such as .slideToggle() or .fadeToggle() would not work properly when animations were stacked on each other and a previous animation was terminated with .stop(). This has been fixed in 1.7 so that the animation system remembers the elements’ initial values and resets them in the case where a toggled animation is terminated prematurely.

Ich habe das noch nicht ausprobiert und wenn das nur bei Toggle funktioniert, braucht es eine der oben genannten Methoden in anderen Fällen weiterhin, aber trotzdem eine gute Nachricht.


SSL und wenigstens halbwegs gute Passwörter

04 05 2009

Jemand hatte für 24 Stunden den Twitter-Account cdu_news gekapert und dort eine politische Kehrtwende lanciert. Dem Hörensagen nach geschah das entweder durch ein in einem offenen WLAN gesnifftes Passwort oder durch schlichtes Raten, was bei Identität von Benutzername und Kennwort schnell gegangen sein mag. So oder so, die CDU hat sich das aus verschiedenen Gründen redlich verdient, in erster Linie aber zeigt es recht schön, wie es um die Medienkompetenz dieser Partei bestellt ist. Darauf wollte ich aber gar nicht hinaus, die Häme ist schon woanders ausgeschüttet worden.

An diesem Beispiel sieht man wieder, dass viel zu wenige Leute sich zumindest im Ansatz für die Sicherheit ihrer Online-Identität interessieren. Da werden trivialste und leicht zu erratende Passwörter genutzt und wahrscheinlich für alle Dienste das gleiche, weil man sich das ja auch merken muss. Und es wird ohne Hirneinsatz in offenen WLANs fröhlich alles ohne Verschlüsselung übertragen. Dass da jeder Anwesende mit ein wenig technischem Verständnis und einem Notebook oder PDA in Reichweite alles, mit der Betonung auf alles, mitlesen kann, hat man auch schon mal gehört irgendwann. Punkt. Ich habe keinerlei Mitleid mit Menschen, denen aus diesen beiden Gründen unschöne Dinge passieren, denn sie haben es verdient. Sie haben mutwillig alle Regeln der Umsicht missachtet und müssen die Konsequenzen tragen. Jemand, der aus Bequemlichkeit vor dem Überqueren einer großen Straße und mit MP3-Player auf den Ohren weder rechts noch links guckt, wird in der Regel früher oder später in Kontakt mit fahrenden Fahrzeugen kommen. Same here.

Also noch mal ein paar simple Regeln für den sicheren Umgang mit Passwörtern:

  1. Wenigstens halbwegs sichere Passwörter wählen. Admin und Admin sind kein gutes Paar, idiot@gmx.de und idiot auch nicht. idiot12345 ist schon besser, aber immer noch leicht zu erraten. Im Prinzip führt kein Weg an sowas wie 2i5j28dQ vorbei. Wer sich sein Passwort merken muss kann auch die ersten Buchstaben jedes Wortes in einem bescheuerten Merksatz nehmen. Nach ein paar Mal eintippen braucht man den Satz eh nicht mehr. Wie auch immer, ein Passwort darf nicht nach ein paar Tausend Versuchen erraten werden, also mit geeignetem Werkzeug nach ein paar Millisekunden bis Minuten. Der Name von Kindern und Haustieren oder der Hochzeitstag fallen also auch flach, ebenso wie Begriffe, die im Wörterbuch stehen.
  2. Nicht überall das gleiche Passwort nehmen. Klingt besonders lästig, aber eine Anekdote beschreibt das Problem ganz gut: Ich hatte mal meinen Benutzernamen vom FH-WLAN vergessen und habe diesen in der DVZ (unser Rechenzentrum) per E-Mail nachgefragt. Der freundliche Mitarbeiter nannte mir ohne weitere Überprüfung meiner Identität meinen Benutzernamen und das erste Zeichen meines Passworts (nach dem ich nicht gefragt hatte, denn das wusste ich noch). Fuck, dieses Passwort hatte ich zu dem Zeitpunkt bei etlichen Diensten genutzt und jeder Honk konnte offenbar durch geschicktes Nachfragen bei der DVZ dieses Passwort herausfinden, das dort im Klartext abgelegt war. Fuck, fuck, fuck! Merke: Man kann nie wissen, was mit den Passwörtern bei den Betreibern passiert, also führt tatsächlich kein Weg daran vorbei, für jeden Dienst ein gesondertes Passwort zu benutzen oder zumindest für die wichtigen Dienste.
  3. Kein Mensch kann sich alle Passwörter merken, wenn man für jeden Dienst ein eigenes definiert. Also führt kein Weg an einem Passwort-Manager vorbei. Ich benutze Keepass, das ein kostenloses (Open-Source) Programm ist, das auf etlichen Plattformen läuft: Neben Windows (auch vom USB Stick), Linux, Mac OSX auch auf den meisten besseren Handys und demnächst irgendwann auch mal auf dem iPhone, wenn Apple es mal im AppStore freischaltet. Aber Vorsicht: Das Master-Passwort muss besonders stark sein, denn wer eine schlecht geschützte Schlüsseldatei in die Finger bekommt, hat den Generalschlüssel.
  4. Fast einen Generalschlüssel hat auch jeder, der das Passwort zum E-Mail Postfach kennt oder sonstigen Zugriff darauf hat: Eigentlich jeder Webdienst bietet die Möglichkeit, sich ein neues Passwort per E-Mail zusenden zu lassen. Und schwupps ist alles geritzt. Immerhin bemerkt man hier den Angriff meistens im Nachhinein, wenn das alte Passwort nicht mehr funktioniert. Merksatz: E-Mail und Passwort-Manager sind die wichtigsten Passwörter von allen. Hüte sie wie Deinen Augapfel.
  5. Das E-Mail Postfach kommt aber gerne in falsche Hände, wenn man unverschlüsselt auf seine E-Mails zugreift. Also immer und ganz besonders in offenen WLANs Passwörter nur über verschlüsselte Verbindungen übertragen. Im E-Mail Programm muss man meist nur zwei Häkchen setzen (SSL oder TLS), je nach Provider muss man aber auch mehr Umstellen: Ein freundlicher Helfer oder die Online-Hilfe des Providers hilft auch Anfängern zuverlässig bei der Einrichtung. Auch der Webmailer sollte nur mit einem https:// vor der Adresse genutzt werden. Nochmal: In offenen WLANs, also allen öffentlichen und hoffentlich nicht dem eigenen, kann jeder in Reichweite des Netzes alle Daten mitlesen, wenn sie nicht verschlüsselt werden.
  6. Und zuletzt der simple Tipp: Nicht jedem das Passwort in die Hand drücken. Ein Passwort geht niemanden etwas an. Ganz besonders gilt das für die vielen Dienste, die eine direkte Twitter-Anbindung anbieten, für die Benutzername und Kennwort eingegeben werden müssen. Das ist scheiße, liebe Betreiber. Bitte erzieht die Nutzer nicht zu solch sorglosem Umgang mit ihren Zugangsdaten.

Ja, Passwörter sind ein unbequemes Thema. Aber spätestens, wenn es um handfesten Identitätsdiebstahl oder finanziellen Schaden geht (PayPal, eBay etc. schicken einem gerne ein neues Passwort per E-Mail zu), sollte etwas Umsicht walten.


Datev, immer wieder Datev

31 03 2009

Neulich hab ich ja schon dreieinhalb Stunden damit verbracht, einem nicht von sich aus funktionierenden Datev-Update auf die Beine zu helfen. Soweit so gut.

Seit gestern versuche ich einen Windows Server 2003 mit einer Reperaturinstallation auf eine neue Hardware zu migrieren und auch das scheitert beharrlich bei verbleibenden 39 Minuten im Setup. Fuck. Die Microsoft Knowledge Base ist bemüht, das Problem zu ergründen und redet etwas von nicht unterstützter Hardware. Also habe ich alle Onboard-Geräte und Power-Management-Einstellungen bis auf den Plattencontroller ausgeschaltet, aber auch das half nicht nicht. Allerdings lief eine testhalber durchgeführte normale Installation durch. Hmm.

Aber was hat das Ganze nun mit Datev zu tun? Dieser Foreneintrag half mir schließlich weiter. Kurz gefasst, soll man mit Shift+F10 eine Konsole öffnen, regedit starten und den Schlüssel "HKEY_LOCAL_MACHINE\Software\Microsoft\MS Setup (ACME)" beherzt löschen. Das habe ich gemacht und das Setup lief sofort weiter. Fein! Nun ratet mal, was in dem Schlüssel als einziger Eintrag zu finden war… Natürlich ein Überbleibsel von irgendeiner Datev-Scheiße. Zorn.


Eine kleine Fuck IE6 PHP Klasse

24 02 2009

Aktuell soll ja wieder mal der IE6 endlich sterben. In Norwegen sind einige große Nachrichtenseiten mit einer IE6-Warnung unterwegs und der IE6 Death March ist auch unterwegs. Ich verkaufe schon seit einiger Zeit IE6-Anpassungen nur noch als extra zu bezahlende Zusatzoption, wobei es bei mir dank semantisch sinnvollem Code hier nur um optische Feinheiten geht, die ich für entbehrlich halte. Wie auch immer: Bisher hat kein Kunde Aufpreis für den IE6 zahlen wollen. Geht doch.

Nun baue ich gerade meine Lifestream-Seite auf und befreie mich bei der Gelegenheit von jedem IE6-Mist. Meine Contentboxen hat der IE6 schlicht gar nicht angezeigt (wohl aber konnte man die unsichtbaren Links anklicken), so dass ich mich entschieden habe, dem IE6 und älteren Versionen einfach alle Styles vollständig wegzunehmen und stattdessen eine rote Alarm-Box einzublenden. Die Inhalte bleiben vollständig nutzbar, es sieht nur total scheiße aus. Sehr befriedigend, kann ich so den IE6-Nutzern doch etwas von der Hässlichkeit vermitteln, die dem IE6 innewohnt.

Zu diesem Zweck habe ich eine kleine statische PHP Klasse geschrieben, die ich hiermit gerne zur Verfügung stellen möchte. Wer Lust hat, kann die einfach für sich anpassen und in seine Projekte einbinden. Ich habe mich gegen eine clientseitige Lösung mit CSS und/oder JavaScript entschieden, weil ich "gute" Besucher nicht mit dem für sie sowieso unsichtbaren IE6-Code belasten wollte. Ich denke, das ist eine gute Idee.

Die Klasse ist recht simpel gestaltet und schreit geradezu danach, bei Bedarf verfeinert zu werden (mir reicht das erst mal so). Also in Kürze:

  1. Einbinden der Klasse, etwa indem man den Code direkt in sein Projekt kopiert oder als Datei abspeichert und mit require_once('PFAD/fuck_ie6.class.php'); einbindet.
  2. Da die Klasse statisch ist, muss sie nicht instanziiert werden. Man ruft also die drei statischen Methoden wo man sie braucht.
  3. Folgende Methoden und Variablen stehen zur Verfügung:
    • fuck_ie6::is_ie6() gibt true zurück, wenn der Besucher mit einem IE4, 5 oder 6 unterwegs ist, sonst false. Praktisch, wenn man bestimmte Inhalte wie Stylesheets ohne Conditional Comments für diese Besucher aus- oder einblenden möchte. Im Prinzip ist das nur eine sehr simple RegEx.
    • fuck_ie6::print_style() und fuck_ie6::print_alert_box() printen den Inhalt der Variablen $alert_style und $alert_content in Abhängigkeit vom Browser des Besuchers. Die beiden Methoden ersparen einem also nur die if-Abfrage mit fuck_ie6::is_ie6() im Template ein.
    • Die beiden Variablen $alert_style und $alert_content enthalten den Style und den Code, die für die Alarm-Box genutzt werden sollen. Hier finden Anpassungen an die eigenen Wünsche statt.

<?php
/**
* a very simple static class for sniffing Internet Explorer 6 and below
*
* can detect whether the user comes with an annoying IE (version 4,5 or 6)
* additionally holds content and styles for a red alert box
*
* @author	Gregor Nathanael Meyer <Gregor [at] der-meyer.de>
* @license  http://creativecommons.org/licenses/by-sa/3.0/de/ Creative Commons cc-by-sa
* @version  0.1  first release
*/
class fuck_ie6
{
  /**
    * the style block used for the alert box
    * @static
    */
  public static $alert_style = '  <style>
  .errorBox {
    background: #fbe3e4;
    color: #8a1f11;
    border: 2px solid #fbc2c4;
    width: 80%;
    padding: 25px;
    margin: 0 auto;
    font-size: 1em;
    line-height: 1.3em;
  }
  </style>';
  
  /**
    * the HTML of the alert box
    * @static
    */
  public static $alert_content = '<p class="errorBox"><strong>Alarm:</strong> Offensichtlich bist Du mit einem alten Internet Explorer (6.0 oder älter) unterwegs. Da dieser Browser aus dem Jahr 2001 die auf dieser Seite genutzten modernen Webstandards nicht hinreichend unterstützt, habe ich das visuelle Beiwerk für Dich deaktiviert. Die Inhalte sind weiterhin erreichbar.</p>';
  
  
  /**
    * the IE6 detector function
    * uses just a simple RegEx to read the UA string
    * @static
    */
  public static function is_ie6()
  {
    return preg_match('#^Mozilla/4.0 \(compatible; MSIE [456]#i', $_SERVER['HTTP_USER_AGENT']) ? true : false;
  }
  
  /**
    * prints the style block in case of IE<=6
    * @static
    */
  public static function print_style()
  {
    if ( self::is_ie6() )
    {
      echo self::$alert_style;
    }
  }
  
  /**
    * prints the alert box content in case of IE<=6
    * @static
    */
  public static function print_alert_box()
  {
    if ( self::is_ie6() )
    {
      echo self::$alert_content;
    }
  }
}

Ein Beispiel könnt ihr bei meinem Lifestream sehen.


Dumm gucken: Phenom II auf Gigabyte GA-MA790GP-DS4H

29 01 2009

Vor ein paar Tagen habe ich mal wieder einen Rechner gebaut, spannender Weise einen AMD Phenom II 920 auf einem Gigabyte GA-MA790GP-DS4H mit AMD 790GX Chipsatz. Dies ist ein oft empfohlenes Board für diesen Prozessor, also erwartete ich keine Schwierigkeiten. Doch weit gefehlt: Beim Druck auf den Einschalter passierte exakt nichts. Kein Piepsen, kein Lüfterzucken, nichts. Als würde das Netzteil keinen Strom liefern, was ich mit einem anderen Netzteil schnell ausschließen konnte. Was ist hier los? Schalter am Gehäuse defekt? Aber auch das direkte Kurzschließen der Power-Kontakte auf dem Board brachte nichts. Ratlosigkeit… Board defekt?

Gelegentlich brauchen Boards ja ein BIOS-Update, um einen neuen Prozessor erkennen zu können. Aber dass sich gar nichts tut, wenn man das passende BIOS nicht hat, hatte ich noch nicht gehört. Trotzdem habe ich mal versuchsweise einen älteren AM2-Prozessor auf das Board gesteckt – was übrigens wegen der unfassbar strammen Halteklammer des Aplenföhn Groß Clockner eine ernsthafte Aufgabe ist. Und siehe da: Das Board startet sofort, lässt sich ein aktuelles BIOS geben (F3H) und erkennt danach auch den Phenom II. Sowas nervt total. Was soll man denn machen, wenn man keinen anderen AM2-Prozessor zur Hand hat?

P.S. Der Kühler ist großartig: Leise, bombastisch, hervorragende Kühlleistung und mit 30 Euro nicht zu teuer.


Milkdrop in AIMP2

22 12 2008

Seit einiger Zeit bin ich begeisterter Nutzer des russischen Audioplayers AIMP2, der genau das macht, was ich von einem Audioplayer erwarte. Die Medienbibliothek taugt zwar wahrscheinlich nichts, aber die installiere ich sowieso nicht: Ich kann Medienbibliotheken nicht ausstehen und verlasse mich lieber auf eine gut sortierte Ordnerstruktur.

Was ich an AIMP2 aber immer sehr vermisst habe, sind gute Visualisierungen wie Milkdrop. Die eingebauten Visualisierer sind ein schlechter Scherz und das Programm kann leider keine Winamp-Plugins laden. ProjectM, die Open-Source OpenGL Implementierung von Milkdrop gibt es nur für Linux und MaxOSX. ProjectM klinkt sich in den Audiopfad ein und kann daher Musik aus beliebigen Quellen visualisieren. Schade, dass es das nicht für Windows gibt.

Jetzt habe ich aber endlich eine Möglichkeit gefunden, zumindest das alte Milkdrop 1.04 in AIMP2 zum laufen zu bringen: aimp_vis_winamp ist ein AIMP2 Plugin, das Winamp-Visualisierungen lädt und ganz brauchbar mit Milkdrop 1.04 zusammen arbeitet. Folgende Schritte brachten mich zu einer erfolgreichen Installation:

  1. Download des aimp_vis_winamp Plugins, momentan in Version 0.3; neuere Versionen finden sich ggf. in diesem Thread.
  2. Entpacken der aimp_vis_winamp.dll in das Plugin-Verzeichnis von AIMP2. Wenn nötig (etwa, wenn der Player nicht neu gestartet wurde), das Plugin über den Plugin-Manager laden.
  3. Milkdrop 1.04 herunterladen und irgendwo entpacken. Die *.milk-Presets aus den Unterordnern musste ich ich auf eine Ebene mit der vis_milk.dll bringen, damit sie geladen wurden.
  4. Das aimp_vis_winamp Plugin als Visualisierungs-Plugin in AIMP2 laden:
    Screenshot AIMP2, Laden von aimp_vis_winamp.dll
  5. Entgegen der Beschreibung im Forum, bringt ein einzelner Linksklick in das Visualisierungsfenster jetzt ein Kontextmenü auf den Schirm, in dem ich eine Winamp vis_*.dll laden, dessen Konfigurationsmenü aufrufen und es starten und stoppen kann. Hier lade ich das zuvor entpackte Milkdrop 1.04, das allerdings nur im Vollbildmodus läuft. Milkdrop2 aus dem aktuellen Winamp bringt AIMP2 leider zum Absturz. Andere Winamp-Plugins laufen übrigens auch, wer also die AVS mag, bekommt auch die zum Laufen.

Fazit: Milkdrop 1.04 ist zwar nicht Milkdrop 2, aber immerhin habe ich endlich eine schöne Visualisierung in AIMP2. Ein Windows-Port von ProjectM wäre freilich besser.

Nachtrag 23.12.2008: Es gibt einen Windows-Port von ProjectM, der allerdings recht alt ist und nicht leicht zu finden. Ich habe die Winamp-Plugin-Version davon (von 2004) mit dem Winamp-Loader geladen und das Teil funktioniert auch leidlich: Es scheint irgendwie zu schnell oder zu hektisch zu laufen, jedenfalls nicht schön. Außerdem bekomme ich das Ding nicht in einen Vollbildmodus geschaltet. Naja. Eine aktuelle Version von ProjectM für Windows, vielleicht sogar als Standalone Programm wär ne feine Sache.

Inzwischen habe ich aber Milkdrop2 zum Laufen gebracht und bin fast glücklich. Die Version aus dem aktuellen Winamp wollte zwar nicht, aber es gibt eine Version 2.04D, die im AIMP2 Mega Pack enthalten ist. Dort habe ich die vis_milk2.dll samt der zugehörigen Dateien und der nscrt.dll herauskopiert und jetzt läuft auch Milkdrop2. Juhu! Die Frage ist nur, wie legal das Ganze jetzt ist? Das AIMP2 Mega Pack kann man jedenfalls nur fragwürdig legal und etwas verschämt von russischen Servern ziehen. Eine etwas lästige Eigenschaft der Winamp Plugin Loaders für AIMP2 ist, dass er das zuletzt geladene Plugin beim Playerstart wieder lädt und startet, auch wenn es zuletzt gar nicht gestartet war.


array_merge_recursive_overwrite()

19 06 2008

Update 30.70.2009: Seit PHP 5.3.0 gibt es endlich die Funktion array_replace_recursive(), die genau das kann, was meine array_merge_recursive_overwrite() macht. Ich habe meine Funktion erweitert, so dass sie lediglich array_replace_recursive() aufruft, wenn diese verfügbar ist. Dies dient nur der Abwärtskompatibilität in bestehenden Projekten und macht keinen Sinn, wenn man neu anfängt. Für einen solchen Fall (hoffentlich der Normalfall) habe ich unter obigem Link einen fertigen Code als Kommentar hinterlassen.

Gestern musste ich mit PHP zwei verschachtelte assoziative Arrays zusammenführen, wobei allerdings bestehende Werte im einen Array durch Werte des anderen überschrieben werden sollten. Leider kommt die PHP-Funktion array_merge_recursive() dafür nicht in Frage, weil sie im Konfliktfall die Werte nicht überschreibt, sondern ein Unterarray baut, in dem beide Werte enthalten sind. Einen Schalter zum Überschreiben gibt es nicht, also musste ich eine eigene Funktion für diesen Zweck bauen. Et voilà, hier ist sie:

/**
 * merges two arrays recursively, overwrite existing values in $base with values from $merge
 * array_merge_recursive does not overwrite values, it creates a new sub array with both values in it, so we need this function
 * since PHP 5.3.0 the built in array_replace_recursive() does the same, so it will be called, if available
 * 
 * @author     Gregor Nathanael Meyer <Gregor at der-meyer.de>
 * @param      array $base base array
 * @param      array $merge array to be merged into base array
 * @param      array $merge,... more merge arrays
 * @return     array merged inputs
 */
function array_merge_recursive_overwrite($base, $merge)
{
  // as of PHP 5.3.0 array_replace_recursive() does the work for us
  if (function_exists('array_replace_recursive'))
  {
    return call_user_func_array('array_replace_recursive', func_get_args());
  }
  
  function recurse($base, $merge)
  {
    foreach ($merge as $key => $value)
    {
      // create new key in $base, if it is empty or not an array
      if (!isset($base[$key]) || (isset($base[$key]) && !is_array($base[$key])))
      {
        $base[$key] = array();
      }
      
      // overwrite the value in the base array
      if (is_array($value))
      {
        $value = recurse($base[$key], $value);
      }
      $base[$key] = $value;
    }
    return $base;
  }
  
  // handle the arguments, merge one by one
  $args = func_get_args();
  $base = $args[0];
  if (!is_array($base))
  {
    return $base;
  }
  for ($i = 1; $i < count($args); $i++)
  {
    if (is_array($args[$i]))
    {
      $base = recurse($base, $args[$i]);
    }
  }
  return $base;
}

Genau wie ihre Schwester array_merge_recursive() nimmt sie zwei oder mehr Arrays entgegen und gibt diese verbunden zurück. Viel Spaß damit, falls es wer gebrauchen kann.

Nachtrag: Ich hätte auch einfach mal bei Google suchen können, so ziemlich die gleiche Funktion gibt es auch im Horde Framework, allerdings nur für zwei Arrays (also so wie meine innere Funktion). Mist, da hätte ich mir die verhasste Rekursion gar nicht selber ausdenken müssen.

Nachtrag: Die Kommentare in der PHP-Doku bringen auch etliche Varianten dieses Themas. Man sollte sich von unleserlicher Schreibweise nicht abschrecken lassen, auch wenn man schon müde ist. Naja, wenigstens ist meine Lösung nicht total doof.