Das musste mal sein

vom 21. March 2008

Spamassassin auf der Jagd

Das musste endlich mal gemacht werden. Seit ein paar Wochen lief mein Mailserver nicht mehr ganz rund und der ganze Spam-Rotz landete mit all den anderen Mails in der Inbox ohne markiert zu werden. Das waren pro Tag ca. 350-400 Mails.

Nun hab ich Spamassassin wieder vernünftig eingerichtet und bei dieser Gelegenheit gleich das Subject der Spam-Mails etwas angepasst. Die Zahl gibt jetzt an, wieviel Spam wirklich in der Mail steckt. Ab einem Wert von von 2 landet die Mail automatisch per Procmail im Trash, der einmal am Tag geleert wird. Also Vorsicht, wer mir eine HTML-Mail mit leerem Betreff und ein paar einschlägigen Schlüsselwörtern schickt geht Gefahr im Müll zu landen :-)

Die Option lässt sich übrigends in der Datei /etc/spamassassin/local.cf einstellen. Dazu einfach folgende Optionen setzen:

rewrite_header Subject [*** SPAM _SCORE_ ***]
report_safe 0

Die Variable SCORE steht für den den Spam-Score, der von Spamassassin berechnet wird, um zu erkennen ob es sich um Spam handelt oder nicht. report_safe muss auf 0 stehen, damit der neue Betreff gesetzt werden kann.

4 Kommentare, delicious bookmark del.icio.us


Legacy-Systeme mit mod_rewrite fixen

vom 18. March 2008

Altanwendungen zu betreuen ist oft nicht einfach, bei Webanwendungen wird es meist noch umständlicher und niemand macht es gerne. Wenn man beispielsweise die Blogsoftware wechselt, auf ein CMS-System wechselt oder gar die neue Version selbst programmiert hat, stimmen die alten URLs nicht mehr. Das ist besonders ärgerlich, wenn die alte Seite viele Backlinks hatte, die nach dem Umzug nicht mehr funktionieren.

Ein Beispiel: In der alten Anwendung sahen die URLs so aus: http://example.com/blog/index.php?id=123. In der neuen Software sieht die URL, die zum gleichen Content führt, anders aus: http://example.com/123-meine-neue-webseite.

Was also tun? Es hinnehmen? Das alte System parallel weiterlaufen lassen, nur damit die Links noch stimmen? Zu umständlich. Aktuelle Webserver bieten die Möglichkeit an, URLs mit Hife von mod_rewrite intern umzuschreiben. Dieses Feature wird meistens dafür verwendet um die URLs einfacher, verständlicher und "sicherer" zu machen.

Diese Funktionalität kann man auch dafür verwenden um das Altsystem zu "faken". Hier eine Lösung in Lighttpd für das obige Beispiel (Voraussetzung hierfür ist natürlich, das die neue Blogsoftware die Seite nur anhand der ID auswählt und nicht zusätzlich am Seitennamen):

url.rewrite-once(
    "/blog/index\.php\?id=([0-9]+)." => "/$1-old-url")

Alles was in der Form /blog/index.php?id=xxx am Webserver eintrifft wird auf das neue URL-Schema umgeformt. So erhält man seine alten URLs weiter am Leben, ohne das sie weiter existieren müssen.

Was aber tun wenn man keine Verknüpfung zwischen alter und neuer URL herstellen kann? Dies passiert beispielsweise dann, wenn man im neuen System die Seite nicht anhand der ID sondern anhand des Seitennamens auswählt (/blog/meine-neue-webseite z.B.). Hier führt kein Weg an einem kleinen Hilfsscript vorbei das die fehlende Verbindung wieder herstellt. Im obrigen Beispiel müssten wir eine Beziehung zwischen der ID und des Seitennamens herstellen. Ein einfaches PHP-Script könnt so aussehen:

$row = $sql->query(
    "SELECT url_name FROM entries WHERE id=?",
    $_GET['id']);
header("LOCATION: /blog/".$row->url_name);

Das kleine Script sucht den Seitennamen anhand der ID raus und leitet dann an diese weiter. Die Regel für mod_rewrite würde dann so aussehen:

