mirror of
				https://gitlab.com/freepascal.org/lazarus/lazarus.git
				synced 2025-10-31 18:31:44 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			3911 lines
		
	
	
		
			123 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
			
		
		
	
	
			3911 lines
		
	
	
		
			123 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
| {******************************************************************}
 | |
| {*               IPMSG.PAS - MIME message classes                 *}
 | |
| {******************************************************************}
 | |
| 
 | |
| { $Id$ }
 | |
| 
 | |
| (* ***** BEGIN LICENSE BLOCK *****
 | |
|  * Version: MPL 1.1
 | |
|  *
 | |
|  * The contents of this file are subject to the Mozilla Public License Version
 | |
|  * 1.1 (the "License"); you may not use this file except in compliance with
 | |
|  * the License. You may obtain a copy of the License at
 | |
|  * http://www.mozilla.org/MPL/
 | |
|  *
 | |
|  * Software distributed under the License is distributed on an "AS IS" basis,
 | |
|  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 | |
|  * for the specific language governing rights and limitations under the
 | |
|  * License.
 | |
|  *
 | |
|  * The Original Code is TurboPower Internet Professional
 | |
|  *
 | |
|  * The Initial Developer of the Original Code is
 | |
|  * TurboPower Software
 | |
|  *
 | |
|  * Portions created by the Initial Developer are Copyright (C) 2000-2002
 | |
|  * the Initial Developer. All Rights Reserved.
 | |
|  *
 | |
|  * Contributor(s):
 | |
|  *
 | |
|  * Markus Kaemmerer <mk@happyarts.de> SourceForge: mkaemmerer
 | |
|  *
 | |
|  * ***** END LICENSE BLOCK ***** *)
 | |
| 
 | |
| { Global defines potentially affecting this unit }
 | |
| 
 | |
| {$I IPDEFINE.INC}
 | |
| 
 | |
| unit IpMsg;
 | |
| 
 | |
| interface
 | |
| 
 | |
| uses
 | |
|   {$IFDEF IP_LAZARUS}
 | |
|   LCLType,
 | |
|   LCLIntf,
 | |
|   {$ELSE}
 | |
|   Windows,
 | |
|   {$ENDIF}
 | |
|   Classes,
 | |
|   SysUtils,
 | |
|   IpStrms,
 | |
|   {$IFNDEF IP_LAZARUS}
 | |
|   IpSock,
 | |
|   {$ENDIF}
 | |
|   IpUtils,
 | |
|   IpConst;
 | |
| 
 | |
| type
 | |
|   TIpMimeEncodingMethod = (em7Bit, em8Bit, emBase64, emBinary, emBinHex,
 | |
|                            emQuoted, emUUEncode, emUnknown);
 | |
| 
 | |
| 
 | |
| { TIpMimeEntity }
 | |
| type
 | |
|   TIpCodingProgressEvent = procedure(Sender : TObject; Progress : Byte;
 | |
|                                      var Abort : Boolean) of object;
 | |
| 
 | |
| {Begin !!.12}
 | |
| type
 | |
|   TIpHeaderTypes = (htBCC, htCC, htControl, htDate, htDispositionNotify,
 | |
|                     htFollowUp, htFrom, htInReplyTo, htKeywords,
 | |
|                     htMessageID, htNewsgroups, htNNTPPostingHost,
 | |
|                     htOrganization, htPath, htPostingHost, htReceived,
 | |
|                     htReferences, htReplyTo, htReturnPath, htSender,
 | |
|                     htSubject, htTo, htUserFields, htXIpro);
 | |
| 
 | |
|   TIpHeaderInfo = record
 | |
|     FieldType   : TIpHeaderTypes;
 | |
|     FieldString : string;
 | |
|   end;
 | |
| 
 | |
| const
 | |
|   IpMaxHeaders = 24;
 | |
| 
 | |
|   IpHeaderXRef : array [0..IpMaxHeaders - 1] of  TIpHeaderInfo =
 | |
|     ((FieldType : htBCC;               FieldString : 'BCC'),
 | |
|      (FieldType : htCC;                FieldString : 'CC'),
 | |
|      (FieldType : htControl;           FieldString : 'Control: '),
 | |
|      (FieldType : htDate;              FieldString : 'Date'),
 | |
|      (FieldType : htDispositionNotify; FieldString : 'Disposition-Notification-To'),
 | |
|      (FieldType : htFollowUp;          FieldString : 'Followup-To: '),
 | |
|      (FieldType : htFrom;              FieldString : 'From'),
 | |
|      (FieldType : htInReplyTo;         FieldString : 'In-Reply-To'),
 | |
|      (FieldType : htKeywords;          FieldString : 'Keywords'),
 | |
|      (FieldType : htMessageID;         FieldString : 'Message-ID'),
 | |
|      (FieldType : htNewsgroups;        FieldString : 'Newsgroups'),
 | |
|      (FieldType : htNNTPPostingHost;   FieldString : 'NNTP-Posting-Host'),
 | |
|      (FieldType : htOrganization;      FieldString : 'Organization'),
 | |
|      (FieldType : htPath;              FieldString : 'Path'),
 | |
|      (FieldType : htPostingHost;       FieldString : 'Posting-Host'),
 | |
|      (FieldType : htReceived;          FieldString : 'Received'),
 | |
|      (FieldType : htReferences;        FieldString : 'References'),
 | |
|      (FieldType : htReplyTo;           FieldString : 'Reply-To'),
 | |
|      (FieldType : htReturnPath;        FieldString : 'Return-Path'),
 | |
|      (FieldType : htSender;            FieldString : 'Sender'),
 | |
|      (FieldType : htSubject;           FieldString : 'Subject'),
 | |
|      (FieldType : htTo;                FieldString : 'To'),
 | |
|      (FieldType : htUserFields;        FieldString : 'X-'),
 | |
|      (FieldType : htXIpro;             FieldString : 'X-Ipro'));
 | |
| 
 | |
| type
 | |
|   TIpHeaderCollection = class;
 | |
| 
 | |
|   TIpHeaderItem = class (TCollectionItem)
 | |
|     private
 | |
|       FCollection  : TIpHeaderCollection;
 | |
|       FName        : string;
 | |
|       FNameL       : string;
 | |
|         { Lower case version of FName. Used to speed up header searches. }
 | |
|       FProperty    : Boolean;                                          {!!.13}
 | |
|       FValue       : TStringList;
 | |
|     protected
 | |
|       procedure SetName(const Name : string);
 | |
|       procedure SetValue (v : TStringList);
 | |
|     public
 | |
|       constructor Create (Collection : TCollection); override;
 | |
|       destructor Destroy; override;
 | |
|     published
 | |
|       property Collection : TIpHeaderCollection
 | |
|                read FCollection write FCollection;
 | |
|       property Name : string read FName write SetName;
 | |
|       property NameL : string read FNameL;
 | |
|         { Lower case version of Name property. }
 | |
|       property IsProperty : Boolean read FProperty write FProperty;    {!!.13}
 | |
|         { Set to True if this header is exposed via an iPRO property. }{!!.13}
 | |
|       property Value : TStringList read FValue write SetValue;
 | |
|   end;
 | |
| 
 | |
|   TIpHeaderCollection = class (TCollection)
 | |
|     private
 | |
|       FOwner : TPersistent;                                              
 | |
| 
 | |
|     protected                                                            
 | |
|       function GetItem (Index : Integer) : TIpHeaderItem;
 | |
|       function GetOwner : TPersistent; override;                         
 | |
|       procedure SetItem (Index : Integer; Value : TIpHeaderItem);        
 | |
| 
 | |
|     public                                                               
 | |
|       constructor Create (AOwner : TPersistent);                         
 | |
| 
 | |
|       {$IFNDEF VERSION5}                                                 
 | |
|       procedure Delete (Item : integer);                                 
 | |
|       {$ENDIF}
 | |
|       function HasHeader (AName : string) : Integer;
 | |
|       procedure HeaderByName (AName   : string;
 | |
|                               Headers : TStringList);
 | |
|       procedure LoadHeaders (AHeaderList : TStringList;                  
 | |
|                              Append      : Boolean);                     
 | |
| 
 | |
|       property Items[Index : Integer] : TIpHeaderItem                    
 | |
|                read GetItem write SetItem;                               
 | |
|   end;                                                                   
 | |
| {End !!.12}
 | |
| 
 | |
|   TIpMimeParts = class; { Forwards }
 | |
| 
 | |
|   TIpMimeEntity = class(TPersistent)
 | |
|   protected {private}
 | |
|     FProgress                : Byte;
 | |
|     PrevProgress             : Byte;
 | |
|     FMimeParts               : TIpMimeParts;
 | |
|     FParentBoundary          : string;
 | |
|     FBody                    : TIpAnsiTextStream;
 | |
|     FEntityName              : string;
 | |
|     FBoundary                : string;
 | |
|     FCharacterSet            : string;
 | |
|     FContentDescription      : string;
 | |
|     FContentDispositionType  : string;
 | |
|     FContentID               : string;
 | |
|     FContentSubtype          : string;
 | |
|     FContentType             : string;
 | |
|     FCreationDate            : string;
 | |
|     FContentTransferEncoding : TIpMimeEncodingMethod;
 | |
|     FFileName                : string;
 | |
|     FIsMime                  : Boolean;
 | |
|     FIsMultipart             : Boolean;
 | |
|     FModificationDate        : string;
 | |
|     FMimeVersion             : string;
 | |
|     FOnCodingProgress        : TIpCodingProgressEvent;
 | |
|     FOriginalSize            : Longint;
 | |
|     FParent                  : TIpMimeEntity;
 | |
|     FReadDate                : string;
 | |
|     FRelatedType             : string;                                 {!!.02}
 | |
|     FRelatedSubtype          : string;                                 {!!.02}
 | |
|     FRelatedStart            : string;                                 {!!.02}
 | |
|     FRelatedStartInfo        : string;                                 {!!.02}
 | |
|     FAttachmentCount         : Integer;                                {!!.12}
 | |
| 
 | |
|   protected {methods}
 | |
|     procedure Clear; virtual;
 | |
|     procedure ClearBodyLargeAttach(const AttachmentSize : Longint); virtual;  {!!.12}
 | |
|     function  ContainsSpecialChars(const Value : string) : Boolean;    {!!.14}
 | |
|     procedure DecodeContentDisposition(const aDisp : string);
 | |
|     procedure DecodeContentType(const aType : string);
 | |
|     function  DecodeContentTransferEncoding(const aEncoding : string) :
 | |
|                                             TIpMimeEncodingMethod;
 | |
|     procedure DecodeMimeHeaders(RawHeaders : TStringlist);
 | |
|     procedure DoOnCodingProgress(Count, TotalSize : Longint; var Abort : Boolean);
 | |
|     procedure EncodeContentDisposition(RawHeaders : TStringList);
 | |
|     procedure EncodeContentType(RawHeaders : TStringList);
 | |
|     procedure EncodeContentTransferEncoding(RawHeaders : TStringList);
 | |
|     procedure EncodeMimeHeaders(RawHeaders : TStringlist);
 | |
|     procedure OctetStreamToHextetStream(InStream : TStream; OutStream : TIpAnsiTextStream;
 | |
|                                         const Table; PadChar, Delim : AnsiChar);
 | |
|     procedure Decode8Bit(OutStream : TStream);
 | |
|     procedure DecodeBase64(OutStream : TStream);
 | |
|     procedure DecodeBinHex(OutStream : TStream);
 | |
|     procedure DecodeQuoted(OutStream : TStream);
 | |
|     procedure DecodeUUEncode(OutStream : TStream);
 | |
|     procedure Encode8Bit(InStream : TStream);
 | |
|     procedure EncodeBase64(InStream : TStream);
 | |
|     procedure EncodeBinHex(InStream : TStream; const aFileName : string);
 | |
|     procedure EncodeQuoted(InStream : TStream);
 | |
|     procedure EncodeUUEncode(InStream : TStream; const aFileName : string);
 | |
|     function DecodeEntity(InStream : TIpAnsiTextStream) : string;
 | |
|     function DecodeEntityAsAttachment(InStream : TIpAnsiTextStream) : string;  {!!.01}
 | |
|     function EncodeEntity(OutStream : TIpAnsiTextStream) : string;
 | |
|     procedure ReadBody(InStream : TIpAnsiTextStream; const StartLine : string); {!!.12}
 | |
| 
 | |
|   protected {properties}
 | |
|     property OnCodingProgress : TIpCodingProgressEvent
 | |
|       read FOnCodingProgress write FOnCodingProgress;
 | |
| 
 | |
|   public {methods}
 | |
|     constructor Create(ParentEntity : TIpMimeEntity); virtual;
 | |
|     destructor  Destroy; override;
 | |
|     procedure ClearBody;
 | |
|     procedure EncodeBodyFile(const InFile : string);
 | |
|     procedure EncodeBodyStream(InStream : TStream; const aFileName : string);
 | |
|     procedure EncodeBodyStrings(InStrings : TStrings; const aFileName : string);
 | |
|     procedure ExtractBodyFile(const OutFile : string);
 | |
|     procedure ExtractBodyStream(OutStream : TStream);
 | |
|     procedure ExtractBodyStrings(OutStrings : TStrings);
 | |
|     function FindNestedMimePart(const aType, aSubType, aContentID : string) : TIpMimeEntity; {!!.02}
 | |
|     function  GetMimePart(const aType, aSubType, aContentID : string;
 | |
|                               CanCreate : Boolean) : TIpMimeEntity;
 | |
|     function  NewMimePart : TIpMimeEntity;
 | |
| 
 | |
|     property AttachmentCount : Integer read FAttachmentCount;          {!!.12}
 | |
| 
 | |
|   public {properties}
 | |
|     property Body : TIpAnsiTextStream
 | |
|       read FBody;
 | |
| 
 | |
|     property Boundary : string
 | |
|       read FBoundary write FBoundary;
 | |
| 
 | |
|     property CharacterSet : string
 | |
|       read FCharacterSet write FCharacterSet;
 | |
| 
 | |
|     property ContentDescription : string
 | |
|       read FContentDescription write FContentDescription;
 | |
| 
 | |
|     property ContentDispositionType : string
 | |
|       read FContentDispositionType write FContentDispositionType;
 | |
| 
 | |
|     property ContentID : string
 | |
|       read FContentID write FContentID;
 | |
| 
 | |
|     property ContentSubtype : string
 | |
|       read FContentSubtype write FContentSubtype;
 | |
| 
 | |
|     property ContentTransferEncoding : TIpMimeEncodingMethod
 | |
|       read FContentTransferEncoding write FContentTransferEncoding;
 | |
| 
 | |
|     property ContentType : string
 | |
|       read FContentType write FContentType;
 | |
| 
 | |
|     property CreationDate : string
 | |
|       read FCreationDate write FCreationDate;
 | |
| 
 | |
|     property EntityName : string
 | |
|       read FEntityName write FEntityName;
 | |
| 
 | |
|     property FileName : string
 | |
|       read FFileName write FFileName;
 | |
| 
 | |
|     property IsMime : Boolean
 | |
|       read FIsMime;
 | |
| 
 | |
|     property IsMultipart : Boolean
 | |
|       read FIsMultipart;
 | |
| 
 | |
|     property MimeParts : TIpMimeParts
 | |
|       read FMimeParts;
 | |
| 
 | |
|     property MimeVersion : string
 | |
|       read FMimeVersion write FMimeVersion;
 | |
| 
 | |
|     property ModificationDate : string
 | |
|       read FModificationDate write FModificationDate;
 | |
| 
 | |
|     property OriginalSize : Longint
 | |
|       read FOriginalSize write FOriginalSize;
 | |
| 
 | |
|     property Parent : TIpMimeEntity
 | |
|       read FParent;
 | |
| 
 | |
|     property ReadDate : string
 | |
|       read FReadDate write FReadDate;
 | |
| 
 | |
|     property RelatedStart : string                                   {!!.02}
 | |
|       read FRelatedStart write FRelatedStart;
 | |
| 
 | |
|     property RelatedStartInfo : string                               {!!.02}
 | |
|       read FRelatedStartInfo write FRelatedStartInfo;
 | |
| 
 | |
|     property RelatedSubtype : string                                 {!!.02}
 | |
|       read FRelatedSubtype write FRelatedSubtype;
 | |
| 
 | |
|     property RelatedType : string                                    {!!.02}
 | |
|       read FRelatedType write FRelatedType;
 | |
| 
 | |
|   end;
 | |
| 
 | |
| 
 | |
| { TIpMimeParts }
 | |
|   TIpMimeParts = class
 | |
|   protected {private}
 | |
|     Entitys : TList;
 | |
|     function GetCount : Integer;
 | |
|     function GetPart(aIndex : Integer) : TIpMimeEntity;
 | |
|   public {methods}
 | |
|     constructor Create;
 | |
|     destructor  Destroy; override;
 | |
|     function Add(aEntity : TIpMimeEntity) : Integer;
 | |
|     function Remove(aEntity : TIpMimeEntity) : Integer;
 | |
|     procedure Clear;
 | |
|     procedure Delete(aIndex : Integer);
 | |
|     function IndexOf(aEntity : TIpMimeEntity) : Integer;
 | |
|   public {properties}
 | |
|     property Count : Integer
 | |
|       read GetCount;
 | |
|     property Parts[aIndex : Integer] : TIpMimeEntity
 | |
|       read GetPart; default;
 | |
|   end;
 | |
| 
 | |
| 
 | |
| { TIpMessage }
 | |
| type
 | |
|   TIpMessage = class(TIpMimeEntity)
 | |
|   protected {private}
 | |
|     MsgStream : TIpAnsiTextStream;
 | |
| 
 | |
|   protected {property variables}
 | |
|     FBCC             : TStringList;
 | |
|     FCC              : TStringList;
 | |
|     FDate            : string;
 | |
|     FFrom            : string;
 | |
|     FInReplyTo       : string;
 | |
|     FKeywords        : string;
 | |
|     FFollowupTo      : string;                                           {!!.12}
 | |
|     FControl         : string;                                           {!!.12}
 | |
|     FMessageID       : string;
 | |
|     FMessageTag      : Integer;
 | |
|     FNewsgroups      : TStringList;
 | |
|     FNNTPPostingHost : string;
 | |
|     FOrganization    : string;
 | |
|     FPath            : TStringList;
 | |
|     FPostingHost     : string;
 | |
|     FReceived        : TStringList;
 | |
|     FRecipients      : TStringList;
 | |
|     FReferences      : TStringList;
 | |
|     FReplyTo         : string;
 | |
|     FReturnPath      : string;
 | |
|     FSender          : string;
 | |
|     FSubject         : string;
 | |
|     FUserFields      : TStringList;
 | |
|     FHeaders         : TIpHeaderCollection;                              {!!.12}
 | |
|     FDispositionNotify: string;
 | |
| 
 | |
|   protected {methods}
 | |
|     procedure CheckAllHeaders;                                           {!!.12}
 | |
|     procedure CheckHeaderType (HeaderInfo : TIpHeaderItem;               {!!.12}
 | |
|                                HeaderType : TIpHeaderTypes);             {!!.12}
 | |
|     procedure Clear; override;
 | |
|     procedure NewMessageStream;
 | |
|     function  GetPosition : Longint;
 | |
|     function  GetSize : Longint;
 | |
|     procedure SetPosition(Value : Longint);
 | |
|     procedure SetBCC(const Value: TStringList);
 | |
|     procedure SetCC(const Value: TStringList);
 | |
|     procedure SetNewsgroups(const Value: TStringList);
 | |
|     procedure SetPath(const Value: TStringList);
 | |
|     procedure SetReceived(const Value: TStringList);
 | |
|     procedure SetRecipients(const Value: TStringList);
 | |
|     procedure SetReferences(const Value: TStringlist);
 | |
|     procedure SetUserFields(const Value: TStringList);
 | |
| 
 | |
|   public {methods}
 | |
|     constructor CreateMessage; virtual;
 | |
|     destructor  Destroy; override;
 | |
| 
 | |
|     procedure AddDefaultAttachment(const aFileName : string);          {!!.02}
 | |
|     procedure AddDefaultAttachmentAs (const aFileName      : string;   {!!.12}
 | |
|                                       const AttachmentName : string);  {!!.12}
 | |
|     procedure Assign(Source : TPersistent); override;
 | |
|     function  AtEndOfStream : Boolean;
 | |
|     procedure DecodeMessage; virtual;
 | |
|     procedure EncodeMessage; virtual;
 | |
|     function  GetBodyHtml(CanCreate : Boolean) : TIpMimeEntity;
 | |
|     function  GetBodyPlain(CanCreate : Boolean) : TIpMimeEntity;
 | |
|     procedure LoadFromFile(const aFileName : string);
 | |
|     procedure LoadFromStream(aStream : TStream);                       {!!.12}
 | |
|     procedure NewMessage;
 | |
|     function  ReadLine : string;
 | |
|     function  ReadLineCRLF : string;
 | |
|     procedure SaveToFile(const aFileName : string);
 | |
|     procedure SaveToStream(Stream: TStream);                           {!!.12}
 | |
|     procedure SetHeaders(Headers : TIpHeaderCollection);               {!!.12}
 | |
|     procedure WriteLine(const aSt : string);
 | |
| 
 | |
|   public {properties}
 | |
|     property BCC : TStringList
 | |
|       read FBCC write SetBCC;                                          {!!.01}
 | |
| 
 | |
|     property CC : TStringList
 | |
|       read FCC write SetCC;                                            {!!.01}
 | |
| 
 | |
|     property Control : string                                          {!!.12}
 | |
|       read FControl write FControl;                                    {!!.12}
 | |
| 
 | |
|     property Date : string
 | |
|       read FDate write FDate;
 | |
| 
 | |
|     property DispositionNotification : string                          {!!.12}
 | |
|       read FDispositionNotify write FDispositionNotify;                {!!.12}
 | |
| 
 | |
|     property FollowupTo : String                                       {!!.12}
 | |
|       read FFollowupTo Write FFollowupTo;                              {!!.12}
 | |
| 
 | |
|     property From : string
 | |
|       read FFrom write FFrom;
 | |
| 
 | |
|     property Headers : TIpHeaderCollection                             {!!.12}
 | |
|              read FHeaders write SetHeaders;                           {!!.12}
 | |
| 
 | |
|     property InReplyTo : string
 | |
|       read FInReplyTo write FInReplyTo;
 | |
| 
 | |
