Archiv der Kategorie: Softwareentwicklung

Smart Cards durch die in Java eingebaute Java Smartcard I/O javax.smartcardio.* ansprechen

Zuerst mache ich eine kleine Einführung in das Thema Java und Smartcads und beschreibe danach wichtige Punkte für Linux und Windows.

Java hat seit Version 1.5 (5.0) eine Unterstützung für Smartcards bereits eingebaut. Es nutzt dafür die eigene Provider Technik und greift über PC/SC auf die Smartcard zu. Das bedeutet es sind keine weiteren Module für Java notwendig (früher JPCSC für Linux) und die Nutzung erfolgt unabhängig vom Betriebssystem. Zumindest kann ich das für Windows XP/Vista und Linux bestätigen. FreeBSD und Sun Solaris sollten genau so gut funktionieren. Weitere Informationen zu Betriebssystemen in den nächsten Absätzen.

Unter Windows ist die Verwendung denkbar einfach. Für die Nutzung ist ein installierter Treiber für das Lesegerät (Terminal) und natürlich Java notwendig. Windows bringt eine eigene PC/SC Implementierung mit, die Java über den eingebauten Provider nutzt. Dadurch ist die Installation und Einrichtung sehr einfach.

Für Linux ist die Installation fast genau so einfach. Durch die verschiedenen pcsclite-Versionen (PC/SC Implementierung unter Linux) sind aber die Header-Datei (.h) für die Bibliothek notwendig, damit Java die PC/SC Bibliothek richtig ansprechen kann. Dafür reicht meistens die Installation der folgenden Pakete:

  • libpcsclite1
  • pcscd
  • libccid
  • libpcsclite-dev  (WICHTIG: Hier sind die notwendigen Header-Dateien)

Fehlt das Paket libpcsclite-dev bzw. die Header Dateien, so kann Java die PC/SC Bibliothek nicht nutzen und findet deswegen kein Lesegerät (Terminal).

Für Debian/Ubuntu und weitere Debian-Derivate ist die Installation denkbar einfach:

[code]
sudo aptitude install libpcsclite1 pcscd libccid libpcsclite-dev
[/code]

Die Nutzung der JPCSC Middleware, welcher veraltet ist, habe ich bereits früher hier beschreiben.

Um die Verwendung der Java Smartcard I/O zu testen, habe ich ein paar kleine Testprogramme geschrieben, die ich hier veröffentliche. Sie sind auf Englisch kommentiert, so dass keine weitere Erklärungen erfolgen. Ich denke die paar Worte Englisch wird wohl jeder verstehen können. Sie sind extra kurz und einfach gehalten.

[code]
import java.util.List;
import javax.smartcardio.*;

/*
* Look in rt.jar for „javax.smartcardio“ to get further information
* Defined in: JSR 268
*/
public class ListSmartcardReaders {

public static int listCounted() {

/* we use the default TerminalFactory */
TerminalFactory factory = TerminalFactory.getDefault();

try {
/* We can have multiple terminals on one System, so we get a list */
List terminals = factory.terminals().list();

for (CardTerminal terminal : terminals) {
System.out.println(„Card_Terminal_Name: „+ terminal.getName());
System.out.println(„Card_in_Terminal_present: „+terminal.isCardPresent());
System.out.println(„——————————————–„);
}
return terminals.size();

} catch (CardException e) {
e.printStackTrace();
}
return 0;
}

public static void main(String[] args) {
listCounted();
}
}
[/code]

[code]
import java.util.List;
import javax.smartcardio.*;