url.rewrite-once(
    "/blog/index\.php\?id=([0-9]+)." => "/legacy.php?id=$1")

Hat die neue Seite den Content der alten Seite nicht mehr, kann man diese Technik auch dazu nutzen um die alten URLs auf eine Übersichtsseite oder eine spezielle "existiert nicht mehr"-Seite umzuleiten.

Kommentar schreiben, delicious bookmark del.icio.us


Von Wasserfällen und scharfer Munition

vom 8. March 2008

Wasserfall (Bild von @t.)

Im Laufe meines Studiums habe ich einige Vorgehensmodelle zur Softwareentwicklung kennengelernt. Besonders ausführlich wurde das Wasserfallmodell behandelt. Wer es (noch) nicht kennt: Der Softwareentwicklungsprozess ist in 5 Phasen eingeteilt, welche linear durchlaufen werden. Zuerst wird die Software geplant, sprich ein Lasten und Pflichtenheft erstellt welches beschreibt was die Software können muss. Daraus wird der Entwurf der Software (meist mit UML) erstellt. Dieser Entwurf wird in Software umgesetzt und anschließend getestet. Am Ende wird die Software ausgeliefert, Installiert und gewartet. Die einzelnen Phasen sind wie bei einem Wasserfall angeordnet und strikt getrennt. Wenn bei der Implementierung Designfehler entdeckt werden wird wieder bis zur Entwurfsphase gesprungen, der Fehler behoben und dann wieder die Kette nach unten geklettert.

Irgend wie konnte ich mich mit diesem Modell nie richtig anfreunden und habe auch nie wirklich geglaubt das ein solches Konzept erfolgreich angewandt werden kann. In dem Buch Ship it! von Richardson und Gwaltney bin ich hier auf meine lang ersehnte Bestätigung gestoßen:

Ein Vorgehensmodell, das Sie vermeiden sollten, ist das berüchtigte Wasserfallmodell. Es wurde in fortschrittlicheren Entwicklerkreisen allgemein verworfen, trotzdem ist es erstaunlich, in wie vielen Unternehmen es immer noch angewendet wird. [...] Ahnungslose Manager, die in Zeitpläne vernarrt sind, sind schon seit vielen Jahren Anhänger dieses Vorgehensmodells.

Als pragmatische Alternative zu den eingerosteten Vorgehensweisen stellt das Buch das von Thomas und Hunt entwickelte Tracer Bullet Development (TBD) vor. Ziel ist es hier, die Anwendung so schnell wie nur möglich lauffähig/ausführbar zu machen. Anfangs wird die Anwendung in Bereiche eingeteilt und Schnittstellen spezifiziert. Die einzelnen Klassen mit Methoden werden als stubs angelegt und geben bestenfalls statische dummy-Werte zurück. Wenn dieses Grundgerüst steht, werden die einzelnen Methoden ausprogrammiert und mit der Zeit verfollständigt.

Ein interessantes Konzept wird auch bei den Schnittstellen angewendet: Alle Schnittstellen kommunizieren über das Netzwerk, auch wenn beide Teile auf einem Prozessor laufen. Das hat den Vorteil, das eine Loose Kopplung erzwungen wird und die Software am Ende viel besser skaliert werden kann.

Doch eines hat dieses Konzept mit dem Wasserfallmodell gemeinsam. Es geht davon aus, das das Konzept der Software entgültig ist und das von Anfang an feststeht wie die Software am Ende auszusehen hat. Es ist zwar viel leichtgewichtiger als die "großen", aber nicht so Agil wie XP und Co. Martin Fowler unterscheidet hier zwischen geplantem und wachsendem Design.

Ich denke es kommt immer darauf an, was für Software man entwickelt. Wenn es eine Neuimplementierung einer alten Steuersoftware ist, spricht eigentlich nichts gegen TBD, doch wenn beim umzusetzenden Programm ganz neue Technologien verwendet werden, Features implementiert werden sollen die es zuvor noch nicht gegeben hat und anfangs noch garnicht klar ist, wie das Endergebnis aussehen soll (oder der Kunde nicht weiß was er will), sind agilere Vorgehensweisen angebrachter.

4 Kommentare, delicious bookmark del.icio.us


Mehr HASS!

