Archiv für die Kategorie 'Administration'

03
Jul

Chaos Singularity (Cosin) im KuZeB Bremgarten

Am kommenden Wochenende findet das Chaos Singularity 2007 im KuZeB in Bremgarten (AG) statt. Der Fahrplan sieht folgends vor:

Freitag, 06. Juli

  • NAT-PMP – NAT Port Mapping Protocol (Vortrag)
  • Reflextraining mit Vim (Workshop)
  • Geekclock Code and Hardware Walkthrough (Vortrag)
  • Token-Search - Web2.0 Suchmaschine (Vortrag)
  • Auf Nummer Sicher (Film)

Samstag, 07. Juli

  • Rechnen wir analog, mit Rechenschieber! (Workshop)
  • Geekclock Workshop Teil 1 (Workshop)
  • Geekclock Code and Hardware Walkthrough (Vortrag)
  • The internals of Debian packages (Vortrag)
  • Freifunk Firmware (Workshop)
  • OpenSSH knack (Vortrag)
  • GPL-kompatibles Lobbying, damit das nächste Silicon Valley in der Schweiz ist (Vortrag)
  • e-Voting in der Schweiz (Vortrag, Diskussion)
  • Projekt Präsentation FreieComputer.ch (Vortrag)
  • Entschwörungstheorie (Vortrag)
  • HBO Special - Hacking Democracy (Film)

Sonntag 08. Juli

  • Geekclock Workshop Teil 2 (Workshop)
  • Müllertube, Radioaktivität selber messen (Vortrag)
  • Amateurfunk: Do-it-yourself (Vortrag)
  • IceCream (Workshop)
  • Cosin - What’s next? (Treffen)

Mehr Infos zu den einzelnen Vortragen, Workshops und Filmen sowie die Zeiten findet ihr auf der Cosin Homepage.

Es besteht am ganzen Wochenende auch die Möglichkeit sich am CaCert Stand über das CaCert-Projekt zu Informieren und ausserdem auch Punkte zu bekommen (2 Ausweise, einer davon amtlich, z.B. Pass oder ID, mitbringen!).

Am Sonntag findet dann auch noch eine GPG Keysigning Party statt.

Alles in allem ein sehr gelungener Mix aus diversen Themenbereichen und eine einmalige Gelegenheit die CaCert Punkte aufzustocken. Das KuZeB bietet auch eine tolle Plattform für weiterführende Diskussionen. Der Anlass selber ist sicher nicht nur für Geeks interessant sondern für alle die sich für Technologie und die Auswirkungen auf die Gesellschaft interessieren.

09
Mai

OpenLDAP unter Ubuntu installieren

Wer seinen eigenen kleinen Server mit diversen Diensten betreibt kennt das Dilemma: Jede Applikation speichert seine Benutzerdaten separat, so dass nach einer gewissen Zeit ein kleines Chaos entstehen kann. Hier springt OpenLDAP in die Bresche, liefert es doch einen so genannten Verzeichnisdienst für die zentrale Authentifizierung und kann auch als zentrales Adressbuch genutzt werden. Die beschriebene Installation kann unter Umständen auch auf andere Linux Derivate (vor allem Debian basierte Linux Distributionen) angewandt werden. Um Konfigurationsdatei, die eventuell in anderen Verzeichnissen abgelegt sind, schnell zu finden, kann locate verwendet werden.
Die Installation der Pakete ist mit dem Paketmanager schnell gemacht:

sudo  apt-get install slapd ldap-utils 

Dabei werden wir nach dem gewünschten Administrationspasswort gefragt. Nach der Installation läuft der LDAP Server bereits, was wir mit nmap leicht überprüfen können:

nmap localhost | grep ldap
389/tcp  open  ldap

Damit wir während der Konfiguration einfach auf die Debuginformationen zugreifen können, richten wir noch den Syslog Deamon für OpenLdap ein. Als erstes setzten wir den gewünschten Log Level, indem wir folgenden Wert in /etc/ldap/slapd.conf anpassen. Um den Wert des gewünschten Loglevels herauszufinden müssen wir in der Man Page nachschauen und die gewünschten Eigenschaften zusammenzählen:

man 5 slapd.conf

Dort suchen wir nach loglevel:

1      trace function calls
2      debug packet handling
4      heavy trace debugging
8      connection management
16     print out packets sent and received
32     search filter processing
64     configuration file processing
128    access control list processing
256    stats log connections/operations/results
512    stats log entries sent
1024   print communication with shell backends
2048   entry parsing

