📄 mimedec.pas
字号:
{*_* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Author: Fran鏾is PIETTE
Object: TMimeDecode is a component whose job is to decode MIME encoded
EMail messages (file attach). You can use it for example to
decode messages received with a POP3 or NNTP component.
MIME is described in RFC-1521. Headers are described if RFC-822.
Creation: March 08, 1998
Version: 1.33
EMail: francois.piette@overbyte.be http://www.overbyte.be
francois.piette@rtfm.be http://www.rtfm.be/fpiette
francois.piette@pophost.eunet.be
Support: Use the mailing list twsocket@elists.org
Follow "support" link at http://www.overbyte.be for subscription.
Legal issues: Copyright (C) 1998-2005 by Fran鏾is PIETTE
Rue de Grady 24, 4053 Embourg, Belgium. Fax: +32-4-365.74.56
<francois.piette@overbyte.be>
This software is provided 'as-is', without any express or
implied warranty. In no event will the author be held liable
for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it
and redistribute it freely, subject to the following
restrictions:
1. The origin of this software must not be misrepresented,
you must not claim that you wrote the original software.
If you use this software in a product, an acknowledgment
in the product documentation would be appreciated but is
not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
4. You must register this software by sending a picture postcard
to the author. Use a nice stamp and mention your name, street
address, EMail address and any comment you like to say.
QUICK REFERENCE:
----------------
TMimeDecode take a file or a stream as input and produce several event when
the message is parsed. each event can be used to display or to save to a file
the message parts.
Two methods can be called to decode either a file or a stream:
procedure DecodeFile(FileName : String);
procedure DecodeStream(aStream : TStream);
During the decode process, the component trigger several events. You have to
use those events to save data to a file or to display somehow on the
user interface.
Events are organized by groups of three for message header, part header and
part data:
Message header events: OnHeaderBegin OnHeaderLine OnHeaderEnd
Part header events: OnPartHeaderBegin OnPartHeaderLine OnPartHeaderEnd
Part data events: OnPartDataBegin OnPartDataLine OnPartDataEnd
The 'Begin' event is triggered once just before the first item will occur.
The 'Line' event is triggered for each item of the given type.
The 'End' event is triggered once after the last item.
For a multi-part message, we have this sequence:
a) The message header
OnHeaderBegin, then many OnHeaderLine, one for each line in the header. Lines
can be continuated in the message. The event here is triggered with continuated
lines concatenated (so it can be quite large !). After the last header line
has been processed, the OnHeaderEnd is triggered once.
b) The non-significant message part which can be empty. This is part 0. We
get OnPartBegin once, then OnPartLine for each line and finally OnPartEnd once.
c) The first significant part header with his three events, just like the
message header: OnPartHeaderBegin, OnPartHeaderLine and OnPartHeaderEnd.
d) The first significant part data with his three events: OnPartBegin once,
OnPartLine for each line and OnPartEnd once at the end of the part.
It's possible to have an empty part. This gives the OnPartBegin and OnPartEnd
events and NO OnPartLine event.
e) We can have many other parts. The sequence is always the same. We restart
at point (b) here above for each part (header, then data). Note that there is
often en empty part at the end of a message.
TMimeDecode decode encoded parts using 'base64' and 'quoted-printable' methods.
For those parts, the OnPartLine event will gives DECODED data. Other methods
are passed not decoded. You can use the property ContentTransferEncoding to
know which encoding method is used and add your own decoding mechanism.
For each OnHeaderLine, OnPartHeaderLine and OnPartLine, you can find the
actual data at the address pointed by the property CurrentData (a PChar).
The reason for a PChar is that the data can be quite large. The data pointed
is a null terminated string. You can get the length using StrLen, or convert
to a string with StrPas. It is more efficient to process the data using a
pointer. Using strings tends to copy the data several times.
The OnPartLine event passes a PChar and a length to the handler. This actully
point to the internal buffer and overwrite the original data (base64 and
quote-printable method produce decoded data smaller tha encoded one).
>From the message header, the component extract the following values:
>From The message author. Not necessary the real author...
Looks like "Francois Piette" <francois.piette@pophost.eunet.be>
Dest The message destination (To field, but To is a reserved word)
Looks like "Francois Piette" <francois.piette@pophost.eunet.be>
Subject The message subject. Free text.
Date The message date.
Look like: Mon, 16 Feb 1998 12:45:11 -0800
ContentType 'multipart/mixed' or empty.
For details about those header fields and others, read RFC-822
For each part, we have the following properties updated (the header is parsed
on the fly):
PartNumber Starting from 0 for the non-significant part
PartLine Starting 1 for the first line of each part or header
PartContentType Such as 'text/plain' or 'application/x-zip-compressed'
PartCharset This is a complement for the PartContentType.
ApplicationType When PartContentType is 'application/something', we
get the 'something' extracted
PartName This is the value for 'name=something' in the
Content-Type header line.
PartEncoding Encoding method (Content-Transfer-Encoding).
Can be used to decode unsupported
methods (supported methods are 'base64' and
'quoted-printable'. '7bit' and '8bit' does'nt
generally require processing.
PartDisposition Can be 'inline' or 'attachement' and is generally
followed by a 'filename=something'
PartFileName The specified filename in Content-Disposition header
line. Be aware that the file name is not necessary
suitable for windows ! Use it with caution...
For details about those header fields and others, read RFC-1521.
To write part data to files, you can either implement your own writing in
the OnPartLine event handler, or use the DestStream property. If assigned,
this property will be used to write the data. If not assigned, it will be
ignore.
To select a file name for each part, you can use the PartFileName property or
the 'PartName' property or a comnination of both. But be aware that those value
can be either missing or even invalid as a filename because the message was
generated with another opertaing system which has different filename
conventions.
Updates:
Apr 13, 1998 V1.01 Corrected a bug in ProcessLineBase64 which decoded one
byte too much. Thanks to Rune Fredriksen <runefr@mail.link.no>.
Apr 15, 1998 V1.02 Corrected bug in ProcessHeaderLine which retreived only
the first word for each item.
Added the ReturnPath property.
Apr 24, 1998 V1.03 Removed the modification made in version 1.01 !
Apr 26, 1998 V1.04 Corrected a bug in ReallocMem with Delphi 1
Aug 27, 1998 V1.05 Corrected a bug in decoding which incorrectly merge
the first message line with the header when the line begon
by a space. Thanks to Mitch Cant <mitchcant@hotmail.com> for
finding the bug and correction.
Sep 13, 1998 V1.06 Correctly handled unterminated messages.
Correctly handled parts without header.
Dec 26, 1998 V1.07 Added features coded by Eric Fortier <efortier@videotron.ca>
(Embedded mime parts, UUDecode).
Dec 30, 1998 V1.08 Check for header end when a header line begin with a
space or tab character. (Normally a header end with a blank
line, we also accept invalid header line).
Feb 01, 1999 V1.09 Corrected a bug ProcessLineUUDecode where 'end' was not
checked. Thanks to Eric Fortier.
Feb 16, 1999 V1.10 Added UUEncoded embedded parts. Thanks to Eric Fortier.
Corrected a line termination problem in ProcessLineBase64.
Jul 21, 1999 V1.11 Added support for encoded message without multipart.
Added Encoding property with the encoding value.
Thanks to Marcelo S Massuda <massuda@4web.com.br> for pinting this
lack of feature.
Aug 20, 1999 V1.12 Added compile time options. Revised for BCB4.
Nov 25, 1999 V1.13 Changed continuation line character for quoted printable
encoding. By Ken Petersen <KPT@edbgruppen.dk>.
Created GetTokenEx function to take care of comments in header
lines. This affect ProcessPartHeaderLine and ProcessHeaderLine.
Thanks to Boris Daljevic <biber@eunet.yu> for his code.
Added CharSet property related to main part charset (see also
existing PartCharset property). Thanks to Boris Daljevic
<biber@eunet.yu> for his code.
Jun 20, 2000 V1.14 Poessler Thomas <Thomas.Poessler@uta.at> corrected a bug in
ProcessLineQuotedPrintable.
Jul 02, 2000 V1.15 Added OnMessageEnd event
Jul 15, 2000 V1.16 Added code from Wolfgang Baron <Wolfgang.Baron@gwtel.de>
to support content-description header line.
Changed GetToken and GetTokenEx so that a space before a delimiter
will not break token parsing. Outlook generate such invalid
formatting thanks for Arno van Rossum <a.van.rossum@mmp-obec.nl>
for finding this bug.
Revised code to handle inline UUEncoded messages.
Jul 21, 2000 V1.17 Use GetValue instead of GetToken to solve problem with
boundaries of embbeded parts.
With help of Jan Bartak <bart@seznam.cz>.
As suggested by Sebastien Gariepy <beeper@globetrotter.net>, I
added PartContentID.
Oct 29, 2000 V1.18 Checked for missing content-type before calling
UUProcessLine. Without the check, a part with a line beginning
with 'begin 666' will be wrongly decoded.
Feb 17, 2001 V1.19 Top of the messages with a field multipart was incorrectly
processed.Property FCharset was not initialized in procedure
MessageBegin. Thanks to Bayanov <bayanov@alt.ru>
Jul 26, 2001 V1.20 Cleared FEncoding in MessageBegin. Thanks to Joel
lauvinerie <joel.lauvinerie@wanadoo.fr> who found this bug.
Poessler Thomas <Thomas.Poessler@uta.at> added new properties:
HeaderName, FileName, HeaderLines, Disposition, EndOfMime,
IsMultipart.
Jul 29, 2001 V1.21 Moved ProcessLineBase64 to public section. Made CurrentData
property read/write. This permit to use Base64 decoding from
outside of the component.
Corrected a glitche with Delphi 1
May 04, 2002 V1.23 Added "Len" argument to OnInlineDecodeLine event.
Corrected UUDec so that nul is handled as space. Thanks to
arnaud.mesnews@free.fr who provided a test case.
Made UUOutDec a little bit faster.
May 10, 2002 V1.24 Accept 'begin 644' as well as 'begin 666' for UUEncoding
start. arnaud.mesnews@free.fr found that OE does that.
Nov 01, 2002 V1.25 Changed PChar arguments to Pointer to work around Delphi 7
bug with PAnsiChar<->PChar.
This will require small changes in your application code: change
PChar args to Pointer and add a PChar cast when using the arg.
Changed Base64 decoding so that is doesn't crash even if input
data is malformed (corrupted message).
Changed UUEncoded detection procedure. Thanks to Arnaud
<arnaud.mesnews@free.fr> for providing his code.
Apr 22, 2003 V1.26 Corrected ProcessLineQuotedPrintable which overflowed input
data when an empty line was given. Thanks to Dmitry Andreev for
finding a test case.
V1.27 Christophe Thiaux <tophet@free.fr> added PartFormat and
format properties.
Jul 20, 2003 V1.28 <arnaud.mesnews@free.fr> added yEnc decoding and fixed
uudecode when "begin" has to be lower case.
Aug 06, 2003 V1.29 Dmitry Andreev <advadvadv@mailgate.ru> and Arnaud
<arnaud.mesnews@free.fr> corrected a bug with continuation
lines in ProcessLineQuotedPrintable.
Aug 10, 2003 V1.30 Reformatted the source line to make Arnaud and Dmitry
changes looking like my own code. Translated all comments to
english. Englicized identificators.
Jan 03, 2004 V1.31 Replaced private section by protected.
Moved procedure ProcessLineQuotedPrintable to public section.
May 31, 2004 V1.32 John Bridgwater <jbridgwater@goodyear.com> fixed GetTokenEx
to allow a space around delimiter.
Jul 24, 2004 V1.33 arnaud.mesnews@free.fr added TriggerInlineDecodeBegin,
TriggerInlineDecodeLine and TriggerInlineDecodeEnd and called
them where needed. He also added InlineDecodeLine and
LengthHeader properties
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
unit MimeDec;
{$B-} { Enable partial boolean evaluation }
{$T-} { Untyped pointers }
{$X+} { Enable extended syntax }
{$I ICSDEFS.INC}
{$IFDEF DELPHI6_UP}
{$WARN SYMBOL_PLATFORM OFF}
{$WARN SYMBOL_LIBRARY OFF}
{$WARN SYMBOL_DEPRECATED OFF}
{$ENDIF}
{$IFNDEF VER80} { Not for Delphi 1 }
{$H+} { Use long strings }
{$J+} { Allow typed constant to be modified }
{$ENDIF}
{$IFDEF BCB3_UP}
{$ObjExportAll On}
{$ENDIF}
interface
uses
{$IFDEF USEWINDOWS}
Windows,
{$ELSE}
WinTypes, WinProcs,
{$ENDIF}
SysUtils, Classes;
const
MimeDecodeVersion = 134;
CopyRight : String = ' TMimeDecode (c) 1998-2005 Francois Piette V1.34 ';
type
TMimeDecodePartLine = procedure (Sender : TObject;
Data : Pointer;
DataLen : Integer) of object;
TInlineDecodeBegin = procedure (Sender: TObject; Filename: String) of object;
TInlineDecodeLine = procedure (Sender: TObject; Line: Pointer; Len : Integer) of object;
TInlineDecodeEnd = procedure (Sender: TObject; Filename: String) of object;
TMimeDecode = class(TComponent)
protected
FFrom : String;
FDest : String;
FSubject : String;
FDate : String;
FReturnPath : String;
FEncoding : String;
FCharSet : String;
FContentType : String;
FMimeVersion : String;
FHeaderName : String;
FDisposition : String;
FFileName : String;
FFormat : String;
FHeaderLines : TStrings;
FIsMultipart : Boolean;
FEndOfMime : Boolean;
FPartContentType : String;
FPartEncoding : String;
FPartNumber : Integer;
FPartHeaderBeginSignaled : Boolean;
FPartName : String;
FPartDisposition : String;
FPartContentID : String;
FPartFileName : String;
FPartFormat : String;
FPartCharset : String;
FApplicationType : String;
FPartOpened : Boolean;
FHeaderFlag : Boolean;
FLineNum : Integer;
FBuffer : PChar;
FBufferSize : Integer;
FCurrentData : PChar;
FBoundary : String;
FUUProcessFlag : Boolean;
FProcessFlagYBegin : Boolean; { AS: YEnc handling }
FSizeFileY : Integer; { AS: YEnc handling }
FSizeBlocY : Integer; { AS: YEnc handling }
FSizeLeftY : Integer; { AS: YEnc handling }
FNext : procedure of object;
FDestStream : TStream;
cUUFilename : String; { ##ERIC }
FEmbeddedBoundary : TStringList; { ##ERIC }
cIsEmbedded : Boolean; { ##ERIC }
FOnHeaderBegin : TNotifyEvent;
FOnHeaderLine : TNotifyEvent;
FOnHeaderEnd : TNotifyEvent;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -