Changeset 97


Ignore:
Timestamp:
Feb 4, 2022, 11:30:24 AM (2 years ago)
Author:
chronos
Message:
  • Fixed: Quoted-printable encoded text was not handled correctly for multiple lines.
Location:
trunk
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/Forms/UFormTest.pas

    r96 r97  
    146146      Input := VCardBegin + LineEnding +
    147147        VCardVersion + LineEnding +
    148         'FN;ENCODING=QUOTED-PRINTABLE:Jm=C3=A9no P=C5=99=C3=ADjmen=C3=AD' + LineEnding +
     148        'FN;ENCODING=QUOTED-PRINTABLE:Jm=C3=A9no=20P=C5=99=C3=ADjmen=C3=AD' + LineEnding +
     149        VCardEnd + LineEnding;
     150      Output := Input;
     151    end;
     152    with TTestCaseLoadSave(AddNew('Quoted-printable load-save multi-line', TTestCaseLoadSave)) do begin
     153      Input := VCardBegin + LineEnding +
     154        VCardVersion + LineEnding +
     155        'FN;ENCODING=QUOTED-PRINTABLE:Jm=C3=A9no=20P=C5=99=C3=ADjmen=C3=AD=' + LineEnding +
     156        'Jm=C3=A9no=20P=C5=99=C3=ADjmen=C3=AD' + LineEnding +
    149157        VCardEnd + LineEnding;
    150158      Output := Input;
  • trunk/Languages/vCardStudio.cs.po

    r95 r97  
    13231323msgstr "Hodnota"
    13241324
    1325 #: uquotedprintable.slinelengtherr
    1326 msgid "Invalid line length for encoded text"
    1327 msgstr "Neplatná délka řádky kódovaného textu"
     1325#: uquotedprintable.sdecodeerror
     1326msgid "Decode error"
     1327msgstr "Chyba dekódování"
  • trunk/Languages/vCardStudio.pot

    r95 r97  
    12921292msgstr ""
    12931293
    1294 #: uquotedprintable.slinelengtherr
    1295 msgid "Invalid line length for encoded text"
    1296 msgstr ""
    1297 
     1294#: uquotedprintable.sdecodeerror
     1295msgid "Decode error"
     1296msgstr ""
     1297
  • trunk/Samples/centrum.cz.vcf

    r41 r97  
    2121EMAIL;INTERNET;ENCODING=QUOTED-PRINTABLE:
    2222EMAIL;INTERNET;WORK;ENCODING=QUOTED-PRINTABLE:firma@neco.cz
    23 NOTE;ENCODING=QUOTED-PRINTABLE:jkjlj=20=20kljklkl=20jl=20jlk
     23NOTE;ENCODING=QUOTED-PRINTABLE:jkjlj=20=20kljklkl=20jl=20jlk=20=C5=99=C4=8D=C5=99=C4=8D=C5=A1=C4=8D=C5=A1=
     24=20=C4=8D=C4=8D=C5=A1=C5=99=C4=8D=20=C5=99=C4=8D=C5=A1=0Affdfsdfsdfs=20f=
     25fsd=20fsd=20fsdfsdf=20sdfs=20;=20_=20/=20\=20$=20,=20'=20"=20.=20=3D=0Ad=
     26asdsaddsa=20a=20dsadsa=20sa=20dsadsa=20dsad=20sadsa=20da=C4=8D=C4=9B=C5=A1=
     27=C4=8D=C5=A1=C4=8D=C4=9B=C5=A1=0A
    2428X-CENTRUM-CZ-ANNIVERSARY;ENCODING=QUOTED-PRINTABLE:89819393;1917-12-19;hjkhjkhjk
    2529X-CENTRUM-CZ-ANNIVERSARY;ENCODING=QUOTED-PRINTABLE:89819393;;
     
    3337X-CENTRUM-CZ-IM;ENCODING=QUOTED-PRINTABLE:jjhhjkhjkhjk;4
    3438END:VCARD
    35 
  • trunk/UContact.pas

    r93 r97  
    603603  end else
    604604  if Encoding = 'QUOTED-PRINTABLE' then begin
    605     Result := DecodeQuotedPrintable(Value);
     605    Result := DecodeQuotedPrintable(Value, True);
    606606  end
    607607  else Result := '';
     
    614614  end else
    615615  if Encoding = 'QUOTED-PRINTABLE' then begin
    616     Result := EncodeQuotedPrintable(Value);
     616    Result := EncodeQuotedPrintable(Value, True);
    617617  end
    618618  else Result := '';
     
    11641164              CutLength := ContactsFile.MaxLineLength;
    11651165              if Encoding = 'QUOTED-PRINTABLE' then begin
    1166                 // Do not cut encoded items
    1167                 if ((CutLength - 2) >= 1) and (OutText[CutLength - 2] = '=') then
     1166                Dec(CutLength); // There will be softline break at the end
     1167                // Do not cut encoded items at the end of line
     1168                if ((CutLength - 1) >= 1) and (OutText[CutLength - 1] = QuotedPrintableEscapeCharacter) then
    11681169                  Dec(CutLength, 2)
    1169                 else if ((CutLength - 1) >= 1) and (OutText[CutLength - 1] = '=') then
     1170                else if OutText[CutLength] = QuotedPrintableEscapeCharacter then
    11701171                  Dec(CutLength, 1);
    11711172              end;
     1173
    11721174              CutText := UTF8Copy(OutText, 1, CutLength);
     1175              System.Delete(OutText, 1, Length(CutText));
     1176              if Encoding = 'QUOTED-PRINTABLE' then
     1177                CutText := CutText + QuotedPrintableEscapeCharacter; // Add soft line break
    11731178              Add(LinePrefix + CutText);
    1174               LinePrefix := ' ';
    1175               System.Delete(OutText, 1, Length(CutText));
     1179              if Encoding <> 'QUOTED-PRINTABLE' then
     1180                LinePrefix := ' ';
    11761181              Inc(LineIndex);
    11771182              Continue;
     
    11991204  CommandPart: string;
    12001205  Names: string;
     1206  QuotedPrintableMultiLine: Boolean;
    12011207begin
    12021208  Result := False;
     
    12281234        CommandPart := GetNext(Line, ':');
    12291235        Names := CommandPart;
     1236        QuotedPrintableMultiLine := Pos('encoding=quoted-printable', LowerCase(CommandPart)) > 0;
    12301237        Value := Line;
    12311238        while True do begin
     
    12371244            Value := Value + Copy(Line2, 2, MaxInt);
    12381245          end else
    1239           if (Length(Line2) > 0) and (Length(Value) > 0) and (Value[Length(Value)] = '=') and
    1240             (Line2[1] = '=') then begin
    1241             Value := Value + Copy(Line2, 2, MaxInt);
     1246          if QuotedPrintableMultiLine and (Length(Value) > 0) and
     1247          (Value[Length(Value)] = QuotedPrintableEscapeCharacter) then begin
     1248            SetLength(Value, Length(Value) - 1);
     1249            Value := Value + Line2;
    12421250          end else begin
    12431251            Dec(I);
  • trunk/UQuotedPrintable.pas

    r46 r97  
    88  Classes, SysUtils;
    99
    10 function DecodeQuotedPrintable(Text: string): string;
    11 function EncodeQuotedPrintable(Text: string): string;
     10function DecodeQuotedPrintable(Text: string; IgnoreErrors: Boolean = False): string;
     11function EncodeQuotedPrintable(Text: string; EncodeSpaces: Boolean = False): string;
    1212
     13const
     14  QuotedPrintableEscapeCharacter = '=';
    1315
    1416implementation
    1517
     18uses
     19  UCommon;
     20
    1621resourcestring
    17   SLineLengthErr = 'Invalid line length for encoded text';
     22  SDecodeError = 'Decode error';
    1823
    19 const
    20   MaxLine = 1000;
    21 
    22 function DecodeQuotedPrintable(Text: string): string;
     24function DecodeQuotedPrintable(Text: string; IgnoreErrors: Boolean = False): string;
    2325var
    24   O, Count, WS: Integer;
    25   I: integer;
    26   InBuf: array[0..Pred(MaxLine)] of Byte;
    27   OutBuf: array[0..Pred(MaxLine)] of Byte;
    28   Decoding: Boolean;
    29   Keeper: Boolean;
    30   Abort: Boolean;
    31   InStream: TMemoryStream;
    32   OutStream: TMemoryStream;
     26  I: Integer;
     27  J: Integer;
     28  C: Char;
     29  IntValue: Integer;
    3330begin
    3431  Result := '';
    35   InStream := TMemoryStream.Create;
    36   OutStream := TMemoryStream.Create;
    37   try
    38   if Text <> '' then begin
    39     InStream.Write(Text[1], Length(Text));
    40     InStream.Position := 0;
    41   end;
    42   Abort := False;
    43   FillChar(InBuf, SizeOf(InBuf), #0);
    44   WS := $FF;
    45   Decoding := True;
    46   Keeper := False;
    47 
    48   { Skip any CR/LF's to get to the encoded stuff }
    49   while True do begin
    50     if InStream.Read(Char(InBuf[0]), 1) = 0then
    51       Exit;
    52     if ((InBuf[0] <> $0D) and (InBuf[0] <> $0A)) then begin
    53       Keeper := True;
    54       Break;
     32  SetLength(Result, Length(Text));
     33  I := 1;
     34  J := 1;
     35  while I <= Length(Text) do begin
     36    C := Text[I];
     37    if C = QuotedPrintableEscapeCharacter then begin
     38      if ((I + 2) <= Length(Text)) and TryHexToInt(Text[I + 1] + Text[I + 2], IntValue) then begin
     39        Result[J] := Chr(IntValue);
     40        Inc(I, 2);
     41        Inc(J);
     42        SetLength(Result, Length(Result) - 2);
     43      end else begin
     44        if not IgnoreErrors then raise Exception.Create(SDecodeError)
     45        else begin
     46          Result[J] := '?';
     47          Inc(J);
     48        end;
     49      end;
     50    end else begin
     51      Result[J] := C;
     52      Inc(J);
    5553    end;
    56   end;
    57 
    58   while Decoding and not Abort do begin
    59     { Initialize }
    60     if Keeper then begin
    61       I := 1;
    62       Keeper := False;
    63     end else begin
    64       I := 0;
    65     end;
    66     O := 0;
    67 
    68     { Read in one line at a time - skipping over bad characters }
    69     while True do begin
    70       if (I > High(InBuf)) then
    71         raise Exception.Create(SLineLengthErr);
    72       if InStream.Read(Char(InBuf[I]), 1) = 0 then
    73         Break;
    74       case InBuf[I] of
    75         $0A : Continue;
    76         $0D : begin
    77                 Inc(I);
    78                 Break;
    79               end;
    80        { Test for potential end of data }
    81        { '--' is probably the next Mime boundary }
    82        { $2D : if (I = 1) and (InBuf[0] = $2D) then Exit;}
    83       end;
    84       Inc(I);
    85     end;
    86 
    87     if I = 0 then Break;
    88     Count := I;
    89     I := 0;
    90 
    91     { Decode data to output stream }
    92     while I < Count do begin
    93       case InBuf[I] of
    94         9       : begin
    95                     if WS = $FF then
    96                       WS := O;
    97                     OutBuf[O] := InBuf[I];
    98                     Inc(O);
    99                     Inc(I);
    100                   end;
    101         13      : if WS = $FF then begin
    102                     OutBuf[O] := 13;
    103                     OutBuf[O+1] := 10;
    104                     Inc(O, 2);
    105                     Inc(I);
    106                   end else begin
    107                     OutBuf[WS] := 13;
    108                     OutBuf[WS+1] := 10;
    109                     O := WS+2;
    110                     Inc(I);
    111                   end;
    112         32      : begin
    113                     if WS = $FF then
    114                       WS := O;
    115                     OutBuf[O] := InBuf[I];
    116                     Inc(O);
    117                     Inc(I);
    118                   end;
    119         33..60  : begin
    120                     WS := $FF;
    121                     OutBuf[O] := InBuf[I];
    122                     Inc(O);
    123                     Inc(I);
    124                   end;
    125         61      : begin
    126                     WS := $FF;
    127                     if I+2 >= Count then Break;
    128                     case InBuf[I+1] of
    129                       48 : OutBuf[O] := 0;    {0}
    130                       49 : OutBuf[O] := 16;   {1}
    131                       50 : OutBuf[O] := 32;   {2}
    132                       51 : OutBuf[O] := 48;   {3}
    133                       52 : OutBuf[O] := 64;   {4}
    134                       53 : OutBuf[O] := 80;   {5}
    135                       54 : OutBuf[O] := 96;   {6}
    136                       55 : OutBuf[O] := 112;  {7}
    137                       56 : OutBuf[O] := 128;  {8}
    138                       57 : OutBuf[O] := 144;  {9}
    139                       65 : OutBuf[O] := 160;  {A}
    140                       66 : OutBuf[O] := 176;  {B}
    141                       67 : OutBuf[O] := 192;  {C}
    142                       68 : OutBuf[O] := 208;  {D}
    143                       69 : OutBuf[O] := 224;  {E}
    144                       70 : OutBuf[O] := 240;  {F}
    145                       97 : OutBuf[O] := 160;  {a}
    146                       98 : OutBuf[O] := 176;  {b}
    147                       99 : OutBuf[O] := 192;  {c}
    148                      100 : OutBuf[O] := 208;  {d}
    149                      101 : OutBuf[O] := 224;  {e}
    150                      102 : OutBuf[O] := 240;  {f}
    151                     end;
    152                     case InBuf[I+2] of
    153                       48 : ;                             {0}
    154                       49 : OutBuf[O] := OutBuf[O] + 1;   {1}
    155                       50 : OutBuf[O] := OutBuf[O] + 2;   {2}
    156                       51 : OutBuf[O] := OutBuf[O] + 3;   {3}
    157                       52 : OutBuf[O] := OutBuf[O] + 4;   {4}
    158                       53 : OutBuf[O] := OutBuf[O] + 5;   {5}
    159                       54 : OutBuf[O] := OutBuf[O] + 6;   {6}
    160                       55 : OutBuf[O] := OutBuf[O] + 7;   {7}
    161                       56 : OutBuf[O] := OutBuf[O] + 8;   {8}
    162                       57 : OutBuf[O] := OutBuf[O] + 9;   {9}
    163                       65 : OutBuf[O] := OutBuf[O] + 10;  {A}
    164                       66 : OutBuf[O] := OutBuf[O] + 11;  {B}
    165                       67 : OutBuf[O] := OutBuf[O] + 12;  {C}
    166                       68 : OutBuf[O] := OutBuf[O] + 13;  {D}
    167                       69 : OutBuf[O] := OutBuf[O] + 14;  {E}
    168                       70 : OutBuf[O] := OutBuf[O] + 15;  {F}
    169                       97 : OutBuf[O] := OutBuf[O] + 10;  {a}
    170                       98 : OutBuf[O] := OutBuf[O] + 11;  {b}
    171                       99 : OutBuf[O] := OutBuf[O] + 12;  {c}
    172                      100 : OutBuf[O] := OutBuf[O] + 13;  {d}
    173                      101 : OutBuf[O] := OutBuf[O] + 14;  {e}
    174                      102 : OutBuf[O] := OutBuf[O] + 15;  {f}
    175                     end;
    176                     Inc(I, 3);
    177                     Inc(O);
    178                   end;
    179         62..126 : begin
    180                     WS := $FF;
    181                     OutBuf[O] := InBuf[I];
    182                     Inc(O);
    183                     Inc(I);
    184                   end;
    185         else
    186           Inc(I);
    187       end;
    188     end;
    189 
    190     if O > 0 then
    191       OutStream.Write(OutBuf, O)
    192     else
    193       Break;   { OutBuf is empty }
    194   end;
    195   SetLength(Result, OutStream.Size);
    196   OutStream.Position := 0;
    197   if OutStream.Size > 0 then
    198     OutStream.Read(Result[1], Length(Result));
    199   finally
    200     OutStream.Free;
    201     InStream.Free;
     54    Inc(I);
    20255  end;
    20356end;
    20457
    205 function EncodeQuotedPrintable(Text: string): string;
     58function EncodeQuotedPrintable(Text: string; EncodeSpaces: Boolean): string;
    20659var
    207   O, W: Integer;
    208   WordBuf, OutBuf: array[0..80] of AnsiChar;
    209   CurChar: AnsiChar;
    210   Abort: Boolean;
    211   InStream: TStream;
    212   OutStream: TMemoryStream;
    213 
    214   procedure SendLine;
    215   begin
    216     if (OutBuf[O - 1] = #9) or (OutBuf[O - 1] = #32) then begin
    217       OutBuf[O] := '=';
    218       Inc(O);
     60  I: Integer;
     61  J: Integer;
     62  C: Char;
     63  LowerLimit: Char;
     64const
     65  HexDigits : array[0..$F] of AnsiChar = '0123456789ABCDEF';
     66begin
     67  if EncodeSpaces then LowerLimit := #33
     68    else LowerLimit := #32;
     69  Result := '';
     70  SetLength(Result, Length(Text));
     71  I := 1;
     72  J := 1;
     73  while I <= Length(Text) do begin
     74    C := Text[I];
     75    if (C = QuotedPrintableEscapeCharacter) or (C < LowerLimit) or (C > #126) then begin
     76      SetLength(Result, Length(Result) + 2);
     77      Result[J] := QuotedPrintableEscapeCharacter;
     78      Result[J + 1] := HexDigits[Ord(C) shr 4];
     79      Result[J + 2] := HexDigits[Ord(C) and $f];
     80      Inc(J, 3);
     81    end else begin
     82      Result[J] := C;
     83      Inc(J);
    21984    end;
    220     OutStream.Write(OutBuf, O);
    221     FillChar(OutBuf, SizeOf(OutBuf), #0);
    222     O := 0;
    223   end;
    224 
    225   procedure AddWordToOutBuf;
    226   var
    227     J : Integer;
    228   begin
    229     if (O + W) > 74 then SendLine;
    230     for J := 0 to (W - 1) do begin
    231       OutBuf[O] := WordBuf[J];
    232       Inc(O);
    233     end;
    234     W := 0;
    235   end;
    236 
    237   procedure AddHexToWord(B : Byte);
    238   const
    239     HexDigits : array[0..$F] of AnsiChar = '0123456789ABCDEF';
    240   begin
    241     if W > 73 then AddWordToOutBuf;
    242     WordBuf[W] := '=';
    243     WordBuf[W + 1] := HexDigits[B shr 4];
    244     WordBuf[W + 2] := HexDigits[B and $F];
    245     Inc(W, 3)
    246   end;
    247 
    248 begin
    249   Result := '';
    250   InStream := TMemoryStream.Create;
    251   OutStream := TMemoryStream.Create;
    252   try
    253     if Text <> '' then begin
    254       InStream.Write(Text[1], Length(Text));
    255       InStream.Position := 0;
    256     end;
    257 
    258     Abort := False;
    259     O := 0;
    260     W := 0;
    261     FillChar(OutBuf, SizeOf(OutBuf), #0);
    262     while (InStream.Read(CurChar, 1) = 1) and not Abort do begin
    263       if (Ord(CurChar) in [33..60, 62..126]) then begin
    264         WordBuf[W] := CurChar;
    265         Inc(W);
    266         if W > 74 then AddWordToOutBuf;
    267       end else if (CurChar = ' ') or (CurChar = #9) then begin
    268         WordBuf[W] := CurChar;
    269         Inc(W);
    270         AddWordToOutBuf;
    271       end else if (CurChar = #13) then begin
    272         AddWordToOutBuf;
    273         SendLine;
    274       end else if (CurChar = #10) then begin
    275         { Do nothing }
    276       end else begin
    277         AddHexToWord(Byte(CurChar));
    278       end;
    279     end;
    280     AddWordToOutBuf;
    281     OutStream.Write(OutBuf, O);
    282     SetLength(Result, OutStream.Size);
    283     OutStream.Position := 0;
    284     if OutStream.Size > 0 then
    285       OutStream.Read(Result[1], Length(Result));
    286   finally
    287     OutStream.Free;
    288     InStream.Free;
     85    Inc(I);
    28986  end;
    29087end;
Note: See TracChangeset for help on using the changeset viewer.