Michael Heydekamp <[EMAIL PROTECTED]> wrote on 03.04.04:

[...]

> Nachdem der Bereich UTF-8 abgeschlossen ist, beginne ich gerade, mich
> mit UTF-7 zu befassen, wo auch noch einige Bugs schlummern.  Auch
> diese Routine geht davon aus, da� bestimmte L�ngen nie �berschritten
> werden und alles immer sauber codiert ist - Fehlerbehandlung
> Mangelware, und so kann der UUZ dann auch schon mal in eine
> Endlosschleife verfallen.

Z.B. bei sowas hier:

----------8<----------
Test: 
+AOQA9gD8AN8AxADWANwA3wDEANYA3ADfAOQA9gD8AN8A5AD2APwA3wDEANYA3ADfAMQA1gDcAN8A5AD2APwA3wDkAPYA/ADfAMQA1gDcAN8AxADWANwA3wDkAPYA/ADfAOQA9gD8AN8AxADWANwA3wDEANYA3ADfAOQA9gD8AN8A5AD2APwA3wDEANYA3ADfAMQA1gDcAN8A5AD2APwA3wDkAPYA/ADfAMQA1gDcAN8AxADWANwA3wDkAPYA/ADfAOQA9gD8AN8AxADWANwA3wDEANYA3ADfAOQA9gD8AN8A5AD2APwA3wDEANYA3ADfAMQA1gDcAN8A5AD2APwA3wDkAPYA/ADfAMQA1gDcAN8AxADWANwA3wDkAPYA/ADfAOQA9gD8AN8AxADWANwA3wDEANYA3ADfAOQA9gD8AN8A5AD2APwA3wDEANYA3ADfAMQA1gDcAN8A5AD2APwA3wDkAPYA/ADfAMQA1gDcAN8AxADWANwA3wDkAPYA/ADfAOQA9gD8AN8AxADWANwA3wDEANYA3ADfAOQA9gD8AN8A5AD2APwA3wDEANYA3ADfAMQA1gDcAN8A5AD2APwA3wDkAPYA/ADfAMQA1gDcAN8AxADWANwA3wDkAPYA/ADfAOQA9gD8AN8AxADWANwA3wDEANYA3ADfAOQA9gD8AN8A5AD2APwA3wDEANYA3ADfAMQA1gDcAN8A5AD2APwA3wDkAPYA/ADfAMQA1gDcAN8AxADWANwA3wDkAPYA/ADfAOQA9gD8AN8AxADWANwA3wDEANYA3ADfAOQA9gD8AN8A5AD2APwA3wDEANYA3ADfAMQA1gDcAN8A5AD2APwA3wDkAPYA/ADfAMQA1gDcAN8AxADWANwA3wDkAPYA/ADf-abc
----------8<----------

Das ist IMO ein valider UTF-7-String, was ich aber noch verifizieren
werde.  Zumindest kann sowas theoretisch vorkommen, also mu� man
mindestens im Interesse der Robustheit darauf vorbereitet sein.

> Leider kenne ich mich mit dem Thema UTF-7 bisher nicht so aus und
> schreibe als F'up auf dieses Posting dazu gleich mal was in c.f.dev.
> Vielleicht mag ja jemand laut mitdenken. ;)

Das Problem mit UTF-7 ist, da� es auf Base64 basiert und man daher
(anders als bei UTF-8) nicht auf Anhieb erkennen kann, wieviele codierte
Zeichen ben�tigt werden, damit bei der Decodierung �berhaupt was
Sinnvolles und Richtiges rauskommt.

Es geht dabei um das grunds�tzliche Problem von Pascal, da� man solche
langen Strings prinzipiell h�ppchenweise decodieren mu�, da� aber bei
jeder Multibyte-Codierung wie UTF-7 und UTF-8 da� Problem besteht, da�
die L�nge jedes H�ppchens willk�rlich ist (man kann es auf 245 oder 250
Zeichen begrenzen oder die max. L�nge von 255 Zeichen nehmen) und daher
die Gefahr besteht, da� der zu bearbeitende String nicht mit einer
g�ltigen und vollst�ndigen Bytesequenz abgeschlossen ist.

Das w�re auch bei einem weniger kryptischen String als oben der Fall,
wenn so ein OjE mal wieder ewig lange Zeilen produziert, die bis Pos.
253 nur ASCII-Zeichen enthalten, aber ausgerechnet an Pos. 254 ein oder
mehrere Umlaute folgen, die UTF-7-codiert sind.  In dem Fall ist die
Codierung "zerhackt" und man kann das nicht decodieren, mu� sich den
Anfang der UTF-7-Sequenz merken und auf den n�chsten Teilstring warten
(der dann auch hoffentlich kommt).

Bei UTF-7 kommt erschwerend die Base64-Codierung hinzu, bei der anders
als bei UTF-8 eine bestimmte Anzahl von Zeichen *nicht* als
abgeschlossene Einheit f�r *ein* Zeichen betrachtet werden kann.  Bei
UTF-8 stehen entweder 2, 3 oder 4 Bytes f�r genau ein Zeichen, und man
kann vor allem am ersten Byte genau erkennen, wieviele es sind.

Ich habe nach Lekt�re von RFC2152, RFC2045 und einigen Grundlagen zur
Base64-Codierung folgende �berlegungen zur UTF-7-Decodierung angestellt,
vielleicht mag die ja jemand nachvollziehen und best�tigen oder
korrigieren:


