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

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.           end;
  772.           'Q':
  773.           begin
  774.             while Pos('_', Texto) > 0 do
  775.               Texto[Pos('_', Texto)] := #32;
  776.             Texto := DecodeQuotedPrintable(Texto);
  777.           end;
  778.           'U':
  779.           begin
  780.             GetMem(Buffer, Length(Texto));
  781.             Size := DecodeLineUUCODE(Texto, Buffer);
  782.             Buffer[Size] := #0;
  783.             Texto := String(Buffer);
  784.           end;
  785.         end;
  786.         Result := Copy(Result, 1, nPos0-1)+Texto+Copy(Result,nPos2+2,Length(Result));
  787.         Found := True;
  788.       end;
  789.     end;
  790.   until not Found;
  791. end;
  792. // Encode a header field e.g. =?iso-8859-1?x?xxxxxx=?=
  793. function EncodeLine7Bit(const Texto, Charset: String): String;
  794. begin
  795.   Result := EncodeQuotedPrintable(Texto, True);
  796.   if Result <> Texto then
  797.     Result := '=?'+Charset+'?Q?'+Result+'?='
  798.   else
  799.     Result := Texto;
  800. end;
  801. // Decode a quoted-printable encoded string
  802. function DecodeQuotedPrintable(const Texto: String): String;
  803. var
  804.   nPos: Integer;
  805.   nLastPos: Integer;
  806. //lFound: Boolean;
  807. begin
  808.   Result := '';
  809.   nPos := 1;
  810.   while nPos <= Length(Texto) do
  811.   begin
  812.     if Texto[nPos] = '=' then
  813.     begin
  814.       if (Length(Texto)-nPos) >= 2 then
  815.       begin
  816.         if (Texto[nPos+1] = #13) and (Texto[nPos+2] = #10) then
  817.         begin
  818.           Inc(nPos, 3);
  819.         end
  820.         else
  821.         begin
  822.           if (Texto[nPos+1] in ['0'..'9', 'A'..'F'])
  823.           and (Texto[nPos+2] in ['0'..'9', 'A'..'F']) then
  824.           begin
  825.             Result := Result + Char(StrToInt('$'+Texto[nPos+1]+Texto[nPos+2]));
  826.             Inc(nPos, 3)
  827.           end
  828.           else
  829.           begin
  830.             Inc(nPos, 3);
  831.           end;
  832.         end;
  833.       end
  834.       else
  835.       begin
  836.         Break;
  837.       end;
  838.     end
  839.     else
  840.     begin
  841.       nLastPos := nPos;
  842.       nPos := Pos('=', Copy(Texto, nLastPos+1, High(Integer)));
  843.       if nPos = 0 then
  844.         nPos := Length(Texto)+1;
  845.       Result := Result + Copy(Texto, nLastPos, nPos);
  846.       Inc(nPos, nLastPos);
  847.     end;
  848.   end;
  849. end;
  850. // Encode a string in quoted-printable format
  851. function EncodeQuotedPrintable(const Texto: String; const HeaderLine: Boolean): String;
  852. var
  853.   nPos: Integer;
  854.   LineLen: Integer;
  855.   Buffer: TStringList;
  856.   Line2: String;
  857. begin
  858.   Result := '';
  859.   for nPos := 1 to Length(Texto) do
  860.   begin
  861.     if (Texto[nPos] > #127) or
  862.        (Texto[nPos] = '=') or
  863.        ((Texto[nPos] < #32) and (not (Texto[nPos] in [#9, #10, #13]))) or
  864.        ((nPos < Length(Texto)) and (Texto[nPos] = #13) and (Texto[nPos+1] <> #10)) or
  865.        ((nPos = Length(Texto)) and (Texto[nPos] = #13)) or
  866.        ((nPos > 1) and (Texto[nPos] = #10) and (Texto[nPos-1] <> #13)) or
  867.        ((nPos = 1) and (Texto[nPos] = #10)) or
  868.        ((Texto[nPos] in [#9..#32, #39, '?', '<', '>', '(', ')', '"', '_']) and HeaderLine) then
  869.     begin
  870.       Result := Result + '=' + PadL(Format('%2x', [Ord(Texto[nPos])]), 2, '0');
  871.     end
  872.     else
  873.     begin
  874.       Result := Result + Texto[nPos];
  875.     end;
  876.     // Encode trailing spaces/tabs
  877.     if not HeaderLine then
  878.     begin
  879.       if (Length(Result) > 2)
  880.       and (Result[Length(Result)] = #10)
  881.       and (Result[Length(Result)-1] = #13)
  882.       and (Result[Length(Result)-2] in [#9, #32]) then
  883.       begin
  884.         LineLen := Length(Result)-2;
  885.         Line2 := '';
  886.         while (LineLen >= 1) and (Result[LineLen] in [#9, #32]) do
  887.         begin
  888.           case Result[LineLen] of
  889.             #09: Line2 := '=09'+Line2;
  890.             #32: Line2 := '=20'+Line2;
  891.           end;
  892.           Dec(LineLen);
  893.         end;
  894.         Result := Copy(Result, 1, LineLen)+Line2+#13#10;
  895.       end;
  896.     end;
  897.   end;
  898.   // Insert soft linebreaks
  899.   if not HeaderLine then
  900.   begin
  901.     Buffer := TStringList.Create;
  902.     Buffer.Text := Result;
  903.     nPos := 0;
  904.     while nPos < Buffer.Count do
  905.     begin
  906.       LineLen := Length(Buffer[nPos]);
  907.       while LineLen >= _LINELEN do
  908.       begin
  909.         if (LineLen >= 3)
  910.         and (Buffer[nPos][LineLen] in ['0'..'9', 'A'..'F'])
  911.         and (Buffer[nPos][LineLen-1] in ['0'..'9', 'A'..'F'])
  912.         and (Buffer[nPos][LineLen-2] = '=') then
  913.           Dec(LineLen, 3)
  914.         else
  915.           Dec(LineLen, 1);
  916.       end;
  917.       if LineLen < Length(Buffer[nPos]) then
  918.       begin
  919.         Buffer.Insert(nPos+1, Copy(Buffer[nPos], LineLen+1, Length(Buffer[nPos])));
  920.         Buffer[nPos] := Copy(Buffer[nPos], 1, LineLen)+'=';
  921.       end;
  922.       Inc(nPos);
  923.     end;
  924.     Result := Buffer.Text;
  925.   end;
  926. end;
  927. // Decode an UUCODE encoded line
  928. function DecodeLineUUCODE(const Buffer: String; Decoded: PChar): Integer;
  929. const
  930. CHARS_PER_LINE = 45;
  931. Table: String = '`!"#$%&''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_';
  932. var
  933. A24Bits: array[0..8 * CHARS_PER_LINE] of Boolean;
  934. i, j, k, b: Word;
  935. LineLen, ActualLen: Byte;
  936. function p_ByteFromTable(Ch: Char): Byte;
  937. var
  938. ij: Integer;
  939. begin
  940. ij := Pos(Ch, Table);
  941. if (ij > 64) or (ij = 0) then
  942. begin
  943. if Ch = #32 then
  944. Result := 0 else
  945. raise Exception.Create(_E_UUCD);
  946. end else
  947. Result := ij - 1;
  948. end;
  949. begin
  950.   if Buffer = '' then
  951.   begin
  952.     Result := 0;
  953.     Exit;
  954.   end;
  955.   try
  956.     LineLen := p_ByteFromTable(Buffer[1]);
  957.     ActualLen := 4 * LineLen div 3;
  958.     FillChar(A24Bits, 8 * CHARS_PER_LINE + 1, 0);
  959.     Result := LineLen;
  960.     if ActualLen <> (4 * CHARS_PER_LINE div 3) then
  961.       ActualLen := Length(Buffer) - 1;
  962.     k := 0;
  963.     for i := 2 to ActualLen + 1 do
  964.     begin
  965.       b := p_ByteFromTable(Buffer[i]);
  966.       for j := 5 downto 0 do
  967.       begin
  968.         A24Bits[k] := b and (1 shl j) > 0;
  969.         Inc(k);
  970.       end;
  971.     end;
  972.     k := 0;
  973.     for i := 1 to CHARS_PER_LINE do
  974.     begin
  975.       b := 0;
  976.       for j := 7 downto 0 do
  977.       begin
  978.         if A24Bits[k] then b := b or (1 shl j);
  979.         Inc(k);
  980.       end;
  981.       Decoded[i-1] := Char(b);
  982.     end;
  983.   except
  984.     Result := 0;
  985.   end;
  986. end;
  987. // Decode an UUCODE text
  988. function DecodeUUCODE(Encoded: PChar; Decoded: TMemoryStream): Boolean;
  989. var
  990.   nTL, nPos, nLen: Integer;
  991.   Line: PChar;
  992.   LineDec: array[0..79] of Char;
  993.   LineLen: Integer;
  994.   DataEnd: Boolean;
  995. begin
  996.   Decoded.Clear;
  997.   DataEnd := False;
  998.   nPos := -1;
  999.   nTL := StrLen(Encoded);
  1000.   DataLinePChar(Encoded, nTL, nPos, nLen, Line, DataEnd);
  1001.   while not DataEnd do
  1002.   begin
  1003.     if nLen > 0 then
  1004.     begin
  1005.       LineLen := DecodeLineUUCODE(String(Line), LineDec);
  1006.       if LineLen > 0 then
  1007.         Decoded.Write(LineDec[0], LineLen);
  1008.     end;
  1009.     DataLinePChar(Encoded, nTL, nPos, nLen, Line, DataEnd);
  1010.   end;
  1011.   Result := True;
  1012. end;
  1013. // Decode a BASE64 encoded line
  1014. function DecodeLineBASE64(const Buffer: String; Decoded: PChar): Integer;
  1015. var
  1016.   A1: array[1..4] of Byte;
  1017.   B1: array[1..3] of Byte;
  1018.   I, J: Integer;
  1019.   BytePtr, RealBytes: Integer;
  1020. begin
  1021.   BytePtr := 0;
  1022.   Result := 0;
  1023.   for J := 1 to Length(Buffer) do
  1024.   begin
  1025.     Inc(BytePtr);
  1026.     case Buffer[J] of
  1027.       'A'..'Z': A1[BytePtr] := Ord(Buffer[J])-65;
  1028.       'a'..'z': A1[BytePtr] := Ord(Buffer[J])-71;
  1029.       '0'..'9': A1[BytePtr] := Ord(Buffer[J])+4;
  1030.       '+': A1[BytePtr] := 62;
  1031.       '/': A1[BytePtr] := 63;
  1032.       '=': A1[BytePtr] := 64;
  1033.     end;
  1034.     if BytePtr = 4 then
  1035.     begin
  1036.       BytePtr := 0;
  1037.       RealBytes := 3;
  1038.       if A1[1] = 64 then RealBytes:=0;
  1039.       if A1[3] = 64 then
  1040.       begin
  1041.         A1[3] := 0;
  1042.         A1[4] := 0;
  1043.         RealBytes := 1;
  1044.       end;
  1045.       if A1[4] = 64 then
  1046.       begin
  1047.         A1[4] := 0;
  1048.         RealBytes := 2;
  1049.       end;
  1050.       B1[1] := A1[1]*4 + (A1[2] div 16);
  1051.       B1[2] := (A1[2] mod 16)*16+(A1[3] div 4);
  1052.       B1[3] := (A1[3] mod 4)*64 + A1[4];
  1053.       for I := 1 to RealBytes do
  1054.       begin
  1055.         Decoded[Result+I-1] := Chr(B1[I]);
  1056.       end;
  1057.       Inc(Result, RealBytes);
  1058.     end;
  1059.   end;
  1060. end;
  1061. // Padronize header labels; remove double spaces, decode quoted text, lower the cases, indentify mail addresses
  1062. function NormalizeLabel(Texto: String): String;
  1063. var
  1064.   Quote: Boolean;
  1065.   Quoted: String;
  1066.   Loop: Integer;
  1067.   lLabel: Boolean;
  1068.   sLabel: String;
  1069.   Value: String;
  1070. begin
  1071.   Quote := False;
  1072.   lLabel := True;
  1073.   Value := '';
  1074.   sLabel := '';
  1075.   for Loop := 1 to Length(Texto) do
  1076.   begin
  1077.     if (Texto[Loop] = '"') and (not lLabel) then
  1078.     begin
  1079.       Quote := not Quote;
  1080.       if Quote then
  1081.       begin
  1082.         Quoted := '';
  1083.       end
  1084.       else
  1085.       begin
  1086.         Value := Value + Quoted;
  1087.       end;
  1088.     end;
  1089.     if not Quote then
  1090.     begin
  1091.       if lLabel then
  1092.       begin
  1093.         if (sLabel = '') or (sLabel[Length(sLabel)] = '-') then
  1094.           sLabel := sLabel + UpCase(Texto[Loop])
  1095.         else
  1096.           if (Copy(sLabel, Length(sLabel)-1, 2) = '-I') and (UpCase(Texto[Loop]) = 'D') and
  1097.              (Loop < Length(Texto)) and (Texto[Loop+1] = ':') then
  1098.             sLabel := sLabel + 'D'
  1099.           else
  1100.             sLabel := sLabel + LowerCase(Texto[Loop]);
  1101.         if Texto[Loop] = ':' then
  1102.         begin
  1103.           lLabel := False;
  1104.           Value := '';
  1105.         end;
  1106.       end
  1107.       else
  1108.       begin
  1109.         if Texto[Loop] = #32 then
  1110.         begin
  1111.           Value := TrimRightSpace(Value) + #32;
  1112.         end
  1113.         else
  1114.         begin
  1115.           Value := Value + Texto[Loop];
  1116.         end;
  1117.       end;
  1118.     end
  1119.     else
  1120.     begin
  1121.       Quoted := Quoted + Texto[Loop];
  1122.     end;
  1123.   end;
  1124.   Result := TrimSpace(sLabel)+' '+TrimSpace(Value);
  1125. end;
  1126. // Return the value of a label; e.g. Label: value
  1127. function LabelValue(cLabel: String): String;
  1128. var
  1129.   Loop: Integer;
  1130.   Quote: Boolean;
  1131.   Value: Boolean;
  1132.   Ins: Boolean;
  1133. begin
  1134.   Quote := False;
  1135.   Value := False;
  1136.   Result := '';
  1137.   for Loop := 1 to Length(cLabel) do
  1138.   begin
  1139.     Ins := True;
  1140.     if cLabel[Loop] = '"' then
  1141.     begin
  1142.       Quote := not Quote;
  1143. //    Ins := False;
  1144.     end;
  1145.     if not Quote then
  1146.     begin
  1147.       if (cLabel[Loop] = ':') and (not Value) then
  1148.       begin
  1149.         Value := True;
  1150.         Ins := False;
  1151.       end
  1152.       else
  1153.       begin
  1154.         if (cLabel[Loop] = ';') and Value then
  1155.         begin
  1156.           Break;
  1157.         end;
  1158.       end;
  1159.     end;
  1160.     if Ins and Value then
  1161.     begin
  1162.       Result := Result + cLabel[Loop];
  1163.     end;
  1164.   end;
  1165.   Result := TrimSpace(Result);
  1166.   if (Copy(Result, 1, 1) = '"') and (Copy(Result, Length(Result), 1) = '"') then
  1167.     Result := Copy(Result, 2, Length(Result)-2);
  1168. end;
  1169. // Set the value of a label;
  1170. function WriteLabelValue(cLabel, Value: String): String;
  1171. var
  1172.   Loop: Integer;
  1173.   Quote: Boolean;
  1174.   ValPos, ValLen: Integer;
  1175. begin
  1176.   Quote := False;
  1177.   ValPos := 0;
  1178.   ValLen := -1;
  1179.   for Loop := 1 to Length(cLabel) do
  1180.   begin
  1181.     if cLabel[Loop] = '"' then
  1182.     begin
  1183.       Quote := not Quote;
  1184.     end;
  1185.     if not Quote then
  1186.     begin
  1187.       if (cLabel[Loop] = ':') and (ValPos = 0) then
  1188.       begin
  1189.         ValPos := Loop+1;
  1190.       end
  1191.       else
  1192.       begin
  1193.         if (cLabel[Loop] = ';') and (ValPos > 0) then
  1194.         begin
  1195.           ValLen := Loop - ValPos;
  1196.           Break;
  1197.         end;
  1198.       end;
  1199.     end;
  1200.   end;
  1201.   Result := cLabel;
  1202.   if (ValLen < 0) and (ValPos > 0) then
  1203.     ValLen := Length(cLabel) - ValPos + 1;
  1204.   if ValPos > 0 then
  1205.   begin
  1206.     Delete(Result, ValPos, ValLen);
  1207.     Insert(' '+TrimSpace(Value), Result, ValPos);
  1208.   end;
  1209. end;
  1210. // Return the value of a label parameter; e.g. Label: xxx; param=value
  1211. function LabelParamValue(cLabel, cParam: String): String;
  1212. var
  1213.   Loop: Integer;
  1214.   Quote: Boolean;
  1215.   Value: Boolean;
  1216.   Params: Boolean;
  1217.   ParamValue: Boolean;
  1218.   Ins: Boolean;
  1219.   Param: String;
  1220. begin
  1221.   Quote := False;
  1222.   Value := False;
  1223.   Params := False;
  1224.   ParamValue := False;
  1225.   Param := '';
  1226.   Result := '';
  1227.   cLabel := TrimSpace(cLabel);
  1228.   if Copy(cLabel, Length(cLabel), 1) <> ';' then cLabel := cLabel + ';';
  1229.   for Loop := 1 to Length(cLabel) do
  1230.   begin
  1231.     Ins := True;
  1232.     if cLabel[Loop] = '"' then
  1233.     begin
  1234.       Quote := not Quote;
  1235. //    Ins := False;
  1236.     end;
  1237.     if not Quote then
  1238.     begin
  1239.       if (cLabel[Loop] = ':') and (not Value) and (not Params) then
  1240.       begin
  1241.         Value := True;
  1242.         Params := False;
  1243.         ParamValue := False;
  1244.         Ins := False;
  1245.       end
  1246.       else
  1247.       begin
  1248.         if (cLabel[Loop] = ';') and (Value or Params) then
  1249.         begin
  1250.           Params := True;
  1251.           Value := False;
  1252.           ParamValue := False;
  1253.           Param := '';
  1254.           Ins := False;
  1255.         end
  1256.         else
  1257.         begin
  1258.           if (cLabel[Loop] = '=') and Params then
  1259.           begin
  1260.             ParamValue := UpperCase(TrimSpace(Param)) = UpperCase(TrimSpace(cParam));
  1261.             Ins := False;
  1262.             Param := '';
  1263.           end;
  1264.         end;
  1265.       end;
  1266.     end;
  1267.     if Ins and ParamValue then
  1268.     begin
  1269.       Result := Result + cLabel[Loop];
  1270.     end;
  1271.     if Ins and (not ParamValue) and Params then
  1272.     begin
  1273.       Param := Param + cLabel[Loop];
  1274.     end;
  1275.   end;
  1276.   Result := TrimSpace(Result);
  1277.   if (Copy(Result, 1, 1) = '"') and (Copy(Result, Length(Result), 1) = '"') then
  1278.     Result := Copy(Result, 2, Length(Result)-2);
  1279. end;
  1280. // Set the value of a label parameter;
  1281. function WriteLabelParamValue(cLabel, cParam, Value: String): String;
  1282. var
  1283.   Loop: Integer;
  1284.   Quote: Boolean;
  1285.   LabelValue: Boolean;
  1286.   Params: Boolean;
  1287.   ValPos, ValLen: Integer;
  1288.   Ins: Boolean;
  1289.   Param: String;
  1290. begin
  1291.   Quote := False;
  1292.   LabelValue := False;
  1293.   Params := False;
  1294.   ValPos := 0;
  1295.   ValLen := -1;
  1296.   Param := '';
  1297.   Result := '';
  1298.   cLabel := TrimSpace(cLabel);
  1299.   if cLabel[Length(cLabel)] <> ';' then
  1300.     cLabel := cLabel + ';';
  1301.   for Loop := 1 to Length(cLabel) do
  1302.   begin
  1303.     Ins := True;
  1304.     if cLabel[Loop] = '"' then
  1305.     begin
  1306.       Quote := not Quote;
  1307. //    Ins := False;
  1308.     end;
  1309.     if not Quote then
  1310.     begin
  1311.       if (cLabel[Loop] = ':') and (not LabelValue) and (not Params) then
  1312.       begin
  1313.         LabelValue := True;
  1314.         Params := False;
  1315.         ValPos := 0;
  1316.         ValLen := 0;
  1317.         Ins := False;
  1318.       end
  1319.       else
  1320.       begin
  1321.         if (cLabel[Loop] = ';') and (LabelValue or Params) then
  1322.         begin
  1323.           if Params and (ValPos > 0) then
  1324.           begin
  1325.             ValLen := Loop - ValPos;
  1326.             Break;
  1327.           end;
  1328.           Params := True;
  1329.           LabelValue := False;
  1330.           Param := '';
  1331.           Ins := False;
  1332.         end
  1333.         else
  1334.         begin
  1335.           if (cLabel[Loop] = '=') and Params then
  1336.           begin
  1337.             if UpperCase(TrimSpace(Param)) = UpperCase(TrimSpace(cParam)) then
  1338.             begin
  1339.               ValPos := Loop+1;
  1340.               ValLen := 0;
  1341.             end;
  1342.             Ins := False;
  1343.             Param := '';
  1344.           end;
  1345.         end;
  1346.       end;
  1347.     end;
  1348.     if Ins and (ValPos = 0) and Params then
  1349.     begin
  1350.       Param := Param + cLabel[Loop];
  1351.     end;
  1352.   end;
  1353.   Result := cLabel;
  1354.   if Result[Length(Result)] = ';' then
  1355.     Delete(Result, Length(Result), 1);
  1356.   if ValPos = 0 then
  1357.   begin
  1358.     Result := TrimSpace(Result) + '; ' + TrimSpace(cParam) + '=' + TrimSpace(Value);
  1359.   end
  1360.   else
  1361.   begin
  1362.     if (ValLen < 0) and (ValPos > 0) then
  1363.       ValLen := Length(cLabel) - ValPos + 1;
  1364.     Delete(Result, ValPos, ValLen);
  1365.     Insert(TrimSpace(Value), Result, ValPos);
  1366.   end;
  1367. end;
  1368. // Return the Timezone adjust in days
  1369. function GetTimeZoneBias: Double;
  1370. var
  1371.   TzInfo: TTimeZoneInformation;
  1372. begin
  1373.   case GetTimeZoneInformation(TzInfo) of
  1374.     1: Result := - (TzInfo.StandardBias + TzInfo.Bias) / (24*60);
  1375.     2: Result := - (TzInfo.DaylightBias + TzInfo.Bias) / (24*60);
  1376.     else Result := - TzInfo.Bias / (24*60);
  1377.   end;
  1378. end;
  1379. // Fills left of string with char
  1380. function PadL(const Str: String; const Tam: Integer; const PadStr: String): String;
  1381. var
  1382.   TempStr: String;
  1383. begin
  1384.   TempStr := TrimLeftSpace(Str);
  1385.   if Length(TempStr) <= Tam then
  1386.   begin
  1387.     while Length(TempStr) < Tam do
  1388.       TempStr := PadStr + TempStr;
  1389.   end
  1390.   else
  1391.   begin
  1392.     TempStr := Copy(TempStr, Length(TempStr) - Tam + 1, Tam);
  1393.   end;
  1394.   Result := TempStr;
  1395. end;
  1396. // Get mime type of a file extension
  1397. function GetMimeType(const FileName: String): String;
  1398. var
  1399.   Key: string;
  1400. begin
  1401.   Result := '';
  1402.   with TRegistry.Create do
  1403.     try
  1404.       RootKey := HKEY_CLASSES_ROOT;
  1405.       Key := ExtractFileExt(FileName);
  1406.       if KeyExists(Key) then
  1407.       begin
  1408.         OpenKey(Key, False);
  1409.         Result := ReadString('Content Type');
  1410.         CloseKey;
  1411.       end;
  1412.     finally
  1413.       if Result = '' then
  1414.         Result := _A_OS;
  1415.       Free;
  1416.     end;
  1417. end;
  1418. // Get file extension of a mime type
  1419. function GetMimeExtension(const MimeType: String): String;
  1420. var
  1421.   Key: string;
  1422. begin
  1423.   Result := '';
  1424.   with TRegistry.Create do
  1425.     try
  1426.       RootKey := HKEY_CLASSES_ROOT;
  1427.       if OpenKey('MIMEDatabaseContent Type', False) then
  1428.       begin
  1429.         Key := MimeType;
  1430.         if KeyExists(Key) then
  1431.         begin
  1432.           OpenKey(Key,false);
  1433.           Result := ReadString('Extension');
  1434.           CloseKey;
  1435.         end;
  1436.       end;
  1437.     finally
  1438.       Free;
  1439.     end;
  1440. end;
  1441. // Generate a random boundary
  1442. function GenerateBoundary: String;
  1443. begin
  1444.   Result := _BDRY + Format('%8.8x', [Random($FFFFFFFF)]);
  1445. end;
  1446. // Encode in base64
  1447. function EncodeBASE64(Encoded: TMemoryStream {TMailText}; Decoded: TMemoryStream): Integer;
  1448. const
  1449.   _Code64: String[64] =
  1450.     ('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/');
  1451. var
  1452.   I: LongInt;
  1453.   B: array[0..2279] of Byte;
  1454.   J, K, L, M, Quads: Integer;
  1455.   Stream: string[76];
  1456.   EncLine: String;
  1457. begin
  1458.   Encoded.Clear;
  1459.   Stream := '';
  1460.   Quads := 0;
  1461.   J := Decoded.Size div 2280;
  1462.   Decoded.Position := 0;
  1463.   for I := 1 to J do
  1464.   begin
  1465.     Decoded.Read(B, 2280);
  1466.     for M := 0 to 39 do
  1467.     begin
  1468.       for K := 0 to 18 do
  1469.       begin
  1470.         L:= 57*M + 3*K;
  1471.         Stream[Quads+1] := _Code64[(B[L] div 4)+1];
  1472.         Stream[Quads+2] := _Code64[(B[L] mod 4)*16 + (B[L+1] div 16)+1];
  1473.         Stream[Quads+3] := _Code64[(B[L+1] mod 16)*4 + (B[L+2] div 64)+1];
  1474.         Stream[Quads+4] := _Code64[B[L+2] mod 64+1];
  1475.         Inc(Quads, 4);
  1476.         if Quads = 76 then
  1477.         begin
  1478.           Stream[0] := #76;
  1479.           EncLine := Stream+#13#10;
  1480.           Encoded.Write(EncLine[1], Length(EncLine));
  1481.           Quads := 0;
  1482.         end;
  1483.       end;
  1484.     end;
  1485.   end;
  1486.   J := (Decoded.Size mod 2280) div 3;
  1487.   for I := 1 to J do
  1488.   begin
  1489.     Decoded.Read(B, 3);
  1490.     Stream[Quads+1] := _Code64[(B[0] div 4)+1];
  1491.     Stream[Quads+2] := _Code64[(B[0] mod 4)*16 + (B[1] div 16)+1];
  1492.     Stream[Quads+3] := _Code64[(B[1] mod 16)*4 + (B[2] div 64)+1];
  1493.     Stream[Quads+4] := _Code64[B[2] mod 64+1];
  1494.     Inc(Quads, 4);
  1495.     if Quads = 76 then
  1496.     begin
  1497.       Stream[0] := #76;
  1498.       EncLine := Stream+#13#10;
  1499.       Encoded.Write(EncLine[1], Length(EncLine));
  1500.       Quads := 0;
  1501.     end;
  1502.   end;
  1503.   if (Decoded.Size mod 3) = 2 then
  1504.   begin
  1505.     Decoded.Read(B, 2);
  1506.     Stream[Quads+1] := _Code64[(B[0] div 4)+1];
  1507.     Stream[Quads+2] := _Code64[(B[0] mod 4)*16 + (B[1] div 16)+1];
  1508.     Stream[Quads+3] := _Code64[(B[1] mod 16)*4 + 1];
  1509.     Stream[Quads+4] := '=';
  1510.     Inc(Quads, 4);
  1511.   end;
  1512.   if (Decoded.Size mod 3) = 1 then
  1513.   begin
  1514.     Decoded.Read(B, 1);
  1515.     Stream[Quads+1] := _Code64[(B[0] div 4)+1];
  1516.     Stream[Quads+2] := _Code64[(B[0] mod 4)*16 + 1];
  1517.     Stream[Quads+3] := '=';
  1518.     Stream[Quads+4] := '=';
  1519.     Inc(Quads, 4);
  1520.   end;
  1521.   Stream[0] := Chr(Quads);
  1522.   if Quads > 0 then
  1523.   begin
  1524.     EncLine := Stream+#13#10;
  1525.     Encoded.Write(EncLine[1], Length(EncLine));
  1526.   end;
  1527.   Result := Encoded.Size;
  1528. end;
  1529. // Encode string to base64
  1530. function EncodeBASE64String(const Decoded: String): String;
  1531. var
  1532.   S1, S2: TMemoryStream;
  1533.   slTemp: TStringList;
  1534.   sTemp: String;
  1535. begin
  1536.   S1 := TMemoryStream.Create;
  1537.   S2 := TMemoryStream.Create;
  1538.   slTemp := TStringList.Create;
  1539.   slTemp.Text := Decoded;
  1540.   slTemp.SaveToStream(S1);
  1541.   try
  1542.     S1.SetSize(S1.Size - 2 );
  1543.     S1.Position := 0;
  1544.     EncodeBASE64(S2, S1);
  1545.     S2.Position := 0;
  1546.     slTemp.LoadFromStream(S2);
  1547.     sTemp := slTemp.Strings[0];
  1548.     Result := sTemp;
  1549.   finally
  1550.     slTemp.Free;
  1551.     S1.Free;
  1552.     S2.Free;
  1553.   end;
  1554. end;
  1555. // Search in a StringList
  1556. function SearchStringList(Lista: TStringList; const Chave: String; const Occorrence: Integer = 0): Integer;
  1557. var
  1558.   nPos: Integer;
  1559.   lAchou: Boolean;
  1560.   Casas: Integer;
  1561.   Temp: String;
  1562.   nOccor: Integer;
  1563. begin
  1564.   Casas := Length(Chave);
  1565.   lAchou := False;
  1566.   nPos := 0;
  1567.   nOccor := 0;
  1568.   try
  1569.     if Lista <> nil then
  1570.     begin
  1571.       while (not lAchou) and (nPos < Lista.Count) do
  1572.       begin
  1573.         Temp := Lista[nPos];
  1574.         if UpperCase(Copy(Temp, 1, Casas)) = UpperCase(Chave) then
  1575.         begin
  1576.           if nOccor = Occorrence then
  1577.           begin
  1578.             lAchou := True;
  1579.           end
  1580.           else
  1581.           begin
  1582.             Inc(nOccor);
  1583.           end;
  1584.         end;
  1585.         if not lAchou then
  1586.           Inc(nPos);
  1587.       end;
  1588.     end;
  1589.   finally
  1590.     if lAchou then
  1591.       result := nPos
  1592.     else
  1593.       result := -1;
  1594.   end;
  1595. end;
  1596. // Search lines into a string
  1597. procedure DataLine(var Data, Line: String; var nPos: Integer);
  1598. begin
  1599.   Line := '';
  1600.   while True do
  1601.   begin
  1602.     Line := Line + Data[nPos];
  1603.     Inc(nPos);
  1604.     if nPos > Length(Data) then
  1605.     begin
  1606.       nPos := -1;
  1607.       Break;
  1608.     end
  1609.     else
  1610.     begin
  1611.       if Length(Line) >= 2 then
  1612.       begin
  1613.         if (Line[Length(Line)-1] = #13) and (Line[Length(Line)] = #10) then
  1614.         begin
  1615.           Break;
  1616.         end;
  1617.       end;
  1618.     end;
  1619.   end;
  1620. end;
  1621. // Search lines into a string
  1622. // I need to do in this confusing way in order to improve performance
  1623. procedure DataLinePChar(const Data: PChar; const TotalLength: Integer; var LinePos, LineLen: Integer; var Line: PChar; var DataEnd: Boolean); assembler;
  1624. begin
  1625.   if LinePos >= 0 then
  1626.   begin
  1627.     Data[LinePos+LineLen] := #13;
  1628.     LinePos := LinePos+LineLen+2;
  1629.     LineLen := 0;
  1630.   end
  1631.   else
  1632.   begin
  1633.     LinePos := 0;
  1634.     LineLen := 0;
  1635.   end;
  1636.   while (LinePos+LineLen) < TotalLength do
  1637.   begin
  1638.     if Data[LinePos+LineLen] = #13 then
  1639.     begin
  1640.       if (LinePos+LineLen+1) < TotalLength then
  1641.       begin
  1642.         if Data[LinePos+LineLen+1] = #10 then
  1643.         begin
  1644.           Data[LinePos+LineLen] := #0;
  1645.           Line := @Data[LinePos];
  1646.           Exit;
  1647.         end;
  1648.       end;
  1649.     end;
  1650.     Inc(LineLen);
  1651.   end;
  1652.   if LinePos < TotalLength then
  1653.     Line := @Data[LinePos]
  1654.   else
  1655.     DataEnd := True;
  1656. end;
  1657. // Determine if string is a numeric IP or not (Thanks to Hou Yg yghou@yahoo.com)
  1658. function IsIPAddress(const SS: String): Boolean;
  1659. var
  1660.   Loop: Integer;
  1661.   P: String;
  1662. begin
  1663.   Result := True;
  1664.   P := '';
  1665.   for Loop := 1 to Length(SS)+1 do
  1666.   begin
  1667.     if (Loop > Length(SS)) or (SS[Loop] = '.') then
  1668.     begin
  1669.       if StrToIntDef(P, -1) < 0 then
  1670.       begin
  1671.         Result := False;
  1672.         Break;
  1673.       end;
  1674.       P := '';
  1675.     end
  1676.     else
  1677.     begin
  1678.       P := P + SS[Loop];
  1679.     end;
  1680.   end;
  1681. end;
  1682. // Remove leading and trailing spaces from string
  1683. // Thanks to Yunarso Anang (yasx@hotmail.com)
  1684. function TrimSpace(const S: string): string;
  1685. var
  1686.   I, L: Integer;
  1687. begin
  1688.   L := Length(S);
  1689.   I := 1;
  1690.   while (I <= L) and (S[I] in [#9, #32]) do
  1691.     Inc(I);
  1692.   if I > L then Result := '' else
  1693.   begin
  1694.     while S[L] = ' ' do
  1695.       Dec(L);
  1696.     Result := Copy(S, I, L - I + 1);
  1697.   end;
  1698. end;
  1699. // Remove left spaces from string
  1700. // Thanks to Yunarso Anang (yasx@hotmail.com)
  1701. function TrimLeftSpace(const S: string): string;
  1702. var
  1703.   I, L: Integer;
  1704. begin
  1705.   L := Length(S);
  1706.   I := 1;
  1707.   while (I <= L) and (S[I] in [#9, #32]) do
  1708.     Inc(I);
  1709.   Result := Copy(S, I, Maxint);
  1710. end;
  1711. // Remove right spaces from string
  1712. // Thanks to Yunarso Anang (yasx@hotmail.com)
  1713. function TrimRightSpace(const S: string): string;
  1714. var
  1715.   I: Integer;
  1716. begin
  1717.   I := Length(S);
  1718.   while (I > 0) and (S[I] in [#9, #32]) do
  1719.     Dec(I);
  1720.   Result := Copy(S, 1, I);
  1721. end;
  1722. // Convert date from message to Delphi format
  1723. // Returns zero in case of error
  1724. function MailDateToDelphiDate(const DateStr: String): TDateTime;
  1725. const
  1726.   Months: String = 'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec,';
  1727. var
  1728.   Field, Loop: Integer;
  1729.   Hour, Min, Sec, Year, Month, Day: Double;
  1730.   sDate, sHour, sMin, sSec, sYear, sMonth, sDay, sTZ: String;
  1731.   HTZM, MTZM: Word;
  1732.   STZM: Integer;
  1733.   TZM: Double;
  1734.   Final: Double;
  1735. begin
  1736.   sHour := '';
  1737.   sMin := '';
  1738.   sSec := '';
  1739.   sYear := '';
  1740.   sMonth := '';
  1741.   sDay := '';
  1742.   sTZ := '';
  1743.   sDate := Trim(DateStr);
  1744.   if sDate <> '' then
  1745.   begin
  1746.     if sDate[1] in ['0'..'9'] then
  1747.       Field := 1
  1748.     else
  1749.       Field := 0;
  1750.     for Loop := 1 to Length(sDate) do
  1751.     begin
  1752.       if sDate[Loop] in [#32, ':', '/'] then
  1753.       begin
  1754.         Inc(Field);
  1755.         if (Field = 6) and (sDate[Loop] = #32) then Field := 7;
  1756.       end
  1757.       else
  1758.       begin
  1759.         case Field of
  1760.           1: sDay := sDay + sDate[Loop];
  1761.           2: sMonth := sMonth + sDate[Loop];
  1762.           3: sYear := sYear + sDate[Loop];
  1763.           4: sHour := sHour + sDate[Loop];
  1764.           5: sMin := sMin + sDate[Loop];
  1765.           6: sSec := sSec + sDate[Loop];
  1766.           7: sTZ := sTZ + sDate[Loop];
  1767.         end;
  1768.       end;
  1769.     end;
  1770.     Hour := StrToIntDef(sHour, 0);
  1771.     Min := StrToIntDef(sMin, 0);
  1772.     Sec := StrToIntDef(sSec, 0);
  1773.     Year := StrToIntDef(sYear, 0);
  1774.     Day := StrToIntDef(sDay, 0);
  1775.     if sMonth[1] in ['0'..'9'] then
  1776.       Month := StrToIntDef(sMonth, 0)
  1777.     else
  1778.       Month := (Pos(sMonth, Months)-1) div 4 + 1;
  1779.     if Year < 100 then
  1780.     begin
  1781.       if Year < 50 then
  1782.         Year := 2000 + Year
  1783.       else
  1784.         Year := 1900 + Year;
  1785.     end;
  1786.     if (Year = 0) or (Month = 0) or (Year = 0) then
  1787.     begin
  1788.       Result := 0;
  1789.     end
  1790.     else
  1791.     begin
  1792.       if (sTZ = 'GMT') or (Length(Trim(sTZ)) <> 5) then
  1793.       begin
  1794.         STZM := 1;
  1795.         HTZM := 0;
  1796.         MTZM := 0;
  1797.       end
  1798.       else
  1799.       begin
  1800.         STZM := StrToIntDef(Copy(sTZ, 1, 1)+'1', 1);
  1801.         HTZM := StrToIntDef(Copy(sTZ, 2, 2), 0);
  1802.         MTZM := StrToIntDef(Copy(sTZ, 4, 2), 0);
  1803.       end;
  1804.       try
  1805.         TZM := EncodeTime(HTZM, MTZM, 0, 0)*STZM;
  1806.         Final := EncodeDate(Trunc(Year), Trunc(Month), Trunc(Day));
  1807.         Final := Final + Hour*(1/24) + Min*(1/24/60) + Sec*(1/24/60/60);
  1808.         Final := Final - TZM + GetTimeZoneBias;
  1809.         Result := Final;
  1810.       except
  1811.         Result := 0;
  1812.       end;
  1813.     end;
  1814.   end
  1815.   else
  1816.   begin
  1817.     Result := 0;
  1818.   end;
  1819. end;
  1820. // Convert numeric date to mail format
  1821. function DelphiDateToMailDate(const Date: TDateTime): String;
  1822. const
  1823.   Months: String = 'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec,';
  1824.   Weeks: String = 'Sun,Mon,Tue,Wed,Thu,Fri,Sat,';
  1825. var
  1826.   TZH: Double;
  1827.   DateStr: String;
  1828.   TZStr: String;
  1829.   Day, Month, Year: Word;
  1830. begin
  1831.   TZH := GetTimeZoneBias;
  1832.   DecodeDate(Date, Year, Month, Day);
  1833.   if TZH < 0 then
  1834.   begin
  1835.     TZStr := '-'+FormatDateTime('hhmm', Abs(TZH));
  1836.   end
  1837.   else
  1838.   begin
  1839.     if TZH = 0 then
  1840.     begin
  1841.       TZStr := 'GMT'
  1842.     end
  1843.     else
  1844.     begin
  1845.       TZStr := '+'+FormatDateTime('hhmm', Abs(TZH));
  1846.     end;
  1847.   end;
  1848.   DateStr := Copy(Weeks, (DayOfWeek(Date)-1)*4+1, 3)+',';
  1849.   DateStr := DateStr + FormatDateTime(' dd ', Date);
  1850.   DateStr := DateStr + Copy(Months, (Month-1)*4+1, 3);
  1851.   DateStr := DateStr + FormatDateTime(' yyyy hh:nn:ss ', Date) + TZStr;
  1852.   Result := DateStr;
  1853. end;
  1854. // To make sure that a file name (without path!) is valid
  1855. function ValidFileName(FileName: String): String;
  1856. const
  1857.   InvChars: String = ':/*?"<>|'#39;
  1858. var
  1859.   Loop: Integer;
  1860. begin
  1861.   FileName := Copy(TrimSpace(FileName), 1, 254);
  1862.   Result := '';
  1863.   for Loop := 1 to Length(FileName) do
  1864.   begin
  1865.     if (Ord(FileName[Loop]) < 32) or (Pos(FileName[Loop], InvChars) > 0) then
  1866.       Result := Result + '_'
  1867.     else
  1868.       Result := Result + FileName[Loop];
  1869.   end;
  1870. end;
  1871. // Wrap an entire message header
  1872. function WrapHeader(Text: String): String;
  1873. var
  1874.   Line: String;
  1875.   nPos: Integer;
  1876.   fPos: Integer;
  1877.   Quote: Char;
  1878.   Ok: Boolean;
  1879. begin
  1880.   Result := '';
  1881.   Text := AdjustLineBreaks(Text);
  1882.   while Copy(Text, Length(Text)-1, 2) = #13#10 do
  1883.     Delete(Text, Length(Text)-1, 2);
  1884.   while Text <> '' do
  1885.   begin
  1886.     nPos := Pos(#13#10, Text);
  1887.     if nPos > 0 then
  1888.     begin
  1889.       Line := Copy(Text, 1, nPos-1);
  1890.       Text := Copy(Text, nPos+2, Length(Text));
  1891.     end
  1892.     else
  1893.     begin
  1894.       Line := Text;
  1895.       Text := '';
  1896.     end;
  1897.     if Length(Line) <= _LINELEN then
  1898.     begin
  1899.       Result := Result + Line + #13#10;
  1900.     end
  1901.     else
  1902.     begin
  1903.       nPos := Length(Line);
  1904.       Quote := #0;
  1905.       Ok := False;
  1906.       if Line[1] <> #9 then
  1907.         fPos := Pos(':'#32, Line)+2
  1908.       else
  1909.         fPos := _LINELEN div 2;
  1910.       while nPos >= fPos do
  1911.       begin
  1912.         if (Quote = #0) and (Line[nPos] in [#39, '"']) then
  1913.           Quote := Line[nPos]
  1914.         else
  1915.           if (Quote <> #0) and (Line[nPos] = Quote) then
  1916.             Quote := #0;
  1917.         if (Quote = #0) and (nPos <= _LINELEN) and (Line[nPos] in [#32, ',', ';']) then
  1918.         begin
  1919.           Ok := True;
  1920.           Break;
  1921.         end;
  1922.         Dec(nPos);
  1923.       end;
  1924.       if Ok then
  1925.       begin
  1926.         if Line[nPos] = #32 then
  1927.           Result := Result + Copy(Line, 1, nPos-1) + #13#10#9
  1928.         else
  1929.           Result := Result + Copy(Line, 1, nPos) + #13#10#9;
  1930.         Text := Copy(Line, nPos+1, Length(Line)) + #13#10 + Text;
  1931.       end
  1932.       else
  1933.       begin
  1934.         Result := Result + Line + #13#10;
  1935.       end;
  1936.     end;
  1937.   end;
  1938. end;
  1939. // Parse text into tokens
  1940. function GetToken(const Token: Integer; const Text, Separators, QuoteStart, QuoteEnd: String): String;
  1941. var
  1942.   Quote: Integer;
  1943.   Loop: Integer;
  1944.   CountItem: Integer;
  1945.   nPos: Integer;
  1946.   EndQuote: Char;
  1947.   Jump: Boolean;
  1948. begin
  1949.   Result := '';
  1950.   Quote := 0;
  1951.   CountItem := 0;
  1952.   Jump := False;
  1953.   EndQuote := #0;
  1954.   for Loop := 1 to Length(Text) do
  1955.   begin
  1956.     nPos := Pos(Text[Loop], QuoteStart);
  1957.     if nPos > 0 then
  1958.     begin
  1959.       Inc(Quote);
  1960.       EndQuote := QuoteEnd[nPos];
  1961.     end
  1962.     else
  1963.     begin
  1964.       if (Quote > 0) and (Text[Loop] = EndQuote) then
  1965.       begin
  1966.         Dec(Quote);
  1967.       end
  1968.       else
  1969.       begin
  1970.         if (Quote = 0) and (Pos(Text[Loop], Separators) > 0) then
  1971.         begin
  1972.           Inc(CountItem);
  1973.           Jump := True;
  1974.         end;
  1975.       end;
  1976.     end;
  1977.     if (Quote = 0) and (not Jump) and (CountItem = Token) then
  1978.     begin
  1979.       Result := Result + Text[Loop];
  1980.     end;
  1981.     Jump := False;
  1982.   end;
  1983. end;
  1984. { TMailPart ================================================================== }
  1985. // Initialize MailPart
  1986. constructor TMailPart.Create(AOwner: TComponent);
  1987. begin
  1988.   inherited Create(AOwner);
  1989.   FHeader := TStringList.Create;
  1990.   FBody := TMemoryStream.Create;
  1991.   FDecoded := TMemoryStream.Create;
  1992.   FSubPartList := TMailPartList.Create;
  1993.   FOwnerPart := nil;
  1994.   FOwnerMessage := nil;
  1995.   FEmbedded := False;
  1996. end;
  1997. // Finalize MailPart
  1998. destructor TMailPart.Destroy;
  1999. var
  2000.   Loop: Integer;
  2001. begin
  2002.   for Loop := 0 to FSubPartList.Count-1 do
  2003.     FSubPartList.Items[Loop].Destroy;
  2004.   FHeader.Free;
  2005.   FBody.Free;
  2006.   FDecoded.Free;
  2007.   FSubPartList.Free;
  2008.   inherited Destroy;
  2009. end;
  2010. // Return the value of a label from the header like "To", "Subject"
  2011. function TMailPart.GetLabelValue(const cLabel: String): String;
  2012. var
  2013.   Loop: Integer;
  2014. begin
  2015.   Result := '';
  2016.   Loop := SearchStringList(FHeader, cLabel+':');
  2017.   if Loop >= 0 then
  2018.     Result := TrimSpace(LabelValue(FHeader[Loop]));
  2019.   if Length(Result) > 2 then
  2020.   begin
  2021.     if (Result[1] in ['"', #39]) and
  2022.        (Result[Length(Result)] in ['"', #39]) then
  2023.       Result := Copy(Result, 2, Length(Result)-2);
  2024.   end;
  2025. end;
  2026. // Return de value of a parameter of a value from the header
  2027. function TMailPart.GetLabelParamValue(const cLabel, Param: String): String;
  2028. var
  2029.   Loop: Integer;
  2030. begin
  2031.   Result := '';
  2032.   Loop := SearchStringList(FHeader, cLabel+':');
  2033.   if Loop >= 0 then
  2034.     Result := TrimSpace(LabelParamValue(FHeader[Loop], Param));
  2035.   if Length(Result) > 2 then
  2036.   begin
  2037.     if (Result[1] in ['"', #39]) and
  2038.        (Result[Length(Result)] in ['"', #39]) then
  2039.       Result := Copy(Result, 2, Length(Result)-2);
  2040.   end;
  2041. end;
  2042. // Set the value of a label
  2043. procedure TMailPart.SetLabelValue(const cLabel, cValue: String);
  2044. var
  2045.   Loop: Integer;
  2046. begin
  2047.   Loop := SearchStringList(FHeader, cLabel+':');
  2048.   if cValue <> '' then
  2049.   begin
  2050.     if Loop < 0 then
  2051.     begin
  2052.       FHeader.Add(cLabel+': ');
  2053.       Loop := FHeader.Count-1;
  2054.     end;
  2055.     FHeader[Loop] := WriteLabelValue(FHeader[Loop], cValue);
  2056.   end
  2057.   else
  2058.   begin
  2059.     if Loop >= 0 then
  2060.     begin
  2061.       FHeader.Delete(Loop);
  2062.     end;
  2063.   end;
  2064. end;
  2065. // Set the value of a label parameter
  2066. procedure TMailPart.SetLabelParamValue(const cLabel, cParam, cValue: String);
  2067. var
  2068.   Loop: Integer;
  2069. begin
  2070.   Loop := SearchStringList(FHeader, cLabel+':');
  2071.   if Loop < 0 then
  2072.   begin
  2073.     FHeader.Add(cLabel+': ');
  2074.     Loop := FHeader.Count-1;
  2075.   end;
  2076.   FHeader[Loop] := WriteLabelParamValue(FHeader[Loop], cParam, cValue);
  2077. end;
  2078. // Look for a label in the header
  2079. function TMailPart.LabelExists(const cLabel: String): Boolean;
  2080. begin
  2081.   Result := SearchStringList(FHeader, cLabel+':') >= 0;
  2082. end;
  2083. // Look for a parameter in a label in the header
  2084. function TMailPart.LabelParamExists(const cLabel, Param: String): Boolean;
  2085. var
  2086.   Loop: Integer;
  2087. begin
  2088.   Result := False;
  2089.   Loop := SearchStringList(FHeader, cLabel+':');
  2090.   if Loop >= 0 then
  2091.     Result := TrimSpace(LabelParamValue(FHeader[Loop], Param)) <> '';
  2092. end;
  2093. // Divide header and body; normalize header;
  2094. procedure TMailPart.Fill(Data: PChar; HasHeader: Boolean);
  2095. const
  2096.   CRLF: array[0..2] of Char = (#13, #10, #0);
  2097. var
  2098.   Loop: Integer;
  2099.   BoundStart: array[0..99] of Char;
  2100.   BoundEnd: array[0..99] of Char;
  2101.   InBound: Boolean;
  2102.   IsBoundStart: Boolean;
  2103.   IsBoundEnd: Boolean;
  2104.   BoundStartLen: Integer;
  2105.   BoundEndLen: Integer;
  2106.   PartText: PChar;
  2107.   DataEnd: Boolean;
  2108.   MultPart: Boolean;
  2109.   NoParts: Boolean;
  2110.   InUUCode: Boolean;
  2111.   UUFile, UUBound: String;
  2112.   Part: TMailPart;
  2113.   nPos: Integer;
  2114.   nLen: Integer;
  2115.   nTL: Integer;
  2116.   nSPos: Integer;
  2117.   Line: PChar;
  2118.   SChar: Char;
  2119. begin
  2120.   if (FOwnerMessage = nil) or (not (FOwnerMessage is TMailMessage2000)) then
  2121.   begin
  2122.     Exception.CreateFmt(_E_MLPT, [Self.Name]);
  2123.     Exit;
  2124.   end;
  2125.   for Loop := 0 to FSubPartList.Count-1 do
  2126.     FSubPartList.Items[Loop].Destroy;
  2127.   FHeader.Clear;
  2128.   FBody.Clear;
  2129.   FDecoded.Clear;
  2130.   FSubPartList.Clear;
  2131.   FIsDecoded := False;
  2132.   FEmbedded := False;
  2133.   FOwnerMessage.FNeedRebuild := True;
  2134.   FOwnerMessage.FNeedNormalize := True;
  2135.   FOwnerMessage.FNeedFindParts := True;
  2136.   nPos := -1;
  2137.   DataEnd := False;
  2138.   nTL := StrLen(Data);
  2139.   nSPos := nTL+1;
  2140.   if (Self is TMailMessage2000) and Assigned(FOwnerMessage.FOnProgress) then
  2141.   begin
  2142.     FOwnerMessage.FOnProgress(Self, nTL, 0);
  2143.     Application.ProcessMessages;
  2144.   end;
  2145.   if HasHeader then
  2146.   begin
  2147.     // Get Header
  2148.     DataLinePChar(Data, nTL, nPos, nLen, Line, DataEnd);
  2149.     while not DataEnd do
  2150.     begin
  2151.       if nLen = 0 then
  2152.       begin
  2153.         Break;
  2154.       end
  2155.       else
  2156.       begin
  2157.         if (Line[0] in [#9, #32]) and (FHeader.Count > 0) then
  2158.         begin
  2159.           FHeader[FHeader.Count-1] := FHeader[FHeader.Count-1] + #32 + TrimLeftSpace(String(PChar(@Line[1])));
  2160.         end
  2161.         else
  2162.         begin
  2163.           FHeader.Add(String(Line));
  2164.         end;
  2165.       end;
  2166.       DataLinePChar(Data, nTL, nPos, nLen, Line, DataEnd);
  2167.       if (Self is TMailMessage2000) and Assigned(FOwnerMessage.FOnProgress) then
  2168.       begin
  2169.         FOwnerMessage.FOnProgress(Self, nTL, nPos+1);
  2170.         Application.ProcessMessages;
  2171.       end;
  2172.     end;
  2173.     for Loop := 0 to FHeader.Count-1 do
  2174.       FHeader[Loop] := NormalizeLabel(FHeader[Loop]);
  2175.   end;
  2176.   MultPart := LowerCase(Copy(GetLabelValue(_C_T), 1, 10)) = _MP;
  2177.   InBound := False;
  2178.   IsBoundStart := False;
  2179.   IsBoundEnd := False;
  2180.   UUBound := '';
  2181.   if MultPart then
  2182.   begin
  2183.     StrPCopy(BoundStart, '--'+GetBoundary);
  2184.     StrPCopy(BoundEnd, '--'+GetBoundary+'--');
  2185.     BoundStartLen := StrLen(BoundStart);
  2186.     BoundEndLen := StrLen(BoundEnd);
  2187.     NoParts := False;
  2188.   end
  2189.   else
  2190.   begin
  2191.     if LabelExists(_C_T) then
  2192.     begin
  2193.       NoParts := True;
  2194.       BoundStartLen := 0;
  2195.       BoundEndLen := 0;
  2196.     end
  2197.     else
  2198.     begin
  2199.       StrPCopy(BoundStart, _UUBG);
  2200.       StrPCopy(BoundEnd, _UUEN);
  2201.       BoundStartLen := StrLen(BoundStart);
  2202.       BoundEndLen := StrLen(BoundEnd);
  2203.       NoParts := False;
  2204.     end;
  2205.   end;
  2206.   PartText := nil;
  2207.   // Get Body
  2208.   DataLinePChar(Data, nTL, nPos, nLen, Line, DataEnd);
  2209.   while (not DataEnd) and (not InBound) do
  2210.   begin
  2211.     if (not NoParts) and (((Line[0] = '-') and (Line[1] = '-')) or ((Line[0] = 'b') and (Line[1] = 'e'))) then
  2212.     begin
  2213.       IsBoundStart := StrLComp(Line, BoundStart, BoundStartLen) = 0;
  2214.     end;
  2215.     if NoParts or (not IsBoundStart) then
  2216.     begin
  2217.       if PartText = nil then
  2218.       begin
  2219.         PartText := Line;
  2220.         nSPos := nPos;
  2221.       end;
  2222.       DataLinePChar(Data, nTL, nPos, nLen, Line, DataEnd);
  2223.       if (Self is TMailMessage2000) and Assigned(FOwnerMessage.FOnProgress) then
  2224.       begin
  2225.         FOwnerMessage.FOnProgress(Self, nTL, nPos+1);
  2226.         Application.ProcessMessages;
  2227.       end;
  2228.     end
  2229.     else
  2230.     begin
  2231.       InBound := True;
  2232.     end;
  2233.   end;
  2234.   if nPos > nSPos then
  2235.   begin
  2236.     SChar := Data[nPos];
  2237.     Data[nPos] := #0;
  2238.     if PartText <> nil then
  2239.       FBody.Write(PartText[0], nPos-nSPos);
  2240.     Data[nPos] := SChar;
  2241.   end;
  2242.   if not NoParts then
  2243.   begin
  2244.     PartText := nil;
  2245.     if MultPart then
  2246.     begin
  2247.       // Get Mime parts
  2248.       while not DataEnd do
  2249.       begin
  2250.         if IsBoundStart or IsBoundEnd then
  2251.         begin
  2252.           if (PartText <> nil) and (PartText[0] <> #0) then
  2253.           begin
  2254.             Part := TMailPart.Create(Self.FOwnerMessage);
  2255.             Part.FOwnerPart := Self;
  2256.             Part.FOwnerMessage := Self.FOwnerMessage;
  2257.             SChar := Data[nPos-2];
  2258.             Data[nPos-2] := #0;
  2259.             Part.Fill(PartText, True);
  2260.             Data[nPos-2] := SChar;
  2261.             Part.FParentBoundary := GetBoundary;
  2262.             FSubPartList.Add(Part);
  2263.             PartText := nil;
  2264.           end;
  2265.           if IsBoundEnd then
  2266.           begin
  2267.             Break;
  2268.           end;
  2269.           IsBoundStart := False;
  2270.           IsBoundEnd := False;
  2271.         end
  2272.         else
  2273.         begin
  2274.           if PartText = nil then
  2275.           begin
  2276.             PartText := Line;
  2277.           end;
  2278.         end;
  2279.         DataLinePChar(Data, nTL, nPos, nLen, Line, DataEnd);
  2280.         if (Self is TMailMessage2000) and Assigned(FOwnerMessage.FOnProgress) then
  2281.         begin
  2282.           FOwnerMessage.FOnProgress(Self, nTL, nPos+1);
  2283.           Application.ProcessMessages;
  2284.         end;
  2285.         if not DataEnd then
  2286.         begin
  2287.           if (Line[0] = '-') and (Line[1] = '-') then
  2288.           begin
  2289.             IsBoundStart := StrLComp(Line, BoundStart, BoundStartLen) = 0;
  2290.             if not IsBoundStart then
  2291.             begin
  2292.               IsBoundEnd := StrLComp(Line, BoundEnd, BoundEndLen) = 0;
  2293.             end;
  2294.           end;
  2295.         end;
  2296.       end;
  2297.     end
  2298.     else
  2299.     begin
  2300.       // Get UUCode parts
  2301.       InUUCode := IsBoundStart;
  2302.       while not DataEnd do
  2303.       begin
  2304.         if IsBoundStart then
  2305.         begin
  2306.           if UUBound = '' then
  2307.           begin
  2308.             GetMem(PartText, FBody.Size+1);
  2309.             UUBound := GenerateBoundary;
  2310.             StrLCopy(PartText, FBody.Memory, FBody.Size);
  2311.             PartText[FBody.Size] := #0;
  2312.             Part := TMailPart.Create(Self.FOwnerMessage);
  2313.             Part.FOwnerPart := Self;
  2314.             Part.FOwnerMessage := Self.FOwnerMessage;
  2315.             Part.Fill(PChar(EncodeQuotedPrintable(String(PartText), False)), False);
  2316.             Part.FParentBoundary := UUBound;
  2317.             Part.SetLabelValue(_C_T, _T_P);
  2318.             Part.SetLabelParamValue(_C_T, _CSET, '"'+FOwnerMessage.FCharset+'"');
  2319.             Part.SetLabelValue(_C_TE, _E_QP);
  2320.             FSubPartList.Add(Part);
  2321.             SetLabelValue(_C_T, '');
  2322.             SetLabelValue(_C_T, _M_M);
  2323.             SetLabelParamValue(_C_T, _BDRY, '"'+UUBound+'"');
  2324.             FreeMem(PartText);
  2325.           end;
  2326.           PartText := nil;
  2327.           IsBoundStart := False;
  2328.           UUFile := TrimSpace(Copy(String(Line), 11, 999));
  2329.         end
  2330.         else
  2331.         begin
  2332.           if IsBoundEnd then
  2333.           begin
  2334.             Part := TMailPart.Create(Self.FOwnerMessage);
  2335.             Part.FOwnerPart := Self;
  2336.             Part.FOwnerMessage := Self.FOwnerMessage;
  2337.             SChar := Data[nPos-2];
  2338.             Data[nPos-2] := #0;
  2339.             DecodeUUCODE(PartText, Part.FDecoded);
  2340.             Data[nPos-2] := SChar;
  2341.             Part.EncodeBinary;
  2342.             Part.FParentBoundary := UUBound;
  2343.             Part.SetLabelValue(_C_T, GetMimeType(UUFile));
  2344.             Part.SetLabelValue(_C_TE, _E_BA);
  2345.             Part.SetLabelValue(_C_D, _ATCH);
  2346.             Part.SetLabelParamValue(_C_T, _NAME, '"'+EncodeLine7Bit(UUFile, FOwnerMessage.FCharSet)+'"');
  2347.             Part.SetLabelParamValue(_C_D, _FLNM, '"'+EncodeLine7Bit(UUFile, FOwnerMessage.FCharSet)+'"');
  2348.             Part.SetLabelValue(_XM2A, '"'+EncodeLine7Bit(UUFile, FOwnerMessage.FCharSet)+'"');
  2349.             Part.SetLabelParamValue(_XM2A, _EMBD, 'no'); 
  2350.             FSubPartList.Add(Part);
  2351.             PartText := nil;
  2352.             IsBoundEnd := False;
  2353.           end
  2354.           else
  2355.           begin
  2356.             if PartText = nil then
  2357.             begin
  2358.               PartText := Line;
  2359.             end;
  2360.           end;
  2361.         end;
  2362.         DataLinePChar(Data, nTL, nPos, nLen, Line, DataEnd);
  2363.         if (Self is TMailMessage2000) and Assigned(FOwnerMessage.FOnProgress) then
  2364.         begin
  2365.           FOwnerMessage.FOnProgress(Self, nTL, nPos+1);
  2366.           Application.ProcessMessages;
  2367.         end;
  2368.         if not DataEnd then
  2369.         begin
  2370.           if (Line[0] = 'b') and (Line[1] = 'e') then
  2371.           begin
  2372.             IsBoundStart := StrLComp(Line, BoundStart, BoundStartLen) = 0;
  2373.             InUUCode := True;
  2374.           end;
  2375.           if (not IsBoundStart) and InUUCode then
  2376.           begin
  2377.             if (Line[0] = 'e') and (Line[1] = 'n') and (Line[2] = 'd') then
  2378.             begin
  2379.               IsBoundEnd := True;
  2380.               InUUCode := False;
  2381.             end;
  2382.           end;
  2383.         end;
  2384.       end;
  2385.     end;
  2386.   end;
  2387.   if (Self is TMailMessage2000) and Assigned(FOwnerMessage.FOnProgress) then
  2388.   begin
  2389.     FOwnerMessage.FOnProgress(Self, nTL, nTL);
  2390.     Application.ProcessMessages;
  2391.   end;
  2392. end;
  2393. // Remove mailpart from its owner
  2394. procedure TMailPart.Remove;
  2395. begin
  2396.   if (FOwnerPart <> nil) and (Self <> FOwnerMessage) and
  2397.      (FOwnerPart.FSubPartList.IndexOf(Self) >= 0) then
  2398.   begin
  2399.     FOwnerPart.FSubPartList.Delete(FOwnerPart.FSubPartList.IndexOf(Self));
  2400.     FOwnerPart := nil;
  2401.   end;
  2402. end;
  2403. // Fill part with a file contents
  2404. procedure TMailPart.LoadFromFile(FileName: String);
  2405. var
  2406.   SL: TStringList;
  2407. begin
  2408.   SL := TStringList.Create;
  2409.   SL.LoadFromFile(FileName);
  2410.   Fill(PChar(SL.Text), True);
  2411.   SL.Free;
  2412. end;
  2413. // Save the part data to a file
  2414. procedure TMailPart.SaveToFile(FileName: String);
  2415. var
  2416.   SL: TStringList;
  2417. begin
  2418.   SL := TStringList.Create;
  2419.   SL.Text := GetSource;
  2420.   try
  2421.     SL.SaveToFile(FileName);
  2422.   finally
  2423.     SL.Free;
  2424.   end;
  2425. end;
  2426. // Fill part with a stream contents
  2427. procedure TMailPart.LoadFromStream(Stream: TStream);
  2428. var
  2429.   Buffer: PChar;
  2430. begin
  2431.   GetMem(Buffer, Stream.Size+1);
  2432.   Stream.Position := 0;
  2433.   Stream.ReadBuffer(Buffer[0], Stream.Size);
  2434.   Buffer[Stream.Size] := #0;
  2435.   Fill(Buffer, True);
  2436.   FreeMem(Buffer);
  2437. end;
  2438. // Save the part data to a stream
  2439. procedure TMailPart.SaveToStream(Stream: TStream);
  2440. var
  2441.   Text: String;
  2442. begin
  2443.   Text := GetSource;
  2444.   Stream.Size := Length(Text);
  2445.   Stream.Position := 0;
  2446.   Stream.WriteBuffer(Text[1], Length(Text));
  2447. end;
  2448. // Fill part with a string contents
  2449. procedure TMailPart.SetSource(Text: String);
  2450. begin
  2451.   Fill(PChar(Text), True);
  2452. end;
  2453. // Copy the part data to a string
  2454. function TMailPart.GetSource: String;
  2455. begin
  2456.   SetLength(Result, FBody.Size);
  2457.   FBody.Position := 0;
  2458.   FBody.ReadBuffer(Result[1], FBody.Size);
  2459.   Result := WrapHeader(FHeader.Text)+#13#10+Result;
  2460. end;
  2461. // Get file name of attachment
  2462. function TMailPart.GetFileName: String;
  2463. var
  2464.   Name: String;
  2465. begin
  2466.   Name := '';
  2467.   if LabelExists(_XM2A) then
  2468.   begin
  2469.     Name := GetLabelValue(_XM2A);
  2470.   end
  2471.   else
  2472.   begin
  2473.     if LabelParamExists(_C_T, _NAME) then
  2474.     begin
  2475.       Name := GetLabelParamValue(_C_T, _NAME);
  2476.     end
  2477.     else
  2478.     begin
  2479.       if LabelParamExists(_C_D, _FLNM) then
  2480.       begin
  2481.         Name := GetLabelParamValue(_C_D, _FLNM);
  2482.       end
  2483.       else
  2484.       begin
  2485.         if LabelExists(_C_ID) then
  2486.         begin
  2487.           Name := GetLabelValue(_C_ID);
  2488.         end
  2489.         else
  2490.         begin
  2491.           if LabelExists(_C_LC) then
  2492.           begin
  2493.             Name := GetLabelValue(_C_LC);
  2494.           end
  2495.           else
  2496.           begin
  2497.             if LabelExists(_C_T) then
  2498.             begin
  2499.               Name := GetLabelValue(_C_T)+GetMimeExtension(GetLabelValue(_C_T));
  2500.             end
  2501.             else
  2502.             begin
  2503.               Name := 'unknown';
  2504.             end;
  2505.           end;
  2506.         end;
  2507.       end;
  2508.     end;
  2509.   end;
  2510.   Name := DecodeLine7Bit(Name);
  2511.   if (Pos('.', Name) = 0) and LabelExists(_C_T) then
  2512.     Name := Name + GetMimeExtension(GetLabelValue(_C_T));
  2513.   Result := ValidFileName(Name);
  2514. end;
  2515. // Get kind of attachment
  2516. function TMailPart.GetAttachInfo: String;
  2517. begin
  2518.   Result := LowerCase(GetLabelValue(_C_T));
  2519. end;
  2520. // Get boundary of this part (when it is a multipart header)
  2521. function TMailPart.GetBoundary: String;
  2522. begin
  2523.   Result := GetLabelParamValue(_C_T, _BDRY);
  2524. end;
  2525. // Decode mail part
  2526. function TMailPart.Decode;
  2527. var
  2528.   Content: String;
  2529.   Encoding: String;
  2530.   Data: String;
  2531.   DecoLine: String;
  2532.   Buffer: PChar;
  2533.   Size: Integer;
  2534.   nPos: Integer;
  2535. begin
  2536.   Result := True;