/*
* Look in rt.jar for „javax.smartcardio“ to get further information
* Defined in: JSR 268
*/
public class ConnectReader {

/**
*
* @param index is the array index of the terminal list (0..x)
* @throws CardException if there are problems accessing the smartcard
*/
public static void connectCard(int index) throws CardException {

/* Is a Reader connected we can access? */
if (TerminalFactory.getDefault().terminals().list().size() == 0) {
System.err.println(„No reader present“);
return;
}

/* Terminal we are working on */
CardTerminal terminal = TerminalFactory.getDefault().terminals().list().get(index);

/* Is a card present? */
if (!terminal.isCardPresent()) {
System.err.println(„No Card present!“);
return;
}

/* Here you have to choose „T=0″,“T=1“, „T=2“, check documentation of your smart card */
//Mostly it’s „T=1“, for older cards its „T=0“
Card card = terminal.connect(„T=1“);

System.out.println(„Card_Info: „+card.toString());
System.out.println(„Card Protocol: „+ card.getProtocol());

//Reset the card for use
ATR atr = card.getATR();

System.out.println(„ATR: “ + atr.getBytes());
System.out.println(„ATR historical bytes: „+ atr.getHistoricalBytes());

/* Get the basic channel. This one can’t be closed */
CardChannel channel = card.getBasicChannel();

/* Try to send a command. This one won’t work! */
byte[] command = { 0, 0, 0, 0};

CommandAPDU someApdu = new CommandAPDU(command);

ResponseAPDU r = channel.transmit(someApdu);
/* Response encoded in bytes */
byte[] response = r.getBytes();

System.out.println(„response: „+ response);

card.disconnect(false);
}

public static void main(String[] args) {
try {
//First Terminal = 0
connectCard(0);
} catch (Exception e) {
e.printStackTrace();
}
}
}
[/code]

Für weitere Information über APDUs und die Nutzung der Smartcards empfehle ich das Lesen des Buches Handbuch der Chipkarten oder direkt die dazugehörigen ISO-Normen ISO-7816 und im Speziellen die ISO-Normen ISO7816-4, ISO7816-8 und ISO7816-9.

Auf die Frage „Kommt man ohne das Buch bzw. die Normen aus?“, muss ich leider mit „definitiv nein!“ beantworten, da die Informationen sehr komplex sind und schwer zu verstehen. Im Internet ist bis Heute fast gar nichts vorhanden.
Ich rate aber nach Diplom-, Master- und Bachelorarbeiten zu dem Thema zu suchen. Da gibt es bestimmt Auszüge aus den Normen, um wenigstens die Zusammehänge und die Anfänge zu verstehen.

In Eclipse Galileo (3.5.X) Plugins installieren/nachinstallieren Probleme

Das Eclipse Update System ist leider sehr complex und damit für Fehler anfällig. Unter bestimmten Bedingungen bekommt man bei der Installation von neuen Plugins folgende Fehler: „No repository found containing: …..“

Dieser Fehler trat bei mir auf und deutet auf die Galileo Update Site direkt von Eclipse:

[code]
An error occurred while collecting items to be installed
session context was:(profile=epp.package.jee, phase=org.eclipse.equinox.internal.provisional.p2.engine.phases.Collect, operand=, action=).
No repository found containing: osgi.bundle,net.sf.cglib,2.1.3.v200906051230
No repository found containing: osgi.bundle,org.eclipse.hyades.probekit,4.2.500.v200906041936
No repository found containing: osgi.bundle,org.eclipse.mylyn.commons.soap,3.2.0.v20090617-0100-e3x
No repository found containing: osgi.bundle,org.eclipse.mylyn.jira.core,3.2.0.v20090617-0100-e3x
No repository found containing: osgi.bundle,org.eclipse.mylyn.jira.ui,3.2.0.v20090617-0100-e3x
[/code]

Eclipse versucht alle Update Sites zu kontaktieren und dort Versionen zu erfragen, damit es Abhängigkeiten zwischen den Biblioetheken richtig auflösen kann. Treten dabei Probleme auf, weil die Seite z.B. überlastet oder gerade nicht erreichbar ist, dann misslingt die Installation.

Eine mögliche Lösung besteht darin, die betroffene Seite unter „Window -> Preferences -> Install/Update -> Available Software Sites“ zu deaktivieren.

Das ist meistens die Haupt-Updateseite von Eclipse:

[code]
Galileo        http://download.eclipse.org/releases/galileo Enabled <-- auf Disabled setzen [/code] Hilft das nicht weiter, so müssen am besten weitere Seiten deaktiviert werden, bis die Installation funktioniert.

Unter Eclipse über Java auf zugriffsbeschränkte (not accessible) Pakete am Beispiel von javax.smartcardio.*) im Java Projekt zugreifen