…und können uns nun den gewünschten Loglevel berechnen. Ich interessiere mich für die Verbindungen, Operationen und deren Resultat (256), die Suchfilter (32) und das Verbindungsmanagement (8), was zusammengezählt 296 ergibt. Also können wir nun den Loglevel setzen:

loglevel 296

Damit wir alle Ausgaben in einer eigenen Datei haben, fügen wir noch folgenden Eintrag in die Syslog-Konfigurationsdatei /etc/syslog.conf ein:

local4.debug /var/log/slapd.log

nach dem Neustart des LDAP und Syslog Deamons können wir nun alle Logeinträge mit tail -f /var/log/slapd.log beobachten.

Als nächstes müssen wir den Dienst noch an unsere Bedürfnisse anpassen. Dazu müssen wir uns für einen eindeutigen Namen für unser Verzeichnis entscheiden. Dieser eindeutige Name wird auch als DN (Distinguished Name) abgekürzt und identifiziert die oberste Ebene unseres Verzeichnisses (Root). In meinem Fall nehme ich einfach meine TLD netzpiraten.ch, was als dc=netzpiraten,dc=ch geschrieben wird. Eine andere Möglichkeit wäre z.B auch o=netzpiraten,c=ch (o für Organisation und c für Land).

Also editieren wir einfach /etc/ldap/slapd.conf und legen als erstes unseren DN fest, indem wir suffix anpassen.

suffix    "dc=netzpiraten,dc=ch"

Wir müssen dann auch konsequent alle nachfolgenden dc=nodomain durch dc=netzpiraten,dc=ch ersetzten. Damit wir auch einen ersten Eintrag in den LDAP Server speichern können, legen wir einen lokalen Administrator an:

rootdn "cn=admin,dc=netzpiraten,dc=ch"
rootpw {SSHA}B3MTCdJ+bDiAUUzMuhPzcrmKG33+8Wkj

Das Passwort können wir mit dem Hilsprogramm slappasswd erzeugen:

slappasswd
New password:
Re-enter new password:
{SSHA}B3MTCdJ+bDiAUUzMuhPzcrmKG33+8Wkj

Und schlussendlich muss noch der Server neu gestartet werden:

/etc/init.d/slapd restart

Der Server wäre nun bereit, jetzt müssen wir noch den Clients sagen wo unser LDAP Server zu finden ist. Dazu legen wir /etc/ldap/ldap.conf mit folgendem Inhalt an:

ldap_version 3
URI     ldap://ldap.netzpiraten.ch:389
SIZELIMIT       0
TIMELIMIT       0
DEREF           never
BASE dc=netzpiraten, dc=ch

Nun sind wir bereit die ersten Daten einzuspeisen, dazu legen wir eine LDIF Datei base.ldif an, welche wir mit folgenden Objekten füllen:

 dn:dc=netzpiraten,dc=ch
objectClass: dcObject
objectClass: organization
o: netzpiraten
dc: netzpiraten

Die einzelnen Objekte müssen zwingen mit einer Leerzeile voneinander getrennt sein!

dn:cn=admin,dc=netzpiraten,dc=ch
objectClass: organizationalRole
cn: admin

Nun verwenden wir ldapadd zum hinzufügen:

ldapadd -x -W -D cn=admin,dc=netzpiraten,dc=ch -f base.ldif
Enter LDAP Password:
adding new entry "dc=netzpiraten,dc=ch"
adding new entry "cn=admin,dc=netzpiraten,dc=ch"

Und können das Ergebnis gleich vom LDAP Server abfragen:

ldapsearch -x
# extended LDIF
#
# LDAPv3
# base <> with scope sub
# filter: (objectclass=*)
# requesting: ALL
#
# netzpiraten.ch
dn: dc=netzpiraten,dc=ch
objectClass: dcObject
objectClass: organization
o: netzpiraten
dc: netzpiraten
# admin, netzpiraten.ch
dn: cn=admin,dc=netzpiraten,dc=ch
objectClass: organizationalRole
cn: admin
# search result
search: 2
result: 0 Success
# numResponses: 3
# numEntries: 2

Nun ist unser LDAP Server einsatzbereit! Damit wir uns den Objektbaum auch besser veranschaulichen können, installieren wir noch phpldapadmin. Ich gehe davon aus, dass bereits ein Apache am laufen ist. Somit ist die Installation ein Kinderspiel:

