Discussion:
HTML Attachments in POP3 (Indy 10)
(too old to reply)
ioan
2008-07-09 17:06:47 UTC
Permalink
I'm using this code to save the body of the email and the attachments.
In Indy 10, if the attachment is a HTML document, the code will save it
as the email body (in the "body text" part of the code). I compiled the
same code in Indy 9, and the HTML attachment was saved as an attachment
(in the "general attachment" part of code).

How should I handle html attachments in indy 10? Can somebody send me a
sample code please?

I have latest Indy 9 and Indy 10 installed.

TIA,
-ioan

FilePrefix := MakeUniqueCorrelation(AppPath);
for i := 0 to AMsg.MessageParts.Count - 1 do
begin
if (AMsg.MessageParts.Items[i] is TIdAttachment) then
begin
//general attachment
TIdAttachment(AMsg.MessageParts.Items[i]).
SaveToFile(TIdAttachment(AppPath + 'inbox' + PathDelim +

AMsg.MessageParts.Items[i]).Filename);
end
else
begin

//body text
if AMsg.MessageParts.Items[i] is TIdText then
begin
TIdText(AMsg.MessageParts.Items[i]).Body.
SaveToFile(AppPath + 'inbox' + PathDelim + FilePrefix + '.bdy');
end
end;
end;
Remy Lebeau (TeamB)
2008-07-09 19:27:18 UTC
Permalink
Post by ioan
In Indy 10, if the attachment is a HTML document, the code will
save it as the email body (in the "body text" part of the code). I
compiled the same code in Indy 9, and the HTML attachment
was saved as an attachment (in the "general attachment" part of code).
Indy 9 stores a "text/..." part (such as "text/html") in a TIdText object
unless its "Content-Disposition" header is set to "attachment", or it has a
FileName specified. So, for your HTML to end up as a TIdAttachment, one of
those conditions had to be present in the email data.

Indy 10 ignores the FileName if present, storing a "text/..." part in a
TIdText object unless its "Content-Disposition" header specifies
"attachment" only. This is more compliant with RFC 2183, which states that
inlined text can have a filename. The RFC also states that attachments
don't require filenames, so you need to expand your code to handle that case
as well. For example:

var
FileName: String;

for i := 0 to AMsg.MessageParts.Count - 1 do
begin
if (AMsg.MessageParts.Items[i] is TIdAttachment) then
begin
//general attachment
// some email senders include full paths...
FileName :=
ExtractFileName(TIdAttachment(AMsg.MessageParts.Items[i]).Filename);
if FileName = '' then
FileName := MakeTempFileName(AppPath + 'inbox')
else
FileName := AppPath + 'inbox' + PathDelim + FileName;

TIdAttachment(AMsg.MessageParts.Items[i]).SaveToFile(FileName);
end else
begin
//body text
if AMsg.MessageParts.Items[i] is TIdText then
begin
TIdText(AMsg.MessageParts.Items[i]).Body.SaveToFile(AppPath
+ 'inbox' + PathDelim + FilePrefix + '.bdy');
end;
end;
end;


Gambit
ioan
2008-07-09 20:02:37 UTC
Permalink
Post by Remy Lebeau (TeamB)
Indy 10 ignores the FileName if present, storing a "text/..." part in a
TIdText object unless its "Content-Disposition" header specifies
"attachment" only. This is more compliant with RFC 2183, which states that
inlined text can have a filename. The RFC also states that attachments
don't require filenames, so you need to expand your code to handle that case
How do I know that what I have stored in a TIdText is an attachment or
the body? The TIdText doesn't have FileName.

Thanks Remy!

-ioan
Remy Lebeau (TeamB)
2008-07-09 23:09:06 UTC
Permalink
Post by ioan
How do I know that what I have stored in a TIdText is an
attachment or the body? The TIdText doesn't have FileName.
Read more carefully what I said earlier. If it were a true attachment, then
the "Content-Disposition" header would be set to "attchment", and the HTML
would thus be stored in a TIdAttachment instead of a TIdText.


Gambit
ioan
2008-07-09 23:38:27 UTC
Permalink
Post by Remy Lebeau (TeamB)
Read more carefully what I said earlier. If it were a true attachment, then
the "Content-Disposition" header would be set to "attchment", and the HTML
would thus be stored in a TIdAttachment instead of a TIdText.
This is how the attachment looks like in the email's source.

--------------010905090609040701070804
Content-Type: text/html;
name="volcano.htm"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="volcano.htm"

<html>
<head>
<title>Test</title>
</head>
<body>
</body>
</html>

--------------010905090609040701070804--


If I read the email that has this attachment (email created with
Thunderbird), in my code this will be saved as body of the email, so the
attachment won't pass the condition
if (AMsg.MessageParts.Items[i] is TIdAttachment) then

the attachment will only pass the condition