Die Nutzung von bestimmten Paketen ist unter Eclipse mit Java mit Access Rules beschränkt (standardmäßig nicht erlaubt). Das gilt insbesondere für „javax.*“ Pakete. In meinem Fall bestand die Schwierigkeit auf die Klassen „javax.smartcardio.*“ zuzugreifen. Das Problem kann auf die folgende Art und Weise (auch für andere Pakete) in Eclipse festgelegt werden:

  1. Java Projekt erstellen oder ein bestehendes Projekt öffnen
  2. Mit rechter Maustaste auf das Projekt gehen und dann die Eigenschaften (Properties) öffnen.
  3. Unter „Java Build Path“ und dann „Libraries“ den „JRE System Library“ Baum aufschlagen.
  4. Den Punkt „Access Rules“ auswählen und rechts auf den Knopf „Edit“ drücken.
  5. Dort mit „Add“ eine neue Regel anlegen; auf „Accessible“ setzen und für mich den Wert „javax/smartcardio/**“ eintragen, um auf alle Klassen von „javax.smartcardio“ zugreifen zu können.

Das Problem besteht unter Eclipse und gilt nicht für ein Programm, welches unter der Kommandozeile erstellt und kompiliert wird.

So jetzt gehts für mich an die Programmierung von APDUs zur Steuerung von Smartcards.

Mit Java über JPCSC auf den Smartcardreader und die Smartcard unter Linux zugreifen

Nach etwas Arbeit habe ich den JPCSC Wrapper in der Version 0.8.0 für PCSC unter Ubuntu Jaunty 9.04 zum laufen gebracht. Dafür waren diverse Änderungen am Quellcode von JPCSC notwendig, die ich zuerst beschreibe.

Die folgenden Pakete müssen zuerst auf dem System installiert werden:
[code]
sudo aptitude install sun-java6-jdk libpcsclite-dev libpcsclite1
[/code]

Danach muss JPCSC von der Linuxnet.com Seite runtergeladen und entpackt werden. Das Erstellen mit dem Befehl „make“ wird nicht funktionieren, weil diverse Header-Dateien von PCSC nicht in dem Standard-Pfad /usr/include/ liegen, sondern in dem Pfad /usr/include/PCSC/.

Die folgenden Header-Dateien sind davon betroffen:
[code]
ifdhandler.h
pcsclite.h
reader.h
winscard.h
wintypes.h
[/code]

Um dieses Problem zu lösen, ist es am Einfachsten die Bibliotheken in dem Pfad /usr/include zu verlinken. Dazu sollten die folgenden Befehle reichen:

[code]
cd /usr/include
sudo ln -s PCSC/ifdhandler.h ifdhandler.h
sudo ln -s PCSC/pcsclite.h pcsclite.h
sudo ln -s PCSC/reader.h reader.h
sudo ln -s PCSC/winscard.h winscard.h
sudo ln -s PCSC/wintypes.h wintypes.h
[/code]

Zusätzlich muss die Zeile 769 in der Datei jpcsc/src/jpcsc/jpcsc.c angepasst werden, weil sie nicht mehr dem Aufruf aus der pcsc-Bibliothek passt:

[code]
<      rv =  SCardControl(card, cin + joff, jlen, cout, &clen); --- >     rv =  SCardControl(card, SCARD_CTL_CODE(jcc), cin + joff, jlen, cout, clen, &clen);
[/code]

Also „SCardControl(card, cin + joff, jlen, cout, &clen);“ ersetzen durch „SCardControl(card, SCARD_CTL_CODE(jcc), cin + joff, jlen, cout, clen, &clen);“.

Danach sollte es möglich sein den Code zu erstellen.

Um über den Wrapper auf die Bibliothek zuzugreifen, muss die Bibliothek jpcsc.so und jpcsc.jar über den folgenden Parameter verlinkt werden. Der folgenden Parameter muss dem Aufruf vor der Ausführung als Parameter direkt übergeben werden:

[code]
java -Djava.library.path=lib/ Test
[/code]

In dem von mir gezeigten Beispiel müssen die beiden Dateien „jpcsc.so“ und „jpcsc.jar“ in den Ordner „lib/“ gelegt werden, damit sie angesteuert werden.

Unter Eclipse muss unter „Run as “ unter Configurations nur der Parameter bei VM-Arguments eingetragen werden:

[code]
-Djava.library.path=lib/
[/code]

Beispielcode:

[code]
import com.linuxnet.jpcsc.*;

public class JPCSCUsecase {

public JPCSCUsecase() {
try {
Context context = new Context();
context.EstablishContext(PCSC.SCOPE_GLOBAL, null, null);

String[] readers = context.ListReaders();
if (readers.length <= 0) { System.err.println("No readers found..."); return; } //Talk to the first reader = 0 String reader = readers[0]; System.out.println("Using reader " + reader); Card card = context.Connect(reader); card.BeginTransaction(); //Do something with APDUS System.out.println("APDU: "+ readApduInfo(card)); card.EndTransaction(PCSC.RESET_CARD); card.Disconnect(); context.ReleaseContext(); } catch (Exception e) { e.printStackTrace(); } } private String readApduInfo(Card card) { Apdu apdu = new Apdu((byte) 0x20, (byte) 0x13, (byte) 0x00, (byte) 0x81, 0, null, 0, 5); byte[] data = card.Transmit(apdu); return bytesToString(data); } private String bytesToString(byte[] bytes) { String output = ""; for (int i = 0; i < bytes.length; i++) { output += "0x"+bytes[i]+ " "; } return output; } public static void main(String[] arg) { new JPCSCUsecase(); } } [/code] Eigentlich ist es besser und vor allem einfacher über die Implementierung von javax.smartcardio.* zuzugreifen. Weitere Informationen folgen noch.

Mit ThreadPoolExecutor Arbeit unter Java effizient parallelisieren

Es gibt immer wieder Aufgaben, die sehr lange dauern und sehr große Mengen an Daten verarbeiten müssen. Dabei geht es meistens um Minuten oder gar Stunden, Tage …

Diese sollten sinnvollerweise parallelisiert werden, um die Leistungs des Systems zu nutzen. Heutige Systeme sind sehr oft Mehrkern- bzw. Mehrprozessorsysteme. Auch ist das GHz-Rennen im Moment zu Ende und die Leistung steigt über die Anzahl der Prozessoren bzw. Kerne.

Das folgende Beispiel zeigt wie man unter Java Aufgaben parallel verarbeiten kann, ohne sich groß den Kopf um die Ausführung zerbrechen zu müssen. Der ThreadPoolExecutor ist meiner Meinung nach eine sehr gute Möglichkeit Aufgaben zu parallelisieren, die Parallel ausgeführt werden können. Der große Vorteil ist, dass er Threads für das Abarbeiten am laufen hält. Das bedeutet bei neuen Aufgaben werden normalerweise keine Threads gestartet und somit entfällt faktisch dieser Overhead. Threads im Pool werden nur beendet, wenn sie länger als der festgelegte Timeout nicht benutzt werden oder sie vom Programmierer explizit beendet werden.

Nicht erschrecken. Das meiste davon sind Kommentare und Beispielcode.

Ich weiß – weniger Gelaber und her mit dem Code!

[code]
import java.util.concurrent.*;

class MyThreadPoolExecutor
{
//Parallel running Threads(Executor) on System
int corePoolSize = 2;

//Maximum Threads allowed in Pool
int maxPoolSize = 4;

//Keep alive time for waiting threads for jobs(Runnable)
long keepAliveTime = 10;

//This is the one who manages and start the work
ThreadPoolExecutor threadPool = null;

//Working queue for jobs (Runnable). We add them finally here
final ArrayBlockingQueue workQueue = new ArrayBlockingQueue(5);

public MyThreadPoolExecutor() {
threadPool = new ThreadPoolExecutor(corePoolSize, maxPoolSize,
keepAliveTime, TimeUnit.SECONDS, workQueue);
}

/**
* Here we add our jobs to working queue
*
* @param task a Runnable task
*/
public void runTask(Runnable task) {
threadPool.execute(task);
System.out.println(„Tasks in workQueue..“ + workQueue.size());
}