apt-get install phpldapadmin

Da phpmyadmin ein LDAP Client ist, haben wir bereits durch das anlegen der ldap.conf genügen angaben gemacht um uns gleich bei der Webapplikation anzumelden. Einfach im Webbrowser den Pfad /phpldapadmin/htdocs/index.php öffnen.

phpLDAPadmin

Leider werden nun alle Passwörter im Klartext an den LDAP Server gesendet, was wiederum ein Sicherheitsrisiko ist. Darum aktivieren wir TLS damit alle Daten verschlüsselt werden in der Client-Server Kommunikation. In diesem Beispiel gehe ich davon aus, dass der Server bereits ein Zertifikat von cacert hat. In dem Fall ist eine der LDAP Server schnell konfiguriert, indem man /etc/ldap/slapd.conf folgendermassen anpasst:

TLSCACertificateFile   /etc/ssl/certs/cacert.org.pem
TLSCertificateFile      /etc/ssl/netzpiraten/netzpiraten.crt
TLSCertificateKeyFile   /etc/ssl/netzpiraten/netzpiraten.key

Als nächstes muss nur noch die Client Konfiguration in /etc/ldap/ldap.conf angepasst werden:

TLS_CACERT /etc/ssl/certs/cacert.org.pem
TLS_REQCERT demand
06
Mrz

Der Ubuntu-Workshop ist tot - lang lebe der Ubuntu-Workshop & das Computerlabor

Für alle Ubuntu-Interessierten in der Region Bremgarten hier noch der Hinweis von Kire auf das Computerlabor im KuZeB:

Alloahe!

Der Ubuntu-Workshop ist tot -
                   lang lebe der Ubuntu-Workshop & das Computerlabor

Nachdem wir uns nun einige Montage ausschliesslich mit Ubuntu Linux
auseinander gesetzt haben, möchten wir die Themen nun breiter fassen:

Mo. 12.3.2007:   Dateien herunterladen & verteilen mit BitTorrent
Mo. 16.4.2007:   Ubuntu-Workshop
Mo. 14.5.2007:   E-Mails verschlüsseln mit PGP/GnuPG
Mo. 11.6.2007:   Ubuntu-Workshop
Mo. 10.9.2007:   Anonym surfen mit TOR, JAP & Freenet
Mo. 8.10.2007:   Ubuntu-Workshop

Die Workshops beginnen jeweils pünktlich um 20.00 Uhr in der neuen
Läsothek und stehen selbstverständlich auch Apfel- und Fenster-
AnwenderInnen offen. Wer möchte, bringt seinen/ihren eigenen Computer
(und ein Netzwerkkabel) mit.

An den Ubuntu-Workshops sind auch NeueinsteigerInnen willkommen.

Taufrisch wurde zudem eine Mailingliste aus der Taufe gehoben, wo
TeilnehmerInnen zu den Themen diskutieren und einander helfen können.

Themenvorschläge, Anregungen und Mutationen an der Mailingliste nehme
ich gerne entgegen.

Grüssle
Kire

P.S.: Den Flyer gibt's auch hier: http://www.kire.ch/linux/workshop.gif

Natürlich sind die Themen-Workshops zu BitTorrent, E-Mail-Verschlüsselung, Anonym surfen auch für Benutzer anderer Linux Derivate als Ubuntu sehr informativ und hilfreich.

Danke Kire für deinen Beitrag zu Opensource!

25
Feb

Wordpress PostRatings Vandalismus beheben

WP-PostRatings ist ein nettes Wordpress Plugin, mit welchem die Blog Leser für jeden Artikel eine Bewertung abgeben können. So erfährt man als Blogger welche Artikel besser ankommen als andere. Natürlich muss man auch einmal mit einer schlechten Bewertung rechnen; was einem vielleicht nicht gerade freut aber doch eine wertvolle Rückmeldung ist. Etwas ganz anderes ist aber die systematische, schlechte Bewertung durch einen Leser quer durch den ganzen Blog. Das hat mit einer Kritik der Artikelqualität wenig zu tun sondern grenzt an virtuellen Vandalismus.

Das Plugin merkt sich zwar die Aktionen der Bewertungen in einer MySQL Tabelle, jedoch gibt es keine einfache Möglichkeit gewissen Bewertungen im Adminbereich wieder zu entfernen. Da hilft nur der direkte Eingriff in die darunterligende MySQL Datenbank.

