Blank Bodies from Outlook Clients

Giganews Newsgroups
Subject: Blank Bodies from Outlook Clients
Posted by:  Robin (robin.fowl…@allchem.com)
Date: Fri, 20 Feb 2004

IdMessage parse from POP3 Server.

First I would like to appaulagize if I didn't find the solution to this
issue in the newsgroup, I looked
and saw a lot of issues with IdMessage and Bodies not parsing correctly, but
nothing specific to what I
have been experiencing.

I use Delphi 5 with Indy 9.

The issue I have found is related to IdMessageClient.pas, Rev 1.5 and
IdMessageCoderMIME.pas, Rev 1.4 .
My application works pretty exclusivly with Oulook generated
messages(Specificaly Outlook xp and 2003).
I have been experiencing blank email bodies for some time and have just
lived with it until I have able
to isolate the issue down to TIdMessageDecoderMIME.ReadBody.
It appears that if a email generated from outlook has no attachments, as
below, it will generate a
"blank Email"(the email has one message part of typ IdText that is blank.

************************MESSAGE**************************
Received: by server.domain.COM
        id <01C3F7C3.157C4A…@server.domain.COM>; Fri, 20 Feb 2004
10:06:14 -0500
MIME-Version: 1.0
Content-Type: text/html;
        charset="us-ascii"
Content-Transfer-Encoding: quoted-printable
Content-Class: urn:content-classes:message
X-MimeOLE: Produced By Microsoft Exchange V6.0.6249.0
Subject: test
Date: Fri, 20 Feb 2004 10:06:14 -0500
Message-ID: <094B92F62645A341950F464AF3DFE12060EC…@server.domain.COM>
X-MS-Has-Attach:
X-MS-TNEF-Correlator:
Thread-Topic: test
thread-index: AcP3wxV2/uwzVeIRTqC5i5z3qnkSUg==
From: "Robin" <rob…@domain.com>
To: "Test User" <testus…@domain.com>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML><HEAD>
<META http-equiv=3DContent-Type content=3D"text/html; =
charset=3Dus-ascii">
<META content=3D"MSHTML 6.00.2800.1400" name=3DGENERATOR></HEAD>
<BODY>
<DIV><FONT face=3D"Courier New" size=3D2><SPAN=20
class=3D230590515-20022004>test</SPAN></FONT></DIV>
<DIV>&nbsp;</DIV>
<DIV align=3Dleft><SPAN=20
style=3D"FONT-WEIGHT: bold; FONT-SIZE: 12pt; COLOR: black; FONT-STYLE: =
normal; FONT-FAMILY: 'Arial'">Robin</SPAN><STRONG><FONT face=3DArial>,

</FONT></STRONG><SPANstyle=3D"FONT-SIZE: 10pt; COLOR: black; FONT-STYLE:
normal; FONT-FAMILY: =
'Arial'">Title<BR>Addr1<BR>City, ST ZIP<BR>Phone number Fax number<BR><A
href=3D"http://www.domain.com/"><SPAN style=3D"FONT-WEIGHT: bold; FONT-SIZE:
10pt; COLOR: #287fcc;

FONT-FAMILY: 'Arial'; TEXT-DECORATION:
none">www.domain.com</SPAN></A></SPAN></DIV>
<DIV>&nbsp;</DIV></BODY></HTML>
.
************************END MESSAGE*************************

What I found was the IdMessageCoderMIME. procedure was waiting for the
Message boundry and skipping the

text while it waited. But as you can tell from the message there is no
boundry and thus the MessagePart

was blank.

Following are the corrections I made to fix my problem(to two functions
TIdMessageDecoderMIME.ReadBody

and ProcessTextPart):

1.In order the fix the main problem when the MIMEBoundary = '' or
Length(MIMEBoundary)=0 I added an else
to the if statement, this collects the text that is skipped when
MIMEBoundary is blank. Then after the
whole Read if The ADestStream is empty and the collected text is not empty I
fill ADestStream with the
collected Text.

function TIdMessageDecoderMIME.ReadBody(ADestStream: TStream; var VMsgEnd:
Boolean): TIdMessageDecoder;
var
  s: string;
  LDecoder: TIdDecoder;
  LLine: string;
  //***********Robin**************
  //          Added 02/20/04
  //{
  OtherText : String;
  //}
  //*************************************
begin
  VMsgEnd := FALSE;
  Result := nil;
  if FBodyEncoded then begin
    s := TIdMessage(Owner).ContentTransferEncoding;
  end else begin
    s := FHeaders.Values['Content-Transfer-Encoding']; {Do not Localize}
  end;
  if AnsiSameText(s, 'base64') then begin {Do not Localize}
    LDecoder := TIdDecoderMIME.Create(nil);
  end else if AnsiSameText(s, 'quoted-printable') then begin {Do not
Localize}
    LDecoder := TIdDecoderQuotedPrintable.Create(nil);
  end else begin
    LDecoder := nil;
  end;
  try
    repeat
      if FFirstLine = '' then begin // TODO: Improve this. Not very
efficient
        LLine := ReadLn;
      end else begin
        LLine := FFirstLine;
        FFirstLine := '';    {Do not Localize}
      end;
      if LLine = '.' then begin // Do not use ADELIM since always ends with
. (standard) {Do not

Localize}
        VMsgEnd := True;
        Break;
      end;
      // New boundary - end self and create new coder
      if MIMEBoundary <> '' then begin
        if AnsiSameText(LLine, '--' + MIMEBoundary) then begin    {Do not
Localize}
          Result := TIdMessageDecoderMIME.Create(Owner);
          Break;
        // End of all coders (not quite ALL coders)
        end
        else if AnsiSameText(LLine, '--' + MIMEBoundary + '--') then begin
{Do not Localize}
          // POP the boundary
          if Owner is TIdMessage then begin
            TIdMessage(Owner).MIMEBoundary.Pop;
          end;
          Break;
        // Data to save, but not decode
        end else if LDecoder = nil then begin
          if (Length(LLine) > 0) and (LLine[1] = '.') then begin // Process
. in front for no encoding

{Do not Localize}
            Delete(LLine, 1, 1);
          end;
          LLine := LLine + EOL;
          ADestStream.WriteBuffer(LLine[1], Length(LLine));
        // Data to decode
        end else begin
          //for TIdDecoderQuotedPrintable, we have
          //to make sure all EOLs are intact
          if LDecoder is TIdDecoderQuotedPrintable then begin
            LDecoder.DecodeToStream(LLine+EOL,ADestStream);
          end else if LLine <> '' then begin
            LDecoder.DecodeToStream(LLine, ADestStream);
          end;
        end;
      //end;//  Corrected
      //*************Added By Robin***************
      //            Added 02/20/04
      //Need to Capture Text from outlook, when Unbounded Text Attachment
Sent
      //{
      end else begin
        if (Length(LLine) > 0) and (LLine[1] = '.') then begin // Process .
in front for no encoding

{Do not Localize}
          Delete(LLine, 1, 1);
        end;
        OtherText := OtherText + LLine + EOL
      end;
      //}
      //*************************************************
    until False;
    //*************Added By Robin***************
    //            Added 02/20/04
    //{
    If (ADestStream.size = 0) and (Length(OtherText) > 0) Then begin
      ADestStream.WriteBuffer(OtherText[1],Length(OtherText));
    end;
    //}
    //*************************************************
  finally FreeAndNil(LDecoder); end;
end;

2.This was added so the ContentType and ContentTransfer are filled in from
the main header.
function ProcessTextPart(ADecoder: TIdMessageDecoder): TIdMessageDecoder;
  var
    LDestStream: TStringStream;
  begin
    LDestStream := TStringStream.Create('');
    try
      Result := ADecoder.ReadBody(LDestStream, LMsgEnd);
      with TIdText.Create(AMsg.MessageParts) do
      begin
        ContentType := ADecoder.Headers.Values['Content-Type'];
        ContentTransfer :=
ADecoder.Headers.Values['Content-Transfer-Encoding'];
        //****************Robin*********************
        //                Added 02/20/04
        If ContentType = '' Then begin
          ContentType := AMsg.ContentType;
        end;
        If ContentTransfer = '' Then begin
          ContentTransfer := AMsg.ContentType;
        end;
        //*************************************************
        Body.Text := LDestStream.DataString;
      end;
      ADecoder.Free;
    finally
      FreeAndNil(LDestStream);
    end;
  end;

I hope this helps and again I appauligize if this has already been
corrected.

Robin

Replies