Bug in Quoted Printable encoding?

Giganews Newsgroups
Subject: Bug in Quoted Printable encoding?
Posted by:  Martijn Melenhorst (martijn.melenhor…@oxolutions.nl)
Date: Mon, 15 Sep 2003

I posted this already to the 'general' newsgroup already, but I think
that was the wrong place as no one replied. It is about a bug I found in
Indy 9 and I even supply a fix for you all to use! Here I go again:

I recently had some serious issues sending SMTP messages to /some/ SMTP
servers. It seems that the Quoted Printable encoding in Indy 9 (.14) is
causing the problem as it /could/ insert a CrLf pair in between an
existing CrLf pair of the to-be-encoded text. Imagine the odds of that
happening, but it happened to me ofcourse!

In particular here in The Netherlands, the (free!) SMTP servers of
Zonnet and Freeler and HetNet are refusing to accept these messages.

Here is the fixed routine I tested succesfully: (look for the //! lines
to find the fixed/new lines)

{ TIdEncoderQuotedPrintable }

function TIdEncoderQuotedPrintable.Encode(ASrcStream: TStream; const
ABytes: integer): string;
//TODO: Change this to be more efficient - dont read the whole data in
ahead of time as it may
// be quite large
const BUF_SIZE = 8192;
var
  i, LDataSize, LBytesRead, LBufSize : Integer;
  Buffer : Array [1..BUF_SIZE] of char;
  Line : String;
  st : TStrings;
  s : String;

    Procedure NewLine;
    begin
      if Copy(Line, Length(Line)-1, 2) <> #13#10 then //! Fixed by
Martijn Melenhorst
        Line := Line + '=';  {Do not Localize}
      st.Add(Line);
      Line := '';    {Do not Localize}
    end;

    Function QPHex(c : Char) : String;
    begin
      Result := '='+ IntToHex(Ord(c),2);  {Do not Localize}
    end;
begin
  st := TStringList.Create;
  try
    Result := '';      {Do not Localize}
    Line := '';      {Do not Localize}
    LBytesRead := 0;

    LDataSize := ASrcStream.Size - ASrcStream.Position;
    if LDataSize > ABytes then
    begin
      LDataSize := ABytes;
    end;

    if (LDataSize > 0) then
    begin
      while LBytesRead < LDataSize do
      begin
        if (LDataSize - LBytesRead) > BUF_SIZE then
        begin
          LBufSize := BUF_SIZE
        end
        else
        begin
          LBufSize := LDataSize - LBytesRead;
        end;
        ASrcStream.Read(Buffer[1],LBufSize);
        LBytesRead := LBytesRead + LBufSize;
        i := 1;
        while (i <= LBufSize) do                      //! Fixed by
Martijn Melenhorst
//!    For i := 1 to LBufSize do                      //! Fixed by
Martijn Melenhorst
        begin
          case Buffer[i] of
            // Special case when '.' is about to be alone on the next line.
            '.': begin
              if Line = '' then begin
                s := QPHex(Buffer[i]);
                Line := Line + s;
              end else begin
                Line := Line + Buffer[i];
              end;
            end;
            ' ', TAB:          {Do not Localize}
            If (i < Length(Buffer)) and (Buffer[i+1] in [#10,#13]) then
            begin
                //Catch whitespace before soft-return of
quoted-printable encoding
                //Modified by Dennies Chang.
                // Line := Line + QPHex(Buffer[i]);
                s := QPHex(Buffer[i]);
                Line := Line + s;
            end
            else
              Line := Line + Buffer[i];
            '=' :  {Do not Localize}
            begin
              //Modified by Dennies Chang.
              //Line := Line + QPHex(Buffer[i]);
              s := QPHex(Buffer[i]);
              Line := Line + s;
            end
            else
            begin
              if ((Buffer[i] >= #33 ) and (Buffer[i] <= #60 )) or
((Buffer[i] >= #62) and (Buffer[i] <= #126 )) then
              begin
                Line := Line + Buffer[i];
              end
              else
              begin
                if Buffer[i] in [#10,#13] then
                begin
                  Line := Line + Buffer[i];
                  if (Buffer[i] = #13) and (i < Length(Buffer)) and
(Buffer[i+1] = #10) then //! Fixed by Martijn Melenhorst
                  begin                      //! Fixed by Martijn
Melenhorst
                    Inc(i);                      //! Fixed by Martijn
Melenhorst
                    Line := Line + Buffer[i];                      //!
Fixed by Martijn Melenhorst
                  end;                      //! Fixed by Martijn
Melenhorst
                end
                else
                begin
                  Line := Line + QPHex(Buffer[i]);
                end;
              end;  //...else
            end; //..else
          end; //case buffer[i] of
          if Length(Line) > 71 then
          begin
            NewLine;
          end;  //if Length(Line > 71 then
          Inc(i);
        end; //For i := 1 to LBufSize do                      //!
Fixed by Martijn Melenhorst
      end; //while LBytesRead < LDataSize do
    end; //if (LDataSize > 0) then    {This ensures that the remaining
is added to the TStrings}
    if Length(Line) >0 then
    begin
      st.Add(Line);
    end;
    Result := st.Text;
    //Delete an extra system EOL that was added by the TStrings itself
    //The EOL varies from system to system
    i := Length(sLineBreak);
    if (Length(Result)>i) then
    begin
      Delete(Result,Length(Result) - i+1,i);
    end;
  finally
    FreeAndNil(st);
  end;
end;

Replies