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