1. Base64 codiert

   - 1 Byte in 2 Bytes,
   - 2 Bytes in 3 Bytes, und
   - 3 Bytes in 4 Bytes.

   Es werden immer Einheiten von 3 Bytes codiert, so da� ein Base64-
   codierter String immer eine durch 4 teilbare L�nge haben mu�.  Ist
   die L�nge des zu codierenden Strings nicht genau durch 3 teilbar,
   "fehlen" nat�rlich am Ende genau 1 oder 2 Bytes, die durch ein oder
   zwei "=" aufgef�llt werden ("padding").  Von links betrachtet, bilden
   4 Bytes eines Base64-codierten Strings immer eine abgeschlossene
   Einheit (2 oder 3 Bytes hingegen nicht!).


2. UTF-7 ist im Prinzip Base64, nur da� es bei der Codierung nicht die
   Zeichen "as is" vorgesetzt bekommt (was ja auch bei Unicode gar nicht
   geht), sondern jedes Zeichen (oder die H�lfte eines sog. "surrogate
   pairs" im Bereich U+10000 bis U+10FFFF) in einer 2-Octet-Sequenz
   vorgelegt wird.

   Beispiel:

   Das 8bit-Zeichen "�" hat in Unicode den code point U+00C4.  Das teilt
   man auf in die Bytes 0x00 und 0xC4 und codiert diese beiden Bytes
   nach Base64.  Heraus kommt die Sequenz "AMQ=", dann packt man ein "+"
   davor, wirft das padding "=" weg, schlie�t die Sequenz mit einem "-"
   ab und somit ist die UTF-7-Codierung f�r das Zeichen "�" der String
   "+AMQ-".

   Codieren wir nicht "�", sondern "A�":

   � = U+00C4 = 0x00 0xC4
   � = U+00D6 = 0x00 0xD6

   Die Sequenz 0x00 0xC4 0x00 0xD6 nach Base64 codiert ergibt "AMQA1g=="
   in UTF-7-Syntax also "+AMQA1g-".

   Bis hier sieht es so aus, als st�nden immer 3 UTF-Zeichen f�r ein
   Zeichen, aber jetzt codieren wir "���":

   � = U+00C4 = 0x00 0xC4
   � = U+00D6 = 0x00 0xD6
   � = U+00DC = 0x00 0xDC

   Machen wir's kurz, es kommt "+AMQA1gDc-" (8 statt 9 Zeichen!) heraus.

   Aus dem Anh�ngen von "Dc" f�r das zus�tzliche "�" darf man jetzt aber
   nicht den Schlu� ziehen, die Base64-Codierung des Zeichens "�" sei
   "Dc" - denn wenn man das als einzelnes Zeichen UTF-7-codiert, ist das
   Ergebnis "+ANw-". %-)

   Damit soll nur gezeigt werden, da� es nicht m�glich ist, einfach zu
   sagen "wir nehmen immer 3 UTF-7-codierte Bytes und haben eine
   abgeschlossene Einheit, die wir decodieren k�nnen".  Noch deutlicher
   wird das, wenn man mit Zeichen konfrontiert ist, deren code point
   nicht mit "U+00" beginnt, wie z.B. Rahmengrafikzeichen oder
   griechische Zeichen aus CP437:

   Kleines alpha = U+03B1 = 0x03 0xB1 = "+A7E-"
   Gro�es  omega = U+03A9 = 0x03 0xA9 = "+A6k-"

   Im Unterschied zu dem Beispiel mit "A�" oben kommt jetzt aber, wenn
   man beide Zeichen direkt hintereinanderschreibt und UTF-7-codiert,
   eben nicht "+A7EA6k-" heraus, sondern:

   U+03B1 U+03A9 = 0x03 0xB1 0x03 0xA9 = "+A7EDqQ-"

   Das hei�t, die Existenz des Gro�en omega ver�ndert auch die Codierung
   des kleinen alpha davor.


F�r Fachleute mag das alles kalter Kaffee sein, ich hab' mich bisher
damit nie eingehend besch�ftigt, mu� es aber jetzt tun.  Gehen wir
weiter:


3. Wie in 1. beschrieben, m��ten bei Base64 vier Bytes immer eine in
   sich geschlossene Einheit sein, die man sauber decodieren kann.
   Nehmen wir unser "A��" von oben, das UTF-7-codiert "+AMQA1gDc-"
   lautet und decodieren die ersten 4 Bytes "AMQA":

   "AMQA" (Base64-decodiert) => 0x00 0xC4 0x00

   Das ist zwar korrekt, aber wir sehen schon, da� wir bei der
   anschlie�enden UTF-7-Decodierung aus 0x00 0xC4 zwar das "�" erhalten
   w�rden, aber auch ein zusammenhangloses 0x00 haben (das zu dem
   folgenden "�" geh�rt).  Die aktuelle UTF-7-Routine w�rde so ein 0x00
   schlicht l�schen, wenn sich "AMQA" am Ende eines Strings befindet,
   ohne auf die n�chsten Bytes zu warten, die evtl. mit dem n�chsten
   Teilstring folgen.

   Die UTF-7-Spezifikation in RFC2152 sagt zwar klar, da� eine ungerade
   Anzahl an Octets "ill-formed" ist, aber bei langen Strings > 255
   Zeichen ist das halt so eine Sache.


Eigentlich wollte ich daraus ja jetzt bereits die richtigen Konsequenzen
ziehen, aber w�hrend des Schreibens habe ich selbst wieder soviele neue
Erkenntnisse gewonnen, da� ich das jetzt erst mal sacken lassen und mir
eine funktionierende Logik �berlegen mu�.

Falls jemand was sagen m�chte, nur zu. :)


        Michael
------------------------------------------------------------------------
FreeXP Entwickler-Mailingliste
[EMAIL PROTECTED]
http://www.freexp.de/cgi-bin/mailman/listinfo/dev-list

Antwort per Email an