Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Evaluate Email with Indy 10 and DELPHI

Tags:

delphi

indy

I use the following code to eval the msg. content (body / lines) of an E Mail msg received with the INDY 10 components

function LinesFromMsg(aMsg: TIdMessage): TStrings; 
var
  i: Integer; 
begin
  for i := 0 to aMsg.MessageParts.AttachmentCount-1 do
  begin
    if (amsg.MessageParts.Items[i].ContentType ='HTML') then
    begin
      if (amsg.MessageParts.Items[i] is Tidtext) then
        Result := TidText(amsg.MessageParts.Items[i]).body;
    end;
  end; 
end;

regarding this code I have 2 questions :

a) is this the correct way of finding the Tlines part in an arbitray mail message ? ( consider the advice shown at INDY 10 EMAIL MSG PARTS )

b) where can I find a tutorial of all the different Contenttype string values?

like image 873
user1769184 Avatar asked Feb 18 '23 15:02

user1769184


1 Answers

The correct ContentType value to look for is text/html. Use Indy's IsHeaderMediaType() function to check it, as the ContentType value may have additional attributes associated with it that your comparison needs to ignore.

You also need to take the TIdMessage.ContentType into account as well, as HTML emails may not be MIME encoded and thus not use the TIdMessage.MessageParts` collection at all.

And lastly, you loop needs to use the MessageParts.Count property instead of the MessageParts.AttachmentsCount property.

Try this:

function HTMLFromMsg(aMsg: TIdMessage): TStrings; 
var
  i: Integer; 
  Part: TIdMessagePart;
begin
  Result := nil;
  if IsHeaderMediaType(aMsg.ContentType, 'text/html') then
  begin
    Result := aMsg.Body;
    Exit;
  end;
  for i := 0 to aMsg.MessageParts.Count-1 do
  begin
    Part := aMsg.MessageParts.Items[i];
    if (Part is TIdText) and IsHeaderMediaType(Part.ContentType, 'text/html') then
    begin
      Result := TIdText(Part).Body;
      Exit;
    end;
  end; 
end;

With that said, this is technically not the correct way to handle MIME. Officially, a conforming reader is supposed to loop backwards through the MIME parts, as they are ordered from the simpliest form downwards towards the most complex form. So you loop backwards, taking MIME nesting into account, looking for the most complex form you support. Something more like this (untested):

procedure DisplayPlainText(Body: TStrings);
begin
  // display plain text as needed...
end;

procedure DisplayHTML(Body: TStrings);
begin
  // display html as needed...
end;

procedure DisplayMultiPartAlternative(aMsg: TIdMessage; aParentIndex, aLastIndex: Integer);
var
  Part: TIdMessagePart;
  i: Integer:
begin
  for i := aLastIndex-1 downto aParentIndex+1 do
  begin
    Part := aMsg.MessageParts.Items[i];
    if (Part.ParentPart = aParentIndex) and (Part is TIdText) then
    begin
      if IsHeaderMediaType(Part.ContentType, 'text/html') then
      begin
        DisplayHTML(TIdText(Part).Body);
        Exit;
      end;
      if IsHeaderMediaType(Part.ContentType, 'text/plain') then
      begin
        DisplayPlainText(TIdText(Part).Body);
        Exit;
      end;
    end;
  end;
  // nothing supported to display...
end;

procedure DisplayMultiPartMixed(aMsg: TIdMessage; aParentIndex, aLastIndex: Integer);
var
  Part: TIdMessagePart;
  i: Integer;
begin
  for i := aLastIndex-1 downto aParentIndex+1 do
  begin
    Part := aMsg.MessageParts.Items[i];
    if (Part.ParentPart = aParentIndex) and (Part is TIdText) then
    begin
      if IsHeaderMediaType(Part.ContentType, 'multipart/alternative') then
      begin
        DisplayMultiPartAlternative(aMsg, ParentPart.Index, aLastIndex);
        Exit;
      end;
      if IsHeaderMediaType(ParentPart.ContentType, 'text/html') then
      begin
        DisplayHTML(TIdText(Part).Body);
        Exit;
      end;
      if IsHeaderMediaType(Part.ContentType, 'text/plain') then
      begin
        DisplayPlainText(TIdText(Part).Body);
        Exit;
      end;
      aLastIndex := i;
    end;
  end;
  // nothing supported to display...
end;

procedure DisplayMsg(aMsg: TIdMessage); 
var
  ContentType: string;
begin
  ContentType := ExtractHeaderMediaType(aMsg.ContentType);
  case PosInStrArray(ContentType, ['multipart/mixed', 'multipart/alternative', 'text/html', 'text/plain'], False) of
    0: begin
      DisplayMultiPartAlternative(aMsg, -1, aMsg.MessageParts.Count);
      Exit;
    end;
    1: begin
      DisplayMultiPartMixed(aMsg, -1, aMsg.MessageParts.Count);
      Exit;
    end;
    2: begin
      DisplayHTML(aMsg.Body);
      Exit;
    end;
    3: begin
      DisplayPlainText(aMsg.Body);
      Exit;
    end;
  else
    // nothing supported to display...
  end;
end;
like image 109
Remy Lebeau Avatar answered Feb 27 '23 09:02

Remy Lebeau