vom 2. March 2008

Der (jetzt nicht mehr ganz) King of the Internet Zed Shaw fordert mehr Hass für das Internet. Schluss mit den braven Protokollen die jeden Schrott zulassen und Servern die alles akzeptieren müssen. Weg mit den Mailservern die jede Spammail mit einem höflichen HELO entgegennehmen. Jeder der in irgend einer Weise Müll von sich gibt wird gnadenlos in die Fresse geschlagen und für alle Ewigkeit gebrandmarkt.

Auf savingtheinternetwithhate.com beschreibt er sein neues Vorhaben Utu mit recht eindeutigen Worten.

The Internet needs more hate. Much more. We’re overrun by morons, assholes, griefers, spammers, porn peddlers, Nazi dictators, little Napoleons, and arbitrary censors who only behave in their socially deficient manner because they know you can’t do anything about it. [...]

In den Griff kriegen will er das durch ein neues Protokoll das einer sicheren Benutzeridentifizierung, einem Bewertungsschema und einem Bestrafungsmechanismus zugrunde liegt. ("[...] The Internet needs identity, reputation, and retribution. [...]").

Nun frage ich euch: Was haltet ihr davon? Soll das Internet mehr einem digitalen Abbild der realen Welt gleichen ("[...] In the real world you would tell them to screw off personally. You’d call the police. You’d move to the other side of the room and tell all your friends to ignore them. You’d punch them in the face for the things they said about you and your family. [...]") oder kann das Internet nur dann weiter bestehen, wenn jeder gleichberechtigt behandelt wird egal was er anstellt und jeder tun und lassen kann was er will?

1 Kommentar, delicious bookmark del.icio.us


Neues Referenzbuch für Ruby

vom 26. February 2008

Auf RubyInside hab ich grade von dem neuen offiziellen Rubybuch "The Ruby Programming Language" gelesen. Nun gibt es endlich neben dem Pickaxe ein weiteres Referenzbuch.

Natürlich musste ich gleich nachschauen was das Buch kostet. Auf Amazon.com wird das Buch für $26 angeboten, was umgerechnet ca. 18€ entsprechen. Für diesen Preis wollte ich es natürlich gleich bestellen und bin auf Amazon.de. Doch hier kostet das gleiche Buch 40€! Wie kann so ein großer Preisunterschied entstehen?

Ich sehe es ja ein, das der Dollar in den letzten Monaten ziemlich bescheiden dasteht und das man die Preise etwas anpassen muss, aber gleich so drastisch? Wenn jemand eine Möglichkeit kennt das Buch bei Amazon.com ohne Kreditkarte zu kaufen würde ich mich sehr über einen Kommentar freuen.

5 Kommentare, delicious bookmark del.icio.us


Neue Features

vom 20. February 2008

Ich hab die letzten Tage etwas an meiner Seite rumgespielt und ein Feature eingebaut, das ich schon sehr lange geplant hatte. Mein Ziel war es, meinen Content (sprich Blogeinträge, Artikel, Tutorials usw.) nicht nur wie üblich am Ende des Textes zu kommentieren, sondern auch Anmerkungen an einzelne Paragraphen zu kleben. Das Ganze hat mich ca. 200 Zeilen JavaScript (ohne Prototype wären es sicher doppelt so viel geworden), ein paar neue Methoden im ApplicationController und eine Menge Index-Schupserei gekostet.

Die Funktionsweise ist einfach: Jeder Absatz der eine gewisse Länge hat, erhält links einen grauen Balken, den man anklicken kann. Klickt man auf einen erscheint darunter die schon abgegebenen Anmerkungen (wenn es schon welche gibt) und ein Kommentarfeld. Jeder Absatz der Anmerkungen enthält wird durch ein kleines "Lesezeichen" markiert. Die Zahl steht für die Anzahl der Anmerkungen in diesem Absatz.

Genutzt werden kann dieses Feature für die unterschiedlichsten Dinge: Wenn ich Unsinn schreibe kann man mich direkt verbessern, falls jemand weitere Informationen zu einem Absatz kennt (eine URL, ein Zitat, eine Erweiterung, ...) kann man dies direkt an Ort und Stelle einfügen und natürlich lassen sich so auch Lobpreisung und Tadel im Text verankern.