if AMsg.MessageParts.Items[i] is TIdText then

I found a temporary solution, I'm sure is not the best, but for now
works fine:

if AMsg.MessageParts.Items[i] is TIdText then
begin
if Pos('NAME=', UpperCase(AMsg.MessageParts.Items[i].Headers.Text)) >
0 then
begin
// we have text/... ATTACHMENT
with TIdMessageDecoderMIME.Create(nil) do
try
FileName :=
GetAttachmentFilename(AMsg.MessageParts.Items[i].Headers.Text,
AMsg.MessageParts.Items[i].Headers.Text);
finally
Free;
end;
if FileName <> '' then
begin
FileName := FilePrefix + '-' + IntToStr(i) + '.' +
GetFileExtNoDot(FileName);
TIdText(AMsg.MessageParts.Items[i]).
Body.SaveToFile(APath + FileName);
end;
end
else
begin {body text or html}
// save the body ...
Remy Lebeau (TeamB)
2008-07-10 05:42:07 UTC
Permalink
Post by ioan
This is how the attachment looks like in the email's source.
That will be stored in a TIdText object, as defined by the "inline" flag in
the "Content-Disposition" header. It just won't have a FileName exposed,
but you can extract that from the "Content-Type" and "Content-Disposition"
headers manually if needed. The
TIdMessageDecoderMIME.GetAttachmentFilename() method can do that for you
(Indy uses that for TIdAttachment but not TIdText yet).
Post by ioan
If I read the email that has this attachment (email created with
Thunderbird), in my code this will be saved as body of the email,
so the attachment won't pass the condition
if (AMsg.MessageParts.Items[i] is TIdAttachment) then
As well it should be. It is not an attachment. The headers say as much.
Post by ioan
I found a temporary solution, I'm sure is not the best, but for now works
You are passing the wrong values to GetAttachmentFilename(). It needs to be
more like this intead:

if AMsg.MessageParts.Items[i] is TIdText then
begin
with TIdMessageDecoderMIME.Create(nil) do
try
FileName :=
GetAttachmentFilename(AMsg.MessageParts.Items[i].Headers.Values['Content-Type'],
AMsg.MessageParts.Items[i].Headers.Values['Content-Disposition']);
finally
Free;
end;

if FileName <> '' then
begin
// we have text/... ATTACHMENT
//TIdText(AMsg.MessageParts.Items[i]).Body.SaveToFile(APath +
FilePrefix + '-' + IntToStr(i) + '.' + GetFileExtNoDot(FileName));
TIdText(AMsg.MessageParts.Items[i]).Body.SaveToFile(APath +
ExtractFileName(FileName));
end else
begin
// we have text/... NO ATTACHMENT
end;
end;


Gambit
Remy Lebeau (TeamB)
2008-07-10 08:11:57 UTC
Permalink
Post by Remy Lebeau (TeamB)
That will be stored in a TIdText object, as defined by the "inline" flag
in the "Content-Disposition" header. It just won't have a FileName
exposed, but you can extract that from the "Content-Type" and
"Content-Disposition" headers manually if needed. The
TIdMessageDecoderMIME.GetAttachmentFilename() method can do
that for you (Indy uses that for TIdAttachment but not TIdText yet).
I just added a FileName property to TIdMessagePart, and updated
TIdMessageClient to use it now for both text parts and attachments. The new
code would look like this:

if AMsg.MessageParts.Items[i] is TIdText then
begin
if AMsg.MessageParts.Items[i].FileName <> '' then // <- here
begin
// we have text/... ATTACHMENT
TIdText(AMsg.MessageParts.Items[i]).Body.SaveToFile(APath +
ExtractFileName(AMsg.MessageParts.Items[i].FileName));
end else
begin
// we have text/... NO ATTACHMENT
end;
end;


Gambit
ioan
2008-07-10 16:11:59 UTC
Permalink
Post by Remy Lebeau (TeamB)
I just added a FileName property to TIdMessagePart, and updated
TIdMessageClient to use it now for both text parts and attachments. The new
Oh man, that was a fast fix! Thank you!
Best regards,
-ioan
ioan
2008-07-10 16:39:53 UTC
Permalink
Post by Remy Lebeau (TeamB)
I just added a FileName property to TIdMessagePart, and updated
TIdMessageClient to use it now for both text parts and attachments. The new
I don't see the changes in svn yet, I suppose you didn't commit yet.
I'll try again later.

Thanks again,
-ioan
Remy Lebeau (TeamB)
2008-07-10 17:15:11 UTC
Permalink
Post by ioan
I don't see the changes in svn yet, I suppose you didn't commit yet.
The changes are in the Tiburon branch, not the trunk. All work on Indy 10
has been in the Tiburon branch only for awhile now, and will not be commited
to the trunk for awhile still.


Gambit

Loading...