Zugriffszeit

Heute mal etwas Empirie…

Ich habe ein Benchmark der Zugriffszeit von einigen Webseiten durchgeführt, um die Werte mal vergleichen zu können. Zuallererst zum Aufbau: verwendet habe ich das Tool ApacheBench mit folgenden Parametern

$ ab -n 1000 -c 10 URL

Aufgeschlüsselt: 10 gleichzeitige Verbindungen und 1000 Anfragen wurden jeweils an jeden Server gestellt.

Das Ergebnis:

Unerwarteterweise schneidet forum.ebesucher.de super schlecht ab. Warum? Das Forum liegt auf dem kleinsten Server von eBesucher und ist für so hohe Anzahl an aufeinanderfolgenden Zugriffen nicht optimiert. Auch muss die Indexseite des Forums viel mehr aus der Datenbank lesen, als die von eBesucher zum Beispiel.

Klamm und eBesucher Hauptseiten schneiden in etwa gleich gut ab. eBay und Paypal sind dagegen im Mittel bis zu drei mal langsamer.

Google ist natürlich der Vorreiter der schnellen Webserver: mit 64ms durchschnittlicher Zugriffszeit ist die die Suchmaschine etwa so schnell wie ein lokaler Webserver.

Google geht auch mal kaputt

Auch die tolle Engine von Google macht mal Fehler. Ich habe eben beim surfen ein sehr schönes Bild erwischt. Alle Seiten wurden von Google plötzlich als unsicher gekennzeichnet. Selbst die eigene Seite kennzeichnete Google stolz als gefährlich!

Cisco VPN-Client auf dem Mac und der Fehler 51

Ich habe mich seit ein Paar Tagen gewundert, warum ich keine VPN-Verbindung mehr in der Uni mit meinem Mac aufbauen konnte. Beim Start des VPN-Clients kam immer der Fehler: Error 51
“Unable to communicate with the VPN subsystem” und bestand darauf, dass ich doch bitte mindestens ein Netzwerk verfügbar machen sollte.

simples
sudo /System/Library/StartupItems/CiscoVPN/CiscoVPN restart
auf der Konsole

löste nach einer Passwortabfrage das Problem. Der Cisco-Client scheint Probleme zu haben, wenn neue Netzwerkadapter erscheinen oder wieder verschwinden. Das ist bei mir durch meinen UMTS-Stick oft der Fall.

Mausbewegung unter MacOsX zu langsam

Ich denke, ich bin nicht der einzige, der sich über die Geschwindigkeit der migty mouse ärgert. Das Gerät ist zwar sehr stylisch, aber mörderisch langsam. Fühlt sich für mich an, wie eine alte Oma, die die Treppe nach oben läuft.

Ich bin heute beim Surfen auf eine geniale Erweiterung gestoßen: MouseZoom verändert den Skalierungswert der Maus weit über dem von Apple vorgegebenen Standartwert. Ich habe gleich vom MacOsx-Standartmaximum 1,7 auf 4,0 gestellt, und kann jetzt endlich vom einen Ende des Bildschirms das andere Ende in einer einzigen Mausbewegung erreichen. Super!

Download MouseZoom

Обезьяна чичичи

Вот он, давно забытый стишок моего детства!

Обезьяна чичичи
Продавала кирпичи
не успела всё продать
Улетела под кровать
Под кроватью пусто
Выросла капуста
Под капустой генерал
Свои трусики стирал
Где моя бумажка
вытереть какашку?

ИЛИ

Обезьяна чичичи
Продавала кирпичи
За веревку дернула
И нечайно пёрнула

Such-Plugins für Firefox

Ich habe eben bestimmt 2 Stunden danach gesucht, weil ich die Seite ja schon mal gesehen hatte und auch schon mal benutzt hatte. Die Suchleiste in Firefox kann noch einiges mehr an Suchmaschinen aufnehmen, man muss nur wissen, wo man die entsprechenden Verweise her holt. Und das bekommt man hier: http://mycroft.mozdev.org/yahoo-search-plugins.html

Man muss den alternativen Suchdiensten auch mal eine Chance geben! Und das habe ich hiermit getan! Yahoo bietet inzwischen meiner Meinung nach ein besseres Interface, daher werde ich mal ausprobieren, ob ich damit vielleicht besser zurechtkomme!

Email-Notifications for Watchlist in DekiWiki

After i have installed DekiWiki for our internal purposes on eBesucher, i realized, that it do not support Email-Notification for the Watchlist. It has only the option to get the notifications as RSS-Feed. Well, ok, this might not be the problem. Why not? Thunderbird supports RSS-Feeds, so i set it up to get my Watchlist as RSS. But what i did not know: you have to be logged in, to view the watchlist. This means that only Firefox can read my RSS-Watchlist properly.

