Mail2000.pas
上传用户:yjb1804
上传日期:2021-01-30
资源大小:3105k
文件大小:159k
源码类别:

Email服务器

开发平台:

Delphi

  1. (*
  2. Component name...................: Mail2000 (Mail2000.pas)
  3. Classes implemented..............: TPOP2000, TSMTP2000, TMailMessage2000
  4. Version..........................: 1.10.3
  5. Status...........................: Beta
  6. Last update......................: 2002-03-25
  7. Author...........................: Marcello Tavares
  8. Website..........................: http://groups.yahoo.com/group/tmail2000
  9. Comments, bugs, suggestions to...: tmail2000@yahoogroups.com
  10. Language.........................: English
  11. Platform (tested)................: Windows 95/98/98SE/2000/XP
  12. Requires.........................: Borland Delphi 5 Professional or better
  13. Features
  14. --------
  15. 1. Retrieve and delete messages from POP3 servers;
  16. 2. Send messages through SMTP servers;
  17. 3. Parse MIME or UUCODE messages in header, body, alternative texts and
  18.    attachments;
  19. 4. Create or modify MIME messages on-the-fly;
  20. 5. HTML and embedded graphics support;
  21. 6. Save or retrieve messages or attachments from files or streams;
  22. 7. Ideal for automated e-mail processing.
  23. Know limitations
  24. ----------------
  25. 1. Does not build UUCODE messages;
  26. 2. Some problems when running on Windows ME (worth a try);
  27. 3. Unexpected behavior when netlink not present;
  28. 4. Some troubles when handling very big messages;
  29. 5. Some bugs and memory leaks;
  30. How to install
  31. --------------
  32. Create a directory;
  33. Extract archive contents on it;
  34. Open Delphi;
  35. Click File/Close All;
  36. Click File/Open;
  37. Select TMail2000.dpk;
  38. Compile the Package;
  39. Install the Package;
  40. Click File/Close All;
  41. Click Tools/Environment/Library and add the path to where you extracted the components;
  42. Now try to build and run Demo.dpr
  43. How to use
  44. ----------
  45. 1. The better way to learn is play with the demo application;
  46. 2. I'm not planning to type a help file;
  47. 3. Fell free to mail your questions to me, expect aswer for 1-2 weeks;
  48. 4. See 'Discussion Group' section below;
  49. 5. Always check for the newest version at component website;
  50. 6. When upgrading, always check out 'What's New' section;
  51. 7. Good luck!
  52. License stuff
  53. -------------
  54. Mail2000 Copyleft 1999-2002
  55. This software is provided as-is, without any express or implied
  56. warranty. In no event will the author be held liable for any damages
  57. arising from the use of this software.
  58. As a freeware, the author reserve your rights to not provide support,
  59. requested changes in the code, specific versions, improvements of any
  60. kind and bug fixes. The main purpose is to help a little the programmers
  61. community over the world as a whole, not just one person or organization.
  62. Permission is granted to anyone to use this software for any purpose,
  63. including commercial applications, and to alter it and redistribute it
  64. freely, subject to the following restrictions:
  65. 1. The origin of this software must not be misrepresented, you must not
  66.    claim that you wrote the original software.
  67. 2. If you use this software in a product, the author must known all details
  68.    about its release, including objectives, availability and contact info. An
  69.    acknowledgment in the product documentation is required.
  70. 3. Altered source versions must be plainly marked as such, and must not be
  71.    misrepresented as being an original software. When redistributed, a copy
  72.    must always be sent to the author.
  73. Please, consider my hard work.
  74. Thanks to
  75. ---------
  76. Mariano D. Podesta (marianopodesta@usa.net) - The author of wlPop3
  77. component, from where I copied some decoding routines;
  78. Sergio Kessler (sergio@perio.unlp.edu.ar) - The author of SakEmail
  79. component, from where I based my encoding and SMTP algorithms;
  80. Delphi Super Page (http://delphi.icm.edu.pl) and Torry Delphi Page
  81. (http://www.torry.ru) - For providing the best way to find great libraries
  82. and to join the Delphi community;
  83. Yunarso Anang (yasx@hotmail.com) - For providing some functions for
  84. correct treatment of oriental charsets;
  85. Christian Bormann (chris@xynx.de) - For giving a lot of suggestions
  86. and hard testing;
  87. Tommy Andersen (sorry, I lost his address) - For warning about some
  88. bugs in code;
  89. Kunikazu Okada (kunikazu@okada.cc) - For detailed and careful suggestions
  90. to help mail composition;
  91. Anderson (andermuller@conex.com.br) - Advices;
  92. Rene de Jong (rmdejong@ism.nl) - Extensive bugfixes;
  93. Hou Yg (yghou@yahoo.com) - Improvements;
  94. Peter Baars (peter.baars@elburg.nl) - Bugfixes;
  95. Giuseppe Mingolla (gmingolla@criptanet.it) - AttachStream method;
  96. Milkopb (milkopb@yahoo.com) - Bugfixes;
  97. David P. Schwartz (davids@desertigloo.com) - Suggestions and bugfixes;
  98. John GBA (john@gbasolutions.co.uk) - Testing;
  99. Mike (michaeldmount@yahoo.com) - Return-Receipt support and bugfixes;
  100. Mabiao Michael (mabiao_michael@yahoo.com) - SMTP authentication code;
  101. Anyone interested in helping me to improve this component, including you,
  102. just by downloading it.
  103. What's new in 1.1 version
  104. -------------------------
  105. 1.  Fixed the threatment of encoded fields in header;
  106. 2.  Fixed some fake attachments found in message;
  107. 3.  Included a string property "LastMessage" containing the source of
  108.     last message retrieved;
  109. 4.  Now decoding file names;
  110. 5.  Fixed way to identify kind of host address;
  111. 6.  Added support for some tunnel proxy servers (eg via telnet port);
  112. 7.  Socket changed to non-blocking to improve communication;
  113. 8.  Fixed crashes when decoding encoded labels;
  114. 9.  Fixed header decoding with ansi charsets;
  115. 10. Fixed crashes when there are deleted messages on server;
  116. 11. Now recognizing text/??? file attachments;
  117. 12. Added Content-ID label at attachment header, now you can reference
  118.     attached files on HTML code as <img src=cid:file.ext>;
  119. 13. Improved a lot the speed when decoding messages;
  120. 14. Thousands of minor bug fixes.
  121. What's new in 1.2 version
  122. -------------------------
  123. 1.  Added HELO command when talking to SMTP server;
  124. 2.  Changed CCO: fields (in portuguese) to BCC:
  125. 3.  It doesn't remove BCC: field after SMTP send anymore;
  126. 4.  Some random bugs fixed.
  127. What's new in 1.3 version
  128. -------------------------
  129. 1.  POP and SMTP routines discontinued, but they will remain in the code;
  130. 2.  Some suggestions added.
  131. What's new in 1.4 version
  132. -------------------------
  133. 1.  Improved UUCODE decoding;
  134. 2.  Range overflow bugs fixed;
  135. 3.  Changed MailMessage to MailMessage2000 to avoid class name conflicts.
  136. What's new in 1.5 version
  137. -------------------------
  138. 1.  I decided to improve POP and SMTP, but still aren't reliable;
  139. 2.  Another sort of bug fixes;
  140. 3.  TPOP2000.RetrieveHeader procedure added;
  141. 4.  TPOP2000.DeleteAfterRetrieve property added;
  142. 5.  Improved threatment of messages with no text parts;
  143. 6.  Proxy support will remain, but has been discontinued;
  144. 7.  TMailMessage2000.LoadFromFile procedure added;
  145. 8.  TMailMessage2000.SaveToFile procedure added.
  146. What's new in 1.6 version
  147. -------------------------
  148. 1.  Fixed expecting '+OK ' instead of '+OK' from SMTP;
  149. 2.  Stopped using TClientSocket.ReceiveLength, which is innacurate.
  150. What's new in 1.7 version
  151. -------------------------
  152. 1.  Handling of 'Received' (hop) headers. Now it is possible to trace the
  153.     path e-mail went on;
  154. 2.  Again, bug fixes;
  155. 3.  Added properties to read (and just to read) 'To:' information and 'Cc:'
  156.     information using TStringList;
  157. 4.  Added procedures to set destinations in comma-delimited format;
  158. 5.  Removed text/rtf handling.
  159. What's new in 1.8 version
  160. -------------------------
  161. 1.  Guess what? Bug fixes;
  162. 2.  Some memory leaks identified and fixed;
  163. 3.  Improved SMTP processing;
  164. 4.  Exception fixed in function 'Fill';
  165. 5.  Added 'AttachStream' method.
  166. What's new in 1.9 version
  167. -------------------------
  168. 1.  Exceptions fixed on date handling;
  169. 2.  Improved 'Received' header handling;
  170. 3.  Added 'Mime-Version' field;
  171. 4.  Added 'Content-Length' field;
  172. 5.  Fixed bug when there is comma on sender/recipient name;
  173. 6.  Several compatibility improvements;
  174. 7.  Several redundancies removed;
  175. 8.  Added 'Embedded' option for attachments;
  176. 9.  Improved mail bulding structure and algorithm;
  177. 10. Added 'FindParts' to identify texts and attachments of foreing messages;
  178. 11. Removed 'GetAttachList' (replaced by 'FindParts');
  179. 12. Added 'Normalize' to reformat foreing messages on Mail2000 standards;
  180. 13. Changed 'SetTextPlain' and 'SetTextHTML' to work with String type;
  181. 14. Added 'LoadFromStream' and 'SaveToStream';
  182. 15. Added 'MessageSource' read/write String property;
  183. 16. Added 'GetUIDL' method to POP component;
  184. 17. Added 'DetachFile' method;
  185. 18. Added 'Abort' method to POP and SMTP components;
  186. 19. Better handling of recipient fields (TMailRecipients);
  187. 20. Added 'AttachString' method;
  188. 21. Added 'AddHop' method;
  189. 22. Added 'SendMessageTo' method to SMTP component;
  190. 23. Added 'SendStringTo' method to SMTP component;
  191. 24. POP and SMTP components hard-tested;
  192. 25. POP and SMTP doesn't require MailMessage to work anymore;
  193. 26. Removed proxy support (but still working with ordinary proxy redirection);
  194. 27. Fixed one dot line causing SMTP to truncate the message;
  195. 28. Long lines on header now being wrapped;
  196. 29. Added 'TextEncoding' published property;
  197. 30. SendMessage will abort on first recipient rejected;
  198. 31. Treatment of date/time without seconds;
  199. 32. Better progress events behavior (but still sloppy);
  200. What's new in 1.10.x version
  201. ----------------------------
  202. 1.  Now mail will be multipart only when needed;
  203. 2.  Fixed empty attachments issues;
  204. 3.  Fixed exceptions when handing text parts;
  205. 4.  Fixed exceptions when message has empty body;
  206. 5.  Now 'Normalize' is only needed when modifying a foreing message;
  207. 6.  Now recognizing 'text' content-type;
  208. 7.  Control characters discarded in 'MailDateToDelphiDate';
  209. 8.  Repeated recipient fields now supported;
  210. 9.  Fixed Memory leak in RetrieveHeader;
  211. 10. Fixed GetTimeZoneBias problem for countries with no daylight saving;
  212. 11. Now text parts will always be in top of the section;
  213. 12. Now ignoring the charcase of encoding type descriptor;
  214. 13. Added support for 'x-uuencode' encoding type;
  215. 14. Added 'EmbedMethod' published property;
  216. 15. Added 'ContentLocationBase' published property;
  217. 16. Added Content-Location embedded file attachment method (like .mht files);
  218. 17. Attachment encoding now defined by 'AttachEncoding' published property;
  219. 18. Address fields with parenthesis now supported;
  220. 19. Added mail priority support;
  221. 20. Added return-receipt support;
  222. 21. Added SMTP authentication support;
  223. 22. Header wrapping with #32 char now supported;
  224. 23. Fixed zero-length embedded attachment;
  225. 24. Added UserName and Password published properties for SMTP;
  226. 25. Added Handshaking property to control auto-authentication of SMTP;
  227. 26. Encode trailing spaces/tabs 韓 quoted-printable process;
  228. 27. Correct treatment of 7bit encoded data (just copy...);
  229. 28. Removed 7bit encoding (never really implemented);
  230. 29. Faster quoted-printable decoding.
  231. Author data
  232. -----------
  233. Marcello Roberto Tavares Pereira
  234. mycelo@yahoo.com
  235. ICQ 5831833
  236. Sorocaba/SP - BRAZIL
  237. Spoken languages: Portuguese, English, Spanish
  238. Discussion Group
  239. ----------------
  240. Please join TMail2000 group, exchange information about mailing
  241. application development with other power programmers, and receive
  242. suggestions, advices, bugfixes and updates about this component.
  243. http://groups.yahoo.com/group/tmail2000
  244. tmail2000-subscribe@yahoogroups.com
  245. This site stores all previous messages, you can find valuable
  246. information about this component there. If you have a question,
  247. please search this site before asking me, I will not post the
  248. same answer twice.
  249. *)
  250. unit Mail2000;
  251. {Please don't remove the following line}
  252. {$BOOLEVAL OFF}
  253. interface
  254. uses
  255.   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  256.   WinSock, ScktComp, Math, Registry, ExtCtrls;
  257. type
  258.   TMailPartList = class;
  259.   TMailMessage2000 = class;
  260.   TSocketTalk = class;
  261.   TMessageSize = array of Integer;
  262.   TSessionState = (stNone, stConnect, stUser, stPass, stStat, stList, stRetr, stDele, stUIDL, stHelo, stMail, stRcpt, stEhlo, stSMTPUser, stSMTPPass, stAuthLogin, stNoop, stData, stSendData, stQuit);
  263.   TTalkError = (teGeneral, teSend, teReceive, teConnect, teDisconnect, teAccept, teTimeout, teNoError);
  264.   TEncodingType = (etBase64, etQuotedPrintable, etNoEncoding);
  265.   TNormalizer = (nrFirst, nrForce, nrAddText, nrAddAttach, nrAddEmbedded);
  266.   TEmbedMethod = (emContentID, emContentLocation);
  267.   TMailPriority = (mpHighest, mpHigh, mpNormal, mpLow, mpLowest);
  268.   THandshaking = (hsAuto, hsManual); 
  269.   TProgressEvent = procedure(Sender: TObject; Total, Current: Integer) of object;
  270.   TEndOfDataEvent = procedure(Sender: TObject; SessionState: TSessionState; Data: String; var ServerResult: Boolean) of object;
  271.   TSocketTalkErrorEvent = procedure(Sender: TObject; SessionState: TSessionState; TalkError: TTalkError) of object;
  272.   TReceiveDataEvent = procedure(Sender: TObject; Sessionstate: TSessionState; Data: String; var ServerResult: Boolean) of object;
  273.   TReceivedField = (reFrom, reBy, reFor, reDate, reNone);
  274.   TReceived = record
  275.     From: String;
  276.     By: String;
  277.     Address: String;
  278.     Date: TDateTime;
  279.   end;
  280.   { TMailPart - A recursive class to handle parts, subparts, and the mail by itself }
  281.   TMailPart = class(TComponent)
  282.   private
  283.     FHeader: TStringList {TMailText};
  284.     FBody: TMemoryStream;
  285.     FDecoded: TMemoryStream;
  286.     FParentBoundary: String;
  287.     FOwnerMessage: TMailMessage2000;
  288.     FSubPartList: TMailPartList;
  289.     FOwnerPart: TMailPart;
  290.     FIsDecoded: Boolean;
  291.     FEmbedded: Boolean;
  292.     function GetAttachInfo: String;
  293.     function GetFileName: String;
  294.     function GetBoundary: String;
  295.     function GetSource: String;
  296.     procedure Fill(Data: PChar; HasHeader: Boolean);
  297.     procedure SetSource(Text: String);
  298.   public
  299.     constructor Create(AOwner: TComponent); override;
  300. destructor Destroy; override;
  301.     function GetLabelValue(const cLabel: String): String;                     // Get the value of a label. e.g. Label: value
  302.     function GetLabelParamValue(const cLabel, Param: String): String;         // Get the value of a label parameter. e.g. Label: xxx; param=value
  303.     function LabelExists(const cLabel: String): Boolean;                      // Determine if a label exists
  304.     function LabelParamExists(const cLabel, Param: String): Boolean;          // Determine if a label parameter exists
  305.     function Decode: Boolean;                                                 // Decode Body stream into Decoded stream and result true if successful
  306.     procedure Encode(const ET: TEncodingType);
  307.     procedure EncodeText;                                                     // Encode Decoded stream into Body stream using quoted-printable
  308.     procedure EncodeBinary;                                                   // Encode Decoded stream into Body stream using Base64
  309.     procedure SetLabelValue(const cLabel, cValue: String);                    // Set the value of a label
  310.     procedure SetLabelParamValue(const cLabel, cParam, cValue: String);       // Set the value of a label parameter
  311.     procedure Remove;                                                         // Delete this mailpart from message
  312.     procedure LoadFromFile(FileName: String);                                 // Load the data from a file
  313.     procedure SaveToFile(FileName: String);                                   // Save the data to a file
  314.     procedure LoadFromStream(Stream: TStream);                                // Load the data from a stream
  315.     procedure SaveToStream(Stream: TStream);                                  // Save the data to a stream
  316.     property PartSource: String read GetSource write SetSource;
  317.     property Header: TStringList read FHeader;                                // The header text
  318.     property Body: TMemoryStream read FBody;                                  // The original body
  319.     property Decoded: TMemoryStream read FDecoded;                            // Stream with the body decoded
  320.     property SubPartList: TMailPartList read FSubPartList;                    // List of subparts of this mail part
  321.     property FileName: String read GetFileName;                               // Name of file when this mail part is an attached file
  322.     property AttachInfo: String read GetAttachInfo;                           // E.g. application/octet-stream
  323.     property OwnerMessage: TMailMessage2000 read FOwnerMessage;               // Main message that owns this mail part
  324.     property OwnerPart: TMailPart read FOwnerPart;                            // Father part of this part (can be the main message too)
  325.     property IsDecoded: Boolean read FIsDecoded;                              // If this part is decoded
  326.     property Embedded: Boolean read FEmbedded write FEmbedded;                // E.g. if is a picture inside HTML text
  327.   end;
  328.   { TMailPartList - Just a collection of TMailPart's }
  329. TMailPartList = class(TList)
  330. private
  331. function Get(const Index: Integer): TMailPart;
  332. public
  333. destructor Destroy; override;
  334. property Items[const Index: Integer]: TMailPart read Get; default;
  335. end;
  336.   { TMailRecipients - Handling of recipient fields }
  337.   TMailRecipients = class(TObject)
  338.   private
  339.     FMessage: TMailMessage2000;
  340.     FField: String;
  341.     FNames: TStringList;
  342.     FAddresses: TStringList;
  343.     FCheck: Integer;
  344.     function GetName(const Index: Integer): String;
  345.     function GetAddress(const Index: Integer): String;
  346.     function GetCount: Integer;
  347.     procedure SetName(const Index: Integer; const Name: String);
  348.     procedure SetAddress(const Index: Integer; const Address: String);
  349.     function FindName(const Name: String): Integer;
  350.     function FindAddress(const Address: String): Integer;
  351.     function GetAllNames: String;
  352.     function GetAllAddresses: String;
  353.     procedure HeaderToStrings;
  354.     procedure StringsToHeader;
  355.   public
  356.     constructor Create(MailMessage: TMailMessage2000; Field: String); //override;
  357.     destructor Destroy; override;
  358.     procedure Add(const Name, Address: String);
  359.     procedure Replace(const Index: Integer; const Name, Address: String);
  360.     procedure Delete(const Index: Integer);
  361.     procedure SetAll(const Names, Addresses: String);
  362.     procedure AddNamesTo(const Str: TStrings);
  363.     procedure AddAddressesTo(const Str: TStrings);
  364.     procedure Clear;
  365.     property Count: Integer read GetCount;
  366.     property Name[const Index: Integer]: String read GetName write SetName;
  367.     property Address[const Index: Integer]: String read GetAddress write SetAddress;
  368.     property ByName[const Name: String]: Integer read FindName;
  369.     property ByAddress[const Name: String]: Integer read FindAddress;
  370.     property AllNames: String read GetAllNames;
  371.     property AllAddresses: String read GetAllAddresses;
  372.   end;
  373.   { TMailMessage2000 - A descendant of TMailPart with some tools to handle the mail }
  374.   TMailMessage2000 = class(TMailPart)
  375.   private
  376.     FAttachList: TMailPartList;
  377.     FTextPlain: TStringList;
  378.     FTextHTML: TStringList;
  379.     FTextPlainPart: TMailPart;
  380.     FTextHTMLPart: TMailPart;
  381.     FMixedPart: TMailPart;
  382.     FRelatedPart: TMailPart;
  383.     FAlternativePart: TMailPart;
  384.     FTextFather: TMailPart;
  385.     FCharset: String;
  386.     FOnProgress: TProgressEvent;
  387.     FNameCount: Integer;
  388.     FToList: TMailRecipients;
  389.     FCcList: TMailRecipients;
  390.     FBccList: TMailRecipients;
  391.     FTextEncoding: TEncodingType;
  392.     FAttachEncoding: TEncodingType;
  393.     FEmbedMethod: TEmbedMethod;
  394.     FContentLocationBase: String;
  395.     FNeedRebuild: Boolean;
  396.     FNeedNormalize: Boolean;
  397.     FNeedFindParts: Boolean;
  398.     procedure GetSenderData(const Field: String; var Name, Address: String);
  399.     function GetReceivedCount: Integer;
  400.     function GetReceived(const Index: Integer): TReceived;
  401.     function GetAttach(const FileName: String): TMailPart;
  402.     function GetFromName: String;
  403.     function GetFromAddress: String;
  404.     function GetReplyToName: String;
  405.     function GetReplyToAddress: String;
  406.     function GetReceiptName: String;
  407.     function GetReceiptAddress: String;
  408.     function GetPriority: TMailPriority;
  409.     function GetSubject: String;
  410.     function GetDate: TDateTime;
  411.     function GetMessageId: String;
  412.     procedure PutText(Text: String; var Part: TMailPart; Content: String);
  413.     procedure RemoveText(var Part: TMailPart);
  414.     procedure SetSubject(const Subject: String);
  415.     procedure SetDate(const Date: TDateTime);
  416.     procedure SetMessageId(const MessageId: String);
  417.     procedure SetPriority(const Priority: TMailPriority);
  418.   public
  419.     constructor Create(AOwner: TComponent); override;
  420.     destructor Destroy; override;
  421.     procedure SetFrom(const Name, Address: String);                           // Create/modify the From: field
  422.     procedure SetReplyTo(const Name, Address: String);                        // Create/modify the Reply-To: field
  423.     procedure SetReceipt(const Name, Address: String);                        // Create/modify the Return-Receipt field
  424.     procedure FindParts;                                                      // Search for the attachments and texts
  425.     procedure Normalize(const Kind: TNormalizer = nrFirst);                                                      // Reconstruct message on Mail2000 standards (multipart/mixed)
  426.     procedure RebuildBody;                                                    // Build the raw mail body according to mailparts
  427.     procedure Reset;                                                          // Clear all stored data in the object
  428.     procedure SetTextPlain(const Text: String);                               // Create/modify a mailpart for text/plain (doesn't rebuild body)
  429.     procedure SetTextHTML(const Text: String);                                // Create/modify a mailpart for text/html (doesn't rebuild body)
  430.     procedure RemoveTextPlain;                                                // Remove the text/plain mailpart (doesn't rebuild body)
  431.     procedure RemoveTextHTML;                                                 // Remove the text/html mailpart (doesn't rebuild body)
  432.     procedure AttachFile(const FileName: String; const ContentType: String = ''; const IsEmbedded: Boolean = False);
  433.               // Create a mailpart and encode a file on it (doesn't rebuild body)
  434.     procedure AttachString(const Text, FileName: String; const ContentType: String = ''; const IsEmbedded: Boolean = False);
  435.               // Create a mailpart and encode a string on it (doesn't rebuild body)
  436.     procedure AttachStream(const AStream: TStream; const FileName: String; const ContentType: String = ''; const IsEmbedded: Boolean = False);
  437.               // Create a mailpart and encode a stream on it (doesn't rebuild body)
  438.     procedure DetachFile(const FileName: String);
  439.               // Remove attached file from message by name
  440.     procedure DetachFileIndex(const Index: Integer);
  441.               // Remove attached file from message by index of AttachList
  442.     procedure AddHop(const From, By, Aplic, Address: String);                 // Add a 'Received:' in message header
  443.     property Received[const Index: Integer]: TReceived read GetReceived;      // Retrieve the n-th 'Received' header
  444.     property ReceivedCount: Integer read GetReceivedCount;                    // Count the instances of 'Received' fields (hops)
  445.     property AttachByName[const FileName: String]: TMailPart read GetAttach;  // Returns the MailPart of an attachment by filename
  446.     property ToList: TMailRecipients read FToList;                            // Handling of To: recipients
  447.     property CcList: TMailRecipients read FCcList;                            // Handling of Cc: recipients
  448.     property BccList: TMailRecipients read FBccList;                          // Handling of Bcc: recipients
  449.     {$WARNINGS OFF}
  450.     property MessageSource: String read GetSource write SetSource;
  451.     {$WARNINGS ON}
  452.     property FromName: String read GetFromName;                               // Retrieve the From: name
  453.     property FromAddress: String read GetFromAddress;                         // Retrieve the From: address
  454.     property ReplyToName: String read GetReplyToName;                         // Retrieve the Reply-To: name
  455.     property ReplyToAddress: String read GetReplyToAddress;                   // Retrieve the Reply-To: address
  456.     property ReceiptName: String read GetReceiptName;                         // Retrieve the Return-Receipt name
  457.     property ReceiptAddress: String read GetReceiptAddress;                   // Retrieve the Return-Receipt address
  458.     property Subject: String read GetSubject write SetSubject;                // Retrieve or set the Subject: string
  459.     property Date: TDateTime read GetDate write SetDate;                      // Retrieve or set the Date: in TDateTime format
  460.     property MessageId: String read GetMessageId write SetMessageId;          // Retrieve or set the Message-Id:
  461.     property Priority: TMailPriority read GetPriority write SetPriority;      // Retrieve or set the mail priority
  462.     property AttachList: TMailPartList read FAttachList;                      // A list of all attached files
  463.     property TextPlain: TStringList read FTextPlain;                          // A StringList with the text/plain from message
  464.     property TextHTML: TStringList read FTextHTML;                            // A StringList with the text/html from message
  465.     property TextPlainPart: TMailPart read FTextPlainPart;                    // The text/plain part
  466.     property TextHTMLPart: TMailPart read FTextHTMLPart;                      // The text/html part
  467.     property NeedRebuild: Boolean read FNeedRebuild;                          // True if RebuildBody is needed
  468.     property NeedNormalize: Boolean read FNeedNormalize;                      // True if message needs to be normalized
  469.     property NeedFindParts: Boolean read FNeedFindParts;                      // True if message has parts to be searched for
  470.   published
  471.     property Charset: String read FCharSet write FCharset;                                     // Charset to build headers and text
  472.     property TextEncoding: TEncodingType read FTextEncoding write FTextEncoding;               // How text will be encoded
  473.     property AttachEncoding: TEncodingType read FAttachEncoding write FAttachEncoding;         // How attachments will be encoded
  474.     property EmbedMethod: TEmbedMethod read FEmbedMethod write FEmbedMethod;                   // Embedded files attachment method
  475.     property ContentLocationBase: String read FContentLocationBase write FContentLocationBase; // Base location for Content-Location embedded files
  476.     property OnProgress: TProgressEvent read FOnProgress write FOnProgress;                    // Occurs when storing message in memory
  477.   end;
  478.   { TSocketTalk }
  479.   TSocketTalk = class(TComponent)
  480.   private
  481.     FTimeOut: Integer;
  482.     FExpectedEnd: String;
  483.     FLastResponse: String;
  484.     FDataSize: Integer;
  485.     FPacketSize: Integer;
  486.     FTalkError: TTalkError;
  487.     FSessionState: TSessionState;
  488.     FClientSocket: TClientSocket;
  489.     FWaitingServer: Boolean;
  490.     FTimer: TTimer;
  491.     FServerResult: Boolean;
  492.     FOnProgress: TProgressEvent;
  493.     FOnEndOfData: TEndOfDataEvent;
  494.     FOnSocketTalkError: TSocketTalkErrorEvent;
  495.     FOnReceiveData: TReceiveDataEvent;
  496.     FOnDisconnect: TNotifyEvent;
  497.     procedure SocketRead(Sender: TObject; Socket: TCustomWinSocket);
  498.     procedure SocketError(Sender: TObject; Socket: TCustomWinSocket; ErrorEvent: TErrorEvent; var ErrorCode: Integer);
  499.     procedure SocketDisconnect(Sender: TObject; Socket: TCustomWinSocket);
  500.     procedure Timer(Sender: TObject);
  501.   public
  502.     constructor Create(AOwner: TComponent); override;
  503.     destructor Destroy; override;
  504.     procedure Talk(Buffer, EndStr: String; SessionState: TSessionState);
  505.     procedure Cancel;
  506.     procedure ForceState(SessionState: TSessionState);
  507.     procedure WaitServer;
  508.     property LastResponse: String read FLastResponse;
  509.     property DataSize: Integer read FDataSize write FDataSize;
  510.     property PacketSize: Integer read FPacketSize write FPacketSize;
  511.     property TimeOut: Integer read FTimeOut write FTimeOut;
  512.     property TalkError: TTalkError read FTalkError;
  513.     property ClientSocket: TClientSocket read FClientSocket;
  514.     property ServerResult: Boolean read FServerResult;
  515.     property OnEndOfData: TEndOfDataEvent read FOnEndOfData write FOnEndOfData;
  516.     property OnSocketTalkError: TSocketTalkErrorEvent read FOnSocketTalkError write FOnSocketTalkError;
  517.     property OnReceiveData: TReceiveDataEvent read FOnReceiveData write FOnReceiveData;
  518.     property OnProgress: TProgressEvent read FOnProgress write FOnProgress;
  519.     property OnDisconnect: TNotifyEvent read FOnDisconnect write FOnDisconnect;
  520.   end;
  521.   { TPOP2000 }
  522.   TPOP2000 = class(TComponent)
  523.   private
  524.     FMailMessage: TMailMessage2000;
  525.     FSessionMessageCount: Integer;
  526.     FSessionMessageSize: TMessageSize;
  527.     FSessionConnected: Boolean;
  528.     FSessionLogged: Boolean;
  529.     FLastMessage: String;
  530.     FSocketTalk: TSocketTalk;
  531.     FUserName: String;
  532.     FPassword: String;
  533.     FPort: Integer;
  534.     FHost: String;
  535.     FDeleteOnRetrieve: Boolean;
  536.     procedure EndOfData(Sender: TObject; SessionState: TSessionState; Data: String; var ServerResult: Boolean);
  537.     procedure SocketTalkError(Sender: TObject; SessionState: TSessionState; TalkError: TTalkError);
  538.     procedure ReceiveData(Sender: TObject; SessionState: TSessionState; Data: String; var ServerResult: Boolean);
  539.     procedure SocketDisconnect(Sender: TObject);
  540.     function GetTimeOut: Integer;
  541.     procedure SetTimeOut(Value: Integer);
  542.     function GetProgress: TProgressEvent;
  543.     procedure SetProgress(Value: TProgressEvent);
  544.     function GetLastResponse: String;
  545.     function SendCMD(CMD:string ;state:TSessionState):string;
  546.   public
  547.     constructor Create(AOwner: TComponent); override;
  548.     destructor Destroy; override;
  549.     function Connect: Boolean;                                                // Connect to mail server
  550.     function Login: Boolean;                                                  // Autenticate to mail server
  551.     function Quit: Boolean;                                                   // Logout and disconnect
  552.     procedure Abort;                                                          // Force disconnect
  553.     function RetrieveMessageByUIDL(UIDL: String): Boolean;
  554.     function RetrieveMessage(Number: Integer): Boolean;                       // Retrieve mail number # and put in MailMessage
  555.     function RetrieveHeader(Number: Integer; Lines: Integer = 0): Boolean;    // Retrieve header and put in MailMessage
  556.     function DeleteMessage(Number: Integer): Boolean;                         // Delete mail number #
  557.     function GetUIDL(Number: Integer): String;                                // Get UIDL from mail number #
  558.     function  GetUIDLS(List:TStrings):Boolean;
  559.     property SessionMessageCount: Integer read FSessionMessageCount;          // Number of messages found on server
  560.     property SessionMessageSize: TMessageSize read FSessionMessageSize;       // Dynamic array with size of the messages
  561.     property SessionConnected: Boolean read FSessionConnected;                // True if conencted to server
  562.     property SessionLogged: Boolean read FSessionLogged;                      // True if autenticated on server
  563.     property LastMessage: String read FLastMessage;                           // Last integral message text
  564.     property LastResponse: String read GetLastResponse;                       // Last string received from server
  565.   published
  566.     property UserName: String read FUserName write FUserName;                 // User name to login on server
  567.     property Password: String read FPassword write FPassword;                 // Password
  568.     property Port: Integer read FPort write FPort;                            // Port (usualy 110)
  569.     property Host: String read FHost write FHost;                             // Host address
  570.     property MailMessage: TMailMessage2000 read FMailMessage write FMailMessage;  // Message retrieved
  571.     property TimeOut: Integer read GetTimeOut write SetTimeOut;               // Max time to wait for server reply in seconds
  572.     property OnProgress: TProgressEvent read GetProgress write SetProgress;   // Occurs when receiving data from server
  573.     property DeleteOnRetrieve: Boolean read FDeleteOnRetrieve write FDeleteOnRetrieve;  // If message will be deleted after successful retrieve
  574.   end;
  575.   { TSMTP2000 }
  576.   TSMTP2000 = class(TComponent)
  577.   private
  578.     FMailMessage: TMailMessage2000;
  579.     FSessionConnected: Boolean;
  580.     FSocketTalk: TSocketTalk;
  581.     FPacketSize: Integer;
  582.     FNeedAuthentication: Boolean;
  583.     FHandshaking: THandshaking;
  584.     FUserName: String;
  585.     FPassword: String;
  586.     FPort: Integer;
  587.     FHost: String;
  588.     procedure EndOfData(Sender: TObject; SessionState: TSessionState; Data: String; var ServerResult: Boolean);
  589.     procedure SocketTalkError(Sender: TObject; SessionState: TSessionState; TalkError: TTalkError);
  590.     procedure ReceiveData(Sender: TObject; SessionState: TSessionState; Data: String; var ServerResult: Boolean);
  591.     procedure SocketDisconnect(Sender: TObject);
  592.     function GetTimeOut: Integer;
  593.     procedure SetTimeOut(Value: Integer);
  594.     function GetProgress: TProgressEvent;
  595.     procedure SetProgress(Value: TProgressEvent);
  596.     function GetLastResponse: String;
  597.   public
  598.     constructor Create(AOwner: TComponent); override;
  599.     destructor Destroy; override;
  600.     function Connect: Boolean;                                                // Connect to mail server
  601.     function Helo: Boolean;                                                   // Sends a HELO command
  602.     function Ehlo: Boolean;                                                   // Sends a EHLO command
  603.     function AuthLogin: Boolean;                                              // Sends a AUTH LOGIN command
  604.     function Login: Boolean;                                                  // Sends user name and password
  605.     function Quit: Boolean;                                                   // Disconnect
  606.     procedure Abort;                                                          // Force disconnect
  607.     function SendMessage: Boolean;                                            // Send MailMessage to server
  608.     function SendMessageTo(const From, Dests: String): Boolean;               // Send MailMessage to specified recipients
  609.     function SendStringTo(const Msg, From, Dests: String): Boolean;           // Send string to specified recipients
  610.     property SessionConnected: Boolean read FSessionConnected;                // True if conencted to server
  611.     property LastResponse: String read GetLastResponse;                       // Last string received from server
  612.     property NeedAuthentication: Boolean read FNeedAuthentication;            // If connected server requests authentication
  613.   published
  614.     property Port: Integer read FPort write FPort;                                // Port (usualy 25)
  615.     property Host: String read FHost write FHost;                                 // Host address
  616.     property TimeOut: Integer read GetTimeOut write SetTimeOut;                   // Max time to wait for a response in seconds
  617.     property MailMessage: TMailMessage2000 read FMailMessage write FMailMessage;  // Message to send
  618.     property PacketSize: Integer read FPacketSize write FPacketSize;              // Size of packets to send to server
  619.     property OnProgress: TProgressEvent read GetProgress write SetProgress;       // Occurs when sending data to server
  620.     property Handshaking: THandshaking read FHandshaking write FHandshaking;      // How connection will behave
  621.     property UserName: String read FUserName write FUserName;                     // User name for authentication
  622.     property Password: String read FPassword write FPassword;                     // Password for authentication
  623.   end;
  624. procedure Register;
  625. { Very useful functions ====================================================== }
  626. function DecodeLine7Bit(Texto: String): String; forward;
  627. function EncodeLine7Bit(const Texto, Charset: String): String; forward;
  628. function DecodeQuotedPrintable(const Texto: String): String; forward;
  629. function EncodeQuotedPrintable(const Texto: String; const HeaderLine: Boolean): String; forward;
  630. function DecodeUUCODE(Encoded: PChar; Decoded: TMemoryStream): Boolean; forward;
  631. function DecodeLineUUCODE(const Buffer: String; Decoded: PChar): Integer; forward;
  632. function DecodeLineBASE64(const Buffer: String; Decoded: PChar): Integer; forward;
  633. function EncodeBASE64(Encoded: TMemoryStream; Decoded: TMemoryStream): Integer; forward;
  634. function EncodeBASE64String(const Decoded: String): String;
  635. function NormalizeLabel(Texto: String): String; forward;
  636. function LabelValue(cLabel: String): String; forward;
  637. function WriteLabelValue(cLabel, Value: String): String; forward;
  638. function LabelParamValue(cLabel, cParam: String): String; forward;
  639. function WriteLabelParamValue(cLabel, cParam, Value: String): String; forward;
  640. function GetTimeZoneBias: Double; forward;
  641. function PadL(const Str: String; const Tam: Integer; const PadStr: String): String; forward;
  642. function GetMimeType(const FileName: String): String; forward;
  643. function GetMimeExtension(const MimeType: String): String; forward;
  644. function GenerateBoundary: String; forward;
  645. function SearchStringList(Lista: TStringList; const Chave: String; const Occorrence: Integer = 0): Integer; forward;
  646. procedure DataLine(var Data, Line: String; var nPos: Integer); forward;
  647. procedure DataLinePChar(const Data: PChar; const TotalLength: Integer; var LinePos, LineLen: Integer; var Line: PChar; var DataEnd: Boolean); forward;
  648. function IsIPAddress(const SS: String): Boolean; forward;
  649. function TrimSpace(const S: string): string; forward;
  650. function TrimLeftSpace(const S: string): string; forward;
  651. function TrimRightSpace(const S: string): string; forward;
  652. function MailDateToDelphiDate(const DateStr: String): TDateTime; forward;
  653. function DelphiDateToMailDate(const Date: TDateTime): String; forward;
  654. function ValidFileName(FileName: String): String; forward;
  655. function WrapHeader(Text: String): String; forward;
  656. function GetToken(const Token: Integer; const Text, Separators, QuoteStart, QuoteEnd: String): String;
  657. implementation
  658. const
  659.   _C_T  = 'Content-Type';
  660.   _C_D  = 'Content-Disposition';
  661.   _C_TE = 'Content-Transfer-Encoding';
  662.   _C_ID = 'Content-ID';
  663.   _C_L  = 'Content-Length';
  664.   _C_LC = 'Content-Location';
  665.   _CONT = 'Content-';
  666.   _X_M2 = 'X-Mail2000-';
  667.   _FFR  = 'From';
  668.   _FRT  = 'Reply-To';
  669.   _M_V  = 'Mime-Version';
  670.   _M_ID = 'Message-ID';
  671.   _X_M  = 'X-Mailer';
  672.   _XM2A = 'X-Mail2000-Attachment';
  673.   _XPRI = 'X-Priority';
  674.   _DNT  = 'Disposition-Notification-To';
  675. const
  676.   _TXT  = 'text/';
  677.   _TEX  = 'text';
  678.   _T_P  = 'text/plain';
  679.   _T_H  = 'text/html';
  680.   _MP   = 'multipart/';
  681.   _M_M  = 'multipart/mixed';
  682.   _M_A  = 'multipart/alternative';
  683.   _M_R  = 'multipart/related';
  684.   _M_RP = 'multipart/report';
  685.   _A_OS = 'application/octet-stream';
  686.   _BDRY = 'boundary';
  687.   _ATCH = 'attachment';
  688.   _INLN = 'inline';
  689.   _NAME = 'name';
  690.   _FLNM = 'filename';
  691.   _CSET = 'charset';
  692.   _E_QP = 'quoted-printable';
  693.   _E_UU = 'uucode';
  694.   _E_XU = 'x-uuencode';
  695.   _E_BA = 'base64';
  696.   _E_7B = '7bit';
  697.   _EMBD = 'embedded';
  698.   _UUBG = 'begin 6';
  699.   _UUEN = 'end';
  700. const
  701.   _MIME_Msg = 'This is a multipart message in mime format.'#13#10;
  702.   _XMailer  = 'Mail2000 1.10 http://groups.yahoo.com/group/tmail2000';
  703.   _TXTFN    = 'textpart.txt';
  704.   _HTMLFN   = 'textpart.htm';
  705.   _CHARSET  = 'iso-8859-1';
  706.   _DATAEND1 = #13#10'.'#13#10;
  707.   _DATAEND2 = #13#10'..'#13#10;
  708.   _LINELEN  = 72;
  709. const
  710.   _E_UUCD = 'UUCODE message format error';
  711.   _E_MLPT = '%s: TMailPart must be owned by a TMailMessage2000';
  712.   _E_ATFN = '%s: Attachment filename not found "%s"';
  713.   _E_ATIN = '%s: Attachment index not found';
  714.   _E_MMUN = '%s: MailMessage unassigned';
  715.   _E_MMNR = '%s: MailMessage need rebuild';
  716.   _E_NRTS = '%s: No recipients to send message';
  717. procedure Register;
  718. begin
  719.   RegisterComponents('TMail2000', [TPOP2000, TSMTP2000, TMailMessage2000]);
  720. end;
  721. // Decode an encoded field e.g. =?iso-8859-1?x?xxxxxx=?=
  722. function DecodeLine7Bit(Texto: String): String;
  723. var
  724.   Buffer: PChar;
  725.   Encoding: Char;
  726.   Size: Integer;
  727.   nPos0: Integer;
  728.   nPos1: Integer;
  729.   nPos2: Integer;
  730.   nPos3: Integer;
  731.   Found: Boolean;
  732. begin
  733.   Result := TrimSpace(Texto);
  734.   repeat
  735.     nPos0 := Pos('=?', Result);
  736.     Found := False;
  737.     if nPos0 > 0 then
  738.     begin
  739.       nPos1 := Pos('?', Copy(Result, nPos0+2, Length(Result)))+nPos0+1;
  740.       nPos2 := Pos('?=', Copy(Result, nPos1+1, Length(Result)))+nPos1;
  741.       nPos3 := Pos('?', Copy(Result, nPos2+1, Length(Result)))+nPos2;
  742.       if nPos3 > nPos2 then
  743.       begin
  744.         if Length(Result) > nPos3 then
  745.         begin
  746.           if Result[nPos3+1] = '=' then
  747.           begin
  748.             nPos2 := nPos3;
  749.           end;
  750.         end;
  751.       end;
  752.       if (nPos1 > nPos0) and (nPos2 > nPos1) then
  753.       begin
  754.         Texto := Copy(Result, nPos1+1, nPos2-nPos1-1);
  755.         if (Length(Texto) >= 2) and (Texto[2] = '?') and (UpCase(Texto[1]) in ['B', 'Q', 'U']) then
  756.         begin
  757.           Encoding := UpCase(Texto[1]);
  758.         end
  759.         else
  760.         begin
  761.           Encoding := 'Q';
  762.         end;
  763.         Texto := Copy(Texto, 3, Length(Texto)-2);
  764.         case Encoding of
  765.           'B':
  766.           begin
  767.             GetMem(Buffer, Length(Texto));
  768.             Size := DecodeLineBASE64(Texto, Buffer);
  769.             Buffer[Size] := #0;
  770.             Texto := String(Buffer);
  771.             FreeMem(Buffer);
  772.           end;
  773.           'Q':
  774.           begin
  775.             while Pos('_', Texto) > 0 do
  776.               Texto[Pos('_', Texto)] := #32;
  777.             Texto := DecodeQuotedPrintable(Texto);
  778.           end;
  779.           'U':
  780.           begin
  781.             GetMem(Buffer, Length(Texto));
  782.             Size := DecodeLineUUCODE(Texto, Buffer);
  783.             Buffer[Size] := #0;
  784.             Texto := String(Buffer);
  785.           end;
  786.         end;
  787.         Result := Copy(Result, 1, nPos0-1)+Texto+Copy(Result,nPos2+2,Length(Result));
  788.         Found := True;
  789.       end;
  790.     end;
  791.   until not Found;
  792. end;
  793. // Encode a header field e.g. =?iso-8859-1?x?xxxxxx=?=
  794. function EncodeLine7Bit(const Texto, Charset: String): String;
  795. begin
  796.   Result := EncodeQuotedPrintable(Texto, True);
  797.   if Result <> Texto then
  798.     Result := '=?'+Charset+'?Q?'+Result+'?='
  799.   else
  800.     Result := Texto;
  801. end;
  802. // Decode a quoted-printable encoded string
  803. function DecodeQuotedPrintable(const Texto: String): String;
  804. var
  805.   nPos: Integer;
  806.   nLastPos: Integer;
  807. //lFound: Boolean;
  808. begin
  809.   Result := '';
  810.   nPos := 1;
  811.   while nPos <= Length(Texto) do
  812.   begin
  813.     if Texto[nPos] = '=' then
  814.     begin
  815.       if (Length(Texto)-nPos) >= 2 then
  816.       begin
  817.         if (Texto[nPos+1] = #13) and (Texto[nPos+2] = #10) then
  818.         begin
  819.           Inc(nPos, 3);
  820.         end
  821.         else
  822.         begin
  823.           if (Texto[nPos+1] in ['0'..'9', 'A'..'F'])
  824.           and (Texto[nPos+2] in ['0'..'9', 'A'..'F']) then
  825.           begin
  826.             Result := Result + Char(StrToInt('$'+Texto[nPos+1]+Texto[nPos+2]));
  827.             Inc(nPos, 3)
  828.           end
  829.           else
  830.           begin
  831.             Inc(nPos, 3);
  832.           end;
  833.         end;
  834.       end
  835.       else
  836.       begin
  837.         Break;
  838.       end;
  839.     end
  840.     else
  841.     begin
  842.       nLastPos := nPos;
  843.       nPos := Pos('=', Copy(Texto, nLastPos+1, High(Integer)));
  844.       if nPos = 0 then
  845.         nPos := Length(Texto)+1;
  846.       Result := Result + Copy(Texto, nLastPos, nPos);
  847.       Inc(nPos, nLastPos);
  848.     end;
  849.   end;
  850. end;
  851. // Encode a string in quoted-printable format
  852. function EncodeQuotedPrintable(const Texto: String; const HeaderLine: Boolean): String;
  853. var
  854.   nPos: Integer;
  855.   LineLen: Integer;
  856.   Buffer: TStringList;
  857.   Line2: String;
  858. begin
  859.   Result := '';
  860.   for nPos := 1 to Length(Texto) do
  861.   begin
  862.     if (Texto[nPos] > #127) or
  863.        (Texto[nPos] = '=') or
  864.        ((Texto[nPos] < #32) and (not (Texto[nPos] in [#9, #10, #13]))) or
  865.        ((nPos < Length(Texto)) and (Texto[nPos] = #13) and (Texto[nPos+1] <> #10)) or
  866.        ((nPos = Length(Texto)) and (Texto[nPos] = #13)) or
  867.        ((nPos > 1) and (Texto[nPos] = #10) and (Texto[nPos-1] <> #13)) or
  868.        ((nPos = 1) and (Texto[nPos] = #10)) or
  869.        ((Texto[nPos] in [#9..#32, #39, '?', '<', '>', '(', ')', '"', '_']) and HeaderLine) then
  870.     begin
  871.       Result := Result + '=' + PadL(Format('%2x', [Ord(Texto[nPos])]), 2, '0');
  872.     end
  873.     else
  874.     begin
  875.       Result := Result + Texto[nPos];
  876.     end;
  877.     // Encode trailing spaces/tabs
  878.     if not HeaderLine then
  879.     begin
  880.       if (Length(Result) > 2)
  881.       and (Result[Length(Result)] = #10)
  882.       and (Result[Length(Result)-1] = #13)
  883.       and (Result[Length(Result)-2] in [#9, #32]) then
  884.       begin
  885.         LineLen := Length(Result)-2;
  886.         Line2 := '';
  887.         while (LineLen >= 1) and (Result[LineLen] in [#9, #32]) do
  888.         begin
  889.           case Result[LineLen] of
  890.             #09: Line2 := '=09'+Line2;
  891.             #32: Line2 := '=20'+Line2;
  892.           end;
  893.           Dec(LineLen);
  894.         end;
  895.         Result := Copy(Result, 1, LineLen)+Line2+#13#10;
  896.       end;
  897.     end;
  898.   end;
  899.   // Insert soft linebreaks
  900.   if not HeaderLine then
  901.   begin
  902.     Buffer := TStringList.Create;
  903.     Buffer.Text := Result;
  904.     nPos := 0;
  905.     while nPos < Buffer.Count do
  906.     begin
  907.       LineLen := Length(Buffer[nPos]);
  908.       while LineLen >= _LINELEN do
  909.       begin
  910.         if (LineLen >= 3)
  911.         and (Buffer[nPos][LineLen] in ['0'..'9', 'A'..'F'])
  912.         and (Buffer[nPos][LineLen-1] in ['0'..'9', 'A'..'F'])
  913.         and (Buffer[nPos][LineLen-2] = '=') then
  914.           Dec(LineLen, 3)
  915.         else
  916.           Dec(LineLen, 1);
  917.       end;
  918.       if LineLen < Length(Buffer[nPos]) then
  919.       begin
  920.         Buffer.Insert(nPos+1, Copy(Buffer[nPos], LineLen+1, Length(Buffer[nPos])));
  921.         Buffer[nPos] := Copy(Buffer[nPos], 1, LineLen)+'=';
  922.       end;
  923.       Inc(nPos);
  924.     end;
  925.     Result := Buffer.Text;
  926.   end;
  927. end;
  928. // Decode an UUCODE encoded line
  929. function DecodeLineUUCODE(const Buffer: String; Decoded: PChar): Integer;
  930. const
  931. CHARS_PER_LINE = 45;
  932. Table: String = '`!"#$%&''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_';
  933. var
  934. A24Bits: array[0..8 * CHARS_PER_LINE] of Boolean;
  935. i, j, k, b: Word;
  936. LineLen, ActualLen: Byte;
  937. function p_ByteFromTable(Ch: Char): Byte;
  938. var
  939. ij: Integer;
  940. begin
  941. ij := Pos(Ch, Table);
  942. if (ij > 64) or (ij = 0) then
  943. begin
  944. if Ch = #32 then
  945. Result := 0 else
  946. raise Exception.Create(_E_UUCD);
  947. end else
  948. Result := ij - 1;
  949. end;
  950. begin
  951.   if Buffer = '' then
  952.   begin
  953.     Result := 0;
  954.     Exit;
  955.   end;
  956.   try
  957.     LineLen := p_ByteFromTable(Buffer[1]);
  958.     ActualLen := 4 * LineLen div 3;
  959.     FillChar(A24Bits, 8 * CHARS_PER_LINE + 1, 0);
  960.     Result := LineLen;
  961.     if ActualLen <> (4 * CHARS_PER_LINE div 3) then
  962.       ActualLen := Length(Buffer) - 1;
  963.     k := 0;
  964.     for i := 2 to ActualLen + 1 do
  965.     begin
  966.       b := p_ByteFromTable(Buffer[i]);
  967.       for j := 5 downto 0 do
  968.       begin
  969.         A24Bits[k] := b and (1 shl j) > 0;
  970.         Inc(k);
  971.       end;
  972.     end;
  973.     k := 0;
  974.     for i := 1 to CHARS_PER_LINE do
  975.     begin
  976.       b := 0;
  977.       for j := 7 downto 0 do
  978.       begin
  979.         if A24Bits[k] then b := b or (1 shl j);
  980.         Inc(k);
  981.       end;
  982.       Decoded[i-1] := Char(b);
  983.     end;
  984.   except
  985.     Result := 0;
  986.   end;
  987. end;
  988. // Decode an UUCODE text
  989. function DecodeUUCODE(Encoded: PChar; Decoded: TMemoryStream): Boolean;
  990. var
  991.   nTL, nPos, nLen: Integer;
  992.   Line: PChar;
  993.   LineDec: array[0..79] of Char;
  994.   LineLen: Integer;
  995.   DataEnd: Boolean;
  996. begin
  997.   Decoded.Clear;
  998.   DataEnd := False;
  999.   nPos := -1;
  1000.   nTL := StrLen(Encoded);
  1001.   DataLinePChar(Encoded, nTL, nPos, nLen, Line, DataEnd);
  1002.   while not DataEnd do
  1003.   begin
  1004.     if nLen > 0 then
  1005.     begin
  1006.       LineLen := DecodeLineUUCODE(String(Line), LineDec);
  1007.       if LineLen > 0 then
  1008.         Decoded.Write(LineDec[0], LineLen);
  1009.     end;
  1010.     DataLinePChar(Encoded, nTL, nPos, nLen, Line, DataEnd);
  1011.   end;
  1012.   Result := True;
  1013. end;
  1014. // Decode a BASE64 encoded line
  1015. function DecodeLineBASE64(const Buffer: String; Decoded: PChar): Integer;
  1016. var
  1017.   A1: array[1..4] of Byte;
  1018.   B1: array[1..3] of Byte;
  1019.   I, J: Integer;
  1020.   BytePtr, RealBytes: Integer;
  1021. begin
  1022.   BytePtr := 0;
  1023.   Result := 0;
  1024.   for J := 1 to Length(Buffer) do
  1025.   begin
  1026.     Inc(BytePtr);
  1027.     case Buffer[J] of
  1028.       'A'..'Z': A1[BytePtr] := Ord(Buffer[J])-65;
  1029.       'a'..'z': A1[BytePtr] := Ord(Buffer[J])-71;
  1030.       '0'..'9': A1[BytePtr] := Ord(Buffer[J])+4;
  1031.       '+': A1[BytePtr] := 62;
  1032.       '/': A1[BytePtr] := 63;
  1033.       '=': A1[BytePtr] := 64;
  1034.     end;
  1035.     if BytePtr = 4 then
  1036.     begin
  1037.       BytePtr := 0;
  1038.       RealBytes := 3;
  1039.       if A1[1] = 64 then RealBytes:=0;
  1040.       if A1[3] = 64 then
  1041.       begin
  1042.         A1[3] := 0;
  1043.         A1[4] := 0;
  1044.         RealBytes := 1;
  1045.       end;
  1046.       if A1[4] = 64 then
  1047.       begin
  1048.         A1[4] := 0;
  1049.         RealBytes := 2;
  1050.       end;
  1051.       B1[1] := A1[1]*4 + (A1[2] div 16);
  1052.       B1[2] := (A1[2] mod 16)*16+(A1[3] div 4);
  1053.       B1[3] := (A1[3] mod 4)*64 + A1[4];
  1054.       for I := 1 to RealBytes do
  1055.       begin
  1056.         Decoded[Result+I-1] := Chr(B1[I]);
  1057.       end;
  1058.       Inc(Result, RealBytes);
  1059.     end;
  1060.   end;
  1061. end;
  1062. // Padronize header labels; remove double spaces, decode quoted text, lower the cases, indentify mail addresses
  1063. function NormalizeLabel(Texto: String): String;
  1064. var
  1065.   Quote: Boolean;
  1066.   Quoted: String;
  1067.   Loop: Integer;
  1068.   lLabel: Boolean;
  1069.   sLabel: String;
  1070.   Value: String;
  1071. begin
  1072.   Quote := False;
  1073.   lLabel := True;
  1074.   Value := '';
  1075.   sLabel := '';
  1076.   for Loop := 1 to Length(Texto) do
  1077.   begin
  1078.     if (Texto[Loop] = '"') and (not lLabel) then
  1079.     begin
  1080.       Quote := not Quote;
  1081.       if Quote then
  1082.       begin
  1083.         Quoted := '';
  1084.       end
  1085.       else
  1086.       begin
  1087.         Value := Value + Quoted;
  1088.       end;
  1089.     end;
  1090.     if not Quote then
  1091.     begin
  1092.       if lLabel then
  1093.       begin
  1094.         if (sLabel = '') or (sLabel[Length(sLabel)] = '-') then
  1095.           sLabel := sLabel + UpCase(Texto[Loop])
  1096.         else
  1097.           if (Copy(sLabel, Length(sLabel)-1, 2) = '-I') and (UpCase(Texto[Loop]) = 'D') and
  1098.              (Loop < Length(Texto)) and (Texto[Loop+1] = ':') then
  1099.             sLabel := sLabel + 'D'
  1100.           else
  1101.             sLabel := sLabel + LowerCase(Texto[Loop]);
  1102.         if Texto[Loop] = ':' then
  1103.         begin
  1104.           lLabel := False;
  1105.           Value := '';
  1106.         end;
  1107.       end
  1108.       else
  1109.       begin
  1110.         if Texto[Loop] = #32 then
  1111.         begin
  1112.           Value := TrimRightSpace(Value) + #32;
  1113.         end
  1114.         else
  1115.         begin
  1116.           Value := Value + Texto[Loop];
  1117.         end;
  1118.       end;
  1119.     end
  1120.     else
  1121.     begin
  1122.       Quoted := Quoted + Texto[Loop];
  1123.     end;
  1124.   end;
  1125.   Result := TrimSpace(sLabel)+' '+TrimSpace(Value);
  1126. end;
  1127. // Return the value of a label; e.g. Label: value
  1128. function LabelValue(cLabel: String): String;
  1129. var
  1130.   Loop: Integer;
  1131.   Quote: Boolean;
  1132.   Value: Boolean;
  1133.   Ins: Boolean;
  1134. begin
  1135.   Quote := False;
  1136.   Value := False;
  1137.   Result := '';
  1138.   for Loop := 1 to Length(cLabel) do
  1139.   begin
  1140.     Ins := True;
  1141.     if cLabel[Loop] = '"' then
  1142.     begin
  1143.       Quote := not Quote;
  1144. //    Ins := False;
  1145.     end;
  1146.     if not Quote then
  1147.     begin
  1148.       if (cLabel[Loop] = ':') and (not Value) then
  1149.       begin
  1150.         Value := True;
  1151.         Ins := False;
  1152.       end
  1153.       else
  1154.       begin
  1155.         if (cLabel[Loop] = ';') and Value then
  1156.         begin
  1157.           Break;
  1158.         end;
  1159.       end;
  1160.     end;
  1161.     if Ins and Value then
  1162.     begin
  1163.       Result := Result + cLabel[Loop];
  1164.     end;
  1165.   end;
  1166.   Result := TrimSpace(Result);
  1167.   if (Copy(Result, 1, 1) = '"') and (Copy(Result, Length(Result), 1) = '"') then
  1168.     Result := Copy(Result, 2, Length(Result)-2);
  1169. end;
  1170. // Set the value of a label;
  1171. function WriteLabelValue(cLabel, Value: String): String;
  1172. var
  1173.   Loop: Integer;
  1174.   Quote: Boolean;
  1175.   ValPos, ValLen: Integer;
  1176. begin
  1177.   Quote := False;
  1178.   ValPos := 0;
  1179.   ValLen := -1;
  1180.   for Loop := 1 to Length(cLabel) do
  1181.   begin
  1182.     if cLabel[Loop] = '"' then
  1183.     begin
  1184.       Quote := not Quote;
  1185.     end;
  1186.     if not Quote then
  1187.     begin
  1188.       if (cLabel[Loop] = ':') and (ValPos = 0) then
  1189.       begin
  1190.         ValPos := Loop+1;
  1191.       end
  1192.       else
  1193.       begin
  1194.         if (cLabel[Loop] = ';') and (ValPos > 0) then
  1195.         begin
  1196.           ValLen := Loop - ValPos;
  1197.           Break;
  1198.         end;
  1199.       end;
  1200.     end;
  1201.   end;
  1202.   Result := cLabel;
  1203.   if (ValLen < 0) and (ValPos > 0) then
  1204.     ValLen := Length(cLabel) - ValPos + 1;
  1205.   if ValPos > 0 then
  1206.   begin
  1207.     Delete(Result, ValPos, ValLen);
  1208.     Insert(' '+TrimSpace(Value), Result, ValPos);
  1209.   end;
  1210. end;
  1211. // Return the value of a label parameter; e.g. Label: xxx; param=value
  1212. function LabelParamValue(cLabel, cParam: String): String;
  1213. var
  1214.   Loop: Integer;
  1215.   Quote: Boolean;
  1216.   Value: Boolean;
  1217.   Params: Boolean;
  1218.   ParamValue: Boolean;
  1219.   Ins: Boolean;
  1220.   Param: String;
  1221. begin
  1222.   Quote := False;
  1223.   Value := False;
  1224.   Params := False;
  1225.   ParamValue := False;
  1226.   Param := '';
  1227.   Result := '';
  1228.   cLabel := TrimSpace(cLabel);
  1229.   if Copy(cLabel, Length(cLabel), 1) <> ';' then cLabel := cLabel + ';';
  1230.   for Loop := 1 to Length(cLabel) do
  1231.   begin
  1232.     Ins := True;
  1233.     if cLabel[Loop] = '"' then
  1234.     begin
  1235.       Quote := not Quote;
  1236. //    Ins := False;
  1237.     end;
  1238.     if not Quote then
  1239.     begin
  1240.       if (cLabel[Loop] = ':') and (not Value) and (not Params) then
  1241.       begin
  1242.         Value := True;
  1243.         Params := False;
  1244.         ParamValue := False;
  1245.         Ins := False;
  1246.       end
  1247.       else
  1248.       begin
  1249.         if (cLabel[Loop] = ';') and (Value or Params) then
  1250.         begin
  1251.           Params := True;
  1252.           Value := False;
  1253.           ParamValue := False;
  1254.           Param := '';
  1255.           Ins := False;
  1256.         end
  1257.         else
  1258.         begin
  1259.           if (cLabel[Loop] = '=') and Params then
  1260.           begin
  1261.             ParamValue := UpperCase(TrimSpace(Param)) = UpperCase(TrimSpace(cParam));
  1262.             Ins := False;
  1263.             Param := '';
  1264.           end;
  1265.         end;
  1266.       end;
  1267.     end;
  1268.     if Ins and ParamValue then
  1269.     begin
  1270.       Result := Result + cLabel[Loop];
  1271.     end;
  1272.     if Ins and (not ParamValue) and Params then
  1273.     begin
  1274.       Param := Param + cLabel[Loop];
  1275.     end;
  1276.   end;
  1277.   Result := TrimSpace(Result);
  1278.   if (Copy(Result, 1, 1) = '"') and (Copy(Result, Length(Result), 1) = '"') then
  1279.     Result := Copy(Result, 2, Length(Result)-2);
  1280. end;
  1281. // Set the value of a label parameter;
  1282. function WriteLabelParamValue(cLabel, cParam, Value: String): String;
  1283. var
  1284.   Loop: Integer;
  1285.   Quote: Boolean;
  1286.   LabelValue: Boolean;
  1287.   Params: Boolean;
  1288.   ValPos, ValLen: Integer;
  1289.   Ins: Boolean;
  1290.   Param: String;
  1291. begin
  1292.   Quote := False;
  1293.   LabelValue := False;
  1294.   Params := False;
  1295.   ValPos := 0;
  1296.   ValLen := -1;
  1297.   Param := '';
  1298.   Result := '';
  1299.   cLabel := TrimSpace(cLabel);
  1300.   if cLabel[Length(cLabel)] <> ';' then
  1301.     cLabel := cLabel + ';';
  1302.   for Loop := 1 to Length(cLabel) do
  1303.   begin
  1304.     Ins := True;
  1305.     if cLabel[Loop] = '"' then
  1306.     begin
  1307.       Quote := not Quote;
  1308. //    Ins := False;
  1309.     end;
  1310.     if not Quote then
  1311.     begin
  1312.       if (cLabel[Loop] = ':') and (not LabelValue) and (not Params) then
  1313.       begin
  1314.         LabelValue := True;
  1315.         Params := False;
  1316.         ValPos := 0;
  1317.         ValLen := 0;
  1318.         Ins := False;
  1319.       end
  1320.       else
  1321.       begin
  1322.         if (cLabel[Loop] = ';') and (LabelValue or Params) then
  1323.         begin
  1324.           if Params and (ValPos > 0) then
  1325.           begin
  1326.             ValLen := Loop - ValPos;
  1327.             Break;
  1328.           end;
  1329.           Params := True;
  1330.           LabelValue := False;
  1331.           Param := '';
  1332.           Ins := False;
  1333.         end
  1334.         else
  1335.         begin
  1336.           if (cLabel[Loop] = '=') and Params then
  1337.           begin
  1338.             if UpperCase(TrimSpace(Param)) = UpperCase(TrimSpace(cParam)) then
  1339.             begin
  1340.               ValPos := Loop+1;
  1341.               ValLen := 0;
  1342.             end;
  1343.             Ins := False;
  1344.             Param := '';
  1345.           end;
  1346.         end;
  1347.       end;
  1348.     end;
  1349.     if Ins and (ValPos = 0) and Params then
  1350.     begin
  1351.       Param := Param + cLabel[Loop];
  1352.     end;
  1353.   end;
  1354.   Result := cLabel;
  1355.   if Result[Length(Result)] = ';' then
  1356.     Delete(Result, Length(Result), 1);
  1357.   if ValPos = 0 then
  1358.   begin
  1359.     Result := TrimSpace(Result) + '; ' + TrimSpace(cParam) + '=' + TrimSpace(Value);
  1360.   end
  1361.   else
  1362.   begin
  1363.     if (ValLen < 0) and (ValPos > 0) then
  1364.       ValLen := Length(cLabel) - ValPos + 1;
  1365.     Delete(Result, ValPos, ValLen);
  1366.     Insert(TrimSpace(Value), Result, ValPos);
  1367.   end;
  1368. end;
  1369. // Return the Timezone adjust in days
  1370. function GetTimeZoneBias: Double;
  1371. var
  1372.   TzInfo: TTimeZoneInformation;
  1373. begin
  1374.   case GetTimeZoneInformation(TzInfo) of
  1375.     1: Result := - (TzInfo.StandardBias + TzInfo.Bias) / (24*60);
  1376.     2: Result := - (TzInfo.DaylightBias + TzInfo.Bias) / (24*60);
  1377.     else Result := - TzInfo.Bias / (24*60);
  1378.   end;
  1379. end;
  1380. // Fills left of string with char
  1381. function PadL(const Str: String; const Tam: Integer; const PadStr: String): String;
  1382. var
  1383.   TempStr: String;
  1384. begin
  1385.   TempStr := TrimLeftSpace(Str);
  1386.   if Length(TempStr) <= Tam then
  1387.   begin
  1388.     while Length(TempStr) < Tam do
  1389.       TempStr := PadStr + TempStr;
  1390.   end
  1391.   else
  1392.   begin
  1393.     TempStr := Copy(TempStr, Length(TempStr) - Tam + 1, Tam);
  1394.   end;
  1395.   Result := TempStr;
  1396. end;
  1397. // Get mime type of a file extension
  1398. function GetMimeType(const FileName: String): String;
  1399. var
  1400.   Key: string;
  1401. begin
  1402.   Result := '';
  1403.   with TRegistry.Create do
  1404.     try
  1405.       RootKey := HKEY_CLASSES_ROOT;
  1406.       Key := ExtractFileExt(FileName);
  1407.       if KeyExists(Key) then
  1408.       begin
  1409.         OpenKey(Key, False);
  1410.         Result := ReadString('Content Type');
  1411.         CloseKey;
  1412.       end;
  1413.     finally
  1414.       if Result = '' then
  1415.         Result := _A_OS;
  1416.       Free;
  1417.     end;
  1418. end;
  1419. // Get file extension of a mime type
  1420. function GetMimeExtension(const MimeType: String): String;
  1421. var
  1422.   Key: string;
  1423. begin
  1424.   Result := '';
  1425.   with TRegistry.Create do
  1426.     try
  1427.       RootKey := HKEY_CLASSES_ROOT;
  1428.       if OpenKey('MIMEDatabaseContent Type', False) then
  1429.       begin
  1430.         Key := MimeType;
  1431.         if KeyExists(Key) then
  1432.         begin
  1433.           OpenKey(Key,false);
  1434.           Result := ReadString('Extension');
  1435.           CloseKey;
  1436.         end;
  1437.       end;
  1438.     finally
  1439.       Free;
  1440.     end;
  1441. end;
  1442. // Generate a random boundary
  1443. function GenerateBoundary: String;
  1444. begin
  1445.   Result := _BDRY + Format('%8.8x', [Random($FFFFFFFF)]);
  1446. end;
  1447. // Encode in base64
  1448. function EncodeBASE64(Encoded: TMemoryStream {TMailText}; Decoded: TMemoryStream): Integer;
  1449. const
  1450.   _Code64: String[64] =
  1451.     ('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/');
  1452. var
  1453.   I: LongInt;
  1454.   B: array[0..2279] of Byte;
  1455.   J, K, L, M, Quads: Integer;
  1456.   Stream: string[76];
  1457.   EncLine: String;
  1458. begin
  1459.   Encoded.Clear;
  1460.   Stream := '';
  1461.   Quads := 0;
  1462.   J := Decoded.Size div 2280;
  1463.   Decoded.Position := 0;
  1464.   for I := 1 to J do
  1465.   begin
  1466.     Decoded.Read(B, 2280);
  1467.     for M := 0 to 39 do
  1468.     begin
  1469.       for K := 0 to 18 do
  1470.       begin
  1471.         L:= 57*M + 3*K;
  1472.         Stream[Quads+1] := _Code64[(B[L] div 4)+1];
  1473.         Stream[Quads+2] := _Code64[(B[L] mod 4)*16 + (B[L+1] div 16)+1];
  1474.         Stream[Quads+3] := _Code64[(B[L+1] mod 16)*4 + (B[L+2] div 64)+1];
  1475.         Stream[Quads+4] := _Code64[B[L+2] mod 64+1];
  1476.         Inc(Quads, 4);
  1477.         if Quads = 76 then
  1478.         begin
  1479.           Stream[0] := #76;
  1480.           EncLine := Stream+#13#10;
  1481.           Encoded.Write(EncLine[1], Length(EncLine));
  1482.           Quads := 0;
  1483.         end;
  1484.       end;
  1485.     end;
  1486.   end;
  1487.   J := (Decoded.Size mod 2280) div 3;
  1488.   for I := 1 to J do
  1489.   begin
  1490.     Decoded.Read(B, 3);
  1491.     Stream[Quads+1] := _Code64[(B[0] div 4)+1];
  1492.     Stream[Quads+2] := _Code64[(B[0] mod 4)*16 + (B[1] div 16)+1];
  1493.     Stream[Quads+3] := _Code64[(B[1] mod 16)*4 + (B[2] div 64)+1];
  1494.     Stream[Quads+4] := _Code64[B[2] mod 64+1];
  1495.     Inc(Quads, 4);
  1496.     if Quads = 76 then
  1497.     begin
  1498.       Stream[0] := #76;
  1499.       EncLine := Stream+#13#10;
  1500.       Encoded.Write(EncLine[1], Length(EncLine));
  1501.       Quads := 0;
  1502.     end;
  1503.   end;
  1504.   if (Decoded.Size mod 3) = 2 then
  1505.   begin
  1506.     Decoded.Read(B, 2);
  1507.     Stream[Quads+1] := _Code64[(B[0] div 4)+1];
  1508.     Stream[Quads+2] := _Code64[(B[0] mod 4)*16 + (B[1] div 16)+1];
  1509.     Stream[Quads+3] := _Code64[(B[1] mod 16)*4 + 1];
  1510.     Stream[Quads+4] := '=';
  1511.     Inc(Quads, 4);
  1512.   end;
  1513.   if (Decoded.Size mod 3) = 1 then
  1514.   begin
  1515.     Decoded.Read(B, 1);
  1516.     Stream[Quads+1] := _Code64[(B[0] div 4)+1];
  1517.     Stream[Quads+2] := _Code64[(B[0] mod 4)*16 + 1];
  1518.     Stream[Quads+3] := '=';
  1519.     Stream[Quads+4] := '=';
  1520.     Inc(Quads, 4);
  1521.   end;
  1522.   Stream[0] := Chr(Quads);
  1523.   if Quads > 0 then
  1524.   begin
  1525.     EncLine := Stream+#13#10;
  1526.     Encoded.Write(EncLine[1], Length(EncLine));
  1527.   end;
  1528.   Result := Encoded.Size;
  1529. end;
  1530. // Encode string to base64
  1531. function EncodeBASE64String(const Decoded: String): String;
  1532. var
  1533.   S1, S2: TMemoryStream;
  1534.   slTemp: TStringList;
  1535.   sTemp: String;
  1536. begin
  1537.   S1 := TMemoryStream.Create;
  1538.   S2 := TMemoryStream.Create;
  1539.   slTemp := TStringList.Create;
  1540.   slTemp.Text := Decoded;
  1541.   slTemp.SaveToStream(S1);
  1542.   try
  1543.     S1.SetSize(S1.Size - 2 );
  1544.     S1.Position := 0;
  1545.     EncodeBASE64(S2, S1);
  1546.     S2.Position := 0;
  1547.     slTemp.LoadFromStream(S2);
  1548.     sTemp := slTemp.Strings[0];
  1549.     Result := sTemp;
  1550.   finally
  1551.     slTemp.Free;
  1552.     S1.Free;
  1553.     S2.Free;
  1554.   end;
  1555. end;
  1556. // Search in a StringList
  1557. function SearchStringList(Lista: TStringList; const Chave: String; const Occorrence: Integer = 0): Integer;
  1558. var
  1559.   nPos: Integer;
  1560.   lAchou: Boolean;
  1561.   Casas: Integer;
  1562.   Temp: String;
  1563.   nOccor: Integer;
  1564. begin
  1565.   Casas := Length(Chave);
  1566.   lAchou := False;
  1567.   nPos := 0;
  1568.   nOccor := 0;
  1569.   try
  1570.     if Lista <> nil then
  1571.     begin
  1572.       while (not lAchou) and (nPos < Lista.Count) do
  1573.       begin
  1574.         Temp := Lista[nPos];
  1575.         if UpperCase(Copy(Temp, 1, Casas)) = UpperCase(Chave) then
  1576.         begin
  1577.           if nOccor = Occorrence then
  1578.           begin
  1579.             lAchou := True;
  1580.           end
  1581.           else
  1582.           begin
  1583.             Inc(nOccor);
  1584.           end;
  1585.         end;
  1586.         if not lAchou then
  1587.           Inc(nPos);
  1588.       end;
  1589.     end;
  1590.   finally
  1591.     if lAchou then
  1592.       result := nPos
  1593.     else
  1594.       result := -1;
  1595.   end;
  1596. end;
  1597. // Search lines into a string
  1598. procedure DataLine(var Data, Line: String; var nPos: Integer);
  1599. begin
  1600.   Line := '';
  1601.   while True do
  1602.   begin
  1603.     Line := Line + Data[nPos];
  1604.     Inc(nPos);
  1605.     if nPos > Length(Data) then
  1606.     begin
  1607.       nPos := -1;
  1608.       Break;
  1609.     end
  1610.     else
  1611.     begin
  1612.       if Length(Line) >= 2 then
  1613.       begin
  1614.         if (Line[Length(Line)-1] = #13) and (Line[Length(Line)] = #10) then
  1615.         begin
  1616.           Break;
  1617.         end;
  1618.       end;
  1619.     end;
  1620.   end;
  1621. end;
  1622. // Search lines into a string
  1623. // I need to do in this confusing way in order to improve performance
  1624. procedure DataLinePChar(const Data: PChar; const TotalLength: Integer; var LinePos, LineLen: Integer; var Line: PChar; var DataEnd: Boolean); assembler;
  1625. begin
  1626.   if LinePos >= 0 then
  1627.   begin
  1628.     Data[LinePos+LineLen] := #13;
  1629.     LinePos := LinePos+LineLen+2;
  1630.     LineLen := 0;
  1631.   end
  1632.   else
  1633.   begin
  1634.     LinePos := 0;
  1635.     LineLen := 0;
  1636.   end;
  1637.   while (LinePos+LineLen) < TotalLength do
  1638.   begin
  1639.     if Data[LinePos+LineLen] = #13 then
  1640.     begin
  1641.       if (LinePos+LineLen+1) < TotalLength then
  1642.       begin
  1643.         if Data[LinePos+LineLen+1] = #10 then
  1644.         begin
  1645.           Data[LinePos+LineLen] := #0;
  1646.           Line := @Data[LinePos];
  1647.           Exit;
  1648.         end;
  1649.       end;
  1650.     end;
  1651.     Inc(LineLen);
  1652.   end;
  1653.   if LinePos < TotalLength then
  1654.     Line := @Data[LinePos]
  1655.   else
  1656.     DataEnd := True;
  1657. end;
  1658. // Determine if string is a numeric IP or not (Thanks to Hou Yg yghou@yahoo.com)
  1659. function IsIPAddress(const SS: String): Boolean;
  1660. var
  1661.   Loop: Integer;
  1662.   P: String;
  1663. begin
  1664.   Result := True;
  1665.   P := '';
  1666.   for Loop := 1 to Length(SS)+1 do
  1667.   begin
  1668.     if (Loop > Length(SS)) or (SS[Loop] = '.') then
  1669.     begin
  1670.       if StrToIntDef(P, -1) < 0 then
  1671.       begin
  1672.         Result := False;
  1673.         Break;
  1674.       end;
  1675.       P := '';
  1676.     end
  1677.     else
  1678.     begin
  1679.       P := P + SS[Loop];
  1680.     end;
  1681.   end;
  1682. end;
  1683. // Remove leading and trailing spaces from string
  1684. // Thanks to Yunarso Anang (yasx@hotmail.com)
  1685. function TrimSpace(const S: string): string;
  1686. var
  1687.   I, L: Integer;
  1688. begin
  1689.   L := Length(S);
  1690.   I := 1;
  1691.   while (I <= L) and (S[I] in [#9, #32]) do
  1692.     Inc(I);
  1693.   if I > L then Result := '' else
  1694.   begin
  1695.     while S[L] = ' ' do
  1696.       Dec(L);
  1697.     Result := Copy(S, I, L - I + 1);
  1698.   end;
  1699. end;
  1700. // Remove left spaces from string
  1701. // Thanks to Yunarso Anang (yasx@hotmail.com)
  1702. function TrimLeftSpace(const S: string): string;
  1703. var
  1704.   I, L: Integer;
  1705. begin
  1706.   L := Length(S);
  1707.   I := 1;
  1708.   while (I <= L) and (S[I] in [#9, #32]) do
  1709.     Inc(I);
  1710.   Result := Copy(S, I, Maxint);
  1711. end;
  1712. // Remove right spaces from string
  1713. // Thanks to Yunarso Anang (yasx@hotmail.com)
  1714. function TrimRightSpace(const S: string): string;
  1715. var
  1716.   I: Integer;
  1717. begin
  1718.   I := Length(S);
  1719.   while (I > 0) and (S[I] in [#9, #32]) do
  1720.     Dec(I);
  1721.   Result := Copy(S, 1, I);
  1722. end;
  1723. // Convert date from message to Delphi format
  1724. // Returns zero in case of error
  1725. function MailDateToDelphiDate(const DateStr: String): TDateTime;
  1726. const
  1727.   Months: String = 'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec,';
  1728. var
  1729.   Field, Loop: Integer;
  1730.   Hour, Min, Sec, Year, Month, Day: Double;
  1731.   sDate, sHour, sMin, sSec, sYear, sMonth, sDay, sTZ: String;
  1732.   HTZM, MTZM: Word;
  1733.   STZM: Integer;
  1734.   TZM: Double;
  1735.   Final: Double;
  1736. begin
  1737.   sHour := '';
  1738.   sMin := '';
  1739.   sSec := '';
  1740.   sYear := '';
  1741.   sMonth := '';
  1742.   sDay := '';
  1743.   sTZ := '';
  1744.   sDate := Trim(DateStr);
  1745.   if sDate <> '' then
  1746.   begin
  1747.     if sDate[1] in ['0'..'9'] then
  1748.       Field := 1
  1749.     else
  1750.       Field := 0;
  1751.     for Loop := 1 to Length(sDate) do
  1752.     begin
  1753.       if sDate[Loop] in [#32, ':', '/'] then
  1754.       begin
  1755.         Inc(Field);
  1756.         if (Field = 6) and (sDate[Loop] = #32) then Field := 7;
  1757.       end
  1758.       else
  1759.       begin
  1760.         case Field of
  1761.           1: sDay := sDay + sDate[Loop];
  1762.           2: sMonth := sMonth + sDate[Loop];
  1763.           3: sYear := sYear + sDate[Loop];
  1764.           4: sHour := sHour + sDate[Loop];
  1765.           5: sMin := sMin + sDate[Loop];
  1766.           6: sSec := sSec + sDate[Loop];
  1767.           7: sTZ := sTZ + sDate[Loop];
  1768.         end;
  1769.       end;
  1770.     end;
  1771.     Hour := StrToIntDef(sHour, 0);
  1772.     Min := StrToIntDef(sMin, 0);
  1773.     Sec := StrToIntDef(sSec, 0);
  1774.     Year := StrToIntDef(sYear, 0);
  1775.     Day := StrToIntDef(sDay, 0);
  1776.     if sMonth[1] in ['0'..'9'] then
  1777.       Month := StrToIntDef(sMonth, 0)
  1778.     else
  1779.       Month := (Pos(sMonth, Months)-1) div 4 + 1;
  1780.     if Year < 100 then
  1781.     begin
  1782.       if Year < 50 then
  1783.         Year := 2000 + Year
  1784.       else
  1785.         Year := 1900 + Year;
  1786.     end;
  1787.     if (Year = 0) or (Month = 0) or (Year = 0) then
  1788.     begin
  1789.       Result := 0;
  1790.     end
  1791.     else
  1792.     begin
  1793.       if (sTZ = 'GMT') or (Length(Trim(sTZ)) <> 5) then
  1794.       begin
  1795.         STZM := 1;
  1796.         HTZM := 0;
  1797.         MTZM := 0;
  1798.       end
  1799.       else
  1800.       begin
  1801.         STZM := StrToIntDef(Copy(sTZ, 1, 1)+'1', 1);
  1802.         HTZM := StrToIntDef(Copy(sTZ, 2, 2), 0);
  1803.         MTZM := StrToIntDef(Copy(sTZ, 4, 2), 0);
  1804.       end;
  1805.       try
  1806.         TZM := EncodeTime(HTZM, MTZM, 0, 0)*STZM;
  1807.         Final := EncodeDate(Trunc(Year), Trunc(Month), Trunc(Day));
  1808.         Final := Final + Hour*(1/24) + Min*(1/24/60) + Sec*(1/24/60/60);
  1809.         Final := Final - TZM + GetTimeZoneBias;
  1810.         Result := Final;
  1811.       except
  1812.         Result := 0;
  1813.       end;
  1814.     end;
  1815.   end
  1816.   else
  1817.   begin
  1818.     Result := 0;
  1819.   end;
  1820. end;
  1821. // Convert numeric date to mail format
  1822. function DelphiDateToMailDate(const Date: TDateTime): String;
  1823. const
  1824.   Months: String = 'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec,';
  1825.   Weeks: String = 'Sun,Mon,Tue,Wed,Thu,Fri,Sat,';
  1826. var
  1827.   TZH: Double;
  1828.   DateStr: String;
  1829.   TZStr: String;
  1830.   Day, Month, Year: Word;
  1831. begin
  1832.   TZH := GetTimeZoneBias;
  1833.   DecodeDate(Date, Year, Month, Day);
  1834.   if TZH < 0 then
  1835.   begin
  1836.     TZStr := '-'+FormatDateTime('hhmm', Abs(TZH));
  1837.   end
  1838.   else
  1839.   begin
  1840.     if TZH = 0 then
  1841.     begin
  1842.       TZStr := 'GMT'
  1843.     end
  1844.     else
  1845.     begin
  1846.       TZStr := '+'+FormatDateTime('hhmm', Abs(TZH));
  1847.     end;
  1848.   end;
  1849.   DateStr := Copy(Weeks, (DayOfWeek(Date)-1)*4+1, 3)+',';
  1850.   DateStr := DateStr + FormatDateTime(' dd ', Date);
  1851.   DateStr := DateStr + Copy(Months, (Month-1)*4+1, 3);
  1852.   DateStr := DateStr + FormatDateTime(' yyyy hh:nn:ss ', Date) + TZStr;
  1853.   Result := DateStr;
  1854. end;
  1855. // To make sure that a file name (without path!) is valid
  1856. function ValidFileName(FileName: String): String;
  1857. const
  1858.   InvChars: String = ':/*?"<>|'#39;
  1859. var
  1860.   Loop: Integer;
  1861. begin
  1862.   FileName := Copy(TrimSpace(FileName), 1, 254);
  1863.   Result := '';
  1864.   for Loop := 1 to Length(FileName) do
  1865.   begin
  1866.     if (Ord(FileName[Loop]) < 32) or (Pos(FileName[Loop], InvChars) > 0) then
  1867.       Result := Result + '_'
  1868.     else
  1869.       Result := Result + FileName[Loop];
  1870.   end;
  1871. end;
  1872. // Wrap an entire message header
  1873. function WrapHeader(Text: String): String;
  1874. var
  1875.   Line: String;
  1876.   nPos: Integer;
  1877.   fPos: Integer;
  1878.   Quote: Char;
  1879.   Ok: Boolean;
  1880. begin
  1881.   Result := '';
  1882.   Text := AdjustLineBreaks(Text);
  1883.   while Copy(Text, Length(Text)-1, 2) = #13#10 do
  1884.     Delete(Text, Length(Text)-1, 2);
  1885.   while Text <> '' do
  1886.   begin
  1887.     nPos := Pos(#13#10, Text);
  1888.     if nPos > 0 then
  1889.     begin
  1890.       Line := Copy(Text, 1, nPos-1);
  1891.       Text := Copy(Text, nPos+2, Length(Text));
  1892.     end
  1893.     else
  1894.     begin
  1895.       Line := Text;
  1896.       Text := '';
  1897.     end;
  1898.     if Length(Line) <= _LINELEN then
  1899.     begin
  1900.       Result := Result + Line + #13#10;
  1901.     end
  1902.     else
  1903.     begin
  1904.       nPos := Length(Line);
  1905.       Quote := #0;
  1906.       Ok := False;
  1907.       if Line[1] <> #9 then
  1908.         fPos := Pos(':'#32, Line)+2
  1909.       else
  1910.         fPos := _LINELEN div 2;
  1911.       while nPos >= fPos do
  1912.       begin
  1913.         if (Quote = #0) and (Line[nPos] in [#39, '"']) then
  1914.           Quote := Line[nPos]
  1915.         else
  1916.           if (Quote <> #0) and (Line[nPos] = Quote) then
  1917.             Quote := #0;
  1918.         if (Quote = #0) and (nPos <= _LINELEN) and (Line[nPos] in [#32, ',', ';']) then
  1919.         begin
  1920.           Ok := True;
  1921.           Break;
  1922.         end;
  1923.         Dec(nPos);
  1924.       end;
  1925.       if Ok then
  1926.       begin
  1927.         if Line[nPos] = #32 then
  1928.           Result := Result + Copy(Line, 1, nPos-1) + #13#10#9
  1929.         else
  1930.           Result := Result + Copy(Line, 1, nPos) + #13#10#9;
  1931.         Text := Copy(Line, nPos+1, Length(Line)) + #13#10 + Text;
  1932.       end
  1933.       else
  1934.       begin
  1935.         Result := Result + Line + #13#10;
  1936.       end;
  1937.     end;
  1938.   end;
  1939. end;
  1940. // Parse text into tokens
  1941. function GetToken(const Token: Integer; const Text, Separators, QuoteStart, QuoteEnd: String): String;
  1942. var
  1943.   Quote: Integer;
  1944.   Loop: Integer;
  1945.   CountItem: Integer;
  1946.   nPos: Integer;
  1947.   EndQuote: Char;
  1948.   Jump: Boolean;
  1949. begin
  1950.   Result := '';
  1951.   Quote := 0;
  1952.   CountItem := 0;
  1953.   Jump := False;
  1954.   EndQuote := #0;
  1955.   for Loop := 1 to Length(Text) do
  1956.   begin
  1957.     nPos := Pos(Text[Loop], QuoteStart);
  1958.     if nPos > 0 then
  1959.     begin
  1960.       Inc(Quote);
  1961.       EndQuote := QuoteEnd[nPos];
  1962.     end
  1963.     else
  1964.     begin
  1965.       if (Quote > 0) and (Text[Loop] = EndQuote) then
  1966.       begin
  1967.         Dec(Quote);
  1968.       end
  1969.       else
  1970.       begin
  1971.         if (Quote = 0) and (Pos(Text[Loop], Separators) > 0) then
  1972.         begin
  1973.           Inc(CountItem);
  1974.           Jump := True;
  1975.         end;
  1976.       end;
  1977.     end;
  1978.     if (Quote = 0) and (not Jump) and (CountItem = Token) then
  1979.     begin
  1980.       Result := Result + Text[Loop];
  1981.     end;
  1982.     Jump := False;
  1983.   end;
  1984. end;
  1985. { TMailPart ================================================================== }
  1986. // Initialize MailPart
  1987. constructor TMailPart.Create(AOwner: TComponent);
  1988. begin
  1989.   inherited Create(AOwner);
  1990.   FHeader := TStringList.Create;
  1991.   FBody := TMemoryStream.Create;
  1992.   FDecoded := TMemoryStream.Create;
  1993.   FSubPartList := TMailPartList.Create;
  1994.   FOwnerPart := nil;
  1995.   FOwnerMessage := nil;
  1996.   FEmbedded := False;
  1997. end;
  1998. // Finalize MailPart
  1999. destructor TMailPart.Destroy;
  2000. var
  2001.   Loop: Integer;
  2002. begin
  2003.   for Loop := 0 to FSubPartList.Count-1 do
  2004.     FSubPartList.Items[Loop].Destroy;
  2005.   FHeader.Free;
  2006.   FBody.Free;
  2007.   FDecoded.Free;
  2008.   FSubPartList.Free;
  2009.   inherited Destroy;
  2010. end;
  2011. // Return the value of a label from the header like "To", "Subject"
  2012. function TMailPart.GetLabelValue(const cLabel: String): String;
  2013. var
  2014.   Loop: Integer;
  2015. begin
  2016.   Result := '';
  2017.   Loop := SearchStringList(FHeader, cLabel+':');
  2018.   if Loop >= 0 then
  2019.     Result := TrimSpace(LabelValue(FHeader[Loop]));
  2020.   if Length(Result) > 2 then
  2021.   begin
  2022.     if (Result[1] in ['"', #39]) and
  2023.        (Result[Length(Result)] in ['"', #39]) then
  2024.       Result := Copy(Result, 2, Length(Result)-2);
  2025.   end;
  2026. end;
  2027. // Return de value of a parameter of a value from the header
  2028. function TMailPart.GetLabelParamValue(const cLabel, Param: String): String;
  2029. var
  2030.   Loop: Integer;
  2031. begin
  2032.   Result := '';
  2033.   Loop := SearchStringList(FHeader, cLabel+':');
  2034.   if Loop >= 0 then
  2035.     Result := TrimSpace(LabelParamValue(FHeader[Loop], Param));
  2036.   if Length(Result) > 2 then
  2037.   begin
  2038.     if (Result[1] in ['"', #39]) and
  2039.        (Result[Length(Result)] in ['"', #39]) then
  2040.       Result := Copy(Result, 2, Length(Result)-2);
  2041.   end;
  2042. end;
  2043. // Set the value of a label
  2044. procedure TMailPart.SetLabelValue(const cLabel, cValue: String);
  2045. var
  2046.   Loop: Integer;
  2047. begin
  2048.   Loop := SearchStringList(FHeader, cLabel+':');
  2049.   if cValue <> '' then
  2050.   begin
  2051.     if Loop < 0 then
  2052.     begin
  2053.       FHeader.Add(cLabel+': ');
  2054.       Loop := FHeader.Count-1;
  2055.     end;
  2056.     FHeader[Loop] := WriteLabelValue(FHeader[Loop], cValue);
  2057.   end
  2058.   else
  2059.   begin
  2060.     if Loop >= 0 then
  2061.     begin
  2062.       FHeader.Delete(Loop);
  2063.     end;
  2064.   end;
  2065. end;
  2066. // Set the value of a label parameter
  2067. procedure TMailPart.SetLabelParamValue(const cLabel, cParam, cValue: String);
  2068. var
  2069.   Loop: Integer;
  2070. begin
  2071.   Loop := SearchStringList(FHeader, cLabel+':');
  2072.   if Loop < 0 then
  2073.   begin
  2074.     FHeader.Add(cLabel+': ');
  2075.     Loop := FHeader.Count-1;
  2076.   end;
  2077.   FHeader[Loop] := WriteLabelParamValue(FHeader[Loop], cParam, cValue);
  2078. end;
  2079. // Look for a label in the header
  2080. function TMailPart.LabelExists(const cLabel: String): Boolean;
  2081. begin
  2082.   Result := SearchStringList(FHeader, cLabel+':') >= 0;
  2083. end;
  2084. // Look for a parameter in a label in the header
  2085. function TMailPart.LabelParamExists(const cLabel, Param: String): Boolean;
  2086. var
  2087.   Loop: Integer;
  2088. begin
  2089.   Result := False;
  2090.   Loop := SearchStringList(FHeader, cLabel+':');
  2091.   if Loop >= 0 then
  2092.     Result := TrimSpace(LabelParamValue(FHeader[Loop], Param)) <> '';
  2093. end;
  2094. // Divide header and body; normalize header;
  2095. procedure TMailPart.Fill(Data: PChar; HasHeader: Boolean);
  2096. const
  2097.   CRLF: array[0..2] of Char = (#13, #10, #0);
  2098. var
  2099.   Loop: Integer;
  2100.   BoundStart: array[0..99] of Char;
  2101.   BoundEnd: array[0..99] of Char;
  2102.   InBound: Boolean;
  2103.   IsBoundStart: Boolean;
  2104.   IsBoundEnd: Boolean;
  2105.   BoundStartLen: Integer;
  2106.   BoundEndLen: Integer;
  2107.   PartText: PChar;
  2108.   DataEnd: Boolean;
  2109.   MultPart: Boolean;
  2110.   NoParts: Boolean;
  2111.   InUUCode: Boolean;
  2112.   UUFile, UUBound: String;
  2113.   Part: TMailPart;
  2114.   nPos: Integer;
  2115.   nLen: Integer;
  2116.   nTL: Integer;
  2117.   nSPos: Integer;
  2118.   Line: PChar;
  2119.   SChar: Char;
  2120. begin
  2121.   if (FOwnerMessage = nil) or (not (FOwnerMessage is TMailMessage2000)) then
  2122.   begin
  2123.     Exception.CreateFmt(_E_MLPT, [Self.Name]);
  2124.     Exit;
  2125.   end;
  2126.   for Loop := 0 to FSubPartList.Count-1 do
  2127.     FSubPartList.Items[Loop].Destroy;
  2128.   FHeader.Clear;
  2129.   FBody.Clear;
  2130.   FDecoded.Clear;
  2131.   FSubPartList.Clear;
  2132.   FIsDecoded := False;
  2133.   FEmbedded := False;
  2134.   FOwnerMessage.FNeedRebuild := True;
  2135.   FOwnerMessage.FNeedNormalize := True;
  2136.   FOwnerMessage.FNeedFindParts := True;
  2137.   nPos := -1;
  2138.   DataEnd := False;
  2139.   nTL := StrLen(Data);
  2140.   nSPos := nTL+1;
  2141.   if (Self is TMailMessage2000) and Assigned(FOwnerMessage.FOnProgress) then
  2142.   begin
  2143.     FOwnerMessage.FOnProgress(Self, nTL, 0);
  2144.     Application.ProcessMessages;
  2145.   end;
  2146.   if HasHeader then
  2147.   begin
  2148.     // Get Header
  2149.     DataLinePChar(Data, nTL, nPos, nLen, Line, DataEnd);
  2150.     while not DataEnd do
  2151.     begin
  2152.       if nLen = 0 then
  2153.       begin
  2154.         Break;
  2155.       end
  2156.       else
  2157.       begin
  2158.         if (Line[0] in [#9, #32]) and (FHeader.Count > 0) then
  2159.         begin
  2160.           FHeader[FHeader.Count-1] := FHeader[FHeader.Count-1] + #32 + TrimLeftSpace(String(PChar(@Line[1])));
  2161.         end
  2162.         else
  2163.         begin
  2164.           FHeader.Add(String(Line));
  2165.         end;
  2166.       end;
  2167.       DataLinePChar(Data, nTL, nPos, nLen, Line, DataEnd);
  2168.       if (Self is TMailMessage2000) and Assigned(FOwnerMessage.FOnProgress) then
  2169.       begin
  2170.         FOwnerMessage.FOnProgress(Self, nTL, nPos+1);
  2171.         Application.ProcessMessages;
  2172.       end;
  2173.     end;
  2174.     for Loop := 0 to FHeader.Count-1 do
  2175.       FHeader[Loop] := NormalizeLabel(FHeader[Loop]);
  2176.   end;
  2177.   MultPart := LowerCase(Copy(GetLabelValue(_C_T), 1, 10)) = _MP;
  2178.   InBound := False;
  2179.   IsBoundStart := False;
  2180.   IsBoundEnd := False;
  2181.   UUBound := '';
  2182.   if MultPart then
  2183.   begin
  2184.     StrPCopy(BoundStart, '--'+GetBoundary);
  2185.     StrPCopy(BoundEnd, '--'+GetBoundary+'--');
  2186.     BoundStartLen := StrLen(BoundStart);
  2187.     BoundEndLen := StrLen(BoundEnd);
  2188.     NoParts := False;
  2189.   end
  2190.   else
  2191.   begin
  2192.     if LabelExists(_C_T) then
  2193.     begin
  2194.       NoParts := True;
  2195.       BoundStartLen := 0;
  2196.       BoundEndLen := 0;
  2197.     end
  2198.     else
  2199.     begin
  2200.       StrPCopy(BoundStart, _UUBG);
  2201.       StrPCopy(BoundEnd, _UUEN);
  2202.       BoundStartLen := StrLen(BoundStart);
  2203.       BoundEndLen := StrLen(BoundEnd);
  2204.       NoParts := False;
  2205.     end;
  2206.   end;
  2207.   PartText := nil;
  2208.   // Get Body
  2209.   DataLinePChar(Data, nTL, nPos, nLen, Line, DataEnd);
  2210.   while (not DataEnd) and (not InBound) do
  2211.   begin
  2212.     if (not NoParts) and (((Line[0] = '-') and (Line[1] = '-')) or ((Line[0] = 'b') and (Line[1] = 'e'))) then
  2213.     begin
  2214.       IsBoundStart := StrLComp(Line, BoundStart, BoundStartLen) = 0;
  2215.     end;
  2216.     if NoParts or (not IsBoundStart) then
  2217.     begin
  2218.       if PartText = nil then
  2219.       begin
  2220.         PartText := Line;
  2221.         nSPos := nPos;
  2222.       end;
  2223.       DataLinePChar(Data, nTL, nPos, nLen, Line, DataEnd);
  2224.       if (Self is TMailMessage2000) and Assigned(FOwnerMessage.FOnProgress) then
  2225.       begin
  2226.         FOwnerMessage.FOnProgress(Self, nTL, nPos+1);
  2227.         Application.ProcessMessages;
  2228.       end;
  2229.     end
  2230.     else
  2231.     begin
  2232.       InBound := True;
  2233.     end;
  2234.   end;
  2235.   if nPos > nSPos then
  2236.   begin
  2237.     SChar := Data[nPos];
  2238.     Data[nPos] := #0;
  2239.     if PartText <> nil then
  2240.       FBody.Write(PartText[0], nPos-nSPos);
  2241.     Data[nPos] := SChar;
  2242.   end;
  2243.   if not NoParts then
  2244.   begin
  2245.     PartText := nil;
  2246.     if MultPart then
  2247.     begin
  2248.       // Get Mime parts
  2249.       while not DataEnd do
  2250.       begin
  2251.         if IsBoundStart or IsBoundEnd then
  2252.         begin
  2253.           if (PartText <> nil) and (PartText[0] <> #0) then
  2254.           begin
  2255.             Part := TMailPart.Create(Self.FOwnerMessage);
  2256.             Part.FOwnerPart := Self;
  2257.             Part.FOwnerMessage := Self.FOwnerMessage;
  2258.             SChar := Data[nPos-2];
  2259.             Data[nPos-2] := #0;
  2260.             Part.Fill(PartText, True);
  2261.             Data[nPos-2] := SChar;
  2262.             Part.FParentBoundary := GetBoundary;
  2263.             FSubPartList.Add(Part);
  2264.             PartText := nil;
  2265.           end;
  2266.           if IsBoundEnd then
  2267.           begin
  2268.             Break;
  2269.           end;
  2270.           IsBoundStart := False;
  2271.           IsBoundEnd := False;
  2272.         end
  2273.         else
  2274.         begin
  2275.           if PartText = nil then
  2276.           begin
  2277.             PartText := Line;
  2278.           end;
  2279.         end;
  2280.         DataLinePChar(Data, nTL, nPos, nLen, Line, DataEnd);
  2281.         if (Self is TMailMessage2000) and Assigned(FOwnerMessage.FOnProgress) then
  2282.         begin
  2283.           FOwnerMessage.FOnProgress(Self, nTL, nPos+1);
  2284.           Application.ProcessMessages;
  2285.         end;
  2286.         if not DataEnd then
  2287.         begin
  2288.           if (Line[0] = '-') and (Line[1] = '-') then
  2289.           begin
  2290.             IsBoundStart := StrLComp(Line, BoundStart, BoundStartLen) = 0;
  2291.             if not IsBoundStart then
  2292.             begin
  2293.               IsBoundEnd := StrLComp(Line, BoundEnd, BoundEndLen) = 0;
  2294.             end;
  2295.           end;
  2296.         end;
  2297.       end;
  2298.     end
  2299.     else
  2300.     begin
  2301.       // Get UUCode parts
  2302.       InUUCode := IsBoundStart;
  2303.       while not DataEnd do
  2304.       begin
  2305.         if IsBoundStart then
  2306.         begin
  2307.           if UUBound = '' then
  2308.           begin
  2309.             GetMem(PartText, FBody.Size+1);
  2310.             UUBound := GenerateBoundary;
  2311.             StrLCopy(PartText, FBody.Memory, FBody.Size);
  2312.             PartText[FBody.Size] := #0;
  2313.             Part := TMailPart.Create(Self.FOwnerMessage);
  2314.             Part.FOwnerPart := Self;
  2315.             Part.FOwnerMessage := Self.FOwnerMessage;
  2316.             Part.Fill(PChar(EncodeQuotedPrintable(String(PartText), False)), False);
  2317.             Part.FParentBoundary := UUBound;
  2318.             Part.SetLabelValue(_C_T, _T_P);
  2319.             Part.SetLabelParamValue(_C_T, _CSET, '"'+FOwnerMessage.FCharset+'"');
  2320.             Part.SetLabelValue(_C_TE, _E_QP);
  2321.             FSubPartList.Add(Part);
  2322.             SetLabelValue(_C_T, '');
  2323.             SetLabelValue(_C_T, _M_M);
  2324.             SetLabelParamValue(_C_T, _BDRY, '"'+UUBound+'"');
  2325.             FreeMem(PartText);
  2326.           end;
  2327.           PartText := nil;
  2328.           IsBoundStart := False;
  2329.           UUFile := TrimSpace(Copy(String(Line), 11, 999));
  2330.         end
  2331.         else
  2332.         begin
  2333.           if IsBoundEnd then
  2334.           begin
  2335.             Part := TMailPart.Create(Self.FOwnerMessage);
  2336.             Part.FOwnerPart := Self;
  2337.             Part.FOwnerMessage := Self.FOwnerMessage;
  2338.             SChar := Data[nPos-2];
  2339.             Data[nPos-2] := #0;
  2340.             DecodeUUCODE(PartText, Part.FDecoded);
  2341.             Data[nPos-2] := SChar;
  2342.             Part.EncodeBinary;
  2343.             Part.FParentBoundary := UUBound;
  2344.             Part.SetLabelValue(_C_T, GetMimeType(UUFile));
  2345.             Part.SetLabelValue(_C_TE, _E_BA);
  2346.             Part.SetLabelValue(_C_D, _ATCH);
  2347.             Part.SetLabelParamValue(_C_T, _NAME, '"'+EncodeLine7Bit(UUFile, FOwnerMessage.FCharSet)+'"');
  2348.             Part.SetLabelParamValue(_C_D, _FLNM, '"'+EncodeLine7Bit(UUFile, FOwnerMessage.FCharSet)+'"');
  2349.             Part.SetLabelValue(_XM2A, '"'+EncodeLine7Bit(UUFile, FOwnerMessage.FCharSet)+'"');
  2350.             Part.SetLabelParamValue(_XM2A, _EMBD, 'no'); 
  2351.             FSubPartList.Add(Part);
  2352.             PartText := nil;
  2353.             IsBoundEnd := False;
  2354.           end
  2355.           else
  2356.           begin
  2357.             if PartText = nil then
  2358.             begin
  2359.               PartText := Line;
  2360.             end;
  2361.           end;
  2362.         end;
  2363.         DataLinePChar(Data, nTL, nPos, nLen, Line, DataEnd);
  2364.         if (Self is TMailMessage2000) and Assigned(FOwnerMessage.FOnProgress) then
  2365.         begin
  2366.           FOwnerMessage.FOnProgress(Self, nTL, nPos+1);
  2367.           Application.ProcessMessages;
  2368.         end;
  2369.         if not DataEnd then
  2370.         begin
  2371.           if (Line[0] = 'b') and (Line[1] = 'e') then
  2372.           begin
  2373.             IsBoundStart := StrLComp(Line, BoundStart, BoundStartLen) = 0;
  2374.             InUUCode := True;
  2375.           end;
  2376.           if (not IsBoundStart) and InUUCode then
  2377.           begin
  2378.             if (Line[0] = 'e') and (Line[1] = 'n') and (Line[2] = 'd') then
  2379.             begin
  2380.               IsBoundEnd := True;
  2381.               InUUCode := False;
  2382.             end;
  2383.           end;
  2384.         end;
  2385.       end;
  2386.     end;
  2387.   end;
  2388.   if (Self is TMailMessage2000) and Assigned(FOwnerMessage.FOnProgress) then
  2389.   begin
  2390.     FOwnerMessage.FOnProgress(Self, nTL, nTL);
  2391.     Application.ProcessMessages;
  2392.   end;
  2393. end;
  2394. // Remove mailpart from its owner
  2395. procedure TMailPart.Remove;
  2396. begin
  2397.   if (FOwnerPart <> nil) and (Self <> FOwnerMessage) and
  2398.      (FOwnerPart.FSubPartList.IndexOf(Self) >= 0) then
  2399.   begin
  2400.     FOwnerPart.FSubPartList.Delete(FOwnerPart.FSubPartList.IndexOf(Self));
  2401.     FOwnerPart := nil;
  2402.   end;
  2403. end;
  2404. // Fill part with a file contents
  2405. procedure TMailPart.LoadFromFile(FileName: String);
  2406. var
  2407.   SL: TStringList;
  2408. begin
  2409.   SL := TStringList.Create;
  2410.   SL.LoadFromFile(FileName);
  2411.   Fill(PChar(SL.Text), True);
  2412.   SL.Free;
  2413. end;
  2414. // Save the part data to a file
  2415. procedure TMailPart.SaveToFile(FileName: String);
  2416. var
  2417.   SL: TStringList;
  2418. begin
  2419.   SL := TStringList.Create;
  2420.   SL.Text := GetSource;
  2421.   try
  2422.     SL.SaveToFile(FileName);
  2423.   finally
  2424.     SL.Free;
  2425.   end;
  2426. end;
  2427. // Fill part with a stream contents
  2428. procedure TMailPart.LoadFromStream(Stream: TStream);
  2429. var
  2430.   Buffer: PChar;
  2431. begin
  2432.   GetMem(Buffer, Stream.Size+1);
  2433.   Stream.Position := 0;
  2434.   Stream.ReadBuffer(Buffer[0], Stream.Size);
  2435.   Buffer[Stream.Size] := #0;
  2436.   Fill(Buffer, True);
  2437.   FreeMem(Buffer);
  2438. end;
  2439. // Save the part data to a stream
  2440. procedure TMailPart.SaveToStream(Stream: TStream);
  2441. var
  2442.   Text: String;
  2443. begin
  2444.   Text := GetSource;
  2445.   Stream.Size := Length(Text);
  2446.   Stream.Position := 0;
  2447.   Stream.WriteBuffer(Text[1], Length(Text));
  2448. end;
  2449. // Fill part with a string contents
  2450. procedure TMailPart.SetSource(Text: String);
  2451. begin
  2452.   Fill(PChar(Text), True);
  2453. end;
  2454. // Copy the part data to a string
  2455. function TMailPart.GetSource: String;
  2456. begin
  2457.   SetLength(Result, FBody.Size);
  2458.   FBody.Position := 0;
  2459.   FBody.ReadBuffer(Result[1], FBody.Size);
  2460.   Result := WrapHeader(FHeader.Text)+#13#10+Result;
  2461. end;
  2462. // Get file name of attachment
  2463. function TMailPart.GetFileName: String;
  2464. var
  2465.   Name: String;
  2466. begin
  2467.   Name := '';
  2468.   if LabelExists(_XM2A) then
  2469.   begin
  2470.     Name := GetLabelValue(_XM2A);
  2471.   end
  2472.   else
  2473.   begin
  2474.     if LabelParamExists(_C_T, _NAME) then
  2475.     begin
  2476.       Name := GetLabelParamValue(_C_T, _NAME);
  2477.     end
  2478.     else
  2479.     begin
  2480.       if LabelParamExists(_C_D, _FLNM) then
  2481.       begin
  2482.         Name := GetLabelParamValue(_C_D, _FLNM);
  2483.       end
  2484.       else
  2485.       begin
  2486.         if LabelExists(_C_ID) then
  2487.         begin
  2488.           Name := GetLabelValue(_C_ID);
  2489.         end
  2490.         else
  2491.         begin
  2492.           if LabelExists(_C_LC) then
  2493.           begin
  2494.             Name := GetLabelValue(_C_LC);
  2495.           end
  2496.           else
  2497.           begin
  2498.             if LabelExists(_C_T) then
  2499.             begin
  2500.               Name := GetLabelValue(_C_T)+GetMimeExtension(GetLabelValue(_C_T));
  2501.             end
  2502.             else
  2503.             begin
  2504.               Name := 'unknown';
  2505.             end;
  2506.           end;
  2507.         end;
  2508.       end;
  2509.     end;
  2510.   end;
  2511.   Name := DecodeLine7Bit(Name);
  2512.   if (Pos('.', Name) = 0) and LabelExists(_C_T) then
  2513.     Name := Name + GetMimeExtension(GetLabelValue(_C_T));
  2514.   Result := ValidFileName(Name);
  2515. end;
  2516. // Get kind of attachment
  2517. function TMailPart.GetAttachInfo: String;
  2518. begin
  2519.   Result := LowerCase(GetLabelValue(_C_T));
  2520. end;
  2521. // Get boundary of this part (when it is a multipart header)
  2522. function TMailPart.GetBoundary: String;
  2523. begin
  2524.   Result := GetLabelParamValue(_C_T, _BDRY);
  2525. end;
  2526. // Decode mail part
  2527. function TMailPart.Decode;
  2528. var
  2529.   Content: String;
  2530.   Encoding: String;
  2531.   Data: String;
  2532.   DecoLine: String;
  2533.   Buffer: PChar;
  2534.   Size: Integer;
  2535.   nPos: Integer;
  2536. begin
  2537.   Result := True;
  2538.   if FIsDecoded then