Posted on Leave a comment

Remote Task Notifier


RTN is a web-based client-server application used to “remotely” notify users of  new tasks. The client sends an update request to the server to fetch available notifications. Once fetched, these notifications are queued locally at the client side in order for the client to execute them one after the other and mark them “read” at the server side. The server application serves as a web-service to merely be able to query a central notification store for new (i.e. unread) notifications for a particular user.

The client application is supposed to periodically query the server side to be able to fetch new notifications.


  • Client Application
    • PHP (5.3+)
  • Server Application
    • PHP (5.3+)
    • Apache Webserver 2+
    • MySQL (5+)
    • SMSTools (3.3+)


The client application is started from the command line either manually or periodically using a daemon (or service) using the following command:

path/to/rtn/root/php/php -f ../client/index.php

The server application can be reached by using the following URI:


It will return a JSON response using the following syntax:

{notifications : []}

The Client Application

The client application is a PHP script which can be easily executed from the command line a daemon or a special service. Once started, it checks its queue for existing notifications that have yet to be delivered to the user. Note that notifications are only removed from the queue if they are successfully delivered to the user, otherwise they remain in the queue and will be processed every time the queue is started again.

If no elements are in the queue the client application will automatically query the server application for unread notifications it can fetch to be delivered to the user. Every notifications fetched from the server application is put into the queue for later processing. Once all new notifications have been fetched the queue is run and the notifications are delivered to the user.

The Server Application

The server application serves as a web-service that can be queried for new notifications for a particular user. Once successfully fetched by the client application available notifications will be marked as “fetched” – thus making them unavailable for subsequent client-application fetch requests. Note that is it hereby the responsibility of the client application to return a response to the server application of the status of the transmission.

» Responsibility: entire design and implementation
» Programming languages: PHP, SQL, HTML, Batchscript
» License: commercial

Posted on 7 Comments

PHP Mailing List

A simple mailing list manager written in PHP.

Using PHP Mailing List you can easily set up mailing lists with unlimited number of users. Messages can be sent using the web interface and will be broadcasted by PHP Mailing List to all list members. Members can be added/removed from lists via the web interface too.

Also, PHP Mailing List offers the possibility to retrieve a list of members. Actions such as adding/removing and approving members is automatically handled by PHP Mailing List through confirmations emails. Furthermore, if an admin account email is set it will be notified of any changes made to the lists.

Spam protection is achieved through the use of a captcha widget included in the web interface. Moreover, member email addresses are shown only partially to avoid harvesting attacks.

Functionality Overview

PHP mailing list provides the following core functionality:

  1. subscribe: Add members to a mailing list via form.php
  2. unsubscribe: Remove subscribed members from a mailing list
  3. authorize: Authorize a subscription request
  4. send message: Send messages to all members of a mailing list

Download: phpMailingList on GitHub

UPDATE: Due to numerous requests I’ve moved the source code to GitHub. Please check the Github repository for the latest version.


  1. Edit config.ini to your needs.
  2. Edit form.php, members.php and error.php to your needs.
  3. Be sure to set correct permissions for the lists-directory set in config.ini.
  4. Test your installation by creating a list, e.g.: lists/mylist/
  5. Call index.php?list=mylist to use your shiny new mailing list. Hint: In case you get write permission errors be sure to set the correct permissions for the lists-directory (and any subdirectories andfiles).


Everything you need (or can) configure can be found in config/config.ini. You are free to customize form.php and members.php to your needs. Make sure to leave the core input fields “as-is”.

Posted on Leave a comment

Email To SMS Gateway


EmailToSMS-Gateway ist ein Dienst zum Versenden von SMS via Email. Sie können damit von Ihrem Standard Emailprogramm bequem und einfach SMS verschicken ohne dafür zusätzliche Client-Software zu benötigen.


Um SMS mittels EmailToSMS-Gateway zu verschicken senden Sie eine Email an die von Ihrem Systembetreuer definierte Emailadresse, z.B. Als Betreff geben Sie bitte die Empfängernummern (beginnend mit einem “+”) und/oder den/die Empfängerbenutzernamen (siehe Abschnitt „Benutzer Anlegen), jeweils getrennt durch ein “;” an, z.B.:

Betreff: +4369922334455; mein.benutzername; +436501234567; …

Wenn Sie nur eine Empfängernummer verwenden möchten ist das “;” am Ende optional. Der Emailnachrichtentext stellt zugleich auch den SMS-Text dar.