/**
* Shutdown the Threadpool if it’s finished
*/
public void shutDown() {
threadPool.shutdown();
}

public static void main(String args[]) {
MyThreadPoolExecutor mtpe = new MyThreadPoolExecutor();

for (int i= 0; i < 6; i++ ) { mtpe.runTask(new WorkerRunnable(i)); } mtpe.shutDown(); System.out.println("Finished! :)"); } /** * This is the one who do the work * * This one is static for accessing from main class */ private static class WorkerRunnable implements Runnable { //so we can see which job is running private int jobNr; /** * This is for understanding only * * @param jobNr number for displaying */ public WorkerRunnable(int jobNr) { this.jobNr = jobNr; } @Override public void run() { for(int i = 0; i < 10; i++) { try { System.out.println("Thread "+jobNr+" calculated "+ i); Thread.currentThread().sleep(1000); } catch (InterruptedException ie) { ie.printStackTrace(); } } } } } [/code] Da in diesem Beispiel die Anzahl der Parallel laufender Threads (corePoolSize) auf statisch 2 begrenzt ist, sollte die Zahl über Prozessoranzahl (siehe hier) bestimmt werden. Auch sollte man nicht vergessen die maximale Anzahl an Threads (maxPoolSize) entsprechend anzupassen.

Anzahl Prozessoren bestimmen welche für die Java Virtual Machine (JVM) vom System zur Verfügung stehen

Da derzeitige Computersysteme in Anzahl der Prozessoren (im Moment Kerne) und nicht mehr im GHz wachsen, muss auch ein Umdenken bei der Programmierung passieren. Das bedeutet wir müssen als Programmierer Aufgaben parallelisieren oder unabhängige Prozesse bzw. Threads parallel ausführen, um die Leistung möglichst effizient ausnutzen zu können. Wenn das möglich ist, dann sollte das Programm über die Anzahl der Prozessoren (im Moment Kerne) selbstständig bestimmen, wie viele Prozesse bzw. Threads es parallel ausführt.

Das folgende Codefragment bestimmt die Prozessoranzahl unter Java:
[code]
public class ProcessorCounter {

public static void main(String[] args) {

Runtime runtime = Runtime.getRuntime();

int nrOfProcessors = runtime.availableProcessors();

System.out.println(„Number of processors available to the Java Virtual Machine: “ + nrOfProcessors);
}
}
[/code]

Die 25 gefährlichsten Programmierfehler zum Thema Sicherheit

Ein Zusammenschluss von vielen namhaften Firmen hat eine List der 25 gefährlichsten Programmierfehlern veröffentlicht. Mindestens diese sollte sich jeder Programmierer merken und sie nicht mehr machen.

Hier ist der Bericht zu finden:

http://www.sans.org/top25errors/

Natürlich helfen solche Maßnahmen, aber die imperfiktion des Menschen ist das eigentliche Problem und der Ausweg daraus sind ordentliche Tests, die sicher stellen, dass Fehler ausgeschlossen werden.
Folgende Stichpunkte helfen hier weiter:

  • Qualitätssicherung (als Oberbegriff)
  • Unit Tests
  • Sicherheitsanalyse durch Spezialisten

Gute Ratschläge für Software und Softwarekomponenten für die Softwareentwicklung sind teuer

Die Entscheidung für die Verwendung einer Software oder Softwarekomponente hat meistens weitreichende Folgen für das Projekt. Natürlich kann durch entsprechende Abstraktion und gute Programmierung das Risiko für Abhängigkeiten gegenüber einer fremden Softwarekomponente reduziert werden und damit die Austauschbarkeit garantiert werden. Aber eigentlich will man direkt am Anfang „die richtige Wahl“ treffen, ohne nachträglich „das Pferd“ mitten im Rennen wechseln zu wollen.

Die Firma Palamida hat eine sehr interessante Auflistung von ihr empfohlener Softwareprojekte für die unbedenkliche Nutzung in der Softwareentwicklung veröffentlicht.

http://www.palamida.com/blogs/25-hot-open-source-projects-organizations-should-be-using-today

Bei vielen der gelisteten Produkte hätte ich auch ohne diese Empfehlung den Ratschlag gegegeben und benutze nicht wenige dieser Produkte. Allerdings hab ich besonders für das hier angesprochene Thema der Softwareentwicklung ein paar Ratschläge für gute Frameworks erwartet. Meiner Meinung nach stellen diese Komponenten die größten Einsparnisse in der Software Entwicklung dar und sparen nicht nur Zeit und Kosten, sondern erhöhen die Qualität der Softwareprodukte, weil sie auf einer getesteter und erprobter Codebasis basieren.

Besonders für die Webentwicklung hätte ich folgende Frameworks als empfehlenswert angsehen:

Trotz der positiven Aspekte an diesem Artikel, fehlen mir persönlich die Bewertungskriterien und die Punkte für das Ausscheiden anderer Softwareprodukte. Hier wäre eine ordentliche Studie und das Aufzeigen der Gründe für die Entscheidung mehr als hilfreich. Vor allem die Dokumentation der Produkte stellt am Anfang das wichtigste Entscheidungskriterium dar und nicht das Produkt selber. Diese Punkte sollten ebenfalls bei Entscheidungen beachtet werden.

Gute und schlechte Programmierer

Ich bin zufälligerweise über eine Seite zum Thema „Gute und schlechte Programmierer“ gestoßen und veröffentliche hiermit nur die Links, weil die Inhalte sehr aussagekräftig sind.

Besonder gut haben mir die ersten zwei Links gefallen. Ich will damit weniger Kritik üben, sondern es mehr als Ansporn geben ein besserer Programmierer zu werden!

Quellen und weitere Hinweise:

HOWTO: Tomcat5.5 unter Debian Lenny (Squeeze, SID wird identisch sein) installieren

Linux ist einfach, aber ab und an gibt es Schwierigkeiten und so wollte Tomcat5.5 nicht, so wie ich wollte. Deswegen schreibe ich hier kurz die Schritte auf, um Tomcat zum Laufen zu überreden. Diese Howto ist nur ein einfacher Einstieg und deckt nicht Sicherheitsmerkmale für den öffentlichen Betrieb ab.

1. Zuerst sollte man die SUN JRE und JDK installieren. Welche Version isntalliert werden soll, bleibt jedem selber überlassen.

Folgende Einträge werden in der /etc/apt/sources.list benötigt:

# Sun java packages are in non-free
deb http://ftp.de.debian.org/debian/ etch main non-free contrib

# also add non-free to security.
deb http://security.debian.org/ etch/updates main non-free contrib

Wer hinter einem Proxy sitzt, der schaut sich bitte die folgenden Informationen an: http://debiananwenderhandbuch.de/apt.conf.html

Welche Quelle (Mirror-Server) ihr benutzt, ist euch überlassen. Am Ende müssen nur die Schlüsselwörter „main non-free contrib“ vorhanden sein, sonst werden die Pakete von apt nicht gefunden.

Java 6 JRE und JDK (empfohlen für Entwickler):

sudo apt-get install sun-java6-jre sun-java6-jdk

oder Java 5 JRE und JDK (empfohlen für Produktivsysteme):

sudo apt-get install sun-java5-jre sun-java5-jdk

2. Dann sollte man zu der jeweiligen Version von java und javac umschalten:

sudo update-alternatives --config java
sudo update-alternatives --config javac

Bitte darauf achten, dass hier zwei Minuse vor „config“ kommen!

und anschließend die Versionen prüfen:

java -version

und

javac -version

3. JAVA_HOME muss gesetzt sein, sonst findet Tomcat die JAVA Umgebung nicht. Dafür habe ich in die Datei /etc/bash.bashrc den folgenden Eintrag eingefügt:

# Setup Jave environment 6
export JAVA_HOME=/usr/lib/jvm/java-6-sun/

oder:

# Setup Jave environment 5
export JAVA_HOME=/usr/lib/jvm/java-5-sun/

Besser ist es die Parameter in /etc/deafault/tomcat5.5 oder /etc/deafault/tomcat6 zu setzen. Allerdings hat es den Nachteil gegenüber der /etc/bash.bashrc Lösung, dass man auf der Shell eine andere Java-Version verwendet, wenn die Parameter nicht einheitlich sind. Für produktive Umgebungen empfehle ich die Parameter in /etc/default/tomcat* zu setzen. Für Entwicklungsumgebungen ist die Lösung über /etc/bash.bashrc weniger fehleranfällig, da alle die gleichen Parameter verwenden.

4. Kurz abmelden und sich wieder als root anmelden.

5. Jetzt kann man Tomcat installieren:


apt-get install tomcat5.5 tomcat5.5-admin tomcat5.5-webapps

Die Einstellungen für Produktivumgebungen überprüfen in /etc/deafault/tomcat5.5 oder /etc/deafault/tomcat6

6. Prüfen ob alles erfolgreich verlaufen ist indem man im Browser die folgende Seite aufruft:

http://localhost:8180

Die Zugangsdaten für den Tomcat-Administrator sind in der folgenden Datei zu finden:

$TOMCAT_HOME/etc/tomcat-users.xml

Ich hoffe diese HOWTO wird jemanden helfen. Für mich ist es eine super Erinnerungshilfe!

Konstantin