Question

I have the following code I use for retrieving mails from a POP3 account. It is working very well most of the time, but from time to time there are some mails where it doesn't retrieve the body. If I test the IdMessage.MessageParts.Count it says that it is 0 - if I use another mailclient to retrieve the mail there is no problem. I have a TIdPOP3 and a TIdMessage component on the form. The connection is OK as there may be some mails that are show OK.

I can't figure out any system in which mails are not shown correct and which are not. But there might be one. I use Delphi XE3 and the Indy is version 10.5.9.0

procedure TfrmJsMailCollect.lstMailsClick(Sender: TObject);
var
  MailBody: string;
  intIndex: integer;
begin
  if (lstMails.Items.Count = 0) or (lstMails.SelCount = 0) then
    Exit;
  MailBody := '';
  try
    begin
      mmoBody.Clear;
      lstMails.Selected.SubItems.Strings[0];
      lstMails.Selected.ImageIndex := 4;
      conPOP3.Retrieve(lstMails.Selected.Index + 1, IdMessage);

      for intIndex := 0 To Pred(IdMessage.MessageParts.Count) do
        begin
          if (IdMessage.MessageParts.Items[intIndex] is TIdAttachmentFile) then
            begin // Attachments are skipped
            end
          else
            begin // body text
              if Pos('text/plain', IdMessage.MessageParts.Items[intIndex].ContentType) <> 0 then
                begin
                  if TIdText(IdMessage.MessageParts.Items[intIndex]).Body.Text <> '' then
                    begin
                      MailBody := MailBody + TIdText(IdMessage.MessageParts.Items[intIndex]).Body.Text;
                      mmoBody.Lines.Add(MailBody);
                      MemoValidate;
                    end;
                end;
            end;
        end;
    end;
    mmoBody.CaretPos.SetLocation(0, 0);
    Application.ProcessMessages;
  except
    Logfile.Error('F_JsMailCollect.lstMailsClick - ' + cxGetResourceString(@sLangPop3ErrorReading));
  end;
end;

With advice from Remy Lebeau and searching the web I ended up with the code below. This does the trick for now, but I would like to improve it so that the memo on my form only shows a nice message that would be readable for everyone - but that may come later.

procedure TfrmJsMailCollect.lstMailsClick(Sender: TObject);
var
  MailBody: string;
  i: integer;
  ContentType: string;
begin
  if (lstMails.Items.Count = 0) or (lstMails.SelCount = 0) then
    Exit;
  try
    MailBody := '';
    mmoBody.Clear;
    lstMails.Selected.SubItems.Strings[0];
    lstMails.Selected.ImageIndex := 4;
    conPOP3.Retrieve(lstMails.Selected.Index + 1, IdMessage);

    ContentType := IdMessage.ContentType;
    case PosInStrArray(ContentType, ['multipart/mixed', 'multipart/alternative', 'text/html', 'text/plain'], False) of
      0:  begin   { multipart/mixed }
            for i := 0 To Pred(IdMessage.MessageParts.Count) do
              begin
                if TIdText(IdMessage.MessageParts.Items[i]).Body.Text <> '' then
                  begin
                    MailBody := MailBody + TIdText(IdMessage.MessageParts.Items[i]).Body.Text;
                    mmoBody.Lines.Add(MailBody);
                    MemoValidate;
                  end;
              end;
          end;
      1:  begin   { multipart/alternative }
            for i := 0 To Pred(IdMessage.MessageParts.Count) do
              begin
                if TIdText(IdMessage.MessageParts.Items[i]).Body.Text <> '' then
                  begin
                    MailBody := MailBody + TIdText(IdMessage.MessageParts.Items[i]).Body.Text;
                    mmoBody.Lines.Add(MailBody);
                    MemoValidate;
                  end;
              end;
          end;
      2:  begin   { text/html }
            mmoBody.Lines := IdMessage.Body;
            MemoValidate;
          end;
      3:  begin   { text/plain }
            mmoBody.Lines := IdMessage.Body;
            MemoValidate;
          end;
    else
      // nothing supported to display...
    end;
    mmoBody.CaretPos.SetLocation(0, 0);
    Application.ProcessMessages;
  except
    Logfile.Error('F_JsMailCollect.lstMailsClick - ' + cxGetResourceString(@sLangPop3ErrorReading));
  end;
end;
Was it helpful?

Solution

Not all email content is parsed into the TIdMessage.MessageParts collection. MIME parts and attachments are, but other content gets parsed into the TIdMessage.Body instead, which you are completely ignoring. You need to look at the TIdMessage.ContentType when deciding where to extract content from. Attachments will always be in TIdMessage.MessageParts, but text may or may not be, depending on the TIdMessage.ContentType.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top