Sollten Sie eine (firmenweite) Fußzeile haben (die z.B. bei jeder Email automatisch von Ihrem Mailserver/-programm angehängt wird) bietet Ihnen EmailToSMS-Gateway die Möglichkeit diese über die Konfigurationseinstellung email_remove_signature anzugeben. Damit wird die Signatur automatisch von EmailToSMS-Gateway vor dem Versand der SMS entfernt (und Sie ersparen sich damit unnötigen SMS-Text). Bitte beachten Sie hierbei, dass Sie auch die Möglichkeit haben nur den Signaturkopf gefolgt von einem „*.*“ als Wert anzugeben. Damit entfernt EmailToSMS-Gateway den gesamten Text beginnend mit dem Signaturkopf.

Sie haben über die Konfigurationseinstellung notification_only_regexp auch die Möglichkeit ein Symbol (oder eine Symbolkette) zu definieren um den festgelegten Nachrichtentext notification_only_text anstatt der ursprünglichen Nachricht zu senden. Dies ist z.B. dann hilfreich, wenn Sie den/die Empfänger nur über eine neue Nachricht, die im Nachrichtenspeicher hinterlegt wurde informieren möchten, oder wenn die Originalnachricht als SMS-Nachricht zu umfangreich wäre.

Ein kleines Beispiel:

Sie wollen den Benutzer per SMS darüber informieren, dass eine umfangreiche Nachricht vorhanden ist. Um dies zu erreichen verfassen Sie die folgende Email:

Nachricht: notification_only_regexp „Ihre_umfangreiche_Nachricht“

Der Benutzer erhält eine SMS mit dem Text notification_only_text als Inhalt und nicht „Ihre_umfangreiche_Nachricht“.

Mehr Informationen zu den Konfigurationseinstellungen erhalten Sie im Abschnitt „Konfiguration“.


Der Nachrichtenspeicher dient als temporärer Speicher um Nachrichten abzulegen. Auf diesen können beispielsweise externe Anwendungen zugreifen und haben so die Möglichkeit auf neue Nachrichten von EmailToSMS-Gateway zu reagieren.

Benutzer Anlegen

Damit Sie Benutzernamen als Empfängeradressen angeben können müssen diese zuvor in der Verwaltung von EmailToSMS-Gateway angelegt werden. Verwenden Sie bitte hierzu das Skript im Verzeichnis server/utils/createUser.php.

Status des Versandes

Sobald sich der Status des SMS-Versandes ändert erhalten Sie automatisch von EmailToSMSGateway eine Benachrichtigungsemail an Ihre Email-Absenderadresse. Weiter können Sie über die Konfigurationseinstellung notification_level die Benachrichtigungsoptionen genauer einstellen.


Über die Konfigurationsdatei server/config/config.ini haben Sie die Möglichkeit diverse Einstellungen von EmailToSMS-Gateway zu verändern. Bitte beachten Sie die Hinweise in der Datei. Eine genauere Beschreibung der Konfigurationsoptionen finden Sie direkt in der Datei.

Hilfe / Unterstützung

Sollten Sie Probleme haben vergewissern Sie sich bitte vorerst, dass Sie die aktuellste Version von EmailToSMS-Gateway installiert haben. Überprüfen Sie bitte auch die Log-dateien im Verzeichnis server/log auf etwaige Fehler.

Bei Fragen können sich mich kontaktieren.

» Verantwortungsbereich: gesamtes Design und Entwicklung
» Sprachen: C++, PHP, SQL, Bash
» Frameworks: smstools
» Lizenz: kommerziell

EmailToSms-Gateway wurde erfolgreich unter Windows 2000/2003 Server mit Cygwin getestet.

Posted on Leave a comment

HTML Table Extractor

Source Code Icon

This PHP script parses “useful” information from HTML tables. It has been developed as an additional tool in the course of my master thesis at the Graz University of Technology.

Please refer to my master thesis for detailed information on this tool.