Markus hat schon bemängelt, das das System keine Notifications für neue Anmerkungen vorsieht. Realisierungsvorschläge hierzu werden gerne angenommen. Mich würde jetzt interessieren, wie ihr diese neue Kommentartechnik findet. Ich habe versucht es so einfach wie möglich zu gestalten, so dass es einfacher ist als einen "normalen" Kommentar zu schreiben.

Ich freue mich auf Feedback.

Kommentar schreiben, delicious bookmark del.icio.us


F# Tutorial #2

vom 13. February 2008

Andreas hat in seinem Blog ein interaktives F#-Tutorial gestartet, in dem er jedes mal eine kleine Aufgabe am Ende stellt. Da ich mich sowieso sehr gerne mit neuen (und alten) Programmiersprachen und -Konzepten beschäftige, kommt mir dies sehr gelegen (vielen Dank für die Tutorials!). (F# (F Sharp) ist eine funktionale Programmiersprache von Microsoft, die auf dem .NET-Framework aufsetzt. Compiler gibt es für Windows (Visual Studio) und Mono (Linux/OSX.))

Hier nun meine Lösung für die erste Aufgabe (Fakultätsfunktion "!" definieren) vom zweiten Blogeintrag:

#light

let rec ( ! ) (n : int) =
    if (n = 1) then n
    else n * !(n-1)

let endresult = !5
printfn "Die fakultaet von 5 ist %i" endresult

1 Kommentar, delicious bookmark del.icio.us


Auserwählt?

vom 9. February 2008

Heute hab ich im Briefkasten einen merkwürdigen Brief entdeckt. Darin befand sich nur eine kleine blaue Sanduhr mit einem Zettel auf dem eine URL, und auf der Rückseite "Problems?" stand.

Image Alt Text

Die URL führt zu einer Präsentation mit einigen Rätseln. Nach etwas Recherche hab ich zwei weitere (der 10 Auserwählten) gefunden: Florian und Joerg. Die anderen heißen Alexander, Frank, Gollo, Hardy, Jan, Joachim und Markus. Florian hat die einfacheren Rätsel schon gelöst: Beispielsweise fühen die Koordinaten 48.856667°N 2.340833°E zu einer Brücke in Paris. Die Morsezeichen bei Folie a führen zu einem Wikipedia-Artikel.

Einige Aufgaben sind allerdings (noch) total nichtssagend. Beispielsweise 2D = 45 oder "Navfbfgvpgn abirzqrpvzchapgngn".

Meine Vermutungen: Die Folie 1 auf der eine Tabelle mit Einsen dargestellt ist verbindet die einzelnen Aufgaben anhand der Buchstaben. Eine andere Vermutung ist, das alle Lösungen irgend wie auf Orte bezogen werden kann und dies am Ende (wenn die Punkte in der richtigen Reihenfolge verbunden werden) ein Bild ergibt. (Vielleicht auch die Wohnorte von uns?). Also: Für alle, die auch eine Sanduhr bekommen haben sollten sich schnell melden :)

Von wem die Post stammt ist uns allerdings noch völlig unklar. Vielleicht verrät uns das die Lösung des Spiels. Nur dumm das ich momentan mitten in den Klausuren stecke und ich mich eigentlich mehr auf Verteilte Systeme II konzentrieren sollte.