|     property Keywords : string
 | |
|       read FKeywords write FKeywords;
 | |
| 
 | |
|     property MessageID : string
 | |
|       read FMessageID write FMessageID;
 | |
| 
 | |
|     property MessageStream : TIpAnsiTextStream                         {!!.03}
 | |
|       read MsgStream;                                                  {!!.03}
 | |
| 
 | |
|     property MessageTag : Integer
 | |
|       read FMessageTag write FMessageTag;
 | |
| 
 | |
|     property Newsgroups : TStringList
 | |
|       read FNewsgroups write SetNewsgroups;                            {!!.01}
 | |
| 
 | |
|     property NNTPPostingHost : string
 | |
|       read FNNTPPostingHost write FNNTPPostingHost;
 | |
| 
 | |
|     property Organization : string
 | |
|       read FOrganization write FOrganization;
 | |
| 
 | |
|     property Path : TStringList
 | |
|       read FPath write SetPath;                                        {!!.01}
 | |
| 
 | |
|     property Position : Longint
 | |
|       read GetPosition write SetPosition;
 | |
| 
 | |
|     property PostingHost : string
 | |
|       read FPostingHost write FPostingHost;
 | |
| 
 | |
|     property Received : TStringList
 | |
|       read FReceived write SetReceived;                                {!!.01}
 | |
| 
 | |
|     property Recipients : TStringList
 | |
|       read FRecipients write SetRecipients;                            {!!.01}
 | |
| 
 | |
|     property References : TStringlist
 | |
|       read FReferences write SetReferences;                            {!!.01}
 | |
| 
 | |
|     property ReplyTo : string
 | |
|       read FReplyTo write FReplyTo;
 | |
| 
 | |
|     property ReturnPath : string
 | |
|       read FReturnPath write FReturnPath;
 | |
| 
 | |
|     property Sender : string
 | |
|       read FSender write FSender;
 | |
| 
 | |
|     property Size : Longint
 | |
|       read GetSize;
 | |
| 
 | |
|     property Subject : string
 | |
|       read FSubject write FSubject;
 | |
| 
 | |
|     property UserFields : TStringList
 | |
|       read FUserFields write SetUserFields;                            {!!.01}
 | |
| 
 | |
|   end;
 | |
| 
 | |
| 
 | |
| { TIpMailMessage}
 | |
| type
 | |
|   TIpMailMessage = class(TIpMessage)
 | |
|   published {properties}
 | |
|     property BCC;
 | |
|     property CC;
 | |
|     property ContentDescription;
 | |
|     property ContentTransferEncoding;
 | |
|     property ContentType;
 | |
|     property Date;
 | |
|     property From;
 | |
|     property Keywords;
 | |
|     property MailTo : TStringList
 | |
|       read FRecipients write SetRecipients;                            {!!.01}
 | |
|     property OnCodingProgress;
 | |
|     property References;
 | |
|     property ReplyTo;
 | |
|     property Sender;
 | |
|     property Subject;
 | |
|     property UserFields;
 | |
| end;
 | |
| 
 | |
| 
 | |
| { TIpNewsArticle }
 | |
| type
 | |
|   TIpNewsArticle = class(TIpMessage)
 | |
|   published {properties}
 | |
|     property ContentDescription;
 | |
|     property ContentTransferEncoding;
 | |
|     property ContentType;
 | |
|     property Date;
 | |
|     property From;
 | |
|     property Keywords;
 | |
|     property Newsgroups;
 | |
|     property NNTPPostingHost;
 | |
|     property OnCodingProgress;
 | |
|     property Path;
 | |
|     property References;
 | |
|     property ReplyTo;
 | |
|     property Sender;
 | |
|     property Subject;
 | |
|     property UserFields;
 | |
| end;
 | |
| 
 | |
| 
 | |
| { TIpFormDataEntity }
 | |
| type
 | |
|   TIpFormDataEntity = class(TIpMimeEntity)
 | |
|   protected
 | |
|     FFilesEntity : TIpMimeEntity;
 | |
|   public {methods}
 | |
|     constructor Create(ParentEntity : TIpMimeEntity); override;
 | |
|     destructor  Destroy; override;
 | |
|     procedure AddFormData(const aName, aText : string);
 | |
|     procedure AddFile(const aFileName, aContentType, aSubtype : string;
 | |
|                       aEncoding : TIpMimeEncodingMethod);
 | |
|     procedure SaveToStream(aStream : TStream);
 | |
|   end;
 | |
| 
 | |
|  {$IFNDEF IP_LAZARUS}
 | |
|  { dummy class so this unit will be added to the uses clause when an }
 | |
|  { IpPop3Client, IpSmtpClient or IpNntpClient component is dropped on the form }
 | |
|  TIpCustomEmailClass = class(TIpCustomClient)
 | |
|  end;
 | |
|  {$ENDIF}
 | |
| 
 | |
| function IpBase64EncodeString(const InStr: string): string;       {!!.02}{!!.03}
 | |
| 
 | |
| {Begin !!.12}
 | |
| const
 | |
|   IpLgAttachSizeBoundry = 5 * 1024 * 1024;
 | |
|     { Attachments over this size will be encoded using a TIpMemMapStream for
 | |
|       greatly improved performance. This boundary also applies to the final
 | |
|       encoding of messages with large attachments. }
 | |
| 
 | |
| implementation
 | |
| 
 | |
| const
 | |
|   { standard headers }
 | |
|   strBCC               = 'BCC: ';
 | |
|   strCC                = 'CC: ';
 | |
|   strDate              = 'Date: ';
 | |
|   strDispositionNotify = 'Disposition-Notification-To: ';
 | |
|   strFrom              = 'From: ';
 | |
|   strInReplyTo         = 'In-Reply-To: ';
 | |
|   strKeywords          = 'Keywords: ';
 | |
|   strMessageID         = 'Message-ID: ';
 | |
|   strNewsgroups        = 'Newsgroups: ';
 | |
|   strNNTPPostingHost   = 'NNTP-Posting-Host: ';
 | |
|   strOrganization      = 'Organization: ';
 | |
|   strPath              = 'Path: ';
 | |
|   strPostingHost       = 'Posting-Host: ';
 | |
|   strReceived          = 'Received: ';
 | |
|   strReferences        = 'References: ';
 | |
|   strReplyTo           = 'Reply-To: ';
 | |
|   strReturnPath        = 'Return-Path: ';
 | |
|   strSender            = 'Sender: ';
 | |
|   strSubject           = 'Subject: ';
 | |
|   strTo                = 'To: ';
 | |
|   strUserFields        = 'X-';
 | |
|   strXIpro             = 'X-Ipro: ';
 | |
|   strFollowUp          = 'Followup-To: ';                               {!!.12}
 | |
|   strControl           = 'Control: ';                                   {!!.12}
 | |
| 
 | |
| {Begin !!.13}
 | |
|   IpMimeHeaders : array [0..5] of string =
 | |
|     { List of MIME headers that must be marked as public properties in
 | |
|       the message's Headers collection. Marking them as a public property
 | |
|       prevents them from being written out twice if the message is saved
 | |
|       to a file or stream. }
 | |
|     (
 | |
|       'Content-Type',
 | |
|       'MIME-Version',
 | |
|       'Content-Transfer-Encoding',
 | |
|       'Content-Description',
 | |
|       'Content-ID',
 | |
|       'Content-Disposition'
 | |
|     );
 | |
| {End !!.13}
 | |
| 
 | |
|   { MIME headers }
 | |
|   strMimeVersion             = 'MIME-Version: ';
 | |
|   strContent                 = 'Content-';
 | |
|   strContentBase             = strContent + 'Base: ';
 | |
|   strContentDescription      = strContent + 'Description: ';
 | |
|   strContentDisposition      = strContent + 'Disposition: ';
 | |
|   strContentID               = strContent + 'ID: ';
 | |
|   strContentLanguage         = strContent + 'Language: ';
 | |
|   strContentLocation         = strContent + 'Location: ';
 | |
|   strContentTransferEncoding = strContent + 'Transfer-Encoding: ';
 | |
|   strContentType             = strContent + 'Type: ';
 | |
| 
 | |
|   { MIME content types }
 | |
|   strApplication = 'application';
 | |
|   strAudio       = 'audio';
 | |
|   strFiles       = 'files';
 | |
|   strFormData    = 'form-data';
 | |
|   strImage       = 'image';
 | |
|   strMessage     = 'message';
 | |
|   strMultiPart   = 'multipart';
 | |
|   strText        = 'text';
 | |
|   strVideo       = 'video';
 | |
| 
 | |
|   { MIME content subtypes and parameters }
 | |
|   strBoundary    = 'boundary=';
 | |
|   strCharSet     = 'charset=';
 | |
|   strMixed       = 'mixed';
 | |
|   strName        = 'name=';
 | |
|   strPlain       = 'plain';
 | |
|   strHTML        = 'html';
 | |
|   strOctetStream = 'octet-stream';
 | |
|   strAlternative = 'alternative';
 | |
|   strRelated     = 'related';                                        {!!.02}
 | |
| 
 | |
|   { MIME content disposition parameters }
 | |
|   strAttachment       = 'attachment';
 | |
|   strInline           = 'inline';
 | |
|   strCreationDate     = 'creation-date=';
 | |
|   strFilename         = 'filename=';
 | |
|   strModificationDate = 'modification-date=';
 | |
|   strReadDate         = 'read-date=';
 | |
|   strStart            = 'start=';                                    {!!.02}
 | |
|   strStartInfo        = 'start-info=';                               {!!.02}
 | |
|   strSize             = 'size=';
 | |
|   strType             = 'type=';                                     {!!.02}
 | |
| 
 | |
| 
 | |
|   { MIME encoding methods }
 | |
|   str7Bit     = '7bit';
 | |
|   str8Bit     = '8bit';
 | |
|   strBase64   = 'base64';
 | |
|   strBinary   = 'binary';
 | |
|   strBinHex   = 'binhex';
 | |
|   strQuoted   = 'quoted-printable';
 | |
|   strUUEncode = 'uuencoded';
 | |
| 
 | |
| 
 | |
|   { default MIME content type information }
 | |
| {$I IPDEFCT.INC}
 | |
| 
 | |
| type
 | |
|   TIp6BitTable = array[0..63] of AnsiChar;
 | |
| 
 | |
| const {- BinHex encoding table }
 | |