* HTMLTableExtractor
* Parses HTML tables and extracts "useful" information from HTML table.
* Valid "keywords" can be identified by their markup: bold-face.
* Note that keywords are only taken into consideration until special delimiters
* are reached. Any further keywords markup will be ignored. Writes parsed
* output in (parser) optimized format to file specified for main().
* main():
* - Reads all files from source folder specified to main().
* - Writes output to file specified to main().
* - Writes log messages to log file specified to main().
* @filesource htmlTableExtractor.html
* @author Matthias Kerstner
* @version 1.6
require_once 'simple_html_dom.php'; //
set_time_limit(0); //remove timeout limit
ini_set('memory_limit', '5000M'); //increase buffer limit for simple_html_dom...
define('LANG_CNT', 2); //including source language
header('Content-type: text/html; charset=utf-8'); //for warnings, errors,...
* Converts given @param{$filename} into UTF-8 encoding and returns content.
* @param <String> $filename
* @return <String>
function file_get_contents_utf8($filename) {
$content = file_get_contents($filename);
return mb_convert_encoding($content, 'UTF-8',
mb_detect_encoding($content, 'UTF-8, ISO-8859-1', true));
* Recursively parses @param{$baseDir} for files to be processed
* @param <String> $baseDir
* @return <array> all files found in @param{$baseDir}
function gatherSrcFiles($baseDir) {
$list = array();
die('ERROR: baseDir "'.$baseDir.'"does not exist');
$handle = null;
if($handle = @opendir($baseDir)) {
while(false !== ($file = readdir($handle))) {
if($file != '.' && $file != '..')
$list[] = $baseDir."/".$file;
return $list;
* Checks @param{$dstFile} for consistent translation entries. Errors
* will be printed to stdout. Calculates (simple) statistical output.
* @param string $dstFile
function checkConsistency($dstFile, $logFile) {
$fp = null;
$fpLog = null;
if(false === ($fp = @fopen($dstFile, 'r')))
die("ERROR: Failed to open dstFile '".$dstFile."', aborting...");
if(false === ($fpLog = @fopen($logFile, 'a+')))
die("ERROR: Failed to open logFile '".$logFile."', aborting...");
$errBuffer = '';
$lineCnt = 0;
$inconsistentCnt = 0;
while(!feof($fp)) {
$line = fgets($fp, 4096);
if(!mb_ereg_match("[^\W]+ -> [^\W]+", $line)) { //check syntax
$errBuffer .= "\nWARNING: invalid line found: '".$line."'";
$inconsistentCnt += 1;
$lineCnt += 1;
$buff = "Consistency-check statistics:\n".$inconsistentCnt." / ".$lineCnt;
$percentage = 0;
if($lineCnt > 0) //calculate percentage
$percentage = round($inconsistentCnt / $lineCnt, 2) * 100;
$buff .= ", ".$percentage."% inconsistent\n".$errBuffer;
fwrite($fpLog, $buff);
* Removes page header information from @param{$fileContent} and returns it.
* @param <String> $fileContent
* @return <String>
function removePageHeader($fileContent) {
$pattern = '<SPAN ID="Frame\d+" DIR="LTR" STYLE="float: left; '.
'width: 7.74cm; height: 0.74cm; border: none; padding: 0cm; '.
'background: #ffffff">\s*<P LANG="de-DE" CLASS="western" '.
'ALIGN=CENTER( STYLE="margin-top: 0cm")*>\s*'.
'(<FONT FACE="Verdana, sans-serif">)*(<FONT SIZE=2>)*\s*'.
return mb_ereg_replace($pattern, '', $fileContent);
* Runs the HTML table extractor.
* @param <String> $baseDir base folder containing files to be processed
* @param <String> $dstFile destination path where output will be written to
* @param <String> $logFile destination path where log output will be written to
function main($baseDir, $dstFile, $logFile) {
$fpDst = null;
$fpLog = null;
$formatted_src_lang = "";
die("ERROR: LANG_CNT not defined");
if(false === ($fpDst = @fopen($dstFile, 'w')))
die("ERROR: Failed to open dstFile '".$dstFile."', aborting...");
if(false === ($fpLog = @fopen($logFile, 'w')))
die("ERROR: Failed to open logFile '".$logFile."', aborting...");
"CONTENT=\"text/html; charset=utf-8\"></HEAD><BODY>");
$files = gatherSrcFiles($baseDir);
$delimiters = array(":", ",");   //valid delimiters for delimiting keyword-search
foreach($files as $f) { //parse files in selection
$fileContent = removePageHeader(file_get_contents($f));
$html = str_get_html(file_get_contents($fileContent));
foreach($html->find('tr') as $tr) { //parse translations
$langs = $tr->getElementsByTagName('td');
$langCnt = 0;
foreach($langs as $td) { //parse translations
if($langCnt >= (int)LANG_CNT) {
fwrite($fpLog, "WARNING: invalid syntax detected ".
"(trying to continue...)\n");
break; //invalid td-count, break loop to retain data consistency
$paragraphs = $td->getElementsByTagName('p');
if(count($paragraphs) < 1) {
fwrite($fpLog, "WARNING: invalid p count detected ".
"(trying to continue...)\n");
break; //invalid td-count, break loop to retain data consistency
$p = $paragraphs[0]; //max 1 per td allowed (all others will be ignored)
$plainUntilDelim = mb_ereg_replace("[\t]", "",
html_entity_decode(trim($p->plaintext), ENT_QUOTES, 'UTF-8'));
$firstFound = mb_strlen($plainUntilDelim);
foreach($delimiters as $d) { //find first delimiter position
$pos = mb_strpos($plainUntilDelim, $d);
if($pos !== false && $pos < $firstFound)
$firstFound = $pos;
if($firstFound >= 1) {
//parse plaintext until first delimiter found (or to the end if none found)
$plainUntilDelim = mb_substr($plainUntilDelim, 0, $firstFound);
//echo "<br><br>plain until delim=".$plainUntilDelim;
$plainUntilDelimLen = mb_strlen($plainUntilDelim);
$keywordParts = $p->getElementsByTagName('b');
$keyword = '';
$searchOffset = 0;
$parsedKeywordParts = array();
foreach($keywordParts as $v) {
//parse keyword(s) (all bold characters until first delimiter is found)
//remove special characters except delimiters (!)
$stripped = mb_ereg_replace("[\t]", "",
html_entity_decode(trim($v->plaintext), ENT_QUOTES, 'UTF-8'));
$firstFoundStripped = mb_strlen($stripped);
foreach($delimiters as $d) { //find first delimiter position
$pos = mb_strpos($stripped, $d);
if($pos !== false && $pos < $firstFoundStripped)
$firstFoundStripped = $pos;
if($firstFound >= 1) //extract plaintext until first delimiter
$stripped = mb_substr($stripped, 0, $firstFoundStripped);
$stripped = mb_ereg_replace("[,:]", "", trim($stripped)); //do not remove spaces here
if(!empty($stripped) && !in_array($stripped, $parsedKeywordParts)) {
//ignore empty tags and duplicates
//echo "<br>searching for ".$stripped . "(".mb_strlen($stripped).") with offset=".$searchOffset.", langCnt=".$langCnt." %2=".($langCnt%2)."<br";
$pos = mb_strpos($plainUntilDelim, $stripped, $searchOffset);
if($pos === false)
break;  //not found inside delimited plaintext -> break loop
if($pos > $searchOffset+1 && ($langCnt % 2 != 0))
$keyword .= " ".$stripped; //translation-td, insert space between keyword-parts
$keyword .= $stripped;
$parsedKeywordParts[] = $stripped;
$searchOffset = $pos; //set next iteration offset
$keyword = mb_ereg_replace("[\t\r\n]", " ", trim($keyword)); //strip any unnecessary chars
fwrite($fpLog, "ERROR: could not determine keyword\n");
if($langCnt % 2 != 0) { //used for pretty formatting in dstFile
fwrite($fpDst, "[".$keyword."]<br>{".$formatted_src_lang."} => {".$p->innertext."}<br><br>");
} else { //src language
fwrite($fpDst, "[".$keyword."] => ");
$formatted_src_lang = $p->innertext;
$langCnt += 1;
} //end parse translations
} //end parse rows
fwrite($fpDst, "<br><br><br>"); //start next file
$html->clear(); //prevent memory-leak
} //end parse files
fwrite($fpDst, "</BODY></HTML>");
checkConsistency($dstFile, $logFile);
echo "DONE!";
// start processing
main('/source/path', '/dst/path/to/output.html', '/dst/path/to/out.log');
Posted on 2 Comments

EML to VCF Converter

Dieses PHP Skript konvertiert Visitenkarten, die als .eml Dateien vorliegen in das VCARD-Format (.vcf). Diese können dann auf einfache Weise in alle gängigen Emailprogramme importiert werden.

Der Quellcode kann über die englische Version dieses Posts heruntergeladen werden: EML to VCF Converter

This PHP script converts contacts saved as .eml files to VCARDs (.vcf) that can easily be imported to any email client.

* EML2VCFConverter.php
* Converts contacts saved as .eml files to VCARDs (.vcf) that can easily be
* imported to any email client.
* Copyright (C) 2010 Matthias Kerstner,
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA  02110-1301, USA.
* Converts contacts saved as .eml files to VCARDs (.vcf) that can easily be
* imported to any email client. Expects .eml files to be UTF-8 encoded.
* The generated VCARD-files will be saved using UTF8-encoding by default. As
* programs such as Windows Contacts for some strange reasons don't handle UTF-8
* encoded VCARDs correctly you can optionally change the default encoding of
* the generated VCARDs (UTF-8) to ISO-8859-1.
* Be sure to set SRCFOLDER. If DSTFOLDER is not specified generated .vcf files
* will be placed inside a folder named "converted" in your SRCFOLDER. Note that
* existing .vcf files will be overwritten! Optionally you can set DEBUG to
* either true or false. Be sure to set PHP's execution timeout to a value high
* enough in order to give the script time to finish.
* 1.1: minor code optimizations
* 1.0: initial release
* @filesource EML2VCFConverter.php
* @author Matthias Kerstner,
* @version 1.1
define("SRCFOLDER", "");
define("DSTFOLDER", "");
define("UTF8DSTENCODING", true); //possible values: true, false (=ISO-8859-1)
define("DEBUG", true); //possible values: true, false
* Courtesy of Justin Cook
function get_string_between($string, $start, $end) {
$string = " ".$string;
$ini = strpos($string,$start);
if($ini == 0) return "";
$ini += strlen($start);
$len = strpos($string,$end,$ini) - $ini;
return substr($string,$ini,$len);
function debug($msg) {
echo "debug: ".$msg."<br>";
debug("using source folder '".SRCFOLDER."'");
$dh = @opendir(SRCFOLDER);
die("ERROR: failed to open source folder");
debug("reading source folder...");
if(strlen(DSTFOLDER) < 1) {
debug("no destination folder specified, using default...");
$dstFolder = SRCFOLDER."/converted";
} else
$dstFolder = DSTFOLDER;
if(!file_exists($dstFolder)) {
if(!mkdir($dstFolder, 0777, true))
die("ERROR: could not create destination folder '$dstFolder'");
debug("created destination folder '$dstFolder'...");
} else
debug("using existing destination folder '$dstFolder'...");
$dstFoldername = substr($dstFolder, strripos($dstFolder, "/")+1);
$i = 0;
while(($srcFile = readdir($dh)) !== false) {
$srcFilename = basename($srcFile, ".eml");
if($srcFilename == $dstFoldername || $srcFilename == "." || $srcFilename == "..")
else {
$srcFile = SRCFOLDER."/".$srcFile;
$handle = fopen($srcFile, "r");
$vcfContent = fread($handle, filesize($srcFile));
$vcfContent = iconv("UTF-8", "ISO-8859-1", $vcfContent);
$vcfContent = get_string_between($vcfContent, "BEGIN:VCARD", "END:VCARD");
$vcfContent = "BEGIN:VCARD".$vcfContent."END:VCARD";
$vcfContent = iconv("ISO-8859-1", "UTF-8", $vcfContent);
$dstFile = $dstFolder."/".$srcFilename.".vcf";
if(($fp = fopen($dstFile, "w+")) === false)
die("ERROR: could not create file '$dstFile'");
fputs($fp, $vcfContent);
debug("Files written: $i");
echo "done!";

Posted on Leave a comment


Source Code Icon

A highly efficient collection of text manipulation functions written in C++ based on the .Net framework. It has been developed as an additional tool in the course of my master thesis at the Graz University of Technology.

You can specify an arbitrary amount of source text (.txt) files to work with.  Command line flags start with a “-“, whereas source files don’t. Command line flags are seperated by spaces and can occur in any order. In the following, usage options will be presented in greater detail.

Note: TextTools is not being maintained anymore. Nevertheless, you can still download the last version in the download section below.


Using the command line version is pretty straight forward. Here is a simple example of how you can call Texttools from the command line:

Texttools -w=my_output_file.txt -s=asc -e=; -ed=my_extracted_lines.txt source.txt

This command does the following:

  1. reads in file “source.txt” (whereas blank lines are excluded by default)
  2. sorts the read lines from “source.txt” lexicographically ascending (default). Can be turned OFF by using flag -b=0
  3. extracts all characters from the lines until the delimiter “;” is found. If delimiter is not found the entire line is extracted (-e=;)
  4. writes the extracted lines to file “my_extracted_lines.txt” (-ef=my_extr…)
  5. writes the sorted lines from (2) back to “my_output_file.txt” (-w=my_out…)


You can always get the flag overview by calling Texttools.exe without any flags:


usage: Texttools


[-e=delim [-ed=dest]]

[-o=delim [-od=dest]]

[-p= [-ps=src] [-pd=dest]]

[-r=expr [-rd=dest]]

[-s=0|asc|desc [-sd=dest]]

[-u=delim [-ud=dest]]


[src_files ...]



  • if 0 statistics will NOT be displayed, is turned ON by default


  • extracts all (sub)strings from [src_files] up to the delimiter specified.
  • Use -ed to specify which destination filename to use, otherwise DEFAULT_EXTRACTION_FILE will be used


  • rotates strings from [src_files] until given delimiter. If delimiter is empty the entire line will be rotated.
  • Use -od to specify which destination filename to use, otherwise DEFAULT_ROTATION_FILE will be used


  • replaces (sub)string from [src_files] based on the rules file specified by the flag -ps.
  • Use -ps to specify which source filename to use, otherwise DEFAULT_RULE_FILE will be used.
  • Use -pd to specify which destination filename to use, otherwise DEFAULT_REPLACED_FILE will be used.


  • used to sort list read from src_files. NOTE that once -s flag is used the internal list will be sorted and the program will use the sorted list from this point onwards!
  • if 0 or empty list will NOT be sorted
  • if asc list is sorted lexicographic ASCending
  • if  desc list is sorted lexicographic DESCending
  • otherwise ERROR


  • used to uniquify list, i.e. removes all double entries
  • if empty simply removes all “equal” strings,
  • otherwise takes predicate based on flag specified [NOT AVAILABLE YET]
  • takes -ud if specified for destination filename to write removed lines to.
  • if empty DEFAULT_REMOVED_FILE is taken,
  • otherwise dest_filename is used


  • used to write list generated during reading of [src_files] to file specified.
  • If empty or not existent DEFAULT_OUTPUT_FILE is taken.
  • If s then the program will automatically write the output-lines to files which start the line’s leading character, i.e. a-z.txt and other.txt are used.
  • otherwise specified filename is taken (if it not already exists)
  • If c the output-lines will be printed to the commandline.


  • removes expression specified from [src_files] [NOT AVAILABLE YET]

List of default filenames









Rules file

This file must have the following format:

old_expr | new_expr [ | begin_env [ | end_env ] ]


Expressions can be simple replaced by other expression in context of the entire line.

With Texttools you can also replace expressions inside so-called environments.

An environment has a special beginning and an end (delimiters). This can either be a single character or an arbitrarely long string.

The environment is optional: If you do not specify an environment Texttools will simply replace ALL occurrences of old_expr by new_expr.

When specifying an environment you have to following options:

... [ | (EMPTY || *#*BOL*#* || left_delim) [ | (EMPTY || *#*EOL*#* || right_delim) ] ]

*#*BOL*#* stands for “begin of line”, meaning that old_expr must be the first word in the current line.

*#*EOL*#* stands for “end of line”, meaning that old_expr must be the last word in the current line.


TEST|new will replace ALL occurrences of TEST with new in each line and is the same as TEST | new | *#*BOL*#* | *#*EOL*#*

TEST|new|*#*BOL*#* will replace ALL occurrences of TEST with new in each line only if TEST is the first word in the current line

TEST|new||*#*EOL*#* will replace ALL occurrences of TEST with new in each line only if TEST is the last word in the current line


Posted on Leave a comment

UTF-8 web-applications using PHP and MySQL

This document presents you a list of things to keep in mind when creating UTF-8 aware web-applications using Dojo and PHP.

Although the focus lies on Dojo and PHP most of the tips below can be used for any other web (scripting) language too.

  1. write files UTF8-encoded with an UTF-8 capable editor, like PSPad

    Basically, ensure that your text editor is capable of writing the BOM correctly (which has been an issue with some editors in the past).

  2. insert the following in your .htaccess:
    php_value default_charset UTF-8

    This will direct PHP to always send out data UTF-8 encoded.

  3. specify correct character set in HTML header:
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
  4. specify correct language in HTML header:
    <html xmlns="" xml:lang="en" lang="en">
  5. force Dojo to use UTF-8 for dojo.iobind:
    var djConfig = {isDebug:true, parseOnLoad:false, bindEncoding:"UTF-8"};
  6. specify character set for your <form>:
    <form accept-charset="UTF-8">

    Hint: Although this is optional (browsers will automatically use the encoding previously used by the server) when specified it is much more obvious what encoding you want to use.

  7. define character set to be used for returning data on the server side:
    header("Content-Type: text/html; charset=utf-8");
  8. encode data to be sent to the server on the client side:

    This makes sure that special characters such as “’$%&,… will be transferred correctly.

  9. decode data received on the server side:
    html_entity_decode(urldecode($p), ENT_QUOTES, "UTF-8");
  10. use utf8_general_ci as collation for your MySQL database.

    This ensures that data will actually be saved UTF-8 encoded and not re-coded to for instance latin1_swedish_ci, which is the default settings of MySQL.

  11. right before reading/writing from your database issue the following query:
    mysql_query("SET NAMES 'utf8'");

    This will direct the MySQL server to process UTF-8 encoded data.

  12. don’t forget to escape quotation marks to avoid code-injection when issuing database queries:

    Note that addSlashes is not the optimal way to escape special characters! This just serves as an example.

  13. when using PHP’s json_encode() function be sure to utf8_encode strings as json_encode will cut off german umlauts (etc.) that were not encoded before



  • encodeURIComponent() , decodeURIComponent


  • html_entity_decode(urldecode($p), ENT_QUOTES, “UTF-8”)
  • mysql_query(“SET NAMES ‘utf8′”), addSlashes()
  • header(“Content-Type: text/html; charset=utf-8”)
  • json_encode(utf8_encode(rawurlencode($x))


  • utf8_general_ci

Posted on Leave a comment

J2ME’s Communication Facilities in Theory and Practice

Paper Icon

This paper discusses communication facilities provided by Java’s Micro Edition (J2ME) in theory and practice.


JavaTM has become a very powerful platform-independent programming language. Java Micro Edition (J2ME) is finally a version that offers a small enough footprint able to run on very resource limited mobile devices, such as mobile phones or PDAs. This paper discusses J2ME’s technical specifications and limitations compared to Java Standard Edition (J2SE) on one hand, as well as its built-in communication facilities, provided by the Generic Connection Framework (GCF) on the other. By providing reference solutions, which target known issues for network execution code, Nautiport’s flexible connection manager class will be presented.

BibTex Citation:

author = {Kerstner, Matthias},
title = {J2ME’s Communication Facilities in Theory and Practice},
year = {2009},
note = {\url{}}

Download: J2ME_Communication_Facilities_in_Theory_and_Practice_Kerstner_2009.pdf

Posted on 2 Comments

Software Test Effort Estimation Methods

Paper Icon

This paper discusses current software test effort estimation methods, in particular “function point analysis”, “test point analysis” and “use case points”.


Especially in the field of software engineering, effort estimation plays an essential role. Moreover,
the task of defining test scenarios and test cases is often underestimated during the early stages of the
software engineering process, due to the fact, that the amount and granularity of required test cases
and test steps is not known in advance, for instance, as a result of missing or incomplete requirement
specifications. Furthermore, the fact that fix price projects are becoming more and more common makes
the process of prior (test) effort estimation even more important, in order to not exceed the budget agreed
upon. This paper discusses three important aspects of this development. First, it elaborates on the
changing practices in project pricing, followed by a discussion of the general problems involved in the
field of effort estimation in software projects. Finally, a distinct selection of today’s commonly used
methods for test effort estimation will be presented: Function Point Analysis, Test Point Analysis and Use Case Points.

BibTex Citation

author       = {Kerstner, Matthias},
title        = {Software Test Effort Estimation Methods},
year         = {2011},
month        = feb,
date         = {2},
note         = {\url{}}

Download: Software_Test_Effort_Estimation_Methods_Kerstner_2011.pdf

Download Presentation: Softwaretechnology_SW_Test_Effort_Estimation_Methods_KerstnerMatthias.odp

Posted on Leave a comment

Dictionary Software based on Ajax Frameworks

Paper Icon

This paper presents a comparison of Ajax Frameworks suitable for developing web-based dictionary software.


The necessity for dictionaries cannot be disputed. Although there are a vast variety of them they all follow a basic schema a key-value combination. It is interesting to see that many of the well known dictionary producers recently also provide electronic versions of their products, ranging from CDROMs over USB media to online databases. With the event of Ajax, web-based applications can be turned into desktop-like environments. It provides developers with an effective way to build web-applications using existing facilities (browsers, etc.), while still profiting from the latest technology.

BibTex Citation:

author = {Kerstner, Matthias},
title = {Dictionary Software based on Ajax Frameworks},
year = {2007},
note = {\url{h<a href='}}

Download: Dictionary_Software_based_on_Ajax_Frameworks_Kerstner_2007.pdf

Posted on Leave a comment


AVWOnline ist ein flexibles Onlinesystem zur Erstellung/Verwaltung und weiteren Behandlung von Aufträgen. Neben dem gesamten Auftragsabwicklungsprozess enthält es ein vollständiges CRM-Modul, Verrechnungs- und Mahnwesen, Mitarbeitermanagement, sowie Statistikmodul.

Auf Grund seines modularen Aufbaus können einzelne Module auf einfache Weise für Ihre
bestehenden Lizenz aktiviert werden.

Sollten Sie mehr Informationen benötigen kontaktieren Sie mich bitte: Kontakt.

Posted on 2 Comments

Custom Apache Directory Listing using PHP

Apache Logo

This snippet overrides Apache’s default presentation of folder contents.

* CustomDirectoryListing
* A simple script for custom directory listing. Adds support for
* - maximum amount of items per row
* - image listing with automatic resizing
* - displaying images before other file types for a prettier layout
* The above options can be controlled via the corresponding $_GET variables,
* @see below.
* Simply put this file into the directory you want to be listed. You might want
* to set the defaultWidth and defaultHeight, as well as maxItemsPerRow to your
* needs.
* @author Matthias Kerstner
* @copyright Matthias Kerstner
* @version 1.0
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see &lt;;.
$maxItemsPerRow = (isset($_GET['maxItemsPerRow']) &amp;&amp; (int)$_GET['maxItemsPerRow'] &gt; 0)
? (int)$_GET['maxItemsPerRow']-1 : 4;
$defaultWidth = (isset($_GET['defaultWidth']) &amp;&amp; (int)$_GET['defaultWidth'] &gt; 0)
? (int)$_GET['defaultWidth'] : 150;
$defaultHeight = (isset($_GET['defaultHeight']) &amp;&amp; (int)$_GET['defaultHeight'] &gt; 0)
? (int)$_GET['defaultHeight'] : 100;
$imagesFirst = isset($_GET['imagesFirst']);
* Calculates resized size based on $dstHeight and $dstWidth by maintaining the
* original width-height-ratio. Checks which dimension is greater and sets it to
* a fixed value specified. The remaining dimension is resized using the
* original ratio to the other dimension's new fixed value.
* @param &lt;string&gt; $srcFile
* @param &lt;int&gt; $dstHeight
* @param &lt;int&gt; $dstWidth
* @return &lt;array&gt; [0]=width, [1]=height
function getResizeSize($srcFile, $dstHeight, $dstWidth) {
$srcFileInfo = getimagesize($srcFile); //[0]=width,[1]=height
throw new Exception('Failed to read image fileinfo.');
if($srcFileInfo[0] &lt;= (int)$dstWidth &amp;&amp;
$srcFileInfo[1] &lt;= (int)$dstHeight) {
return array($srcFileInfo[0], $srcFileInfo[1]); //nothing to do
$newSize = array();
if($srcFileInfo[0] &gt;= $srcFileInfo[1]) { //calculate new dimensions while keeping current ratio
$newSize[0] = $dstWidth;
$newSize[1] = ($srcFileInfo[1]/$srcFileInfo[0]) * $dstWidth;
} else {
$newSize[0] = ($srcFileInfo[0]/$srcFileInfo[1]) * $dstHeight;
$newSize[1] = $dstHeight;
return $newSize;
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Custom Directory Listing</title>
<style type="text/css">
html {
font-family: Courier, Verdana, sans-serif;
font-size: 9pt;
border: 0;
try {
$handle = opendir('.');
$file = null;
throw new Exception('Failed to read directory.');
$nonImageItems = array();
$itemPerRowCount = 0;
while(false !== ($file = readdir($handle))) {
if($itemPerRowCount >= $maxItemsPerRow) {
echo '<br/>';
$itemPerRowCount = 0;
if($file !== "." && $file !== "..") {
if(mb_eregi('.jpg$', $file) === 1) { //image
$filesize = getResizeSize($file, $defaultWidth, $defaultHeight);
echo '<a href="'.$file.'">'.
'<img src="'.$file.'" width="'.$filesize[0].'" '.
'height="'.$filesize[1].'" alt="'.$file.'" '.
} else {
$nonImageItems[] = $file;
else {
echo '<a href="'.$file.'">'.$file.'</a>&nbsp;&nbsp;';
$itemPerRowCount = 0;
foreach($nonImageItems as $v) { //display non-image files afterwards
if($itemPerRowCount >= $maxItemsPerRow) {
echo '<br/>';
$itemPerRowCount = 0;
echo '<a href="'.$v.'">'.$v.'</a>&nbsp;&nbsp;';
} catch(Exception $e) {
echo 'Sorry an error occurred: '.$e->getMessage();