Nachtrag: Es sieht so aus, als wäre ich geradewegs in ein ARG reingerutscht. Mehr Infos dazu gibts im Wiki und im IRC (irc.chat-solutions.org, #ARGR).

4 Kommentare, delicious bookmark del.icio.us


Sinnloses Stöckchen, Level 16

vom 7. February 2008

Über eine lange, verworrene Bloggerkette bin ich über Markus auch ein Teil des sinnlosen Stöckchen geworden. Die Aufgabe:

  1. Nimm das nächste Buch in deiner Nähe mit mindestens 123 Seiten.
  2. Schlage Seite 123 auf.
  3. Suche den fünften Satz auf der Seite.
  4. Poste die nächste drei Sätze.

Das "näheste" Buch ist bei mir gerade "Generative Programming" von Czarnecki und Eisenecker:

It also states that important issues should be represented in programs intentionally (i.e., explicitly, declaratively, and with little or no "extra noise") and should be well localized. This facilitates unserstandability, adaptability, reusability, and the many other good qualities of a program because intentionality and localisation allow us to easily verify how a program implements out requirements. Unfortunately, the relevant issues are usually mutually dependent and overlapping because they all concern one common model (i.e., out program being constructed). Thus, if we try to represent all these issues explicitly and locally, we will introduce a lot of redundancies.

An meinem Nachttisch liegt grade "Getting Things Done" vom Großmeister David Allen. Auf Seite 123 steht:

You may find you have a tendency, while processing your inbasket, to pick something up, not know exactly what you want zo do about it, and then let your eyes wander into another item fatther down the stack and get engaged with it. That item may be more attractive to your psyche because you know right away what to do with it - and you don't feel like thinking about what's in you hand. This is dangerous terretory.

Und ganz im Sinne von "Dont Break the chain" reiche ich das Stöcklein weiter an Andreas, Florian, Rubn und kb.

Kommentar schreiben, delicious bookmark del.icio.us


ETag

vom 4. February 2008

Ich habe mich heute wieder mal etwas mit Web-Caching beschäftigt und bin dabei auf eine sehr interessante Sache gestoßen: den ETag. Dieser ETag ist ein HTTP-Header der mit HTTP/1.1 eingeführt wurde. Mit ihm kann man feststellen ob sich der Content auf dem Server geändert hat, bevor man ihn herunterläd.

Da es dazu noch keinen deutschen Wikipedia-Artikel gab, hab ich schnell einen angelegt, so muss ich das ganze hier nicht noch einmal erklären :)

Das ganze funktioniert folgendermaßen (irb):

http = Net::HTTP.new('aaron-mueller.de', 80)
# <Net::HTTP aaron-mueller.de:80 open=false>

res = http.send_request('GET', '/rss')
# <Net::HTTPOK 200 OK readbody=true>
etag = res.header['etag']
# ""947e30c936""

res = http.send_request('GET', '/rss', nil, {'If-None-Match' => etag})
# <Net::HTTPNotModified 304 Not Modified readbody=true>
res.code
# "304"
res.body
# nil

Eine HTTP-Verbindung wird aufgebaut und eine GET-Anfrage gesendet. Die Antwort (response) enthält den Header ETag, den wir uns zwischenspeichern. Bei der zweiten Anfrage der gleichen Datei senden wir den Header If-None-Match mit dem zuvor gespeicherten ETag mit. Als Antwort erhalten wir einen 304-Statuscode, allerdings diesmal ohne body.

Moderne Webserver unterstützen das automatische Generieren und Anhängen eines ETags an jede Response. In LigHTTPd haben die Option den Prefix etag.* (Siehe Doku). Wenn der Webserver dies nicht unterstützt oder man nur bestimmte Anfragen mit den ETag verstehen will (oder man aber dynamischen Content hat statt statische Seiten) kann man die Header-Manipulation auch eine Stufe darüber (in der Scriptsprache seiner Wahl) erledigen. Hier ein Beispiel in PHP:

// Generate the ETag from the last Entry in the Database
$res = Entry::findAll('Entry', array(
    'order' => 'date DESC, id DESC',
    'limit' => 1
));
$etag = '"' . substr(md5($res[0]->date . $res[0]->title), 0, 10) . '"';
header("ETag: $etag");
        
// Check ETag
if (isset($_SERVER['HTTP_IF_NONE_MATCH']) &&
    $_SERVER['HTTP_IF_NONE_MATCH'] == $etag) {
    header('HTTP/1.0 304 Not Modified');
    die();
}

Wirklich genutzt wird dieses tolle Feature momentan leider nur bei Feed-Readern. Es wird langsam Zeit (HTTP/1.1 ist schon über 10 Jahre alt!) sowas unter den Reload-Button der Webbroser zu legen. Bei einem Klick kann statt einem kompletten Refresh der Seite nur eine Meldung ala "nothing changed" angezeigt werden. Das würde einiges an Traffic sparen.

Kommentar schreiben, delicious bookmark del.icio.us