Ich möchte in diesem Artikel aufzeigen, wie man IP basiert WP-PostRatings Bewertungen aus dem System entfernt. Dazu muss man auf dem Server in die MySQL Konsole gehen und die Wordpress Datenbank auswählen. Zur zusätzlichen Sicherheit starten wir auch gleich eine neue Transaktion:

mysql> USE wordpress;
mysql> START TRANSACTION;

Als nächstes schauen wir uns die PostRatings Log Tabelle an, welches auch im Wordpress Administrationsbereich zugänglich ist (dort werden ich normalerweise auch auf die gezielten schlechten Bewertungen aufmerksam und merke mit dann die IP):

mysql> DESC wp_ratings;
+------------------+--------------+------+-----+---------+----------------+
| Field            | Type         | Null | Key | Default | Extra          |
+------------------+--------------+------+-----+---------+----------------+
| rating_id        | int(11)      |      | PRI | NULL    | auto_increment |
| rating_postid    | int(11)      |      |     | 0       |                |
| rating_posttitle | text         |      |     |         |                |
| rating_rating    | int(2)       |      |     | 0       |                |
| rating_timestamp | varchar(15)  |      |     |         |                |
| rating_ip        | varchar(40)  |      |     |         |                |
| rating_host      | varchar(200) |      |     |         |                |
| rating_username  | varchar(50)  |      |     |         |                |
| rating_userid    | int(10)      |      |     | 0       |                |
+------------------+--------------+------+-----+---------+----------------+

Somit können wir mit einem einfachen SQL Befehlt die betroffenen Artikel ID’s und die abgegebene Bewertung für eine bestimmte IP abfragen:

mysql> SELECT rating_postid, rating_rating FROM wp_ratings WHERE rating_ip='83.76.155.53';
+---------------+---------------+
| rating_postid | rating_rating |
+---------------+---------------+
|             3 |             1 |
|            38 |             1 |
+---------------+---------------+

Wenn nötig kann man das Resultat ja auch noch anhand weiterer Kritierien wie z.B. den Zeitstempel eingrenzen. Die aktuellen Werte welche das Plugin bei jedem Artikel anzeigt sind sehr generisch in einer Wordpress eigenen Metadaten-Tabelle abgelegt:

mysql> desc wp_postmeta;
+------------+--------------+------+-----+---------+----------------+
| Field      | Type         | Null | Key | Default | Extra          |
+------------+--------------+------+-----+---------+----------------+
| meta_id    | bigint(20)   |      | PRI | NULL    | auto_increment |
| post_id    | bigint(20)   |      | MUL | 0       |                |
| meta_key   | varchar(255) | YES  | MUL | NULL    |                |
| meta_value | longtext     | YES  |     | NULL    |                |
+------------+--------------+------+-----+---------+----------------+

Das Plugin speichert die Werte als drei verschiedene Schlüssel/Wertepaare ratings_users, ratings_score und ratings_average. Der Schlüssel ratings_users steht für die Anzahl abgegebenen Bewertungen, ratings_score für die gesamte Anzahl der Punkte aller Bewertungen und ratings_average für den daraus resultierenden Durchschnitt. Man benötigt also einen self-join um alle diese Informationen in Bezug auf den Artikel auszugeben. Desshalb kann man aber auch keine veränderbare View erstellen sondern muss mit einer temporären Tabelle arbeiten:

mysql> CREATE TEMPORARY TABLE
  tmp_postratings
SELECT
  a.post_id AS post_id,
  a.meta_id as users_meta_id,
  a.meta_value AS users,
  b.meta_id as score_meta_id,
  b.meta_value AS score,
  c.meta_id as average_meta_id,
  c.meta_value AS average
FROM
  wp_postmeta a,
  wp_postmeta b,
  wp_postmeta c
 WHERE
  a.post_id=b.post_id AND
  a.post_id=c.post_id AND
  a.meta_key='ratings_users' AND
  b.meta_key='ratings_score' AND
  c.meta_key='ratings_average';

Diese Tabelle kann nun verwendet werden um all die benötigten Informationen anzuzeigen. Mich interessiert nun aber gerade die in diesem Beispiel geänderten Artikel mit der ID 3 und 38, welche wir ja vorher aus dem Log extrahiert haben:

mysql> SELECT * FROM tmp_postratings WHERE post_id IN (3,38);
+---------+---------------+-------+---------------+-------+-----------------+---------+
| post_id | users_meta_id | users | score_meta_id | score | average_meta_id | average |
+---------+---------------+-------+---------------+-------+-----------------+---------+
|      38 |           424 | 2     |           425 | 6     |             426 | 3       |
|       3 |           730 | 1     |           731 | 1     |             732 | 1       |
+---------+---------------+-------+---------------+-------+-----------------+---------+

Natürlich kann man jetzt in der Tabelle die Werte einzeln anpassen, aber eleganter geht es, wenn wir gleich pro IP die Werte aus dem Log holen:

mysql> UPDATE tmp_postratings a LEFT JOIN wp_ratings b ON a.post_id = b.rating_postid
SET a.users=a.users-1, a.score=a.score-b.rating_rating
WHERE b.rating_ip='83.76.155.53';

Natürlich überprüfen wir das Resultat gleich:

mysql> SELECT * FROM tmp_postratings WHERE post_id IN (3,38);
+---------+---------------+-------+---------------+-------+-----------------+---------+
| post_id | users_meta_id | users | score_meta_id | score | average_meta_id | average |
+---------+---------------+-------+---------------+-------+-----------------+---------+
|      38 |           424 | 1     |           425 | 5     |             426 | 3       |
|       3 |           730 | 0     |           731 | 0     |             732 | 1       |
+---------+---------------+-------+---------------+-------+-----------------+---------+

Die Anzahl der Benutzer und auch der Gesamtwert ist nun korrigiert, den Durchschnitt müssen wir aber neu berechnen:

mysql> UPDATE tmp_postratings SET average=ROUND(score/users);

Da aber eine Division durch 0 als Resultat NULL ergibt, müssen wir diese Fälle noch extra auf 0 zurücksetzten:

mysql> UPDATE tmp_postratings SET average=0 WHERE average IS NULL;

Eine nochmalige Kontrolle bestätigt uns nun, dass die Werte korrekt sind:

mysql> SELECT * FROM tmp_postratings WHERE post_id IN (3,38);
+---------+---------------+-------+---------------+-------+-----------------+---------+
| post_id | users_meta_id | users | score_meta_id | score | average_meta_id | average |
+---------+---------------+-------+---------------+-------+-----------------+---------+
|      38 |           424 | 1     |           425 | 5     |             426 | 5       |
|       3 |           730 | 0     |           731 | 0     |             732 | 0       |
+---------+---------------+-------+---------------+-------+-----------------+---------+

Die Daten sind also korrekt und können wieder zurück in Wordpress Tabelle geschrieben werden. Zuerst die Anzahl der Bewertungen:

mysql> UPDATE wp_postmeta a LEFT JOIN tmp_postratings b ON a.meta_id = b.users_meta_id
SET a.meta_value = b.users WHERE a.meta_key='ratings_users';

Dann die Bewertungspunkte:

mysql> UPDATE wp_postmeta a LEFT JOIN  tmp_postratings b ON a.meta_id = b.score_meta_id
SET a.meta_value = b.score WHERE a.meta_key='ratings_score';

Und schlussendlich noch den Durchschnitt:

mysql> UPDATE wp_postmeta a LEFT JOIN tmp_postratings b ON a.meta_id = b.average_meta_id
SET a.meta_value = b.average WHERE a.meta_key='ratings_average';

Um ganz sicher zu sein überprüfen wir das Ergebniss nochmals:

mysql> SELECT
  a.post_id AS id,
  a.meta_value AS users,
  b.meta_value AS score,
  c.meta_value AS average
FROM
  wp_postmeta a,
  wp_postmeta b,
  wp_postmeta c
 WHERE
  a.post_id=b.post_id AND
  a.post_id=c.post_id AND
  a.meta_key='ratings_users' AND
  b.meta_key='ratings_score' AND
  c.meta_key='ratings_average' AND
  a.post_id IN (3,38);

Ist alles wie gewünscht bestätigen wir die wir die Transaktion mit COMMIT; oder brechen die ganze Geschichte mit einem ROLLBACK; ab.

Tja lieber Vandale, deine Arbeit ist also mit ein paar wenigen SQL Queries wieder zunichte gemacht, und beim nächsten Mal brauche ich dank diesem Artikel sogar noch weniger Zeit als du ;-)

31
Dez

Postfix Logdateien mit AWStats auswerten

Um die Logdateien von Postfix auszuwerten und auch noch grafisch schön aufzubereiten eignet sich AWStats hervorragend. AWStats bringt auch mit maillogconvert.pl ein Perlscript für die Konvertierung von Maillogs von diversen Mailserrvern mit um die Mailserver Logfiles auswerten zu können. Unter Ubuntu in Kombination mit Avamis klappt nun nach der Umstellung zur aktuellsten Version Edgy Eft die Auswertung nicht mehr richtig: Das Perlscript findet nur noch alle fehlgeschlagenen Emails, jedoch keine welche erfolgreich versendet wurden (SMTP Status 250). Da das Script durch die Unterstützung diverser Mailserver ziemlich gross ist und ich auch kein Perl-Fan, habe ich mir selber ein kleines Script für die Konvertierung in Ruby geschrieben:

#!/usr/bin/env ruby
# Converts a postfix log file into awstats mailserver log
# Author: Michael Kessler
# No rights reserved
require 'date'
senders = Hash.new
mailservers = Hash.new
year = Time.now.strftime(" %Y")
for line in STDIN.read.split("\n") do
 
  #rejected messages with full qualified hostname of mailserver
  if line =~ /^(\w\w\w [\d\s]\d) (\d\d:\d\d:\d\d).*NOQUEUE.*RCPT from ((?:[-a-z0-9]+\.)+[a-z]{2,})\[.*\]: (\d\d\d).*from=<([^@\s]+@(?:[-a-z0-9]+\.)+[a-z]{2,})> to=<([^@\s]+@(?:[-a-z0-9]+\.)+[a-z]{2,})>/
    puts “#{Date.parse($1 + year).strftime(’%Y-%m-%d’)} #{$2} #{$5} #{$6} #{$3} - SMTP - #{$4} ?”
 
  #rejected messages with only ip address of mailserver
  elsif line =~ /^(\w\w\w [\d\s]\d) (\d\d:\d\d:\d\d).*NOQUEUE.*RCPT from unknown\[(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})?\]: (\d\d\d).*from=<([^@\s]+@(?:[-a-z0-9]+\.)+[a-z]{2,})> to=<([^@\s]+@(?:[-a-z0-9]+\.)+[a-z]{2,})>/
    puts “#{Date.parse($1 + year).strftime(’%Y-%m-%d’)} #{$2} #{$5} #{$6} #{$3} - SMTP - #{$4} ?”
 
  #amavis passed email, get sender for accepted messages
  elsif line =~ /^.*amavis.* Passed.*<([^@\s]+@(?:[-a-z0-9]+\.)+[a-z]{2,})> -> <.* queued_as: (\w+),/
    senders.store($2,$1)
 
  #connected mailserver with full qualified hostname
  elsif line =~ /^.*postfix\/smtpd\[\d+\]: (\w+): client=((?:[-a-z0-9]+\.)+[a-z]{2,})\[.*\]/
    mailservers.store($1,$2)
 
  #connected mailserver with only ip address
  elsif line =~ /^.*postfix\/smtpd\[\d+\]: (\w+): client=unknown\[(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})?\]/
    mailservers.store($1,$2)
 
  #accepted messages
  elsif line =~ /^(\w\w\w [\d\s]\d) (\d\d:\d\d:\d\d).* (\w+): to=<([^@\s]+@(?:[-a-z0-9]+\.)+[a-z]{2,})>.* queued as (\w+)/
    puts “#{Date.parse($1 + year).strftime(’%Y-%m-%d’)} #{$2} #{senders.fetch($5,’unknown’)} #{$4} #{mailservers.fetch($3,’unknown’)} - SMTP - 250 ?”
  end
end

Das Problem mit den gesendeten Emails ist, dass die benötigten Informationen nicht auf einer Logzeile zu finden sind. Desshalb müssen diese Vorgängig geparst werden und anhand der Message-ID beim Senden angereichert werden. Dies kann bei einer täglichen Rotation der Logdateien auch dazu führen, dass nicht alle Informationen in der gleichen Logdatei zu finden ist.

Das Script sieht auf den ersten Blick nicht wirklich lesbarer als ein Perl Script aus, was jedoch an den langen regulären Ausdrücken liegt und nicht an Ruby. Das Script wird übrigens genau gleich wie maillogconvert.pl in die AWStats Konfiguration eingebunden.