After that some using of google, i found an api-Link for the feed, that handles the authentication via standard-HTTPauth. This is something like this: http://yourwiki/@api/deki/site/feed?title=Special:Watchlist

So now i could add the Watchlist in my Thunderbird. But what’s this? The messages seemed broken: they only said that some page has been changed. But without the changes really to be included. At first, i thought, it was a Thunderbird-Problem. But after some testing, I found out, that the feed was only 6kb unstead of 13kb. Why that? I think there is a bug in the DekiWiki-Permissionsmangement, the API-HTTPauth does not give me the full view permissions. It is still necessary to pass the authorisation-cookie (authtoken) to get the full feed.

So to summ up, I can not use any RSS-Feed-Reader other than my browser. Firefox can view the feed properly, but there is no notification feeling. And rss2email doesn’t work with this too.

That’s why i’ve spent about a day on writing a php-script to forward the messages from this tricky feed to email properly. And i would like to share this. So let’s start!

Here is the Part that handles RSS and Email:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
<?php    
/**
 * DekiReader
 * is a class for getting instant email notifications with DekiWiki, if someone
 * changes a site in your Watchlist
 *
 * This script is free to use for private and commercial purposes
 * Author: Evgeny Anisiforov
 * mailtO: jeff at ebesucher point de
 *
 * I spend a whole day on writing this, so 
 * Please just leave a comment in my blog, if you use this. ;)
 */ 
 
/**
 * The most problem about getting notifications is that DekiWiki requires us
 * to be logged in (with a cookie) to be able to view the full rss feed
 * of changes (including changed things)
 * This is a big problem, as the most rss read does not have such function.
 * This script emulates a login for each user to get the auth cookie
 * and then sends the contents of the RSS-feed to the specified email-address
 * It also takes care of marking old messages as read, so your users won't
 * get notifications more than one time
 * 
 * The best practice would be to use a cronjob to start DekiReader every X minutes.
 * like this:
          * / 10 * * * * cd /www/Cron_Jobs/DekiReader; nice php notifyfromdb.php
 
 * notice: this file has only the class definitions, of cource you need a script
 * to create an object of DekiReader and pass the userdata to it.
 * */
 
/*
* this script needs the PEAR-packages "http_client" and "http_request"
*/
require_once "HTTP/Request.php";
require_once "HTTP/Client.php";
 
define('ERROR_DEKIWIKI_WRONG_CREDENTIALS', 'ERROR_DEKIWIKI_WRONG_CREDENTIALS');
define('ERROR_WEB_SERVICE', 'ERROR_WEB_SERVICE');
 
/* ------- Cookie's creation for Deki Wiki ------- */
class DekiCookie {
    public $url = "http://wiki.ebesucher.in";
 
    /** thanks to kurdte from: http://wiki.developer.mindtouch.com/MindTouch_Deki/Specs/Authentication_Providers */
    public function getAuthToken($login, $password) {
        /* Request a value for the user's cookie */
        $url = $this->url."/@api/deki/users/authenticate/";
        $req =& new HTTP_Request($url);
        $req->setBasicAuth($login, $password);
        $response = $req->sendRequest();
 
        if (!PEAR::isError($response)) {
        $output=$req->getResponseBody();
 
        /* Authentication proccess : if the response contains 401 authentication failed, the user is not registered */
        if(strpos($output,"authentication")==0 && strpos($output,"401")==0)
        {
            //$cookie_deki=$req->getResponseCookies();
            return $output;
            //setcookie("authtoken",str_replace("\"","",$cookie_deki[0]['value']),time()+3600, '/');
        }
        else
        {
            $error = ERROR_DEKIWIKI_WRONG_CREDENTIALS;
        }
        }
        else {
            $response->getMessage();
            $error = ERROR_WEB_SERVICE;
        } 
    }
}
 
class DekiReader {
    /**
        array with the date of users to check
        every item must contain: username, password
        every item may contain: authtoken
    */
    public $userdata;
    public $url;
    public $emaildomain;
    private $domain;
    /** Directory to store cache-Data (what feed-messages did we
      * allready read? */
    public $cachedir = "cache/";
 
    /**
     *  creates a DekiReader
     *  $emaildomain = the domain for sender-address
     *  if not given, we'll take the domain
    */
    public function DekiReader($domain, $userdata, $emaildomain = false) {
        $this->domain = $domain;
        $this->url = $domain."/index.php?title=Special:Watchlist&feed=rss";
        $this->userdata = $userdata;
        if (!$emaildomain) {
            //delete http:// or https:// at the beginning
            $d = eregi_replace('^http://', '', $domain);
            $d = eregi_replace('^https://', '', $d);
            $this->emaildomain = $d;   
        }
        else {
            $this->emaildomain = $emaildomain;
        }    
    }
 