|   IpBinHexTable : TIp6BitTable = (
 | |
|     '!', '"', '#', '$', '%', '&', '''', '(',
 | |
|     ')', '*', '+', ',', '-', '0', '1',  '2',
 | |
|     '3', '4', '5', '6', '8', '9', '@',  'A',
 | |
|     'B', 'C', 'D', 'E', 'F', 'G', 'H',  'I',
 | |
|     'J', 'K', 'L', 'M', 'N', 'P', 'Q',  'R',
 | |
|     'S', 'T', 'U', 'V', 'X', 'Y', 'Z',  '[',
 | |
|     '`', 'a', 'b', 'c', 'd', 'e', 'f',  'h',
 | |
|     'i', 'j', 'k', 'l', 'm', 'p', 'q',  'r');
 | |
| 
 | |
| const {-BinHex decoding table }
 | |
|   IpHexBinTable : array[33..114] of Byte = (
 | |
|     $00, $01, $02, $03, $04, $05, $06, $07,
 | |
|     $08, $09, $0A, $0B, $0C, $FF, $FF, $0D,
 | |
|     $0E, $0F, $10, $11, $12, $13, $FF, $14,
 | |
|     $15, $FF, $FF, $FF, $FF, $FF, $FF, $16,
 | |
|     $17, $18, $19, $1A, $1B, $1C, $1D, $1E,
 | |
|     $1F, $20, $21, $22, $23, $24, $FF, $25,
 | |
|     $26, $27, $28, $29, $2A, $2B, $FF, $2C,
 | |
|     $2D, $2E, $2F, $FF, $FF, $FF, $FF, $30,
 | |
|     $31, $32, $33, $34, $35, $36, $FF, $37,
 | |
|     $38, $39, $3A, $3B, $3C, $FF, $FF, $3D,
 | |
|     $3E, $3F);
 | |
| 
 | |
| const { Base64 encoding table }
 | |
|   Ip64Table : TIp6BitTable = (
 | |
|     #065, #066, #067, #068, #069, #070, #071, #072,
 | |
|     #073, #074, #075, #076, #077, #078, #079, #080,
 | |
|     #081, #082, #083, #084, #085, #086, #087, #088,
 | |
|     #089, #090, #097, #098, #099, #100, #101, #102,
 | |
|     #103, #104, #105, #106, #107, #108, #109, #110,
 | |
|     #111, #112, #113, #114, #115, #116, #117, #118,
 | |
|     #119, #120, #121, #122, #048, #049, #050, #051,
 | |
|     #052, #053, #054, #055, #056, #057, #043, #047);
 | |
| 
 | |
| const { Base64 decoding table }
 | |
|   IpD64Table : array[#43..#122] of Byte = (                          {!!.12}
 | |
|     $3E, $7F, $7F, $7F, $3F, $34, $35, $36,
 | |
|     $37, $38, $39, $3A, $3B, $3C, $3D, $7F,
 | |
|     $7F, $7F, $7F, $7F, $7F, $7F, $00, $01,
 | |
|     $02, $03, $04, $05, $06, $07, $08, $09,
 | |
|     $0A, $0B, $0C, $0D, $0E, $0F, $10, $11,
 | |
|     $12, $13, $14, $15, $16, $17, $18, $19,
 | |
|     $7F, $7F, $7F, $7F, $7F, $7F, $1A, $1B,
 | |
|     $1C, $1D, $1E, $1F, $20, $21, $22, $23,
 | |
|     $24, $25, $26, $27, $28, $29, $2A, $2B,
 | |
|     $2C, $2D, $2E, $2F, $30, $31, $32, $33);
 | |
| 
 | |
| const { UUEncode encoding table }
 | |
|   IpUUTable : TIp6BitTable = (
 | |
|     #96, #33, #34, #35, #36, #37, #38, #39,
 | |
|     #40, #41, #42, #43, #44, #45, #46, #47,
 | |
|     #48, #49, #50, #51, #52, #53, #54, #55,
 | |
|     #56, #57, #58, #59, #60, #61, #62, #63,
 | |
|     #64, #65, #66, #67, #68, #69, #70, #71,
 | |
|     #72, #73, #74, #75, #76, #77, #78, #79,
 | |
|     #80, #81, #82, #83, #84, #85, #86, #87,
 | |
|     #88, #89, #90, #91, #92, #93, #94, #95);
 | |
| 
 | |
| const
 | |
|   HexDigits : array[0..$F] of AnsiChar = '0123456789ABCDEF';
 | |
|   RLEChar : Byte = $90;
 | |
|   BinHexFileType : array[0..3] of Byte = ($49, $42, $4D, $3F);  { "IBM?" }
 | |
|   CRLF = #13#10;
 | |
|   MaxLine = 1000;                                                       {!!.12}
 | |
|   MaxLineEncode = 77;                                                   {!!.13}
 | |
|     { Maximum line length for QuotablePrintable & Base64 encoding. }    {!!.13}
 | |
| 
 | |
| type
 | |
|   BinHexHeader = packed record
 | |
|     Version  : Byte;
 | |
|     FileType : array[0..3] of Byte;
 | |
|     Creator  : array[0..3] of Byte;
 | |
|     Flags    : Word;
 | |
|     DFLong   : Longint;
 | |
|     RFLong   : Longint;
 | |
|   end;
 | |
| 
 | |
| function IsSameString (Str1          : string;                           {!!.12}
 | |
|                        Str2          : string;                           {!!.12}
 | |
|                        CaseSensitive : Boolean) : Boolean;               {!!.12}
 | |
| begin                                                                    {!!.12}
 | |
|   if CaseSensitive then                                                  {!!.12}
 | |
|     Result := (Str1 = Str2)                                              {!!.12}
 | |
|   else                                                                   {!!.12}
 | |
|     Result := StrIComp (PChar (Str1), PChar (Str2)) = 0;                 {!!.12}
 | |
| end;                                                                     {!!.12}
 | |
| 
 | |
| { Parse string into string list }
 | |
| procedure Parse(const Line : string; Delim : AnsiChar; var List : TStringList);
 | |
| var
 | |
|   iPos, jPos : Integer;
 | |
|   Term : string;
 | |
| begin
 | |
|   iPos := 1;
 | |
|   jPos := IpUtils.CharPos(Delim, Line);
 | |
|   while (jPos > 0) do begin
 | |
|     Term := Copy(Line, iPos, jPos - iPos);                           {!!.02}
 | |
|     if (Term <> '') then
 | |
|       List.Add(Trim(Term));
 | |
|     iPos := jPos + 1;
 | |
|     jPos := IpUtils.CharPosIdx(Delim, Line, iPos);
 | |
|   end;
 | |
|   if (iPos < Length(Line)) then
 | |
|      List.Add(Trim(Copy(Line, iPos, Length(Line))));
 | |
| end;
 | |
| 
 | |
| { Return a particular parameter from a parsed header parameter list }
 | |
| procedure DecodeSingleParameter(const ParamName : string;
 | |
|                                 RawParams : TStringList;
 | |
|                                 var ParamFieldStr : string);
 | |
| var
 | |
|   S : string;
 | |
|   i, j : Integer;
 | |
| begin
 | |
|   ParamFieldStr := '';
 | |
|   {find the line containing the parameter field name}
 | |
|   for i := 1 to RawParams.Count do begin
 | |
|     S := RawParams[i-1];
 | |
|     if StrLIComp(PChar(ParamName), PChar(S), Length(ParamName)) = 0 then begin
 | |
|       {strip off the parameter field name and remove quotes }
 | |
|       ParamFieldStr := Copy(S, Length(ParamName) + 1, Length(S));
 | |
|       j := IpUtils.CharPos('"', ParamFieldStr);
 | |
|       while (j > 0) do begin
 | |
|         Delete(ParamFieldStr, j, 1);
 | |
|         j := IpUtils.CharPos('"', ParamFieldStr);
 | |
|       end;
 | |
|       Break;
 | |
|     end;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| { Return a particular header as string }
 | |
| procedure DecodeSingleHeader(const HeaderName : string;
 | |
|                              RawHeaders : TStringList;
 | |
|                              var HeaderFieldStr : string);
 | |
| var
 | |
|   S, S2 : string;
 | |
|   i, j : Integer;
 | |
| begin
 | |
|   HeaderFieldStr := '';
 | |
|   {find the line containing the header field name}
 | |
|   for i := 1 to RawHeaders.Count do begin
 | |
|     S := RawHeaders[i-1];
 | |
|     if StrLIComp(PChar(HeaderName), PChar(S), Length(HeaderName)) = 0 then begin
 | |
|       {strip off the header field name}
 | |
|       S := Copy(S, Length(HeaderName) + 1, Length(S));
 | |
|       {unfold the header if continued on more than one line}
 | |
|       if (i < RawHeaders.Count) then
 | |
|         for j := i to Pred(RawHeaders.Count) do begin
 | |
|           S2 := RawHeaders[j];
 | |
|           if (Length(S2) > 0) and (S2[1] <> #09) and (S2[1] <> ' ') then
 | |
|             Break
 | |
|           else
 | |
|             S := S + S2;
 | |
|         end;
 | |
|       HeaderFieldStr := S;
 | |
|       Break;
 | |
|     end;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| { Return a particular header as string list }
 | |
| (*procedure DecodeListHeader(const HeaderName : string;
 | |
|                            RawHeaders, HeaderFieldList : TStringList);
 | |
| var
 | |
|   S : string;
 | |
|   i, j : Integer;
 | |
| begin
 | |
|   {find the line containing the header field name}
 | |
|   for i := 1 to RawHeaders.Count do begin
 | |
|     S := RawHeaders[i-1];
 | |
|     if StrLIComp(PChar(HeaderName), PChar(S), Length(HeaderName)) = 0 then begin
 | |
|       {strip off the header field name}
 | |
|       HeaderFieldList.Add(Copy(S, Length(HeaderName) + 1, Length(S)));
 | |
|       {unfold the header if continued on more than one line}
 | |
|       if (i < RawHeaders.Count) then
 | |
|         for j := i to Pred(RawHeaders.Count) do begin
 | |
|           S := RawHeaders[j];
 | |
|           if (Length(S) > 0) and (S[1] <> #09) and (S[1] <> ' ') then
 | |
|             Break
 | |
|           else
 | |
|             HeaderFieldList.Add(S);
 | |
|         end;
 | |
|       Break;
 | |
|     end;
 | |
|   end;
 | |
| end;*)
 | |
| 
 | |
| { Return multiple instance headers as string list }
 | |
| (*procedure DecodeMultiHeader(const HeaderName : string;
 | |
|                             RawHeaders, HeaderFieldList : TStringList);
 | |
| 
 | |
| var
 | |
|   S, S2 : string;
 | |
|   i, j : Integer;
 | |
| begin
 | |
|   {find the next line containing the header field name}
 | |
|   for i := 1 to RawHeaders.Count do begin
 | |
|     S := RawHeaders[i-1];
 | |
|     if StrLIComp(PChar(HeaderName), PChar(S), Length(HeaderName)) = 0 then begin
 | |
|       if HeaderName <> strUserFields then begin                         {!!.11}
 | |
|         {strip off the header field name}
 | |
|         S := Copy(S, Length(HeaderName) + 1, Length(S));
 | |
|         {unfold the header if continued on more than one line}
 | |
|         if (i < RawHeaders.Count) then
 | |
|           for j := i to Pred(RawHeaders.Count) do begin
 | |
|             S2 := RawHeaders[j];
 | |
|             if (Length(S2) > 0) and (S2[1] <> #09) and (S2[1] <> ' ') then
 | |
|               Break
 | |
|             else
 | |
|               S := S + S2;
 | |
|           end;
 | |
|       end;                                                              {!!.11}
 | |
|       HeaderFieldList.Add(S);
 | |
|     end;
 | |
|   end;
 | |
| end;*)
 | |
| 
 | |
| { Add header string to raw headers }
 | |
| procedure EncodeSingleHeader(const HeaderName : string;
 | |
|                              RawHeaders : TStringList;
 | |
|                              HeaderFieldStr : string);
 | |
| begin
 | |
|   if (HeaderFieldStr <> '') then
 | |
|     RawHeaders.Add(HeaderName + HeaderFieldStr);
 | |
| end;
 | |
| 
 | |
| { Unfold multiple line header and add to raw headers }
 | |
| procedure EncodeListHeader(const HeaderName : string;
 | |
|                            RawHeaders, HeaderFieldList : TStringList;
 | |
|                            const Delim : string;
 | |
|                            Fold : Boolean);
 | |
| var
 | |
|   S : string;
 | |
|   i : Integer;
 | |
| begin
 | |
|   if (HeaderFieldList.Count > 0) then begin
 | |
|     S := HeaderName;
 | |
|     for i := 0 to Pred(HeaderFieldList.Count) do begin
 | |
|       if (Length(S + HeaderFieldList[i]) > MaxLine) then begin
 | |
|         RawHeaders.Add(S);
 | |
|         S := #09;
 | |
|       end;
 | |
|       S := S + HeaderFieldList[i];
 | |
|       if (i < HeaderFieldList.Count - 1) and (S <> '') then begin
 | |
|         S := S + Delim;                                                {!!.14}
 | |
|         if Fold then begin
 | |
|           RawHeaders.Add(S);
 | |
|           S := #09;
 | |
|         end;
 | |
|       end;
 | |
|     end;
 | |
|     RawHeaders.Add(S);
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| { Add multiple instance header to raw headers }
 | |
| procedure EncodeMultiHeader(const HeaderName : string;
 | |
|                             RawHeaders, HeaderFieldList : TStringList;
 | |
|                             Delim : AnsiChar;
 | |
|                             Fold : Boolean);
 | |
| var
 | |
|   i, j : Integer;
 | |
|   SL : TStringList;
 | |
|   S : string;
 | |
| begin
 | |
|   if (HeaderFieldList.Count > 0) then
 | |
|     for j := 1 to HeaderFieldList.Count do begin
 | |
|       if not Fold then
 | |
|         RawHeaders.Add(HeaderName + HeaderFieldList[j-1])
 | |
|       else begin
 | |
|         SL := TStringList.Create;
 | |
|         try
 | |
|           Parse(HeaderFieldList[j-1], Delim, SL);
 | |
|           S := HeaderName;
 | |
|           for i := 1 to SL.Count do begin
 | |
|             S := S + SL[i-1];
 | |
|             if (i < SL.Count) and (S <> '') then begin
 | |
| {Begin !!.13}
 | |
|               RawHeaders.Add(S);
 | |
|               S := Delim;
 | |
| {End !!.13}
 | |
|             end;
 | |
|           end;
 | |
|         finally
 | |
|           SL.Free;
 | |
|         end;
 | |
|         RawHeaders.Add(S);
 | |
|       end;
 | |
|     end;
 | |
| end;
 | |
| 
 | |
| { Generate "unique" boundary string }
 | |
| function GenerateBoundary : string;
 | |
| var
 | |
|   Temp : TDateTime;
 | |
| begin
 | |
|   Temp := Now;
 | |
|   Randomize;
 | |
|   Result := '_NextPart_' + IntToHex(Trunc(Temp), 8) + '-' +
 | |
|     IntToHex(Trunc(Frac(Temp) * 10000), 8) + '-' +
 | |
|     IntToHex(GetTickCount, 8) + '-' + IntToHex(Random($FFFF), 4);
 | |
| end;
 | |
| 
 | |
| { 16-bit CRC of stream between starting and ending offset }
 | |
| function BinHexCRC(Stream : TStream; StartOffset, EndOffset : Longint) : Word;
 | |
| var
 | |
|   Crc : Word;
 | |
|   InByte : Byte;
 | |
|   ByteStream : TIpByteStream;
 | |
| 
 | |
|   procedure DoCRC(b : Byte);
 | |
|     {- carry CRC division on with next byte }
 | |
|   var
 | |
|     j : Byte;
 | |
|     t : Boolean;
 | |
|   begin
 | |
|     for j := 1 to 8 do begin
 | |
|       t := (Crc and $8000) <> 0;
 | |
|       Crc := (Crc shl 1) xor (b shr 7);
 | |
|       if t then
 | |
|         Crc := Crc xor $1021;
 | |
|       b := b shl 1;
 | |
|     end;
 | |
|   end;
 | |
| 
 | |
| begin
 | |
|   if (StartOffset > Stream.Size) or (EndOffset > Stream.Size) then
 | |
|     raise EIpBaseException.Create(SBadOffset);
 | |
| 
 | |
|   Crc := 0;
 | |
|   Stream.Position := StartOffset;
 | |
|   ByteStream := TIpByteStream.Create(Stream);
 | |
|   try
 | |
|     while (ByteStream.Position < EndOffset) do begin
 | |
|       if ByteStream.Read(InByte) then
 | |
|         DoCrc(InByte);
 | |
|     end;
 | |
|   finally
 | |
|     ByteStream.Free;
 | |
|   end;
 | |
|   DoCrc(0);
 | |
|   DoCrc(0);
 | |
|   Result := Swap(Crc);
 | |
| end;
 | |
| 
 | |
| { Reverse bytes and words }
 | |
| function htonl(HostLong : Longint) : Longint;
 | |
| var
 | |
|   dw : Longint;
 | |
|   wa : array[0..1] of Word absolute dw;
 | |
|   w  : Word;
 | |
| begin
 | |
|   dw := HostLong;
 | |
|   w := wa[0];
 | |
|   wa[0] := Swap(wa[1]);
 | |
|   wa[1] := Swap(w);
 | |
|   Result := dw;
 | |
| end;
 | |
| 
 | |
| {Begin !!.12}
 | |
| { TIpHeaderItem ****************************************************** }
 | |
| 
 | |
| constructor TIpHeaderItem.Create (Collection : TCollection);
 | |
| begin
 | |
|   inherited Create (Collection);
 | |
|   FCollection := TIpHeaderCollection.Create (
 | |
|                      TIpHeaderCollection(Collection).FOwner);
 | |
| 
 | |
|   FValue := TStringList.Create;
 | |
|   FName  := '';
 | |
|   FProperty := False;                                                  {!!.13}
 | |
| end;                                                                     
 | |
| 
 | |
| destructor TIpHeaderItem.Destroy;                                        
 | |
| begin                                                                    
 | |
|   FCollection.Free;                                                      
 | |
|   FCollection := nil;                                                    
 | |
| 
 | |
|   FValue.Free;                                                           
 | |
|   FValue := nil;                                                         
 | |
| 
 | |
|   inherited Destroy;                                                     
 | |
| end;                                                                     
 | |
| 
 | |
| procedure TIpHeaderItem.SetName(const Name : string);
 | |
| begin
 | |
|   FName := Name;
 | |
|   FNameL := LowerCase(Name);
 | |
| end;
 | |
| 
 | |
| procedure TIpHeaderItem.SetValue (v : TStringList);                      
 | |
| begin                                                                    
 | |
|   FValue.Assign (v);                                                     
 | |
| end;                                                                     
 | |
| 
 | |
| { TIpHeaderCollection ************************************************ } 
 | |
| 
 | |
| constructor TIpHeaderCollection.Create(AOwner : TPersistent);            
 | |
| begin                                                                    
 | |
|   inherited Create (TIpHeaderItem);                                      
 | |
|   FOwner := AOwner;                                                      
 | |
| end;                                                                     
 | |
| 
 | |
| {$IFNDEF VERSION5}                                                       
 | |
| procedure TIpHeaderCollection.Delete(Item: integer);                     
 | |
| begin                                                                    
 | |
|   GetItem(Item).Free;                                                    
 | |
| end;                                                                     
 | |
| {$ENDIF}                                                                 
 | |
| 
 | |
| function TIpHeaderCollection.GetItem (Index : Integer) : TIpHeaderItem;  
 | |
| begin                                                                    
 | |
|   Result := TIpHeaderItem (inherited GetItem (Index));                   
 | |
| end;                                                                     
 | |
| 
 | |
| function TIpHeaderCollection.GetOwner : TPersistent;                     
 | |
| begin                                                                    
 | |
|   Result := FOwner;                                                      
 | |
| end;                                                                     
 | |
| 
 | |
| function TIpHeaderCollection.HasHeader (AName : string) : Integer;
 | |
| var                                                                      
 | |
|   i : Integer;                                                           
 | |
| begin                                                                    
 | |
|   Result := -1;                                                          
 | |
|   AName := LowerCase(AName);
 | |
|   for i := 0 to Count - 1 do
 | |
|     if Items[i].NameL = AName then begin                      
 | |
|       Result := i;                                                       
 | |
|       Break;                                                             
 | |
|     end;                                                                 
 | |
| end;                                                                     
 | |
| 
 | |
| procedure TIpHeaderCollection.HeaderByName (AName   : string;            
 | |
|                                             Headers : TStringList);      
 | |
| var
 | |
|   HeaderPos : Integer;                                                   
 | |
| begin                                                                    
 | |
|   Headers.Clear;                                                         
 | |
|   HeaderPos := HasHeader (AName);                                        
 | |
|   if HeaderPos >= 0 then                                                 
 | |
|     Headers.Assign (Items[HeaderPos].Value);                             
 | |
| end;                                                                     
 | |
| 
 | |
| procedure TIpHeaderCollection.LoadHeaders (AHeaderList : TStringList;
 | |
|                                            Append      : Boolean);
 | |
| var
 | |
|   CurPos : Integer;
 | |
| 
 | |
|   function ExtractHeaderName (const AName : string) : string;
 | |
|   {!!.15 - replaced local variable i with inx in order to avoid confusion with
 | |
|     variable i in parent routine. }
 | |
|   var
 | |
|     inx     : Integer;
 | |
|     NameLen : Integer;
 | |
|   begin
 | |
|     Result := '';
 | |
|     CurPos := 0;
 | |
| 
 | |
|     inx := 0;
 | |
|     NameLen := Length (AName);
 | |
|     while (inx < NameLen) and (AName[inx + 1] <> ':') and
 | |
|           (AName[inx + 1] >= #33) and (AName[inx + 1] <= #126) do
 | |
|       Inc (inx);
 | |
|     if (inx > 0) then
 | |
|       Result := Copy (AName, 1, inx);
 | |
|     CurPos := inx + 2;
 | |
|   end;
 | |
| 
 | |
|   function IsWrappedLine (AHeaderList : TStringList;
 | |
|                           LineToCheck : Integer) : Boolean;
 | |
|   begin
 | |
|     if LineToCheck < AHeaderList.Count then begin
 | |
|       if Length (AHeaderList[LineToCheck]) > 0 then begin
 | |
|         if (AHeaderList[LineToCheck][1] = ' ') or
 | |
|            (AHeaderList[LineToCheck][1] = #09) then
 | |
|           Result := True
 | |
|         else
 | |
|           Result := False;
 | |
|       end else
 | |
|         Result := False;
 | |
|     end else
 | |
|       Result := False;
 | |
|   end;
 | |
| 
 | |
|   procedure GetFieldValue (    AHeaderList : TStringList;
 | |
|                            var CurLine     : Integer;
 | |
|                            var NewField    : TIpHeaderItem);
 | |
|   var
 | |
|     WorkLine : string;
 | |
|     LineLen  : Integer;
 | |
| 
 | |
|   begin
 | |
|     if CurLine >= AHeaderList.Count then
 | |
|       Exit;
 | |
|     LineLen  := Length (AHeaderList[CurLine]);
 | |
|     while (CurPos < LineLen) and
 | |
|           ((AHeaderList[CurLine][CurPos] = ' ') or
 | |
|            (AHeaderList[CurLine][CurPos] = #09)) do
 | |
|       Inc (CurPos);
 | |
|     WorkLine := Copy (AHeaderList[CurLine],
 | |
|                       CurPos, LineLen - CurPos + 1);
 | |
| {Begin !!.13}
 | |
|     Inc(CurLine);
 | |
| 
 | |
|     while IsWrappedLine (AHeaderList, CurLine) do begin
 | |
|       WorkLine := WorkLine + #9 + Trim(AHeaderList[CurLine]);
 | |
|       Inc(CurLine);
 | |
|     end;
 | |
|     NewField.Value.Add (Trim (WorkLine));
 | |
| {End !!.13}
 | |
|   end;
 | |
| 
 | |
| var                                                                      
 | |
|   i          : Integer;                                                  
 | |
|   HeaderName : string;                                                   
 | |
|   NewHeader  : TIpHeaderItem;                                            
 | |
| begin                                                                    
 | |
|   if not Append then                                                     
 | |
|     Clear;
 | |
| 
 | |
|   i := 0;                                                                
 | |
|   while i < AHeaderList.Count do begin                                   
 | |
|     HeaderName := ExtractHeaderName (AHeaderList[i]);                    
 | |
|     if HeaderName <> '' then begin                                       
 | |
|       NewHeader := TIpHeaderItem (Add);                                  
 | |
|       NewHeader.Name := HeaderName;                                      
 | |
|       GetFieldValue (AHeaderList, i, NewHeader);
 | |
| {Begin !!.15}
 | |
|     end
 | |
|     else
 | |
|       Inc(i);
 | |
| {End !!.15}
 | |
|   end;
 | |
| end;                                                                     
 | |
| 
 | |
| procedure TIpHeaderCollection.SetItem (Index : Integer;                  
 | |
|                                        Value : TIpHeaderItem);           
 | |
| begin                                                                    
 | |
|   inherited SetItem (Index, Value);                                      
 | |
| end;
 | |
| {End !!.12}
 | |
| 
 | |
| { TIpMimeParts }
 | |
| constructor TIpMimeParts.Create;
 | |
| begin
 | |
|   inherited Create;
 | |
|   Entitys := TList.Create;
 | |
| end;
 | |
| 
 | |
| destructor TIpMimeParts.Destroy;
 | |
| begin
 | |
|   Clear;
 | |
|   Entitys.Free;
 | |
|   inherited Destroy;
 | |
| end;
 | |
| 
 | |
| { Add Mime block to list }
 | |
| function TIpMimeParts.Add(aEntity : TIpMimeEntity) : Integer;
 | |
| begin
 | |
|   Result := Entitys.Add(aEntity);
 | |
| end;
 | |
| 
 | |
| { Clear list }
 | |
| procedure TIpMimeParts.Clear;
 | |
| var
 | |
|   i : Integer;
 | |
| begin
 | |
|   for i := Pred(Entitys.Count) downto 0 do
 | |
|     Delete(i);
 | |
| end;
 | |
| 
 | |
| { Delete block from list }
 | |
| procedure TIpMimeParts.Delete(aIndex : Integer);
 | |
| begin
 | |
|   if (aIndex >= 0) and (aIndex < Entitys.Count) then begin
 | |
|     TIpMimeEntity(Entitys[aIndex]).Free;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| { Remove block from list }
 | |
| function TIpMimeParts.Remove(aEntity : TIpMimeEntity) : Integer;
 | |
| begin
 | |
|   Result := Entitys.Remove(Pointer(aEntity));
 | |
| end;
 | |
| 
 | |
| { Count property read access method }
 | |
| function TIpMimeParts.GetCount : Integer;
 | |
| begin
 | |
|   Result := Entitys.Count;
 | |
| end;
 | |
| 
 | |
| { Parts property read access method }
 | |
| function TIpMimeParts.GetPart(aIndex : Integer) : TIpMimeEntity;
 | |
| begin
 | |
|   if (aIndex >= 0) and (aIndex < Entitys.Count) then
 | |
|     Result := TIpMimeEntity(Entitys[aIndex])
 | |
|   else
 | |
|     Result := nil;
 | |
| end;
 | |
| 
 | |
| { Returns list index of specified Mime block }
 | |
| function TIpMimeParts.IndexOf(aEntity : TIpMimeEntity) : Integer;
 | |
| begin
 | |
|   Result := Entitys.IndexOf(aEntity);
 | |
| end;
 | |
| 
 | |
| 
 | |
| { TIpMimeEntity }
 | |
| constructor TIpMimeEntity.Create(ParentEntity : TIpMimeEntity);
 | |
| begin
 | |
|   inherited Create;
 | |
|   FBody := TIpAnsiTextStream.CreateEmpty;
 | |
|   FBody.Stream := TMemoryStream.Create;
 | |
|   FMimeParts := TIpMimeParts.Create;
 | |
|   FParent := ParentEntity;
 | |
|   if (FParent <> nil) then
 | |
|     FParentBoundary := FParent.Boundary;
 | |
| end;
 | |
| 
 | |
| destructor TIpMimeEntity.Destroy;
 | |
| begin
 | |
|   FMimeParts.Free;
 | |
|   FBody.FreeStream;
 | |
|   FBody.Free;
 | |
|   if (FParent <> nil) then
 | |
|     FParent.MimeParts.Remove(Self);
 | |
|   inherited Destroy;
 | |
| end;
 | |
| 
 | |
| { Clear Body property }
 | |
| procedure TIpMimeEntity.ClearBody;
 | |
| begin
 | |
|   FBody.FreeStream;
 | |
|   FBody.Stream := TMemoryStream.Create;
 | |
| end;
 | |
| 
 | |
| {Begin !!.12}
 | |
| { Clear Body property in preparation for large attachment }
 | |
| procedure TIpMimeEntity.ClearBodyLargeAttach(const AttachmentSize : Longint);
 | |
| var
 | |
|   FileName : string;
 | |
|   Strm : TIpMemMapStream;
 | |
| begin
 | |
|   FBody.FreeStream;
 | |
|   FileName := GetTemporaryFile(GetTemporaryPath);
 | |
|   if FileExists(FileName) then
 | |
|     DeleteFile(FileName);
 | |
|   Strm := TIpMemMapStream.Create(FileName, False, True);
 | |
|   Strm.Size := Trunc(AttachmentSize * 1.3695);
 | |
|   Strm.Open;
 | |
|   FBody.Stream := Strm;
 | |
| end;
 | |
| {End !!.12}
 | |
| 
 | |
| { Clear all properties }
 | |
| procedure TIpMimeEntity.Clear;
 | |
| begin
 | |
|   ClearBody;
 | |
|   FMimeParts.Clear;
 | |
|   FBoundary := '';
 | |
|   FCharacterSet := '';
 | |
|   FContentDescription := '';
 | |
|   FContentDispositionType := '';
 | |
|   FContentID := '';
 | |
|   FContentSubtype := '';
 | |
|   FContentType := '';
 | |
|   FContentTransferEncoding := emUnknown;
 | |
|   FFileName := '';
 | |
|   FIsMime := False;
 | |
|   FIsMultipart := False;
 | |
|   FMimeVersion := '';
 | |
|   FEntityName := '';
 | |
|   FRelatedType := '';                                                {!!.02}
 | |
|   FRelatedSubtype := '';                                             {!!.02}
 | |
|   FRelatedStart := '';                                               {!!.02}
 | |
|   FRelatedStartInfo := '';                                           {!!.02}
 | |
| end;
 | |
| 
 | |
| { Build Mime (and nested Mime) block(s) from incoming text stream }
 | |
| function TIpMimeEntity.DecodeEntity(InStream : TIpAnsiTextStream) : string;
 | |
| var
 | |
|   Blk : TIpMimeEntity;
 | |
|   RawHeaders : TStringList;
 | |
|   Decoded : Boolean;                                                   {!!.12}
 | |
|   i,                                                                   {!!.13}
 | |
|   LeadingBlankLines : Integer;                                         {!!.13}
 | |
| begin
 | |
|   Decoded := False;                                                    {!!.12}
 | |
|   LeadingBlankLines := 0;                                              {!!.13}
 | |
|   { skip blank lines in front of mime headers or body }
 | |
|   Result := InStream.ReadLine;
 | |
|   while (Result = '') and not InStream.AtEndOfStream do begin
 | |
|     inc(LeadingBlankLines);
 | |
|     Result := InStream.ReadLine;
 | |
|   end;
 | |
| 
 | |
|   { decode mime headers if any }
 | |
| {Begin !!.15}
 | |
|   if (StrLIComp(PChar(strContent), PChar(Result), Length(strContent)) = 0) or
 | |
|      (StrLIComp(PChar(strMimeVersion), PChar(Result),
 | |
|                 Length(strMimeVersion)) = 0) then begin
 | |
| {End !!.15}
 | |
|     RawHeaders := TStringList.Create;
 | |
|     try
 | |
|       repeat
 | |
|         RawHeaders.Add(Result);
 | |
|         Result := InStream.ReadLine;
 | |
|       until (Result = '') or (InStream.AtEndOfStream);
 | |
|       DecodeMimeHeaders(RawHeaders);
 | |
|     finally
 | |
|       RawHeaders.Free;
 | |
|     end;
 | |
|     Result := InStream.ReadLine;
 | |
|     { skip blank lines between mime headers and mime body }
 | |
|     while (Result = '') and not InStream.AtEndOfStream do
 | |
|       Result := InStream.ReadLine;
 | |
|   end;
 | |
| 
 | |
|   { decode body - main loop }
 | |
| {Begin !!.15}
 | |
|   if (FParentBoundary <> '') and
 | |
|      (Result = '--' + FParentBoundary) then
 | |
|     { The body of this entity is empty & we are now positioned at the boundary
 | |
|       marker for the next entity. }
 | |
|     Decoded := True
 | |
|   else
 | |
| {End !!.15}
 | |
|   while not (((FParentBoundary <> '') and                              {!!.12}
 | |
|               (Result = '--' + FParentBoundary)                        {!!.12}
 | |
|              ) or InStream.AtEndOfStream) do begin                     {!!.12}
 | |
|     Decoded := True;
 | |
|     { check for ending boundary - in which case were done }
 | |
|     if (FParentBoundary <> '') then
 | |
|       if Pos('--' + FParentBoundary + '--', Result) = 1 {> 0} then begin
 | |
|         Result := InStream.ReadLine;
 | |
|         Exit;
 | |
|       end;
 | |
| 
 | |
|     { decode any nested mime parts - recursively }
 | |
|     if IsMultiPart and (Boundary <> '') and                            {!!.03}
 | |
|       (Pos('--' + Boundary, Result) = 1)  then begin
 | |
|       Blk := TIpMimeEntity.Create(Self);
 | |
|       Result := Blk.DecodeEntity(Instream);
 | |
|       FMimeParts.Add(Blk);
 | |
|     end else begin
 | |
|       { read raw text line into body }
 | |
|       for i := 1 to LeadingBlankLines do                               {!!.13}
 | |
|         Body.WriteLine('');                                            {!!.13}
 | |
|       Body.WriteLine(Result);
 | |
|       Result := InStream.ReadLine;
 | |
|     end;
 | |
|     if InStream.AtEndOfStream then break;                              {!!.12}
 | |
|     LeadingBlankLines := 0;                                            {!!.13}
 | |
|   end;
 | |
| {Begin !!.12}
 | |
|   { If did not find a MIME entity then assume the body is text &
 | |
|     read it into the Body property. }
 | |
|   if not Decoded then
 | |
|     ReadBody(InStream, Result)
 | |
|   else if (not (Pos('--' + FParentBoundary, Result) = 1)) then
 | |
|     { If the last line is not a MIME separator then add the last line
 | |
|       to the Body. }
 | |
|     Body.WriteLine(Result);
 | |
| {End !!.12}
 | |
| end;
 | |
| 
 | |
| {!!.01}
 | |
| { Build Mime block as subpart from incoming text stream }
 | |
| function TIpMimeEntity.DecodeEntityAsAttachment(InStream : TIpAnsiTextStream) : string;
 | |
| var
 | |
|   Blk : TIpMimeEntity;
 | |
| begin
 | |
|   Blk := TIpMimeEntity.Create(Self);
 | |
|   Blk.ContentType := FContentType;
 | |
|   Blk.ContentSubtype := FContentSubtype;
 | |
|   Blk.ContentDispositionType := FContentDispositionType;
 | |
|   Blk.ContentDescription := FContentDescription;
 | |
|   Blk.ContentTransferEncoding := FContentTransferEncoding;
 | |
|   Blk.CharacterSet := FCharacterSet;
 | |
|   Blk.CreationDate := FCreationDate;
 | |
|   Blk.FileName := FFileName;
 | |
|   Blk.EntityName := FEntityName;
 | |
|   Blk.FIsMime := True;
 | |
|   Blk.FIsMultipart := False;
 | |
|   Blk.ModificationDate := FModificationDate;
 | |
|   Blk.MimeVersion := FMimeVersion;
 | |
|   Blk.OriginalSize := FOriginalSize;
 | |
|   Blk.ReadDate := FReadDate;
 | |
| 
 | |
|   Result := Blk.DecodeEntity(Instream);
 | |
|   FMimeParts.Add(Blk);
 | |
|   Body.Position := 0;
 | |
| end;
 | |
| 
 | |
| { Decode Content-Disposition header field and sub-fields }
 | |
| procedure TIpMimeEntity.DecodeContentDisposition(const aDisp : string);
 | |
| var
 | |
|   RawParams : TStringList;
 | |
|   S : string;
 | |
| begin
 | |
|   { split up parameters }
 | |
|   RawParams := TStringList.Create;
 | |
|   try
 | |
|     Parse(aDisp, ';', RawParams);
 | |
| 
 | |
|     { decode disposition type and parameters }
 | |
|     if (RawParams.Count > 0) then begin
 | |
|       FContentDispositionType := RawParams[0];
 | |
|       if (RawParams.Count > 1) then begin
 | |
|         DecodeSingleParameter(strFileName, RawParams, FFileName);
 | |
|         DecodeSingleParameter(strCreationDate, RawParams, FCreationDate);
 | |
|         DecodeSingleParameter(strModificationDate, RawParams, FModificationDate);
 | |
|         DecodeSingleParameter(strReadDate, RawParams, FReadDate);
 | |
|         DecodeSingleParameter(strSize, RawParams, S);
 | |
|         FOriginalSize := StrToIntDef(S, 0);
 | |
|       end;
 | |
|     end else
 | |
|       FContentDispositionType := '';
 | |
|   finally
 | |
|     RawParams.Free;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| { Decode Content-Type header field and sub-fields }
 | |
| procedure TIpMimeEntity.DecodeContentType(const aType : string);
 | |
| var
 | |
|   RawParams : TStringList;
 | |
|   S : string;
 | |
|   i : Integer;
 | |
| begin
 | |
|   { split up parameters }
 | |
|   RawParams := TStringList.Create;
 | |
|   try
 | |
|     Parse(aType, ';', RawParams);
 | |
| 
 | |
|     { decode type and subtype }
 | |
|     FContentType := '';
 | |
|     FContentSubType := '';
 | |
|     if (RawParams.Count > 0) then begin
 | |
|       S := RawParams[0];
 | |
|       i := IpUtils.CharPos('/', S);
 | |
|       if (i > 0) then begin
 | |
|         FContentType := Copy(S, 1, i - 1);
 | |
|         FContentSubType := Copy(S, i + 1, Length(S));
 | |
|       end else
 | |
|         FContentType := S;
 | |
|     end;
 | |
|     FIsMultipart := StrIComp(PChar(FContentType), PChar(strMultipart)) = 0;
 | |
| 
 | |
|     { decode the parameters }
 | |
|     DecodeSingleParameter(strName, RawParams, FEntityName);
 | |
|     DecodeSingleParameter(strBoundary, RawParams, FBoundary);
 | |
|     DecodeSingleParameter(strCharSet, RawParams, FCharacterSet);
 | |
| 
 | |
|     {!!.02}
 | |
|     { decode multipart/related parameters }
 | |
|     DecodeSingleParameter(strType, RawParams, S);
 | |
|     if (S <> '') then begin
 | |
|       i := IpUtils.CharPos('/', S);
 | |
|       if (i > 0) then begin
 | |
|         FRelatedType := Copy(S, 1, i - 1);
 | |
|         FRelatedSubType := Copy(S, i + 1, Length(S));
 | |
|       end else
 | |
|         FRelatedType := S;
 | |
|       DecodeSingleParameter(strStart, RawParams, FRelatedStart);
 | |
|       DecodeSingleParameter(strStartInfo, RawParams, FRelatedStartInfo);
 | |
|     end;
 | |
|     {!!.02}
 | |
| 
 | |
|   finally
 | |
|     RawParams.Free;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| { Decode Content-TranferEncoding header field }
 | |
| function TIpMimeEntity.DecodeContentTransferEncoding(const aEncoding : string) :
 | |
|   TIpMimeEncodingMethod;
 | |
| begin
 | |
|   if (UpperCase(aEncoding) = UpperCase(str7Bit)) then
 | |
|     Result := em7bit
 | |
|   else if (UpperCase(aEncoding) = UpperCase(str8Bit)) then
 | |
|     Result := em8bit
 | |
|   else if (UpperCase(aEncoding) = UpperCase(strBase64)) then
 | |
|     Result := emBase64
 | |
|   else if (UpperCase(aEncoding) = UpperCase(strBinary)) then
 | |
|     Result := emBinary
 | |
|   else if (UpperCase(aEncoding) = UpperCase(strBinHex)) then
 | |
|     Result := emBinHex
 | |
|   else if (UpperCase(aEncoding) = UpperCase(strQuoted)) then
 | |
|     Result := emQuoted
 | |
|   else if (UpperCase(aEncoding) = UpperCase(strUUEncode)) then
 | |
|     Result := emUUEncode
 | |
|   else
 | |
|     Result := emUnknown;
 | |
| end;
 | |
| 
 | |
| 
 | |
| { Decode Mime headers from raw header list }
 | |
| procedure TIpMimeEntity.DecodeMimeHeaders(RawHeaders : TStringList);
 | |
| var
 | |
|   S : string;
 | |
| begin
 | |
|   { decode content type header }
 | |
|   DecodeSingleHeader(strContentType, RawHeaders, S);
 | |
|   if (S <> '') then begin
 | |
|     FIsMime := True;
 | |
|     DecodeContentType(S);
 | |
|     if FIsMultipart and (FBoundary = '') then
 | |
|       raise EIpBaseException.Create(SNoBoundary);
 | |
|   end else begin
 | |
|     FIsMime := False;
 | |
|     Exit;
 | |
|   end;
 | |
| 
 | |
|   { decode the others }
 | |
|   DecodeSingleHeader(strMIMEVersion, RawHeaders, FMimeVersion);
 | |
|   DecodeSingleHeader(strContentTransferEncoding, RawHeaders, S);
 | |
|   FContentTransferEncoding := DecodeContentTransferEncoding(S);
 | |
|   DecodeSingleHeader(strContentDescription, RawHeaders, FContentDescription);
 | |
|   DecodeSingleHeader(strContentID, RawHeaders, FContentID);
 | |
|   DecodeSingleHeader(strContentDisposition, RawHeaders, S);
 | |
|   if (S <> '') then
 | |
|     DecodeContentDisposition(S);
 | |
|   if (FContentDispositionType = strAttachment) then                    {!!.12}
 | |
|     Inc (FParent.FAttachmentCount);                                    {!!.12}{!!.15}
 | |
| end;
 | |
| 
 | |
| { Compute attachment coding progress and fire OnCodingProgress event }
 | |
| procedure TIpMimeEntity.DoOnCodingProgress(Count, TotalSize : Longint;
 | |
|                                           var Abort : Boolean);
 | |
|   { IMPORTANT: The progress event must only be fired by the root parent }
 | |
| begin
 | |
|   if (Parent = nil) or (Parent = Self) then begin
 | |
|     FProgress := ((Count*100) div TotalSize);
 | |
|     if (FProgress > 100) then
 | |
|       FProgress := 100;
 | |
|     if (FProgress div 10) = 0 then
 | |
|       PrevProgress := 0;
 | |
| 
 | |
|     { report progress in 10% increments }
 | |
|     if ((FProgress div 10) > (PrevProgress div 10)) then begin
 | |
|       PrevProgress := FProgress;
 | |
|       if Assigned(FOnCodingProgress) then
 | |
|         FOnCodingProgress(Self, FProgress, Abort);
 | |
|     end;
 | |
|   end else
 | |
|     Parent.DoOnCodingProgress(Count, TotalSize, Abort);
 | |
| end;
 | |
| 
 | |
| { Generate Mime message stream from properties (and nested Mime blocks) }
 | |
| function TIpMimeEntity.EncodeEntity(OutStream : TIpAnsiTextStream) : string;
 | |
| var
 | |
|   i : Integer;
 | |
|   S : string;
 | |
|   RawHeaders : TStringList;
 | |
|   Ch : AnsiChar;
 | |
| begin
 | |
|   Result := FParentBoundary;
 | |
| 
 | |
|   { write out mime headers }
 | |
|   if (Result <> '') then begin
 | |
|     OutStream.WriteLine('--' + Result);
 | |
|     RawHeaders := TStringList.Create;
 | |
|     try
 | |
|       EncodeMimeHeaders(RawHeaders);
 | |
|       if (RawHeaders.Count > 0) then
 | |
|         for i := 0 to Pred(RawHeaders.Count) do
 | |
|           if (RawHeaders[i] <> '') then
 | |
|             OutStream.WriteLine(RawHeaders[i]);
 | |
|       OutStream.WriteLine('');
 | |
|     finally
 | |
|       RawHeaders.Free;
 | |
|     end;
 | |
|   end;
 | |
| 
 | |
|   // flush to update underlaying memory streams
 | |
|   Body.Flush;
 | |
|   { write out mime body }
 | |
|   if (Body.FastSize > 0) then
 | |
|   begin
 | |
|     // presize stream for more speed
 | |
|     OutStream.Stream.Size := OutStream.Stream.Size + Body.FastSize;
 | |
|     // use optimal method depending on the source stream to copy the stream
 | |
|     if Body.Stream is TIpMemMapStream then
 | |
|       OutStream.Write((Body.Stream as TIpMemMapStream).Memory^, Body.FastSize)
 | |
|     else   
 | |
|       if Body.Stream is TMemoryStream then
 | |
|         OutStream.Write((Body.Stream as TMemoryStream).Memory^, Body.Stream.Size)
 | |
|       else 
 | |
|         OutStream.CopyFrom(Body, 0); // copy the entire stream from the beginning
 | |
| 
 | |
|     { make sure the body is properly terminated }                    {!!.01}
 | |
|     OutStream.Position := OutStream.Size - 1;                        {!!.01}
 | |
|     TIpBufferedStream(OutStream).ReadChar(Ch);                       {!!.01}
 | |
|     if ((Ch <> #13) and (Ch <> #10)) then                            {!!.01}
 | |
|       OutStream.WriteLine('');                                       {!!.01}
 | |
|   end;
 | |
| 
 | |
|   { encode nested mime parts - recursively }
 | |
|   if (FMimeParts.Count > 0) then begin
 | |
|     for i := 0 to Pred(FMimeParts.Count) do
 | |
|       S := FMimeParts[i].EncodeEntity(OutStream);
 | |
|     OutStream.WriteLine('--' + S + '--');
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| {Begin !!.14}
 | |
| function TIpMimeEntity.ContainsSpecialChars(const Value : string) : Boolean;
 | |
| var
 | |
|   Inx : Integer;
 | |
| begin
 | |
|   Result := False;
 | |
|   for Inx := 1 to Length(Value) do
 | |
|     if (Ord(Value[Inx]) <= 32) or
 | |
|        (Value[Inx] in ['(', ')', '<', '>', '@',
 | |
|                        ',', ';', ':', '\', '"',
 | |
|                        '/', '[', ']', '?', '=']) then begin
 | |
|       Result := True;
 | |
|       Break;
 | |
|     end; { if }
 | |
| end;
 | |
| {End !!.14}
 | |
| 
 | |
| { Generate Content-Disposition header into raw header list }
 | |
| procedure TIpMimeEntity.EncodeContentDisposition(RawHeaders : TStringList);
 | |
| var
 | |
|   Params : TStringList;
 | |
| begin
 | |
|   if (FContentDispositionType = '') then
 | |
|     Exit;
 | |
| 
 | |
|   Params := TStringList.Create;
 | |
|   try
 | |
|     Params.Add(FContentDispositionType);
 | |
| {Begin !!.14}
 | |
|     if (FFileName <> '') then begin
 | |
|       { If the filename contains spaces, control characters, or any of the
 | |
|         special characters identified in RFC 1521 then wrap the filename in
 | |
|         quotes.
 | |
| 
 | |
|         Assumption: FFileName length is <= 78 characters. Future enhancement
 | |
|         is to support RFC 2184. }
 | |
|       if ContainsSpecialChars(FFileName) then
 | |
|         Params.Add(strFileName + '"' + FFileName + '"')
 | |
|       else
 | |
|         Params.Add(strFileName + FFileName);
 | |
|     end;  { if }
 | |
| {End !!.14}
 | |
|     if (FCreationDate <> '') then
 | |
|       Params.Add(strCreationDate + FCreationDate);
 | |
|     if (FModificationDate <> '') then
 | |
|       Params.Add(strModificationDate + FModificationDate);
 | |
|     if (FReadDate <> '') then
 | |
|       Params.Add(strReadDate + FReadDate);
 | |
|     if (FOriginalSize > 0) then
 | |
|       Params.Add(strSize + IntToStr(FOriginalSize));
 | |
|     EncodeListHeader(strContentDisposition, RawHeaders, Params, ';', False);
 | |
|   finally
 | |
|     Params.Free;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| { Generate Content-Type header into raw header list }
 | |
| procedure TIpMimeEntity.EncodeContentType(RawHeaders : TStringList);
 | |
| var
 | |
|   S : string;
 | |
|   Params : TStringList;
 | |
| begin
 | |
|   if (FContentType = '') then
 | |
|     Exit;
 | |
| 
 | |
|   Params := TStringList.Create;
 | |
|   try
 | |
|     S := FContentType;
 | |
|     if (FContentSubType <> '') then
 | |
|       S := S + '/' + FContentSubType;
 | |
|     Params.Add(S);
 | |
|     if IsMultipart then
 | |
|       Params.Add(strBoundary + '"' + FBoundary + '"');
 | |
|     if (FEntityName <> '') then
 | |
|       Params.Add(strName + '"' + FEntityName + '"');
 | |
|     if (FCharacterSet <> '') then
 | |
|       Params.Add(strCharSet + FCharacterSet); {no quotes}
 | |
| 
 | |
|     {!!.02}
 | |
|     { encode multipart/related parameters }
 | |
|     if (FRelatedType <> '') then begin
 | |
|       if (FRelatedSubtype <> '') then
 | |
|         Params.Add(strType + '"' + FRelatedType + '/' + FRelatedSubtype + '"')
 | |
|       else
 | |
|         Params.Add(strType + '"' + FRelatedType + '"');
 | |
|       if (FRelatedStart <> '') then
 | |
|         Params.Add(strStart + '"' + FRelatedStart + '"');
 | |
|       if (FRelatedStartInfo <> '') then
 | |
|         Params.Add(strStartInfo + '"' + FRelatedStartInfo + '"');
 | |
|     end;
 | |
|     {!!.02}
 | |
| 
 | |
|     EncodeListHeader(strContentType, RawHeaders, Params, ';', False);
 | |
|   finally
 | |
|     Params.Free;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| { Generate Content-TranferEncoding header into raw header list }
 | |
| procedure TIpMimeEntity.EncodeContentTransferEncoding(RawHeaders : TStringList);
 | |
| begin
 | |
|   case FContentTransferEncoding of
 | |
|     em7bit     : EncodeSingleHeader(strContentTransferEncoding, RawHeaders, str7Bit);
 | |
|     em8bit     : EncodeSingleHeader(strContentTransferEncoding, RawHeaders, str8Bit);
 | |
|     emBase64   : EncodeSingleHeader(strContentTransferEncoding, RawHeaders, strBase64);
 | |
|     emBinary   : EncodeSingleHeader(strContentTransferEncoding, RawHeaders, strBinary);
 | |
|     emBinHex   : EncodeSingleHeader(strContentTransferEncoding, RawHeaders, strBinHex);
 | |
|     emQuoted   : EncodeSingleHeader(strContentTransferEncoding, RawHeaders, strQuoted);
 | |
|     emUUEncode : EncodeSingleHeader(strContentTransferEncoding, RawHeaders, strUUEncode);
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| { Generate Mime headers into raw header list }
 | |
| procedure TIpMimeEntity.EncodeMimeHeaders(RawHeaders : TStringList);
 | |
| begin
 | |
|   if (FContentType <> '') then begin
 | |
|     EncodeSingleHeader(strMimeVersion, RawHeaders, FMimeVersion);
 | |
|     EncodeContentType(RawHeaders);
 | |
|     EncodeSingleHeader(strContentDescription, RawHeaders, FContentDescription);
 | |
|     EncodeSingleHeader(strContentID, RawHeaders, FContentID);
 | |
|     EncodeContentTransferEncoding(RawHeaders);
 | |
|     EncodeContentDisposition(RawHeaders);
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| { Encode Mime body from TStream - file name is optional }
 | |
| procedure TIpMimeEntity.EncodeBodyStream(InStream : TStream; const aFileName : string);
 | |
| {Begin !!.12}
 | |
| var
 | |
|   LargeAttachment : Boolean;
 | |
|     { Large attachments are handled with memory map streams in order to avoid
 | |
|       whacko memory issues with TMemoryStream. }
 | |
| begin
 | |
|   if (Instream.Size > 0) then begin
 | |
|     LargeAttachment := (InStream.Size > IpLgAttachSizeBoundry);
 | |
|     if LargeAttachment then
 | |
|       ClearBodyLargeAttach(InStream.Size)
 | |
|     else
 | |
|     begin
 | |
|       ClearBody;
 | |
|       // presize stream for more speed
 | |
|       FBody.Stream.Size := Trunc(InStream.Size * 1.3695);
 | |
|     end;
 | |
| {End !!.12}
 | |
|     case FContentTransferEncoding of
 | |
|       em7Bit     : Encode8Bit(InStream);
 | |
|       em8Bit     : Encode8Bit(InStream);
 | |
|       emBase64   : EncodeBase64(InStream);
 | |
|       emBinary   : Encode8Bit(InStream);
 | |
|       emBinHex   : EncodeBinHex(InStream, aFileName);
 | |
|       emQuoted   : EncodeQuoted(InStream);
 | |
|       emUUEncode : EncodeUUEncode(InStream, aFileName);
 | |
|       emUnknown  : Encode8Bit(InStream);
 | |
|     end;
 | |
|   {Begin !!.12}
 | |
|     FBody.Flush;
 | |
|     if LargeAttachment then
 | |
|       { This is a large attachment that was written to a memory map stream.
 | |
|         Memory map streams are usually created larger than necessary so shrink
 | |
|         it down to the correct size. }
 | |
|       TIpMemMapStream(FBody.Stream).Size := TIpMemMapStream(FBody.Stream).DataSize;
 | |
|   {End !!.12}
 | |
|   end;
 | |
|   FOriginalSize := InStream.Size;
 | |
|   FFileName := ExtractFileName(aFileName);
 | |
| end;
 | |
| 
 | |
| { Encode Mime body from TStrings - file name is optional }
 | |
| procedure TIpMimeEntity.EncodeBodyStrings(InStrings : TStrings; const aFileName : string);
 | |
| var
 | |
|   MS : TMemoryStream;
 | |
| begin
 | |
|   if (InStrings.Count > 0) then begin
 | |
|     MS := TMemoryStream.Create;
 | |
|     try
 | |
|       InStrings.SaveToStream(MS);
 | |
|       MS.Position := 0;                                              {!!.03}
 | |
|       FOriginalSize := MS.Size;
 | |
|       FFileName := ExtractFileName(aFileName);
 | |
|       EncodeBodyStream(MS, aFileName);
 | |
|     finally
 | |
|       MS.Free;
 | |
|     end;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| { Encode Mime body from file }
 | |
| procedure TIpMimeEntity.EncodeBodyFile(const InFile : string);
 | |
| var
 | |
|   FS : TIpMemMapStream;                                                {!!.12}
 | |
|   i : Integer;
 | |
|   aExt, aTyp, aSub : string;
 | |
|   aEnc : TIpMimeEncodingMethod;
 | |
| begin
 | |
|   { If content-type, has not been defined for this entity,    }
 | |
|   { default values for that file extension  will be used.     }
 | |
|   { These values are defined in the include file, IPDEFCT.INC }
 | |
|   aTyp := strApplication;
 | |
|   aSub := strOctetStream;
 | |
|   aEnc := emBase64;
 | |
|   aExt := ExtractFileExt(InFile);
 | |
|   for i := 0 to High(DefExtensions) do
 | |
|     if (aExt = DefExtensions[i]) then begin
 | |
|       aTyp := DefContent[i].Typ;
 | |
|       aSub := DefContent[i].Sub;
 | |
|       aEnc := DefContent[i].Enc;
 | |
|       Break;
 | |
|     end;
 | |
|   if (FContentType = '') then begin
 | |
|     FContentType := aTyp;
 | |
|     FContentSubtype := aSub;
 | |
|     FContentTransferEncoding := aEnc;
 | |
|   end;
 | |
|   if (FContentTransferEncoding = emUnknown) then
 | |
|     FContentTransferEncoding := aEnc;
 | |
| 
 | |
|   FS := TIpMemMapStream.Create(InFile, True, False);                   {!!.12}
 | |
|   try
 | |
|     FS.Open;                                                           {!!.12}
 | |
|     FOriginalSize := FS.Size;
 | |
|     FFileName := ExtractFileName(InFile);
 | |
|     EncodeBodyStream(FS, FFileName);
 | |
|   finally
 | |
|     FS.Free;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| { Decode encoded Mime block body to TStream }
 | |
| procedure TIpMimeEntity.ExtractBodyStream(OutStream : TStream);
 | |
| var
 | |
|   MS : TMemoryStream;
 | |
| begin
 | |
|   if (FBody.Size > 0) then begin
 | |
|    { We want to append the decoded data to the end of OutStream, }
 | |
|    { so a local memory stream is used since OutStream may be a   }
 | |
|    { TIpAnsiTextStream, in which case the decoding algorithms    }
 | |
|    { will overwrite its existing contents.                       }
 | |
|     MS := TMemoryStream.Create;
 | |
|     try
 | |
|       case FContentTransferEncoding of
 | |
|         em7Bit     : Decode8Bit(MS);
 | |
|         em8Bit     : Decode8Bit(MS);
 | |
|         emBase64   : DecodeBase64(MS);
 | |
|         emBinary   : OutStream.CopyFrom(FBody, FBody.Size);            {!!.14}
 | |
|         emBinHex   : DecodeBinHex(MS);
 | |
|         emQuoted   : DecodeQuoted(MS);
 | |
|         emUUEncode : DecodeUUEncode(MS);
 | |
|         emUnknown  : Decode8Bit(MS);
 | |
|       end;
 | |
|       OutStream.CopyFrom(MS, 0);
 | |
|     finally
 | |
|       MS.Free;
 | |
|     end;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| { Decode encoded Mime block body to TStrings }
 | |
| procedure TIpMimeEntity.ExtractBodyStrings(OutStrings : TStrings);
 | |
| var
 | |
|   MS : TMemoryStream;
 | |
| begin
 | |
|   if (FBody.Size > 0) then begin
 | |
|     MS := TMemoryStream.Create;
 | |
|     try
 | |
|       ExtractBodyStream(MS);
 | |
|       MS.Position := 0;
 | |
|       OutStrings.LoadFromStream(MS);
 | |
|     finally
 | |
|       MS.Free;
 | |
|     end;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| { Decode encoded Mime block body to file }
 | |
| procedure TIpMimeEntity.ExtractBodyFile(const OutFile : string);
 | |
| var
 | |
|   FS : TFileStream;
 | |
| begin
 | |
|   if (FBody.Size > 0) then begin
 | |
|     FS := TFileStream.Create(OutFile, fmCreate);
 | |
|     try
 | |
|       ExtractBodyStream(FS);
 | |
|     finally
 | |
|       FS.Free;
 | |
|     end;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| { Access/create specified MIME part }
 | |
| function TIpMimeEntity.GetMimePart(const aType, aSubType, aContentID : string;
 | |
|                                        CanCreate : Boolean) : TIpMimeEntity;
 | |
| var
 | |
|   i : Integer;
 | |
| begin
 | |
|   Result := nil;
 | |
|   if (MimeParts.Count > 0) then
 | |
|     for i := 0 to Pred(MimeParts.Count) do
 | |
|       { ContentID is primary search key }
 | |
|       if (aContentID <> '') then begin
 | |
|         if (MimeParts[i].ContentID = aContentID) then begin
 | |
|           Result := MimeParts[i];
 | |
|           Break;
 | |
|         end;
 | |
|       end else begin
 | |
|         if (MimeParts[i].ContentType = aType) and
 | |
|            (MimeParts[i].ContentSubtype = aSubType) then begin
 | |
|           Result := MimeParts[i];
 | |
|           Break;
 | |
|         end;
 | |
|       end;
 | |
| 
 | |
|   if Assigned(Result) then
 | |
|     Result.Body.Position := 0
 | |
|   else if CanCreate then begin
 | |
|     Result := NewMimePart;
 | |
|     Result.ContentType := aType;
 | |
|     Result.ContentSubtype := aSubtype;
 | |
|     Result.ContentID := aContentID;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| {!!.02}
 | |
| { Search all nested levels for specified MIME part }
 | |
| function TIpMimeEntity.FindNestedMimePart(const aType, aSubType, aContentID : string) : TIpMimeEntity;
 | |
| var
 | |
|   i : Integer;
 | |
|   Blk : TIpMimeEntity;
 | |
| begin
 | |
|   Result := nil;
 | |
|   if (MimeParts.Count > 0) then
 | |
|     for i := 0 to Pred(MimeParts.Count) do begin
 | |
|       { ContentID is primary search key }
 | |
|       if (aContentID <> '') and                                          {!!.12}
 | |
|          (IsSameString (MimeParts[i].ContentID,                          {!!.12}
 | |
|                         aContentID, False)) then begin                   {!!.12}
 | |
|         Result := MimeParts[i];
 | |
|         Break;
 | |
|       end else if (IsSameString (MimeParts[i].ContentType,               {!!.12}
 | |
|                                  aType, False)) and                      {!!.12}
 | |
|                   (IsSameString (MimeParts[i].ContentSubtype,            {!!.12}
 | |
|                                  aSubType, False)) then begin            {!!.12}
 | |
|         Result := MimeParts[i];
 | |
|         Break;
 | |
|       end else begin
 | |
|         Blk := MimeParts[i];
 | |
|         Result := Blk.FindNestedMimePart(aType, aSubType, aContentID);
 | |
|         if Assigned(Result) then
 | |
|           Break;
 | |
|       end;
 | |
|     end;
 | |
|   if Assigned(Result) then
 | |
|     Result.Body.Position := 0;
 | |
| end;
 | |
| 
 | |
| { Create nested Mime block and add to list }
 | |
| function TIpMimeEntity.NewMimePart : TIpMimeEntity;
 | |
| begin
 | |
|   {parent Entity is now multipart}
 | |
|   FIsMime := True;
 | |
|   FIsMultipart := True;
 | |
|   FContentType := strMultipart;
 | |
|   if (FBoundary = '') then
 | |
|     FBoundary := GenerateBoundary;
 | |
| 
 | |
|   Result := TIpMimeEntity.Create(Self);
 | |
|   FMimeParts.Add(Result);
 | |
| end;
 | |
| 
 | |
| { Copy Instream to OutStream as is - no decoding }
 | |
| procedure TIpMimeEntity.Decode8Bit(OutStream : TStream);
 | |
| var
 | |
|   FS : TIpAnsiTextStream;
 | |
|   Abort : Boolean;
 | |
| begin
 | |
|   Abort := False;
 | |
|   FS := TIpAnsiTextStream.Create(OutStream);
 | |
|   try
 | |
|     FBody.Position := 0;
 | |
|     while (FBody.Position < FBody.Size) and not Abort do begin
 | |
|       FS.WriteLine(FBody.ReadLine);
 | |
|       DoOnCodingProgress(OutStream.Position, FBody.Size, Abort);
 | |
|     end;
 | |
|   finally
 | |
|     FS.Free;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| { Decode InStream to OutStream - Base64 }
 | |
| procedure TIpMimeEntity.DecodeBase64(OutStream : TStream);
 | |
|   { rewritten }                                                      {!!.12}
 | |
| var
 | |
|   I : Integer;                                                         {!!.16}
 | |
|   C : Char;
 | |
|   InBuf  : array[0..3] of Char;
 | |
|   OutBuf : array[0..2] of Byte;
 | |
|   Done  : Boolean;
 | |
|   Abort : Boolean;
 | |
|   BufStream : TIpBufferedStream;
 | |
| begin
 | |
|   BufStream := (FBody as TIpBufferedStream);
 | |
|   BufStream.Position := 0;
 | |
|   Done := False;
 | |
|   Abort := False;
 | |
| 
 | |
|   while not (Done or Abort) do begin
 | |
|     { read in the next 4 valid Base64 characters }
 | |
|     I := 0;
 | |
|     InBuf := '====';                                                   {!!.15}
 | |
|     while (I < 4) do begin
 | |
|       if not BufStream.ReadChar(C) then begin
 | |
|         Done := True;
 | |
|         Break;
 | |
|       end;
 | |
| 
 | |
|       { skip bad characters }
 | |
|       if (Low(IpD64Table) <= C) and (C <= High(IpD64Table)) then
 | |
|         if (IpD64Table[C] <> $7F) then begin
 | |
|           InBuf[I] := C;
 | |
|           Inc(I);
 | |
|         end;
 | |
|     end;
 | |
| 
 | |
|     { Decode 4 characters to 3 bytes }
 | |
|     I := 0;
 | |
|     OutBuf[0] := ((IpD64Table[InBuf[0]] shl 2) or (IpD64Table[InBuf[1]] shr 4));
 | |
|     Inc(I);
 | |
|     if InBuf[2] <> '=' then begin
 | |
|       OutBuf[1] := ((IpD64Table[InBuf[1]] shl 4) or (IpD64Table[InBuf[2]] shr 2));
 | |
|       Inc(I);
 | |
|       if InBuf[3] <> '=' then begin
 | |
|         OutBuf[2] := ((IpD64Table[InBuf[2]] shl 6) or IpD64Table[InBuf[3]]);
 | |
|         Inc(I);
 | |
|       end else
 | |
|         Done := True;
 | |
|     end else
 | |
|         Done := True;
 | |
|     OutStream.Write(OutBuf, I);
 | |
|     DoOnCodingProgress(OutStream.Position, BufStream.FastSize, Abort); {!!.16}
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| { Decode InStream to OutStream - BinHex }
 | |
| procedure TIpMimeEntity.DecodeBinHex(OutStream : TStream);
 | |
| var
 | |
|   InBuf : array[1..4] of Byte;
 | |
|   OutBuf : array[1..3] of Byte;
 | |
|   i : Byte;
 | |
|   btThis, btLast, btNext : Byte;
 | |
|   ch : AnsiChar;
 | |
|   // headerlength is encoded as byte, HeaderFileName can only 256 bytes long
 | |
|   HeaderFileName : Array [0..MaxByte] of Byte;                   {!!.12}{!!.16}
 | |
|   HeaderLength : byte;                                                  {!!.12}
 | |
|   CRC : Word;
 | |
|   DataOffset, DataEnd, HeaderEnd : Longint;
 | |
|   WS1, WS2 : TMemoryStream;
 | |
|   Header : BinHexHeader;
 | |
|   Abort : Boolean;
 | |
|   BufStream : TIpBufferedStream;
 | |
| 
 | |
|   function NextChar : AnsiChar;
 | |
|     {- skip past any CRLF's and return the next message stream char }
 | |
|   var
 | |
|     c : AnsiChar;
 | |
|   begin
 | |
|     c := #0;
 | |
|     repeat
 | |
|       BufStream.ReadChar(c);
 | |
|     until ((c <> #13) and (c <> #10)) or (BufStream.Position = BufStream.Size);
 | |
|     Result := c;
 | |
|   end;
 | |
| 
 | |
|   function ValidChar(ch : AnsiChar) : Boolean;
 | |
|     {- test if ch is a valid BinHex encoded char }
 | |
|   var
 | |
|     b : Byte;
 | |
|   begin
 | |
|     Result := False;
 | |
|     b := Ord(ch);
 | |
|     if (b > 32) and (b < 115) then
 | |
|       if IpHexBinTable[b] <> $0FF then
 | |
|         Result := True;
 | |
|   end;
 | |
| 
 | |
| begin
 | |
|   Abort := False;
 | |
|   FBody.Position := 0;
 | |
|   if Pos('(This file must be converted with BinHex', FBody.ReadLine) = 0 then
 | |
|     raise EIpBaseException.Create(SBinHexBadFormat);
 | |
|   if (NextChar <> ':') then
 | |
|     raise EIpBaseException.Create(SBinHexColonExpected);
 | |
| 
 | |
|   { decode attachment into working stream }
 | |
|   BufStream := (FBody as TIpBufferedStream);
 | |
|   WS1 := TMemoryStream.Create;
 | |
|   try
 | |
|     i := 0;
 | |
|     ch := NextChar;
 | |
|     while (ch <> ':') and (BufStream.Position < BufStream.Size) and not Abort do begin
 | |
|       if not ValidChar(ch) then
 | |
|         raise EIpBaseException.Create(SBinHexBadChar);
 | |
|       Inc(i);
 | |
|       InBuf[i] := IpHexBinTable[Ord(ch)];
 | |
|       { decode 4 characters into 3 bytes }
 | |
|       if (i = 4) then begin
 | |
|         i := 0;
 | |
|         { 1st :    upper 6          lower 2 }
 | |
|         OutBuf[1] := (InBuf[1] shl 2) or ((InBuf[2] shr 4) and $03);
 | |
|         { 2nd :    upper 4          lower 4 }
 | |
|         OutBuf[2] := (InBuf[2] shl 4) or ((InBuf[3] shr 2) and $0F);
 | |
|         { 3rd :    upper 2          lower 6 }
 | |
|         OutBuf[3] := (InBuf[3] shl 6) or (InBuf[4] and $03F);
 | |
|         WS1.Write(OutBuf, SizeOf(OutBuf));
 | |
|       end;
 | |
|       ch := NextChar;
 | |
|     end;
 | |
| 
 | |
|     { handle odd characters }
 | |
|     if (i > 0) then begin
 | |
|       if (i = 1) then
 | |
|         raise EIpBaseException.Create(SBinHexOddChar);
 | |
|       OutBuf[1] := (InBuf[1] shl 2) or ((InBuf[2] shr 4) and $03);
 | |
|       if (i = 2) then
 | |
|         WS1.Write(OutBuf, 1)
 | |
|       else begin
 | |
|         OutBuf[2] := (InBuf[2] shl 4) or ((InBuf[3] shr 2) and $0F);
 | |
|         WS1.Write(OutBuf, 2);
 | |
|       end;
 | |
|       DoOnCodingProgress(BufStream.Position, BufStream.Size, Abort);
 | |
|     end;
 | |
|     if Abort then
 | |
|       Exit;
 | |
| 
 | |
|     { should be the end of file marker }
 | |
|     if (ch <> ':') then
 | |
|       raise EIpBaseException.Create(SBinHexColonExpected);
 | |
| 
 | |
|     { expand RLE sequences }
 | |
|     WS2 := TMemoryStream.Create;
 | |
|     try
 | |
|       WS1.Position := 0;
 | |
|       btThis := 0;
 | |
|       while (WS1.Position < WS1.Size) and not Abort do begin
 | |
|         btLast := btThis;
 | |
|         WS1.Read(btThis, 1);
 | |
|         if (btThis <> RLEChar) then
 | |
|           WS2.Write(btThis, 1)
 | |
|         else begin
 | |
|           WS1.Read(btNext, 1);
 | |
|           if (btNext = 0) then
 | |
|             WS2.Write(btThis, 1)
 | |
|           else begin
 | |
|             btThis := btLast;
 | |
|             for i := 1 to (btNext - 1) do
 | |
|               WS2.Write(btThis, 1);
 | |
|           end;
 | |
|         end;
 | |
|         DoOnCodingProgress(WS1.Position, WS1.Size, Abort);
 | |
|       end;
 | |
|       if Abort then
 | |
|         WS2.Free;
 | |
| 
 | |
|       { strip off header }
 | |
|       FillChar (HeaderFileName, SizeOf (HeaderFileName), $00);           {!!.12}
 | |
|       FillChar(Header, SizeOf(Header), #0);
 | |
|       WS2.Position := 0;
 | |
|       WS2.Read(HeaderLength, SizeOf (Byte));                             {!!.12}
 | |
|       WS2.Read(HeaderFileName, HeaderLength);                            {!!.12}
 | |
|       WS2.Read(Header, SizeOf(Header));
 | |
| 
 | |
|       { check header CRC }
 | |
|       HeaderEnd := WS2.Position;
 | |
|       WS2.Read(CRC, 2);
 | |
|       DataOffset := WS2.Position;
 | |
|       if (CRC <> BinHexCRC(WS2, 0, HeaderEnd)) then
 | |
|         raise EIpBaseException.Create(SBinHexBadHeaderCRC);
 | |
|       DataEnd := DataOffset + htonl(Header.DFLong);
 | |
|       if (DataEnd > WS2.Size) then
 | |
|         raise EIpBaseException.Create(SBinHexLengthErr);
 | |
|       if (htonl(Header.RFLong) > 0) then
 | |
|         raise EIpBaseException.Create(SBinHexResourceForkErr);
 | |
| 
 | |
|       { check data fork CRC - follows data fork }
 | |
|       WS2.Position := DataEnd;
 | |
|       WS2.Read(CRC, 2);
 | |
|       if (CRC <> BinHexCRC(WS2, DataOffset, DataEnd)) then
 | |
|         raise EIpBaseException.Create(SBinHexBadDataCRC);
 | |
| 
 | |
|       { copy data fork to OutStream }
 | |
|       WS2.Position := DataOffset;
 | |
|       OutStream.CopyFrom(WS2, DataEnd - DataOffset);
 | |
|     finally
 | |
|       WS2.Free;
 | |
|     end;
 | |
|   finally
 | |
|     WS1.Free;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| { Decode InStream to OutStream - QuotedPrintable }
 | |
| procedure TIpMimeEntity.DecodeQuoted(OutStream : TStream);
 | |
| var
 | |
|   O, Count, WS : Byte;                                                   {!!.12}
 | |
|   I : integer;                                                           {!!.12}
 | |
|   InBuf  : array[0..pred (MaxLine)] of Byte;                             {!!.15}
 | |
|   OutBuf : array[0..pred (MaxLine)] of Byte;                             {!!.15}
 | |
|   Decoding : Boolean;
 | |
|   Keeper : Boolean;
 | |
|   Abort : Boolean;
 | |
|   BufStream : TIpBufferedStream;
 | |
| begin
 | |
|   Abort := False;
 | |
|   FBody.Position := 0;
 | |
|   BufStream := FBody as TIpBufferedStream;
 | |
|   FillChar(InBuf, SizeOf(InBuf), #0);
 | |
|   WS := $FF;
 | |
|   Decoding := True;
 | |
|   Keeper := False;
 | |
| 
 | |
|   { Skip any CR/LF's to get to the encoded stuff }
 | |
|   while True do begin
 | |
|     if not BufStream.ReadChar(Char(InBuf[0])) then
 | |
|       Exit;
 | |
|     if ((InBuf[0] <> $0D) and (InBuf[0] <> $0A)) then begin
 | |
|       Keeper := True;
 | |
|       Break;
 | |
|     end;
 | |
|   end;
 | |
| 
 | |
|   while Decoding and not Abort do begin
 | |
|     { Initialize }
 | |
|     if Keeper then begin
 | |
|       I := 1;
 | |
|       Keeper := False;
 | |
|     end else begin
 | |
|       I := 0;
 | |
|     end;
 | |
|     O := 0;
 | |
| 
 | |
|     { Read in one line at a time - skipping over bad characters }
 | |
|     while True do begin
 | |
|       if (I > High(InBuf)) then                                        {!!.01}
 | |
|         raise EIpBaseException.Create(SLineLengthErr);                 {!!.01}
 | |
|       if not BufStream.ReadChar(Char(InBuf[I])) then
 | |
|         Break;
 | |
|       case InBuf[I] of
 | |
|         $0A : Continue;
 | |
|         $0D : begin
 | |
|                 Inc(I);
 | |
|                 Break;
 | |
|               end;
 | |
|        { Test for potential end of data }
 | |
|        { '--' is probably the next Mime boundary }
 | |
|        { $2D : if (I = 1) and (InBuf[0] = $2D) then Exit;}             {!!.03}
 | |
|       end;
 | |
|       Inc(I);
 | |
|     end;
 | |
| 
 | |
|     if I = 0 then Exit;
 | |
|     Count := I;
 | |
|     I := 0;
 | |
| 
 | |
|     { Decode data to output stream }
 | |
|     while I < Count do begin
 | |
|       case InBuf[I] of
 | |
|         9       : begin
 | |
|                     if WS = $FF then
 | |
|                       WS := O;
 | |
|                     OutBuf[O] := InBuf[I];
 | |
|                     Inc(O);
 | |
|                     Inc(I);
 | |
|                   end;
 | |
|         13      : if WS = $FF then begin
 | |
|                     OutBuf[O] := 13;
 | |
|                     OutBuf[O+1] := 10;
 | |
|                     Inc(O, 2);
 | |
|                     Inc(I);
 | |
|                   end else begin
 | |
|                     OutBuf[WS] := 13;
 | |
|                     OutBuf[WS+1] := 10;
 | |
|                     O := WS+2;
 | |
|                     Inc(I);
 | |
|                   end;
 | |
|         32      : begin
 | |
|                     if WS = $FF then
 | |
|                       WS := O;
 | |
|                     OutBuf[O] := InBuf[I];
 | |
|                     Inc(O);
 | |
|                     Inc(I);
 | |
|                   end;
 | |
|         33..60  : begin
 | |
|                     WS := $FF;
 | |
|                     OutBuf[O] := InBuf[I];
 | |
|                     Inc(O);
 | |
|                     Inc(I);
 | |
|                   end;
 | |
|         61      : begin
 | |
|                     WS := $FF;
 | |
|                     if I+2 >= Count then Break;
 | |
|                     case InBuf[I+1] of
 | |
|                       48 : OutBuf[O] := 0;    {0}
 | |
|                       49 : OutBuf[O] := 16;   {1}
 | |
|                       50 : OutBuf[O] := 32;   {2}
 | |
|                       51 : OutBuf[O] := 48;   {3}
 | |
|                       52 : OutBuf[O] := 64;   {4}
 | |
|                       53 : OutBuf[O] := 80;   {5}
 | |
|                       54 : OutBuf[O] := 96;   {6}
 | |
|                       55 : OutBuf[O] := 112;  {7}
 | |
|                       56 : OutBuf[O] := 128;  {8}
 | |
|                       57 : OutBuf[O] := 144;  {9}
 | |
|                       65 : OutBuf[O] := 160;  {A}
 | |
|                       66 : OutBuf[O] := 176;  {B}
 | |
|                       67 : OutBuf[O] := 192;  {C}
 | |
|                       68 : OutBuf[O] := 208;  {D}
 | |
|                       69 : OutBuf[O] := 224;  {E}
 | |
|                       70 : OutBuf[O] := 240;  {F}
 | |
|                       97 : OutBuf[O] := 160;  {a}
 | |
|                       98 : OutBuf[O] := 176;  {b}
 | |
|                       99 : OutBuf[O] := 192;  {c}
 | |
|                      100 : OutBuf[O] := 208;  {d}
 | |
|                      101 : OutBuf[O] := 224;  {e}
 | |
|                      102 : OutBuf[O] := 240;  {f}
 | |
|                     end;
 | |
|                     case InBuf[I+2] of
 | |
|                       48 : ;                             {0}
 | |
|                       49 : OutBuf[O] := OutBuf[O] + 1;   {1}
 | |
|                       50 : OutBuf[O] := OutBuf[O] + 2;   {2}
 | |
|                       51 : OutBuf[O] := OutBuf[O] + 3;   {3}
 | |
|                       52 : OutBuf[O] := OutBuf[O] + 4;   {4}
 | |
|                       53 : OutBuf[O] := OutBuf[O] + 5;   {5}
 | |
|                       54 : OutBuf[O] := OutBuf[O] + 6;   {6}
 | |
|                       55 : OutBuf[O] := OutBuf[O] + 7;   {7}
 | |
|                       56 : OutBuf[O] := OutBuf[O] + 8;   {8}
 | |
|                       57 : OutBuf[O] := OutBuf[O] + 9;   {9}
 | |
|                       65 : OutBuf[O] := OutBuf[O] + 10;  {A}
 | |
|                       66 : OutBuf[O] := OutBuf[O] + 11;  {B}
 | |
|                       67 : OutBuf[O] := OutBuf[O] + 12;  {C}
 | |
|                       68 : OutBuf[O] := OutBuf[O] + 13;  {D}
 | |
|                       69 : OutBuf[O] := OutBuf[O] + 14;  {E}
 | |
|                       70 : OutBuf[O] := OutBuf[O] + 15;  {F}
 | |
|                       97 : OutBuf[O] := OutBuf[O] + 10;  {a}
 | |
|                       98 : OutBuf[O] := OutBuf[O] + 11;  {b}
 | |
|                       99 : OutBuf[O] := OutBuf[O] + 12;  {c}
 | |
|                      100 : OutBuf[O] := OutBuf[O] + 13;  {d}
 | |
|                      101 : OutBuf[O] := OutBuf[O] + 14;  {e}
 | |
|                      102 : OutBuf[O] := OutBuf[O] + 15;  {f}
 | |
|                     end;
 | |
|                     Inc(I, 3);
 | |
|                     Inc(O);
 | |
|                   end;
 | |
|         62..126 : begin
 | |
|                     WS := $FF;
 | |
|                     OutBuf[O] := InBuf[I];
 | |
|                     Inc(O);
 | |
|                     Inc(I);
 | |
|                   end;
 | |
|         else
 | |
|           Inc(I);
 | |
|       end;
 | |
|     end;
 | |
| 
 | |
|     if O>0 then
 | |
|       OutStream.Write(OutBuf, O)
 | |
|     else
 | |
|       Break;   { OutBuf is empty }
 | |
|     DoOnCodingProgress(OutStream.Position, FBody.Size, Abort);
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| { Decode InStream to OutStream - UUEncode }
 | |
| procedure TIpMimeEntity.DecodeUUEncode(OutStream : TStream);
 | |
| var
 | |
|   I, O, Len, Count : Byte;
 | |
|   InBuf  : array[0..85] of Byte;
 | |
|   OutBuf : array[0..65] of Byte;
 | |
|   FirstLine : Boolean;
 | |
|   Abort : Boolean;
 | |
|   BufStream : TIpBufferedStream;
 | |
| begin
 | |
|   Abort := False;
 | |
|   FBody.Position := 0;
 | |
|   BufStream := FBody as TIpBufferedStream;
 | |
|   FirstLine := True;
 | |
|   while True and not Abort do begin
 | |
|     { Initialize }
 | |
|     I := 0;
 | |
|     O := 0;
 | |
| 
 | |
|     { Skip any CR/LF's to get to the encoded stuff }
 | |
|     while True do begin
 | |
|       if not BufStream.ReadChar(Char(InBuf[0])) then
 | |
|         Exit;
 | |
|       if FirstLine then begin
 | |
|         if ((InBuf[0] <> $0D) and (InBuf[0] <> $0A)) then begin
 | |
|           FirstLine := False;
 | |
|           Break;
 | |
|         end;
 | |
|      end else begin
 | |
|         if ((InBuf[0] = $0D) or (InBuf[0] = $0A)) then FirstLine := True;
 | |
|       end;
 | |
|     end;
 | |
| 
 | |
|     { We're done }
 | |
|     if AnsiChar(InBuf[0]) = '`' then Exit;
 | |
| 
 | |
|     { Get count for this line }
 | |
|     Len := (((InBuf[0] - $20) and $3F) * 4) div 3;
 | |
|     if (((InBuf[0] - $20) and $3F) * 4) mod 3 <> 0 then
 | |
|       Inc(Len);
 | |
| 
 | |
|     Count := FBody.Read(InBuf, Len);
 | |
| 
 | |
|     { Unexpected situation }
 | |
|     if (Count <> Len) or (Count > 63) then
 | |
|       raise EIpBaseException.Create(SUUEncodeCountErr);
 | |
| 
 | |
|     { Decode buffer }
 | |
|     while (I < Count) do begin
 | |
|       if ((Count - I) >= 4) then begin
 | |
|         OutBuf[O] := (((InBuf[I] - $20) and $3F) shl 2) or
 | |
|           (((InBuf[I+1] - $20) and $3F) shr 4);
 | |
|         OutBuf[O+1] := (((InBuf[I+1] - $20) and $3F) shl 4) or
 | |
|           (((InBuf[I+2] - $20) and $3F) shr 2);
 | |
|         OutBuf[O+2] := (((InBuf[I+2] - $20) and $3F) shl 6) or
 | |
|           (((InBuf[I+3] - $20) and $3F));
 | |
|         Inc(O, 3);
 | |
|       end else begin
 | |
|         if (Count >= 2) then begin
 | |
|           OutBuf[O] := (((InBuf[I] - $20) and $3F) shl 2) or
 | |
|             (((InBuf[I+1] - $20) and $3F) shr 4);
 | |
|           Inc(O);
 | |
|         end;
 | |
|         if (Count >= 3) then begin
 | |
|           OutBuf[O+1] := (((InBuf[I+1] - $20) and $3F) shl 4) or
 | |
|             (((InBuf[I+2] - $20) and $3F) shr 2);
 | |
|           Inc(O);
 | |
|         end;
 | |
|       end;
 | |
|       Inc(I, 4);
 | |
|     end;
 | |
|     OutStream.Write(OutBuf, O);
 | |
|     DoOnCodingProgress(OutStream.Position, FBody.Size, Abort);
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| { Encode InStream to OutStream - as is, no encoding }
 | |
| procedure TIpMimeEntity.Encode8Bit(InStream : TStream);
 | |
| var
 | |
|   FS : TIpAnsiTextStream;
 | |
|   Abort : Boolean;
 | |
| begin
 | |
|   Abort := False;
 | |
|   FS := TIpAnsiTextStream.Create(InStream);
 | |
|   try
 | |
|     while not (FS.AtEndOfStream or Abort) do begin
 | |
|       FBody.WriteLine(FS.ReadLine);
 | |
|       DoOnCodingProgress(FS.Position, FS.Size, Abort);
 | |
|     end;
 | |
|   finally
 | |
|     FS.Free;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| { Encode InStream to OutStream - Base64 }
 | |
| procedure TIpMimeEntity.EncodeBase64(InStream : TStream);
 | |
| begin
 | |
|   OctetStreamToHextetStream(InStream, FBody, Ip64Table, '=', #0);
 | |
| end;
 | |
| 
 | |
| { Encode InStream to OutStream - BinHex }
 | |
| procedure TIpMimeEntity.EncodeBinHex(InStream : TStream;
 | |
|                                      const aFileName : string);
 | |
| var
 | |
|   HeaderFileName : string;                                              {!!.12}
 | |
|   CRC : Word;
 | |
|   DataOffset : DWord;
 | |
|   PrevByte, CurrByte, i : Byte;
 | |
|   Header : BinHexHeader;
 | |
|   WS1, WS2 : TMemoryStream;
 | |
|   Abort : Boolean;
 | |
| 
 | |
| begin
 | |
|   Abort := False;
 | |
|   WS1 := TMemoryStream.Create;
 | |
|   try
 | |
|     { start with file name }
 | |
|     if (Length(aFileName) < MaxLine) then
 | |
|       HeaderFileName := UpperCase(ExtractFileName(aFileName))
 | |
|     else
 | |
|       HeaderFileName := Copy(UpperCase(ExtractFileName(aFileName)), 1, MaxLine);
 | |
|     WS1.Write(HeaderFileName, Length(HeaderFileName) + 1);
 | |
| 
 | |
|     { build rest of file header and header CRC and add to working stream }
 | |
|     FillChar(Header, SizeOf(Header), #0);
 | |
|     Move(BinHexFileType, Header.FileType, SizeOf(Header.FileType));
 | |
|     Move(BinHexFileType, Header.Creator, SizeOf(Header.Creator));
 | |
|     Header.DFLong := htonl(InStream.Size);
 | |
|     Header.RFLong := 0;
 | |
|     WS1.Write(Header, SizeOf(Header));
 | |
|     CRC := BinHexCRC(WS1, 0, WS1.Size);
 | |
|     WS1.Write(CRC, 2);
 | |
| 
 | |
|     { append data fork and data CRC to working stream }
 | |
|     DataOffset := WS1.Position;
 | |
|     InStream.Position := 0;
 | |
|     WS1.CopyFrom(InStream, InStream.Size);
 | |
|     CRC := BinHexCRC(WS1, DataOffset, WS1.Size);
 | |
|     WS1.Write(CRC, 2);
 | |
| 
 | |
|     { tack on resource fork CRC - not used but still required }
 | |
|     CRC := 0;
 | |
|     WS1.Write(CRC, 2);
 | |
| 
 | |
|     { go back and compress RLE sequences }
 | |
|     WS2 := TMemoryStream.Create;
 | |
|     try
 | |
|       WS1.Position := 0;
 | |
|       CurrByte := 0;
 | |
|       while (WS1.Position < WS1.Size) and not Abort do begin
 | |
|         PrevByte := CurrByte;
 | |
|         WS1.Read(CurrByte, 1);
 | |
|         if (CurrByte <> PrevByte) then
 | |
|           WS2.Write(CurrByte, 1)
 | |
|         else begin
 | |
|           i := 1;
 | |
|           repeat
 | |
|             i := i + WS1.Read(CurrByte, 1);
 | |
|           until (CurrByte <> PrevByte) or (i = 255) or
 | |
|                 (WS1.Position = WS1.Size);
 | |
|           if (i > 2) then begin
 | |
|             WS2.Write(RLEChar, 1);
 | |
|             WS2.Write(i, 1);
 | |
|             WS2.Write(CurrByte, 1);
 | |
|           end else begin
 | |
|             WS2.Write(PrevByte, 1);
 | |
|             WS2.Write(CurrByte, 1);
 | |
|           end;
 | |
|         end;
 | |
|         DoOnCodingProgress(WS1.Position, WS1.Size, Abort);
 | |
|       end;
 | |
|       if Abort then
 | |
|         Exit;
 | |
| 
 | |
|       { write out preamble }
 | |
|       FBody.WriteLine('(This file must be converted with BinHex 4.0)');
 | |
| 
 | |
|       { Encode compressed stream and stream it out }
 | |
|       WS2.Position := 0;
 | |
|       OctetStreamToHextetStream(WS2, FBody, IpBinHexTable, #0, ':');
 | |
|     finally
 | |
|       WS2.Free;
 | |
|     end;
 | |
|   finally
 | |
|     WS1.Free;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| { Encode InStream to OutStream - QuotedPrintable }
 | |
| procedure TIpMimeEntity.EncodeQuoted(InStream : TStream);
 | |
| var
 | |
|   O, W : Integer;
 | |
|   WordBuf, OutBuf : array[0..80] of AnsiChar;
 | |
|   CurChar : AnsiChar;
 | |
|   Abort : Boolean;
 | |
|   ByteStream : TIpByteStream;
 | |
| 
 | |
|   procedure SendLine;
 | |
|   begin
 | |
|     if (OutBuf[O-1] = #9) or (OutBuf[O-1] = #32) then begin
 | |
|       OutBuf[O] := '=';
 | |
|       Inc(O);
 | |
|     end;
 | |
|     FBody.WriteLineZ(OutBuf);
 | |
|     FillChar(OutBuf, SizeOf(OutBuf), #0);
 | |
|     O := 0;
 | |
|   end;
 | |
| 
 | |
|   procedure AddWordToOutBuf;
 | |
|   var
 | |
|     J : Integer;
 | |
|   begin
 | |
|     if (O + W) > 74 then SendLine;
 | |
|     for J := 0 to (W - 1) do begin
 | |
|       OutBuf[O] := WordBuf[J];
 | |
|       Inc(O);
 | |
|     end;
 | |
|     W := 0;
 | |
|   end;
 | |
| 
 | |
|   procedure AddHexToWord(B : Byte);
 | |
|   begin
 | |
|     if W > 73 then AddWordToOutBuf;
 | |
|     WordBuf[W] := '=';
 | |
|     WordBuf[W+1] := HexDigits[B shr 4];
 | |
|     WordBuf[W+2] := HexDigits[B and $F];
 | |
|     Inc(W, 3)
 | |
|   end;
 | |
| 
 | |
| begin
 | |
|   Abort := False;
 | |
|   O := 0;
 | |
|   W := 0;
 | |
|   FillChar(OutBuf, SizeOf(OutBuf), #0);
 | |
|   ByteStream := TIpByteStream.Create(InStream);
 | |
|   try
 | |
|     while ByteStream.Read(Byte(CurChar)) and not Abort do begin
 | |
|       if (Ord(CurChar) in [33..60, 62..126]) then begin
 | |
|         WordBuf[W] := CurChar;
 | |
|         Inc(W);
 | |
|         if W > 74 then AddWordToOutBuf;
 | |
|       end else if (CurChar = ' ') or (CurChar = #9) then begin
 | |
|         WordBuf[W] := CurChar;
 | |
|         Inc(W);
 | |
|         AddWordToOutBuf;
 | |
|       end else if (CurChar = #13) then begin
 | |
|         AddWordToOutBuf;
 | |
|         SendLine;
 | |
|       end else if (CurChar = #10) then begin
 | |
|         { Do nothing }
 | |
|       end else begin
 | |
|         AddHexToWord(Byte(CurChar));
 | |
|       end;
 | |
|       DoOnCodingProgress(ByteStream.Position, ByteStream.Size, Abort);
 | |
|     end;
 | |
|   finally
 | |
|     ByteStream.Free;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| { Encode InStream to OutStream - UUEncode }
 | |
| procedure TIpMimeEntity.EncodeUUEncode(InStream : TStream;
 | |
|                                        const aFileName : string);
 | |
| var
 | |
|   I, O, Count, Temp : Byte;
 | |
|   InBuf  : array[1..45] of Byte;
 | |
|   OutBuf : array[0..63] of AnsiChar;
 | |
|   Abort : Boolean;
 | |
| begin
 | |
|   Abort := False;
 | |
|   FBody.WriteLine('begin 600 ' + aFileName);
 | |
| 
 | |
|   { Encode and stream the attachment }
 | |
|   repeat
 | |
|     Count := InStream.Read(InBuf, SizeOf(InBuf));
 | |
|     if Count <= 0 then Break;
 | |
|     I := 1;
 | |
|     O := 0;
 | |
|     OutBuf[O] := AnsiChar(IpUUTable[Count and $3F]);
 | |
|     Inc(O);
 | |
|     while I+2 <= Count do begin
 | |
|       { Encode 1st byte }
 | |
|       Temp := (InBuf[I] shr 2);
 | |
|       OutBuf[O] := AnsiChar(IpUUTable[Temp and $3F]);
 | |
| 
 | |
|       { Encode 1st/2nd byte }
 | |
|       Temp := (InBuf[I] shl 4) or (InBuf[I+1] shr 4);
 | |
|       OutBuf[O+1] := AnsiChar(IpUUTable[Temp and $3F]);
 | |
| 
 | |
|       { Encode 2nd/3rd byte }
 | |
|       Temp := (InBuf[I+1] shl 2) or (InBuf[I+2] shr 6);
 | |
|       OutBuf[O+2] := AnsiChar(IpUUTable[Temp and $3F]);
 | |
| 
 | |
|       { Encode 3rd byte }
 | |
|       Temp := (InBuf[I+2] and $3F);
 | |
|       OutBuf[O+3] := AnsiChar(IpUUTable[Temp]);
 | |
| 
 | |
|       Inc(I, 3);
 | |
|       Inc(O, 4);
 | |
|     end;
 | |
| 
 | |
|     { Are there odd bytes to add? }
 | |
|     if (I <= Count) then begin
 | |
|       Temp := (InBuf[I] shr 2);
 | |
|       OutBuf[O] := AnsiChar(IpUUTable[Temp and $3F]);
 | |
| 
 | |
|       { One odd byte }
 | |
|       if (I = Count) then begin
 | |
|         Temp := (InBuf[I] shl 4) and $30;
 | |
|         OutBuf[O+1] := AnsiChar(IpUUTable[Temp and $3F]);
 | |
|         Inc(O, 2);
 | |
|       { Two odd bytes }
 | |
|       end else begin
 | |
|         Temp := ((InBuf[I] shl 4) and $30) or ((InBuf[I+1] shr 4) and $0F);
 | |
|         OutBuf[O+1] := AnsiChar(IpUUTable[Temp and $3F]);
 | |
|         Temp := (InBuf[I+1] shl 2) and $3C;
 | |
|         OutBuf[O+2] := AnsiChar(IpUUTable[Temp and $3F]);
 | |
|         Inc(O, 3);
 | |
|       end;
 | |
|     end;
 | |
| 
 | |
|     { Add CR/LF }
 | |
|     OutBuf[O] := #13;
 | |
|     OutBuf[O+1] := #10;
 | |
| 
 | |
|     { Write line to stream }
 | |
|     FBody.Write(OutBuf, (O + 2));
 | |
|     DoOnCodingProgress(InStream.Position, InStream.Size, Abort);
 | |
|   until (Count < SizeOf(InBuf)) or Abort;
 | |
| 
 | |
|   { Add terminating end }
 | |
|   FBody.WriteLine('`');
 | |
|   FBody.WriteLine('end');
 | |
| end;
 | |
| 
 | |
| { Translate each 3 bytes into 4 hextets and encode according to table }
 | |
| procedure TIpMimeEntity.OctetStreamToHextetStream(InStream : TStream;
 | |
|                                                   OutStream : TIpAnsiTextStream;
 | |
|                                                   const Table;
 | |
|                                                   PadChar, Delim : AnsiChar);
 | |
| var
 | |
|   OutBuf: array[0..MaxLineEncode-1] of Char;                           {!!.12}{!!.13}
 | |
|   OutBufLen: Integer;                                                  {!!.12}
 | |
|   Abort : Boolean;
 | |
| 
 | |
|   procedure FlushOutBuf;
 | |
|     {- write out encoded buffer to message stream }
 | |
|   begin
 | |
|     if OutBufLen > 0 then begin                                        {!!.12}
 | |
|       OutStream.WriteLineArray(OutBuf, OutBufLen);
 | |
|       OutBufLen := 0;                                                  {!!.12}
 | |
|     end;
 | |
|   end;
 | |
| 
 | |
|   procedure OutChar(ch : AnsiChar);
 | |
|     {- buffer the character to go out }
 | |
|   begin
 | |
|     if OutBufLen >= MaxLineEncode - 1 then                             {!!.12}{!!.13}
 | |
|       FlushOutBuf;
 | |
|     OutBuf[OutBufLen] := Ch;                                           {!!.12}
 | |
|     inc(OutBufLen);                                                    {!!.12}
 | |
|   end;
 | |
| 
 | |
| type
 | |
|   TBuffer = array[0..MaxInt-1] of Byte;
 | |
| var
 | |
|   Buffer: ^TBuffer;
 | |
|   I, Count: Cardinal;
 | |
| begin
 | |
|   if InStream is TMemoryStream then
 | |
|     Buffer := (InStream as TMemoryStream).Memory
 | |
|   else
 | |
|     if InStream is TIpMemMapStream then
 | |
|       Buffer := (InStream as TIpMemMapStream).Memory
 | |
|     else
 | |
|       raise EIpBaseException.Create(SNoMemoryStreamErr);
 | |
| 
 | |
|   Abort := False;
 | |
|   OutBufLen := 0;                                                      {!!.12}
 | |
|   if (Delim <> #0) then
 | |
|     OutChar(Delim);
 | |
| 
 | |
|   { Encode and stream the attachment }
 | |
|   I := 0;
 | |
|   Count := InStream.Size div 3 * 3;
 | |
|   while I < Count do
 | |
|   begin
 | |
|     { Encode 1st byte }
 | |
|     OutBuf[OutBufLen] := Char(TIp6BitTable(Table)[Buffer[I] shr 2]);
 | |
| 
 | |
|     { Encode 1st/2nd byte }
 | |
|     OutBuf[OutBufLen+1] := Char(TIp6BitTable(Table)[((Buffer[I] shl 4) or (Buffer[I+1] shr 4)) and $3F]);
 | |
| 
 | |
|     { Encode 2nd/3rd byte }
 | |
|     OutBuf[OutBufLen+2] := Char(TIp6BitTable(Table)[((Buffer[I+1] shl 2) or (Buffer[I+2] shr 6)) and $3F]);
 | |
| 
 | |
|     { Encode 3rd byte }
 | |
|     OutBuf[OutBufLen+3] := Char(TIp6BitTable(Table)[Buffer[I+2] and $3F]);
 | |
| 
 | |
|     Inc(OutBufLen, 4);
 | |
|     if OutBufLen >= MaxLineEncode - 1 then                             {!!.12}{!!.13}
 | |
|     begin
 | |
|       FlushOutBuf;
 | |
|       if i mod 100 = 0 then
 | |
|         DoOnCodingProgress(I, Count, Abort);
 | |
|       if Abort then
 | |
|         break;
 | |
|     end;
 | |
|     Inc(I, 3);
 | |
|   end;
 | |
| 
 | |
|   Count := InStream.Size;
 | |
|   { Are there odd bytes to add? }
 | |
|   if (I < Count) then begin
 | |
|     OutChar(TIp6BitTable(Table)[Buffer[I] shr 2]);
 | |
| 
 | |
|     { One odd byte }
 | |
|     if I = Count-1 then begin
 | |
|       OutChar(TIp6BitTable(Table)[(Buffer[I] shl 4) and $30]);
 | |
| 
 | |
|       if (PadChar <> #0) then
 | |
|         OutChar(PadChar);
 | |
|     { Two odd bytes }
 | |
|     end else begin
 | |
|       OutChar(TIp6BitTable(Table)[((Buffer[I] shl 4) and $30) or (((Buffer[I+1] shr 4) and $0F)) and $3F]);
 | |
|       OutChar(TIp6BitTable(Table)[(Buffer[I+1] shl 2) and $3C]);
 | |
|     end;
 | |
|     { Add padding }
 | |
|       if (PadChar <> #0) then
 | |
|         OutChar(PadChar);
 | |
|   end;
 | |
| 
 | |
|   if (Delim <> #0) then
 | |
|     OutChar(Delim);
 | |
|   FlushOutBuf;
 | |
| end;
 | |
| 
 | |
| {Begin !!.12}
 | |
| procedure TIpMIMEEntity.ReadBody(InStream : TIpAnsiTextStream; const StartLine : string);
 | |
| var
 | |
|   S : string;
 | |
| begin
 | |
|   S := StartLine;
 | |
|   { read in message body up to message terminator '.' }
 | |
|   {while not ((S = '.') or AtEndOfStream) do begin}
 | |
|   while not InStream.AtEndOfStream do begin
 | |
|     Body.WriteLine(S);
 | |
|     S := InStream.ReadLine;
 | |
|   end;
 | |
|   { write final line }
 | |
|   Body.WriteLine(S);
 | |
| end;
 | |
| {End !!.12}
 | |
| 
 | |
| 
 | |
| { TIpMessage }
 | |
| constructor TIpMessage.CreateMessage;
 | |
| begin
 | |
|   inherited Create(nil);
 | |
|   FBCC        := TStringList.Create;
 | |
|   FCC         := TStringList.Create;
 | |
|   FNewsgroups := TStringList.Create;
 | |
|   FPath       := TStringList.Create;
 | |
|   FReceived   := TStringList.Create;
 | |
|   FRecipients := TStringList.Create;
 | |
|   FReferences := TStringList.Create;
 | |
|   FUserFields := TStringList.Create;
 | |
|   FHeaders    := TIpHeaderCollection.Create (Self);                    {!!.12}
 | |
|   MsgStream := TIpAnsiTextStream.CreateEmpty;
 | |
|   NewMessageStream;
 | |
| end;
 | |
| 
 | |
| destructor TIpMessage.Destroy;
 | |
| begin
 | |
|   Clear;
 | |
|   FBCC.Free;
 | |
|   FCC.Free;
 | |
|   FNewsgroups.Free;
 | |
|   FPath.Free;
 | |
|   FReceived.Free;
 | |
|   FRecipients.Free;
 | |
|   FReferences.Free;
 | |
|   FUserFields.Free;
 | |
|   FHeaders.Free;                                                         {!!.12}
 | |
|   MsgStream.FreeStream;
 | |
|   MsgStream.Free;
 | |
|   inherited Destroy;
 | |
| end;
 | |
| 
 | |
| {Begin !!.13}
 | |
| procedure TIpMessage.CheckAllHeaders;
 | |
| var
 | |
|   i         : Integer;
 | |
|   j         : Integer;
 | |
|   HeaderNum : Integer;
 | |
| begin
 | |
|   FAttachmentCount := 0;
 | |
| 
 | |
|   { Roll through the list of headers specifically handled by iPRO.
 | |
|     When one is found, move it into the data structure specific to that
 | |
|     header field. }
 | |
|   for i := 0 to IpMaxHeaders - 1 do begin
 | |
|     if (IpHeaderXRef[i].FieldType = htUserFields) or
 | |
|        (IpHeaderXRef[i].FieldType = htReceived) then begin
 | |
|       for j := 0 to Headers.Count - 1 do begin
 | |
|         if StrLIComp (PChar (IpHeaderXRef[i].FieldString),
 | |
|                       PChar (Headers.Items[j].Name),
 | |
|                       Length (IpHeaderXRef[i].FieldString)) = 0 then
 | |
|           CheckHeaderType (Headers.Items[j],
 | |
|                            IpHeaderXRef[i].FieldType);
 | |
|       end;
 | |
|       
 | |
|     end else begin
 | |
|       HeaderNum := Headers.HasHeader (IpHeaderXRef[i].FieldString);
 | |
|       if HeaderNum >= 0 then
 | |
|         CheckHeaderType (Headers.Items[HeaderNum],
 | |
|                          IpHeaderXRef[i].FieldType);
 | |
|     end;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TIpMessage.CheckHeaderType (HeaderInfo : TIpHeaderItem;
 | |
|                                       HeaderType : TIpHeaderTypes);
 | |
| 
 | |
|   function ExtractSingleHeader(HeaderInfo : TIpHeaderItem) : string;
 | |
|   begin
 | |
|     Result := Trim(HeaderInfo.Value.Text);
 | |
|     HeaderInfo.IsProperty := True;                                     {!!.13}
 | |
|   end;
 | |
| 
 | |
|   procedure ExtractCSVHeader(HeaderInfo : TIpHeaderItem;
 | |
|                          var AList      : TStringList);
 | |
|   var
 | |
|     WorkString : string;
 | |
|   begin
 | |
|     WorkString := ExtractSingleHeader(HeaderInfo);
 | |
|     Parse (WorkString, ',', AList);
 | |
|     HeaderInfo.IsProperty := True;                                     {!!.13}
 | |
|   end;
 | |
| 
 | |
|   procedure ExtractListHeader(HeaderInfo : TIpHeaderItem;
 | |
|                            var AList      : TStringList);
 | |
|   begin
 | |
|     AList.Assign (HeaderInfo.Value);
 | |
|     HeaderInfo.IsProperty := True;                                     {!!.13}
 | |
|   end;
 | |
| 
 | |
|   procedure ExtractAppendListHeader(HeaderInfo : TIpHeaderItem;
 | |
|                               const IncludeName : Boolean;             {!!.13}
 | |
|                                 var AList      : TStringList);
 | |
|   var
 | |
|     i : Integer;
 | |
|   begin
 | |
|     for i := 0 to HeaderInfo.Value.Count - 1 do
 | |
| {Begin !!.13}
 | |
|       if IncludeName then
 | |
|         AList.Add (HeaderInfo.Name + ': ' + HeaderInfo.Value[i])
 | |
|       else
 | |
|         AList.Add (HeaderInfo.Value[i]);
 | |
|     HeaderInfo.IsProperty := True;
 | |
| {End !!.13}
 | |
|   end;
 | |
| 
 | |
| begin
 | |
|   case HeaderType of
 | |
|     htBCC             :
 | |
|       ExtractCSVHeader(HeaderInfo, FBCC);
 | |
|     htCC              :
 | |
|       ExtractCSVHeader(HeaderInfo, FCC);
 | |
|     htControl         :
 | |
|       FControl := ExtractSingleHeader(HeaderInfo);
 | |
|     htDate            :
 | |
|       FDate := ExtractSingleHeader(HeaderInfo);
 | |
|     htDispositionNotify :
 | |
|       FDispositionNotify := ExtractSingleHeader(HeaderInfo);
 | |
|     htFrom            :
 | |
|       FFrom := ExtractSingleHeader(HeaderInfo);
 | |
|     htFollowUp        :
 | |
|       FFollowUpTo := ExtractSingleHeader(HeaderInfo);
 | |
|     htInReplyTo       :
 | |
|       FInReplyTo := ExtractSingleHeader(HeaderInfo);
 | |
|     htKeywords        :
 | |
|       FKeywords := ExtractSingleHeader(HeaderInfo);
 | |
|     htMessageID       :
 | |
|       FMessageID := ExtractSingleHeader(HeaderInfo);
 | |
|     htNewsgroups      :
 | |
|       ExtractCSVHeader(HeaderInfo, FNewsgroups);
 | |
|     htNNTPPostingHost :
 | |
|       FNNTPPostingHost := ExtractSingleHeader(HeaderInfo);
 | |
|     htOrganization    :
 | |
|       FOrganization := ExtractSingleHeader(HeaderInfo);
 | |
|     htPath            :
 | |
|       ExtractListHeader(HeaderInfo, FPath);
 | |
|     htPostingHost     :
 | |
|       FPostingHost := ExtractSingleHeader(HeaderInfo);
 | |
|     htReceived        :
 | |
|       ExtractAppendListHeader(HeaderInfo, False, FReceived);           {!!.13}
 | |
|     htReferences      :
 | |
|       ExtractListHeader(HeaderInfo, FReferences);
 | |
|     htReplyTo         :
 | |
|       FReplyTo := ExtractSingleHeader(HeaderInfo);
 | |
|     htReturnPath      :
 | |
|       FReturnPath := ExtractSingleHeader(HeaderInfo);
 | |
|     htSender          :
 | |
|       FSender := ExtractSingleHeader(HeaderInfo);
 | |
|     htSubject         :
 | |
|       FSubject := ExtractSingleHeader(HeaderInfo);
 | |
|     htTo              :
 | |
|       ExtractCSVHeader(HeaderInfo, FRecipients);
 | |
|     htUserFields      :
 | |
|       ExtractAppendListHeader(HeaderInfo, True, FUserFields);          {!!.13}
 | |
|     htXIpro           : begin
 | |
|     end;
 | |
|   end;
 | |
| end;
 | |
| {End !!.12}
 | |
| 
 | |
| { Clear properties and free message stream }
 | |
| procedure TIpMessage.Clear;
 | |
| begin
 | |
|   inherited Clear;
 | |
| 
 | |
|   FAttachmentCount := 0;                                               {!!.12}
 | |
|   FMessageTag := 0;                                                    {!!.15}
 | |
| 
 | |
|   FBCC.Clear;
 | |
|   FCC.Clear;
 | |
|   FDate := '';
 | |
|   FDispositionNotify := '';                                            {!!.12}
 | |
|   FFrom := '';
 | |
|   FInReplyTo := '';
 | |
|   FKeywords := '';
 | |
|   FFollowupTo := '';                                                   {!!.15}
 | |
|   FControl := '';                                                      {!!.15}
 | |
|   FMessageID := '';
 | |
|   FNewsgroups.Clear;
 | |
|   FNNTPPostingHost := '';
 | |
|   FOrganization := '';
 | |
|   FPath.Clear;
 | |
|   FPostingHost := '';
 | |
|   FReceived.Clear;
 | |
|   FRecipients.Clear;
 | |
|   FReferences.Clear;
 | |
|   FReplyTo := '';
 | |
|   FReturnPath := '';
 | |
|   FSender := '';
 | |
|   FSubject := '';
 | |
|   FUserFields.Clear;
 | |
|   FHeaders.Clear;                                                      {!!.15}
 | |
|   MsgStream.FreeStream;
 | |
| end;
 | |
| 
 | |
| {Begin !!.12}
 | |
| { Get headers, body, and MIME parts (if any) }
 | |
| procedure TIpMessage.DecodeMessage;
 | |
| 
 | |
| var
 | |
|   AttDepth     : Integer;
 | |
| 
 | |
|   function IsAttachmentStart (const s : string) : Boolean;
 | |
|   type
 | |
|     TAttState = (asBegin, asHaveBegin,
 | |
|                  asNumber1, asNumberSp,
 | |
|                  asOpenCurly, asNumber2, asNumber2Sp, asCloseCurly,
 | |
|                  asQuote1, asDblQuote1, AsAlnum1);
 | |
| 
 | |
|   var
 | |
|     State : TAttState;
 | |
|     i     : Integer;
 | |
|     SLen  : Integer;
 | |
| 
 | |
|   begin
 | |
|     Result := False;
 | |
|     State  := asBegin;
 | |
|     i      := 1;
 | |
|     SLen   := Length (s);
 | |
| 
 | |
|     while i < SLen do begin
 | |
|       case State of
 | |
|         asBegin     : begin
 | |
|           if s[i] in [' ', #09] then
 | |
|             Inc (i)
 | |
|           else if LowerCase (Copy (s, i, 5)) = 'begin' then begin
 | |
|             State := asHaveBegin;
 | |
|             Inc (i, 5);
 | |
|           end else
 | |
|             Break;
 | |
|         end;
 | |
| 
 | |
|         asHaveBegin : begin
 | |
|           if s[i] in [' ', #09] then
 | |
|             Inc (i)
 | |
|           else if s[i] = '{' then begin
 | |
|             Inc (i);
 | |
|             State := asNumber2;
 | |
|           end else if s[i] in ['0'..'9'] then begin
 | |
|             Inc (i);
 | |
|             State := asNumber1;
 | |
|           end else
 | |
|             Break;
 | |
|         end;
 | |
| 
 | |
|         asNumber1 : begin
 | |
|           if s[i] in ['0'..'9'] then
 | |
|             Inc (i)
 | |
|           else if s[i] in [' ', #09] then begin
 | |
|             Inc (i);
 | |
|             State := asNumberSp;
 | |
|           end else
 | |
|             Break;
 | |
|         end;
 | |
| 
 | |
|         asNumberSp : begin
 | |
|           if s[i] in [' ', #09] then
 | |
|             Inc (i)
 | |
|           else if s[i] = '"' then begin
 | |
|             Inc (i);
 | |
|             State := asDblQuote1;
 | |
|           end else if s[i] = '''' then begin
 | |
|             Inc (i);
 | |
|             State := asQuote1;
 | |
|           end else if s[i] in ['!'..'~'] then begin
 | |
|             Inc (i);
 | |
|             State := asAlNum1;
 | |
|           end else
 | |
|             Break;
 | |
|         end;
 | |
| 
 | |
|         asOpenCurly : begin
 | |
|           if s[i] in [' ', #09] then
 | |
|             Inc (i)
 | |
|           else if s[i] in ['0'..'9'] then begin
 | |
|             Inc (i);
 | |
|             State := asNumber2;
 | |
|           end else
 | |
|             Break;
 | |
|         end;
 | |
| 
 | |
|         asNumber2 : begin
 | |
|           if s[i] in ['0'..'9'] then
 | |
|             Inc (i)
 | |
|           else if s[i] in [' ', #09] then begin
 | |
|             Inc (i);
 | |
|             State := asNumber2Sp;
 | |
|           end else if s[i] = '}' then begin
 | |
|             State := asCloseCurly;
 | |
|             Inc (i);
 | |
|           end else
 | |
|             Break;
 | |
|         end;
 | |
| 
 | |
|         asNumber2Sp : begin
 | |
|           if s[i] in [' ', #09] then
 | |
|             Inc (i)
 | |
|           else if s[i] = '}' then begin
 | |
|             Inc (i);
 | |
|             State := asCloseCurly;
 | |
|           end else
 | |
|             Break;
 | |
|         end;
 | |
| 
 | |
|         asCloseCurly : begin
 | |
|           if s[i] in [' ', #09] then
 | |
|             Inc (i)
 | |
|           else if s[i] = '"' then begin
 | |
|             Inc (i);
 | |
|             State := asDblQuote1;
 | |
|           end else if s[i] = '''' then begin
 | |
|             Inc (i);
 | |
|             State := asQuote1;
 | |
|           end else
 | |
|             Break;
 | |
|         end;
 | |
| 
 | |
|         asQuote1 : begin
 | |
|           if s[i] in [' '..'&', '('..'~'] then
 | |
|             Inc (i)
 | |
|           else if s[i] = '''' then begin
 | |
|             Result := True;
 | |
|             Break;
 | |
|           end else
 | |
|             Break;
 | |
|         end;
 | |
| 
 | |
|         asDblQuote1 : begin
 | |
|           if s[i] in [' '..'!', '#'..'~'] then
 | |
|             Inc (i)
 | |
|           else if s[i] = '"' then begin
 | |
|             Result := True;
 | |
|             Break;
 | |
|           end else
 | |
|             Break;
 | |
|         end;
 | |
| 
 | |
|         AsAlnum1 : begin
 | |
|           if s[i] in ['!'..'~'] then begin
 | |
|             Result := True;
 | |
|             Break;
 | |
|           end else
 | |
|             Break;
 | |
|         end;
 | |
| 
 | |
|       end;
 | |
|     end;
 | |
|   end;
 | |
| 
 | |
|   function IsAttachmentEnd (const s : string) : Boolean;
 | |
|   begin
 | |
|     if LowerCase (Copy (s, 1, 3)) = 'end' then
 | |
|       Result := True
 | |
|     else
 | |
|       Result := False;
 | |
|   end;
 | |
| 
 | |
|   procedure CheckForAttachment (const s : string);
 | |
|   begin
 | |
|     if IsAttachmentStart (s) then begin
 | |
|       if AttDepth = 0 then
 | |
|         Inc (FAttachmentCount);
 | |
|       Inc (AttDepth);
 | |
|     end else if (IsAttachmentEnd (s)) and
 | |
|                 (FAttachmentCount > 0) then
 | |
|       Dec (AttDepth);
 | |
|   end;
 | |
| {End !!.12}
 | |
| var
 | |
|   RawHeaders : TStringList;
 | |
|   S : string;
 | |
|   i, j : Integer;                                                      {!!.13}
 | |
| begin
 | |
|   { get message headers}
 | |
|   Position := 0;
 | |
|   RawHeaders := TStringList.Create;
 | |
|   try
 | |
|     S := ReadLine;
 | |
|     repeat
 | |
|       if S <> '' then                                                  {!!.15}
 | |
|         RawHeaders.Add(S);
 | |
|       S := ReadLine;
 | |
|     until (S = '');
 | |
| 
 | |
|     FHeaders.Clear;                                                    {!!.12}
 | |
|     FHeaders.LoadHeaders (RawHeaders, False);                          {!!.12}
 | |
|     CheckAllHeaders;                                                   {!!.12}
 | |
| 
 | |
|     { decode MIME headers }
 | |
|     DecodeMimeHeaders(RawHeaders);
 | |
| 
 | |
| {Begin !!.13}
 | |
|     { If this is a MIME message, mark the MIME headers as being exposed via an
 | |
|       iPRO property. }
 | |
|     if FIsMime then
 | |
|       for i := Low(IpMimeHeaders) to High(IpMimeHeaders) do begin
 | |
|         j := FHeaders.HasHeader(IpMimeHeaders[i]);
 | |
|         if j > -1 then
 | |
|           FHeaders.Items[j].IsProperty := True;
 | |
|       end;
 | |
| {End !!.13}
 | |
|   finally
 | |
|     RawHeaders.Free;
 | |
|   end;
 | |
| 
 | |
|   { if message is mime, then decode mime parts }
 | |
|   if IsMime then begin                                                 {!!.01}
 | |
|     if (FContentDispositionType = strAttachment) then begin            {!!.12}
 | |
|       Inc (FParent.FAttachmentCount);                                  {!!.12}{!!.15}
 | |
|       DecodeEntityAsAttachment(MsgStream)                              {!!.01}
 | |
|     end else                                                           {!!.12}
 | |
|       DecodeEntity(MsgStream);
 | |
|   end else begin
 | |
|     { otherwise, just read in the message body. }
 | |
|     repeat  { skip over blank lines between headers and body }
 | |
|       S := ReadLine;
 | |
|     until (S <> '') or AtEndOfStream;
 | |
| 
 | |
|     { read in message body up to message terminator '.' }
 | |
|     {while not ((S = '.') or AtEndOfStream) do begin}                  {!!.10}
 | |
|     while not AtEndOfStream do begin                                   {!!.10}
 | |
|       Body.WriteLine(S);
 | |
|       AttDepth := 0;                                                   {!!.12}
 | |
|       CheckForAttachment (S);                                          {!!.12}
 | |
|       S := ReadLine;
 | |
|     end;
 | |
|     { write final line }                                               {!!.10}
 | |
|     if S <> '' then                                                    {!!.13}
 | |
|       Body.WriteLine(S);                                               {!!.10}
 | |
| 
 | |
| {Begin !!.12}
 | |
|     { Read the message body. }
 | |
|     {ReadBody(MsgStream, S); }
 | |
| {End !!.12}
 | |
|   end;
 | |
|   Body.Position := 0;
 | |
| end;
 | |
| 
 | |
| { Build message stream with headers, body, and MIME parts (if any) }
 | |
| procedure TIpMessage.EncodeMessage;
 | |
| var
 | |
|   i : Integer;
 | |
|   Size : Longint;                                                      {!!.12}
 | |
|   FileName : string;                                                   {!!.12}
 | |
|   Strm : TIpMemMapStream;                                              {!!.12}
 | |
|   RawHeaders : TStringList;
 | |
| begin
 | |
|   NewMessageStream;
 | |
| {Begin !!.12}
 | |
|   { If we have some very large attachments then we need to use a memory mapped
 | |
|     file stream instead of TMemory, in order to improve performance. }
 | |
|   Size := 0;
 | |
|   for i := 0 to Pred(FMimeParts.Count) do
 | |
|     inc(Size, FMimeParts[i].FOriginalSize);
 | |
|   if Size > IpLgAttachSizeBoundry then begin
 | |
|     MsgStream.FreeStream;
 | |
|     FileName := GetTemporaryFile(GetTemporaryPath);
 | |
|     if FileExists(FileName) then
 | |
|       DeleteFile(FileName);
 | |
|     Strm := TIpMemMapStream.Create(FileName, False, True);
 | |
|     Strm.Size := Trunc(Size * 1.5);
 | |
|     Strm.Open;
 | |
|     MsgStream.Stream := Strm;
 | |
|   end;
 | |
| {End !!.12}
 | |
|   if (FContentType <> '') then begin
 | |
|     FIsMime := True;
 | |
|     FMimeVersion := '1.0';
 | |
|   end;
 | |
|   RawHeaders := TStringList.Create;
 | |
|   try
 | |
|     EncodeSingleHeader(strReturnPath, RawHeaders, FReturnPath);
 | |
|     EncodeMultiHeader(strReceived, RawHeaders, FReceived, #09, True);
 | |
|     EncodeListHeader(strPath, RawHeaders, FPath, ',', True);
 | |
|     EncodeListHeader(strNewsgroups, RawHeaders, FNewsgroups, ',', False); {!!.14}
 | |
|     EncodeSingleHeader(strMessageID, RawHeaders, FMessageID);
 | |
|     EncodeSingleHeader (strDispositionNotify, RawHeaders,                {!!.12}
 | |
|                         FDispositionNotify);                             {!!.12}
 | |
|     EncodeSingleHeader(strReplyTo, RawHeaders, FReplyTo);
 | |
|     EncodeSingleHeader(strFrom, RawHeaders, FFrom);
 | |
|     EncodeListHeader(strTo, RawHeaders, FRecipients, ',', True);
 | |
|     EncodeSingleHeader(strSubject, RawHeaders, FSubject);
 | |
|     EncodeSingleHeader(strDate, RawHeaders, FDate);
 | |
|     EncodeSingleHeader(strOrganization, RawHeaders, FOrganization);
 | |
|     EncodeListHeader(strCC, RawHeaders, FCC, ',', False);
 | |
|     EncodeListHeader(strBCC, RawHeaders, FBCC, ',', False);
 | |
|     EncodeSingleHeader(strInReplyTo, RawHeaders, FInReplyTo);
 | |
|     EncodeListHeader(strReferences, RawHeaders, FReferences, '', False);
 | |
|     EncodeSingleHeader(strSender, RawHeaders, FSender);
 | |
|     EncodeSingleHeader(strKeywords, RawHeaders, FKeywords);
 | |
|     EncodeMultiHeader('', RawHeaders, FUserFields, Char(0), False);
 | |
|     EncodeSingleHeader(strControl, RawHeaders, FControl);               {!!.12}
 | |
|     EncodeSingleHeader(strFollowUp, RawHeaders, FFollowupTo);           {!!.12}
 | |
| {Begin !!.13}
 | |
|     for i := 0 to Pred(Headers.Count) do
 | |
|       { Write the header out only if it is not a header exposed via an iPRO
 | |
|         property. }
 | |
|       if (not Headers.Items[i].IsProperty) then begin
 | |
|         if Headers.Items[i].Value.Count = 1 then
 | |
|           EncodeSingleHeader(Headers.Items[i].Name + ': ', RawHeaders,
 | |
|                              Headers.Items[i].Value[0])
 | |
|         else
 | |
|           EncodeMultiheader(Headers.Items[i].Name + ': ', RawHeaders,
 | |
|                             Headers.Items[i].Value, #09, True);
 | |
|       end;
 | |
| {End !!.13}
 | |
|     if IsMime then
 | |
|       EncodeMimeHeaders(RawHeaders);
 | |
|     if (RawHeaders.Count = 0) then
 | |
|       Exit;
 | |
|     for i := 0 to Pred(RawHeaders.Count) do
 | |
|       WriteLine(RawHeaders[i]);
 | |
|   finally
 | |
|     RawHeaders.Free;
 | |
|   end;
 | |
| 
 | |
| {Begin !!.13}
 | |
|   WriteLine('');
 | |
|   if IsMime then
 | |
|     EncodeEntity(MsgStream)
 | |
|   else if (FBody.Size > 0) then begin
 | |
|     FBody.Position := 0;
 | |
|     repeat
 | |
|       WriteLine(Body.ReadLine);
 | |
|     until FBody.AtEndOfStream;
 | |
|   end;  { if }
 | |
| {End !!.13}
 | |
| end;
 | |
| 
 | |
| { Load message from file stream and decode }
 | |
| procedure TIpMessage.LoadFromFile(const aFileName : string);
 | |
| {Begin !!.12}
 | |
| var
 | |
|   SourceStream : TIpMemMapStream;
 | |
| {End !!.12}
 | |
| begin
 | |
|   Clear;
 | |
|   NewMessageStream;                                                    {!!.03}
 | |
| {Begin !!.12}
 | |
|   SourceStream := TIpMemMapStream.Create(aFileName, True, False);
 | |
|   try
 | |
|     SourceStream.Open;
 | |
| {Begin !!.15}
 | |
|     if SourceStream.Size > IpLgAttachSizeBoundry then begin
 | |
|       MsgStream.FreeStream;
 | |
|       MsgStream.Stream := SourceStream;
 | |
|     end
 | |
|     else
 | |
|       MsgStream.CopyFrom(SourceStream, 0);
 | |
|   finally
 | |
|     if MsgStream.Stream <> SourceStream then
 | |
|       SourceStream.Free;
 | |
| {End !!.15}
 | |
|   end;
 | |
| {End !!.12}
 | |
| 
 | |
|   try                                                                  {!!.03}
 | |
|     DecodeMessage;
 | |
|   except                                                               {!!.03}
 | |
|     { just eat the exception, the messge might be corrupt, but the }
 | |
|     { raw text (MessageStream property) will still be available    }
 | |
|   end;                                                                 {!!.03}
 | |
| end;
 | |
| 
 | |
| {Begin !!.12}
 | |
| procedure TIpMessage.LoadFromStream(aStream : TStream);
 | |
| var
 | |
|   FileName : string;
 | |
|   Strm : TIpMemMapStream;
 | |
| begin
 | |
|   Clear;
 | |
|   NewMessageStream;
 | |
|   if aStream.Size > IpLgAttachSizeBoundry then begin
 | |
|     MsgStream.FreeStream;
 | |
|     FileName := GetTemporaryFile(GetTemporaryPath);
 | |
|     if FileExists(FileName) then
 | |
|       DeleteFile(FileName);
 | |
|     Strm := TIpMemMapStream.Create(FileName, False, True);
 | |
|     Strm.Size := aStream.Size;
 | |
|     Strm.Open;
 | |
|     MsgStream.Stream := Strm;
 | |
|   end;
 | |
|   MsgStream.CopyFrom(aStream, 0);
 | |
| 
 | |
|   try
 | |
|     DecodeMessage;
 | |
|   except
 | |
|     { just eat the exception, the messge might be corrupt, but the }
 | |
|     { raw text (MessageStream property) will still be available    }
 | |
|   end;                                                               
 | |
| end;
 | |
| 
 | |
| 
 | |
| { Create new message stream but retain existing decoded message }
 | |
| procedure TIpMessage.NewMessageStream;
 | |
| begin
 | |
|   MsgStream.FreeStream;
 | |
|   MsgStream.Stream := TMemoryStream.Create;
 | |
|   MsgStream.bsInitForNewStream;                                        {!!.02}
 | |
| end;
 | |
| 
 | |
| { Clear all and create new empty message stream }
 | |
| procedure TIpMessage.NewMessage;
 | |
| begin
 | |
|   Clear;
 | |
|   NewMessageStream;
 | |
| end;
 | |
| 
 | |
| { Position property read access method }
 | |
| function TIpMessage.GetPosition : Longint;
 | |
| begin
 | |
|   if Assigned(MsgStream) then
 | |
|     Result := MsgStream.Position
 | |
|   else
 | |
|     Result := 0;
 | |
| end;
 | |
| 
 | |
| { Size property read access method }
 | |
| function TIpMessage.GetSize : Longint;
 | |
| begin
 | |
|   if Assigned(MsgStream) then
 | |
|     Result := MsgStream.Size
 | |
|   else
 | |
|     Result := 0;
 | |
| end;
 | |
| 
 | |
| { Return next line from the message stream (CRLF stripped) }
 | |
| function TIpMessage.ReadLine : string;
 | |
| begin
 | |
|   if Assigned(MsgStream) then
 | |
|     Result := MsgStream.ReadLine
 | |
|   else
 | |
|     Result := '';
 | |
| end;
 | |
| 
 | |
| { Return next line from the message stream (CRLF retained) }
 | |
| function TIpMessage.ReadLineCRLF : string;
 | |
| begin
 | |
|   if Assigned(MsgStream) then
 | |
|     Result := MsgStream.ReadLine + CRLF
 | |
|   else
 | |
|     Result := '';
 | |
| end;
 | |
| 
 | |
| {- Save raw message stream to file }
 | |
| procedure TIpMessage.SaveToFile(const aFileName : string);
 | |
| var
 | |
|   FS : TFileStream;
 | |
| begin
 | |
|   EncodeMessage;
 | |
|   Position := 0;
 | |
|   FS := TFileStream.Create(aFileName, fmCreate);
 | |
|   try
 | |
|     FS.CopyFrom(MsgStream, MsgStream.Size);
 | |
|   finally
 | |
|     FS.Free;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| {Begin !!.12}
 | |
| {- Save raw message stream }
 | |
| procedure TIpMessage.SaveToStream(Stream: TStream);
 | |
| begin
 | |
|   Position := 0;
 | |
|   Stream.CopyFrom(MsgStream, MsgStream.Size);
 | |
| end;
 | |
| 
 | |
| procedure TIpMessage.SetHeaders(Headers : TIpHeaderCollection);
 | |
| begin
 | |
|   FHeaders.Assign(Headers);
 | |
| end;
 | |
| {End !!.12}
 | |
| 
 | |
| { Position property write access method }
 | |
| procedure TIpMessage.SetPosition(Value : Longint);
 | |
| begin
 | |
|   if Assigned(MsgStream) then
 | |
|     MsgStream.Position := Value;
 | |
| end;
 | |
| 
 | |
| { Write string onto the message stream and append CRLF terminator }
 | |
| procedure TIpMessage.WriteLine(const aSt : string);
 | |
| begin
 | |
|   if Assigned(MsgStream) then
 | |
|     MsgStream.WriteLine(aSt);
 | |
| end;
 | |
| 
 | |
| { Indicates whether or not we're at the end of the message stream }
 | |
| function TIpMessage.AtEndOfStream : Boolean;
 | |
| begin
 | |
|   if Assigned(MsgStream) then
 | |
|     Result := MsgStream.AtEndOfStream
 | |
|   else
 | |
|     Result := True;
 | |
| end;
 | |
| 
 | |
| { Return 'alternative' text/plain mime part }
 | |
| function TIpMessage.GetBodyPlain(CanCreate : Boolean) : TIpMimeEntity;
 | |
| var
 | |
|   aParent : TIpMimeEntity;
 | |
| begin
 | |
|   aParent := FindNestedMimePart(strMultipart, strAlternative, '');   {!!.02}
 | |
|   if not Assigned(aParent) then
 | |
|     aParent := Self;
 | |
| {Begin !!.15}
 | |
|   Result := aParent.FindNestedMimePart(strText, strPlain, '');
 | |
|   if (Result = nil) and CanCreate then begin
 | |
|     Result := NewMimePart;
 | |
|     Result.ContentType := strText;
 | |
|     Result.ContentSubtype := strPlain;
 | |
|   end;
 | |
| {End !!.15}
 | |
| end;
 | |
| 
 | |
| { Return 'alternative' text/html mime part }
 | |
| function TIpMessage.GetBodyHtml(CanCreate : Boolean) : TIpMimeEntity;
 | |
| var
 | |
|   aParent : TIpMimeEntity;
 | |
| begin
 | |
|   aParent := FindNestedMimePart(strMultipart, strAlternative, '');   {!!.02}
 | |
|   if not Assigned(aParent) then
 | |
|     aParent := Self;
 | |
| {Begin !!.15}
 | |
|   Result := aParent.FindNestedMimePart(strText, strHtml, '');
 | |
|   if (Result = nil) and CanCreate then begin
 | |
|     Result := NewMimePart;
 | |
|     Result.ContentType := strText;
 | |
|     Result.ContentSubtype := strHTML;
 | |
|   end;
 | |
| {End !!.15}
 | |
| end;
 | |
| 
 | |
| { Add a file attachment using default types }
 | |
| procedure TIpMessage.AddDefaultAttachment(const aFileName: string);     {!!.02}
 | |
| begin
 | |
|   with NewMimePart do begin
 | |
|     EntityName := ExtractFileName(aFileName);
 | |
|     ContentDispositionType := 'attachment';
 | |
|     EncodeBodyFile(aFileName);
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TIpMessage.AddDefaultAttachmentAs (const aFileName      : string;  {!!.12}
 | |
|                                              const AttachmentName : string); {!!.12}
 | |
| begin                                                                    {!!.12}
 | |
|   with NewMimePart do begin                                              {!!.12}
 | |
|     EntityName := ExtractFileName (AttachmentName);                      {!!.12}
 | |
|     ContentDispositionType := 'attachment';                              {!!.12}
 | |
|     EncodeBodyFile (aFileName);                                          {!!.12}
 | |
|   end;                                                                   {!!.12}
 | |
| end;                                                                     {!!.12}
 | |
| 
 | |
| { Set message properties from another TIpMessage }
 | |
| procedure TIpMessage.Assign(Source: TPersistent);
 | |
| var
 | |
|   SourcePos : Integer;
 | |
|   SourceMsg : TIpMessage;
 | |
| begin
 | |
|   if Source is TIpMessage then begin
 | |
|     SourceMsg := TIpMessage(Source);
 | |
|     { clear our streams and properties }
 | |
|     NewMessage;
 | |
|     { ensure we are at the beginning of our streams }
 | |
|     Position := 0;
 | |
|     SourcePos := SourceMsg.Position;
 | |
|     SourceMsg.Position := 0;
 | |
|     MsgStream.CopyFrom(SourceMsg.MsgStream, 0);
 | |
|     Position := 0;
 | |
|     SourceMsg.Position := SourcePos;
 | |
|     try                                                                {!!.03}
 | |
|       DecodeMessage;
 | |
|     except                                                             {!!.03}
 | |
|       { just eat the exception, the messge might be corrupt, but the }
 | |
|       { raw text (MessageStream property) will still be available    }
 | |
|     end;                                                               {!!.03}
 | |
|   end else
 | |
|     inherited Assign(Source);
 | |
| end;
 | |
| 
 | |
| procedure TIpMessage.SetBCC(const Value: TStringList);                 {!!.01}
 | |
| begin
 | |
|   FBCC.Assign(Value);
 | |
| end;
 | |
| 
 | |
| procedure TIpMessage.SetCC(const Value: TStringList);                  {!!.01}
 | |
| begin
 | |
|   FCC.Assign(Value);
 | |
| end;
 | |
| 
 | |
| procedure TIpMessage.SetNewsgroups(const Value: TStringList);          {!!.01}
 | |
| begin
 | |
|   FNewsgroups.Assign(Value);
 | |
| end;
 | |
| 
 | |
| procedure TIpMessage.SetPath(const Value: TStringList);                {!!.01}
 | |
| begin
 | |
|   FPath.Assign(Value);
 | |
| end;
 | |
| 
 | |
| procedure TIpMessage.SetReceived(const Value: TStringList);            {!!.01}
 | |
| begin
 | |
|   FReceived.Assign(Value);
 | |
| end;
 | |
| 
 | |
| procedure TIpMessage.SetRecipients(const Value: TStringList);          {!!.01}
 | |
| begin
 | |
|   FRecipients.Assign(Value);
 | |
| end;
 | |
| 
 | |
| procedure TIpMessage.SetReferences(const Value: TStringlist);          {!!.01}
 | |
| begin
 | |
|   FReferences.Assign(Value);
 | |
| end;
 | |
| 
 | |
| procedure TIpMessage.SetUserFields(const Value: TStringList);          {!!.01}
 | |
| begin
 | |
|   FUserFields.Assign(Value);
 | |
| end;
 | |
| 
 | |
| 
 | |
| 
 | |
| { TIpFormDataEntity }
 | |
| constructor TIpFormDataEntity.Create(ParentEntity : TIpMimeEntity);
 | |
| begin
 | |
|   inherited Create(ParentEntity);
 | |
|   ContentType := strMultipart;
 | |
|   ContentSubType := strFormData;
 | |
|   Boundary := GenerateBoundary;
 | |
| end;
 | |
| 
 | |
| destructor TIpFormDataEntity.Destroy;
 | |
| begin
 | |
|   inherited Destroy;
 | |
| end;
 | |
| 
 | |
| { Add file as nested Mime part of FilesEntity block }
 | |
| procedure TIpFormDataEntity.AddFile(const aFileName,
 | |
|                                     aContentType,
 | |
|                                     aSubtype : string;
 | |
|                                     aEncoding : TIpMimeEncodingMethod);
 | |
| var
 | |
|   Blk : TIpMimeEntity;
 | |
|   MS : TIpMemMapStream;
 | |
| begin
 | |
|   if not Assigned(FFilesEntity) then begin
 | |
|     FFilesEntity := NewMimePart;
 | |
|     FFilesEntity.EntityName := strFiles;
 | |
|     FFilesEntity.ContentDispositionType := strFormData;
 | |
|     FFilesEntity.ContentType := strMultipart;
 | |
|     FFilesEntity.ContentSubtype := strMixed;
 | |
|   end;
 | |
| 
 | |
|   Blk := FFilesEntity.NewMimePart;
 | |
|   Blk.ContentDispositionType := strAttachment;
 | |
|   Blk.ContentType := aContentType;
 | |
|   Blk.ContentSubtype := aSubtype;
 | |
|   Blk.ContentTransferEncoding := aEncoding;
 | |
| 
 | |
|   MS := TIpMemMapStream.Create(aFileName, True, False);
 | |
|   try
 | |
|     MS.Open;
 | |
|     Blk.EncodeBodyStream(MS, aFileName);
 | |
|   finally
 | |
|     MS.Free;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| { Add FormData Mime part }
 | |
| procedure TIpFormDataEntity.AddFormData(const aName, aText : string);
 | |
| var
 | |
|   Blk : TIpMimeEntity;
 | |
| begin
 | |
|   Blk := NewMimePart;
 | |
|   Blk.EntityName := aName;
 | |
|   Blk.ContentDispositionType := strFormData;
 | |
|   Blk.Body.WriteLine(aText);
 | |
| end;
 | |
| 
 | |
| { Generate raw Mime message and save to stream }
 | |
| procedure TIpFormDataEntity.SaveToStream(aStream : TStream);
 | |
| var
 | |
|   TS : TIpAnsiTextStream;
 | |
|   SL : TStringList;
 | |
| begin
 | |
|   TS := TIpAnsiTextStream.Create(aStream);
 | |
|   try
 | |
|     SL := TStringList.Create;
 | |
|     try
 | |
|       EncodeMimeHeaders(SL);
 | |
|       SL.SaveToStream(TS);
 | |
|       EncodeEntity(TS);
 | |
|     finally
 | |
|       SL.Free;
 | |
|     end;
 | |
|   finally
 | |
|     TS.Free;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| {HTTP Authentication Support -- .02}
 | |
| function IpBase64EncodeString(const InStr: string): string;              {!!.03}
 | |
| {
 | |
| encode a string into Base64, intended for producing short ( < 100 chars or so)
 | |
| coded strings to be passed as part of HTTP authentications via HTTP headers.
 | |
| 
 | |
| NO LINE ORIENTED SMARTS: if you need to work with blocks of text use the
 | |
| IpMsg class
 | |
| }
 | |
| var
 | |
|   CvtBuff: PChar;
 | |
|   I, Ct, Count, OutLen: Cardinal;
 | |
| 
 | |
| function CodeByte(byt : Byte) : char;
 | |
| {- encode 6-bit value to BinHex char and send it }
 | |
| begin
 | |
|   Result := Ip64Table[byt and $3F];
 | |
| end;
 | |
| 
 | |
| begin
 | |
|   Result := '';
 | |
|   Count := Length(InStr);
 | |
|   if Count = 0 then // empty input string nothing to encode              {!!.03}
 | |
|     Exit;                                                                {!!.03}
 | |
|   OutLen := Count * 2; // leave plenty of room for encoded string        {!!.03}
 | |
|   GetMem(CvtBuff, OutLen + 1);
 | |
| 
 | |
|   Ct := 0;
 | |
|   I := 1;
 | |
| 
 | |
|   if Count >= 3 then begin                                               {!!.03}
 | |
|     while I <= (Count - 2) do begin
 | |
|       { Encode 1st byte }
 | |
|       CvtBuff[Ct] := CodeByte(Ord(InStr[I]) shr 2);
 | |
|       Inc(Ct);
 | |
| 
 | |
|       { Encode 1st/2nd byte }
 | |
|       CvtBuff[Ct]  := CodeByte((Ord(InStr[I]) shl 4) or (Ord(InStr[I+1]) shr 4));
 | |
|       Inc(Ct);
 | |
| 
 | |
|       { Encode 2nd/3rd byte }
 | |
|       CvtBuff[Ct] := CodeByte((Ord(InStr[I+1]) shl 2) or (Ord(InStr[I+2]) shr 6));
 | |
|       Inc(Ct);
 | |
| 
 | |
|       { Encode 3rd byte }
 | |
|       CvtBuff[Ct] := CodeByte(Ord(InStr[I+2]) and $3F);
 | |
|       Inc(Ct);
 | |
| 
 | |
|       Inc(I, 3);
 | |
|     end;
 | |
|   end;                                                                   {!!.03}
 | |
| 
 | |
|   { Are there odd bytes to add? }
 | |
|   if (I <= Count) then begin
 | |
|     CvtBuff[Ct] := CodeByte(Ord(InStr[I]) shr 2);
 | |
|     Inc(Ct);
 | |
| 
 | |
|     { One odd byte }
 | |
|     if I = Count then begin
 | |
|       CvtBuff[Ct] := CodeByte((Ord(InStr[I]) shl 4) and $30);
 | |
|       Inc(Ct);
 | |
|       CvtBuff[Ct] := '='; // pad char
 | |
|       Inc(Ct);
 | |
|     { Two odd bytes }
 | |
|     end else begin
 | |
|       CvtBuff[Ct] := CodeByte(((Ord(InStr[I]) shl 4) and $30)
 | |
|         or ((Ord(InStr[I+1]) shr 4) and $0F));
 | |
|       Inc(Ct);
 | |
|       CvtBuff[Ct] := CodeByte((Ord(InStr[I+1]) shl 2) and $3C);
 | |
|       Inc(Ct);
 | |
|     end;
 | |
|     { Add padding }
 | |
|       CvtBuff[Ct] := '=';
 | |
|       Inc(Ct);
 | |
|   end;
 | |
| 
 | |
|   CvtBuff[Ct] := #0;
 | |
|   Result := StrPas(CvtBuff);
 | |
|   FreeMem(CvtBuff, OutLen + 1);
 | |
| end;
 | |
| 
 | |
| 
 | |
| end.
 | 