    /**
    * Returns the proper RFC 822 formatted date. 
    * @return string
    */
    public function RFCDate($timestamp) {
       $tz = date("Z", $timestamp);
       $tzs = ($tz < 0) ? "-" : "+";
       $tz = abs($tz);
       $tz = ($tz/3600)*100 + ($tz%3600)/60;
       $result = sprintf("%s %s%04d", date("D, j M Y H:i:s", $timestamp), $tzs, $tz);
 
       return $result;
    }
 
    /** convert the Dateformat of the Feed to a Timestamp */
    public function strangeDate2Timestamp($date) {
        $regs = array();
        preg_match('~((\d+)-(\d+)-(\d+))T((\d+):(\d+):(\d+))Z~is', $date, $regs);
 
        //date and time
        $d = $regs[0];
        $t = $regs[1];
        return mktime($regs[6], $regs[7], $regs[8], $regs[3], $regs[4], $regs[2]);
    }
 
    /** read the message cache for a user */
    public function getCache($username) {
        //we use username and feed-url to cache read messages
        $cache  = $this->getCacheFilename($username);
 
        //read the cache
        $contents = file_exists($cache) ? file_get_contents($cache) : '';
 
        return $contents;
    }
 
    //get the filename of the message cache for a username
    public function getCacheFilename($username) {
        return $this->cachedir.'rss_' . md5($username."@".$this->url) . '.txt';
    }
 
    //add something to messagecache
    public function addToCache($username, $url) {
        $handle = fopen($this->getCacheFilename($username), 'a');
        fwrite($handle, $url . "\r\n");
        fclose($handle);
    }
 
    public function run() {
        $deki = new DekiCookie();
        header('Content-Type: text/plain');
        foreach ($this->userdata as $user) {
            echo "trying to get ".$this->url." for user ".$user['username']."\n";
            $req = new HTTP_Request($this->url);            
            $req->setMethod(HTTP_REQUEST_METHOD_GET);
            //do we have an authtoken?
            if (!$user['authtoken']) {
                $user['authtoken'] = $deki->getAuthToken($user['username'], $user['password']);
                echo "estimated authtoken for user ".$user['username'].": ".$user['authtoken']."\n";
            }
            $req->addCookie("authtoken", $user['authtoken']);
            $req->sendRequest();
            $rss = $req->getResponseBody();
 
            $xml    = new SimpleXMLElement($rss);
            $cache_contents = $this->getCache($user['username']);
 
            //some output
            echo 'Feed: ' . (string)$xml->link['href'] . "\r\n";
 
            //iterate the items in the feed
 
            foreach ($xml->entry as $item) {
                $url = (string)$item->link['href'];
                $id          = (string)$item->id;
                //did we allready read this?
                if (strpos($cache_contents, $id) !== false) {
                    echo "   read: $id\r\n";    
                    continue;
                }
                else {
                    echo "   unread: $id\r\n";
                }
 
                // The data from the feed
                $authorname  = (string)$item->author->name;
                //delete scopes for better compartibility
                $authorname  = str_replace("(", "", $authorname);
                $authorname  = str_replace(")", "", $authorname);
                //here we don't have a read possibility to get the email of
                //the author, we just simulate it by adding our domain name
                //to the username (or you could write a function that does that better..)
                $author      = '"'.$authorname.'"'."<".$authorname."@".$this->emaildomain.">";
 
                $title       = (string)$item->title;
                $link        = (string)$item->link['href'];
                $pubDate     = (string)$item->published;
                //convert the date from the feed to a compartible date format
                //for the emails, so that we can see the correct date of
                //the posting in our email program
                $updatedtimestamp = $this->strangeDate2Timestamp((string)$item->updated);
                $updated     = $this->RFCDate($updatedtimestamp);
                //we want all tags between <summary> and </summary>, so just
                //iterate through the children
                $description = "";
                foreach ($item->summary->children() as $child)
                {
                    $description .= $child->asXML();
                }
 
 
 
                $to   = $user['email']; 
                $mail = "
                <html><head></head><body>
                Page: <a href=\"$url\">$title</a>
                $description
                </body></html>"; 
 
                mail($to, '[Wiki] ' . $title, $mail, "From: $author"
                     ."\nContent-Type: text/html; charset=utf-8"
                     ."\nDate: ".$updated
                     ); 
 
                // we have send a notification for this item, so send
                // write this to the cache
                $this->addToCache($user['username'], $id);
            }
        }
    }
}
 
?>

So now lets use this! I use remote authentication on my site, so i’ve written a small file to read user data from my auth db.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
  //include some internal db stuff
 // ....
 
  //then
include_once("dekireader.php");
 
$watch_accounts = array("jeff", "sebastian");
if (count($watch_accounts)<1) die("Please define Accounts to watch!"); 
$sql = "SELECT Username as username, PW as password, Email as email FROM Admins WHERE Username IN ('".implode("','", $watch_accounts)."')";
$res = mysql_query($sql);
echo mysql_error();
$userdata = mysql_get_whole_result($res);
 
$dekireader =  new DekiReader('http://mywiki',
                              $userdata, 'myemaildomain.no');
 
$dekireader->run();        
 
?>

But you could also just select all all admins. Or hard-code userdata:

1
2
3
4
5
6
7
8
9
10
11
12
<?php
  $userdata = array(array(
            'username'  => 'jeff',
            'password'  => 'secret',
            //'authtoken' => 'something', //you could cache authtokens to make the script faster
            'email'     => 'bla@bla.bla'
            )); 
$dekireader =  new DekiReader('http://mywiki',
                              $userdata, 'myemaildomain.no');
 
$dekireader->run();        
?>

I did not handle using internal dekiwiki database for authorizing local users. I also did not try this. But this should be no problem after you examine the db structure of dikiwiki. Let me know, if you try this!

So now just set up a cronjob to run this script regulary, and you have a good email notification for your wiki. Like this:
* / 10 * * * * cd /www/Cron_Jobs/DekiReader; nice php notifyfromdb.php

I’m still looking forward and hope that Deki-team will soon implement Deki to support this out-of-the-box.

You can use this script for any purpose, just please leave a comment! My blog is hungry ;)
Download the main script: dekireader.phps

eLuna unter Ubuntu

Wie installiert man eLuna (Monitoring-Tool) unter Ubuntu?

1) eLuna herunterladen:
wget http://steph.eluna.org/fileadmin/eluna_graph_system/latest/eluna_graph_system.tar.gz

2) eLuna entpacken:
ich mache das immer mit mc

3) eLuna in ein vom www-server erreichbares Verzeichnis legen und vhost einrichten

4) Dependencies erfüllen
sudo apt-get install rrdtool libhtml-template-expr-perl libdatetime-perl

5) Webserver restarten

6) Cronjob einrichten
crontab -e
*/5 * * * * /var/www/graphs/update.pl

So, das wars! Jetzt kann man noch das Verzeichnis mit einem .htpasswd absichern, damit nicht jeder darauf zugreifen kann, und sich über schöne Graphiken freuen!

Windows crappy Codepage

Nachdem ich nun über 5 Stunden damit verbracht habe die Umwandlung der Zeichen auf meiner Webseite zu korrigieren, möchte ich das veröffntlichen, damit andere davon einen Nutzen haben.
Umwandlung: latin1 (ISO8859-1) => utf8

Nun gut, das kann ja php auch von sich aus, dafür gibt es die Funktion utf8_encode(). Aber… wenn man sie verwendet, stellt man ganz schnell fest, dass einige Zeichen falsch dargestellt werden, z.B. das Euro-Zeichen. Warum ist das so?

Auch wenn man eine Webseite als latin1 deklariert hat, und die Zeichen entsprechend abspeichern möchte, ist Windows da etwas freizügiger und mischt in diese Codierung eigene Zeichen zu, die in latin1 eigentlich gar nicht enthalten sind. Diese zusätzlichen Zeichen ersetzen einige in latin1 definierten Steuerzeichen, die sonst nicht verwendet werden. Diese Mischung nennt Windows CP1252 (CP = Codepage). Interessanterweise werden diese Zeichen aber auch unter MacOsx auf der Webseite richtig dargestellt. Scheinbar haben die Softwarehersteller das einfach so hingenommen. Nur PHP nicht…

Ich habe mir nun auf der Grundlage von dieser I18N Aufstellung eine eigene Umwandlungsfunktion geschrieben, die genau diese Steuerzeichen ersetzt. Die Datei ist zwingenderweise utf8-codiert, das macht aber nichts. Man kann sie einfach in die bestehenden Dateien per include einbinden, die anderen Projektdateien können weiterhin latin1-codiert sein.

Quelltext für die Umwandlungsfuntkion: cp1252_to_utf8.php

Private-Key unter MacOsX auf einen entfernten Server kopieren

Um einen zuvor erstellten SSH-Key auf einen entfernten Rechner zu kopieren, gibt man auf der Konsole ein:

cat ~/.ssh/id_dsa.pub | ssh medium.ebesucher.de ‘cat - >> ~/.ssh/authorized_keys’