PageRenderTime 43ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/components/turbopower_ipro/ipmsg.pas

http://github.com/graemeg/lazarus
Pascal | 3913 lines | 3167 code | 357 blank | 389 comment | 307 complexity | 49ed960178c43d853e819ba5dc768291 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, MPL-2.0-no-copyleft-exception
  1. {******************************************************************}
  2. {* IPMSG.PAS - MIME message classes *}
  3. {******************************************************************}
  4. { $Id$ }
  5. (* ***** BEGIN LICENSE BLOCK *****
  6. * Version: MPL 1.1
  7. *
  8. * The contents of this file are subject to the Mozilla Public License Version
  9. * 1.1 (the "License"); you may not use this file except in compliance with
  10. * the License. You may obtain a copy of the License at
  11. * http://www.mozilla.org/MPL/
  12. *
  13. * Software distributed under the License is distributed on an "AS IS" basis,
  14. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  15. * for the specific language governing rights and limitations under the
  16. * License.
  17. *
  18. * The Original Code is TurboPower Internet Professional
  19. *
  20. * The Initial Developer of the Original Code is
  21. * TurboPower Software
  22. *
  23. * Portions created by the Initial Developer are Copyright (C) 2000-2002
  24. * the Initial Developer. All Rights Reserved.
  25. *
  26. * Contributor(s):
  27. *
  28. * Markus Kaemmerer <mk@happyarts.de> SourceForge: mkaemmerer
  29. *
  30. * ***** END LICENSE BLOCK ***** *)
  31. { Global defines potentially affecting this unit }
  32. {$I IPDEFINE.INC}
  33. unit IpMsg;
  34. interface
  35. uses
  36. {$IFDEF IP_LAZARUS}
  37. LCLType,
  38. LCLIntf,
  39. LazFileUtils, LazUTF8Classes,
  40. {$ELSE}
  41. Windows,
  42. {$ENDIF}
  43. Classes,
  44. SysUtils,
  45. IpStrms,
  46. {$IFNDEF IP_LAZARUS}
  47. //IpSock, //JMN
  48. {$ENDIF}
  49. IpUtils,
  50. IpConst;
  51. type
  52. TIpMimeEncodingMethod = (em7Bit, em8Bit, emBase64, emBinary, emBinHex,
  53. emQuoted, emUUEncode, emUnknown);
  54. { TIpMimeEntity }
  55. type
  56. TIpCodingProgressEvent = procedure(Sender : TObject; Progress : Byte;
  57. var Abort : Boolean) of object;
  58. {Begin !!.12}
  59. type
  60. TIpHeaderTypes = (htBCC, htCC, htControl, htDate, htDispositionNotify,
  61. htFollowUp, htFrom, htInReplyTo, htKeywords,
  62. htMessageID, htNewsgroups, htNNTPPostingHost,
  63. htOrganization, htPath, htPostingHost, htReceived,
  64. htReferences, htReplyTo, htReturnPath, htSender,
  65. htSubject, htTo, htUserFields, htXIpro);
  66. TIpHeaderInfo = record
  67. FieldType : TIpHeaderTypes;
  68. FieldString : string;
  69. end;
  70. const
  71. IpMaxHeaders = 24;
  72. IpHeaderXRef : array [0..IpMaxHeaders - 1] of TIpHeaderInfo =
  73. ((FieldType : htBCC; FieldString : 'BCC'),
  74. (FieldType : htCC; FieldString : 'CC'),
  75. (FieldType : htControl; FieldString : 'Control: '),
  76. (FieldType : htDate; FieldString : 'Date'),
  77. (FieldType : htDispositionNotify; FieldString : 'Disposition-Notification-To'),
  78. (FieldType : htFollowUp; FieldString : 'Followup-To: '),
  79. (FieldType : htFrom; FieldString : 'From'),
  80. (FieldType : htInReplyTo; FieldString : 'In-Reply-To'),
  81. (FieldType : htKeywords; FieldString : 'Keywords'),
  82. (FieldType : htMessageID; FieldString : 'Message-ID'),
  83. (FieldType : htNewsgroups; FieldString : 'Newsgroups'),
  84. (FieldType : htNNTPPostingHost; FieldString : 'NNTP-Posting-Host'),
  85. (FieldType : htOrganization; FieldString : 'Organization'),
  86. (FieldType : htPath; FieldString : 'Path'),
  87. (FieldType : htPostingHost; FieldString : 'Posting-Host'),
  88. (FieldType : htReceived; FieldString : 'Received'),
  89. (FieldType : htReferences; FieldString : 'References'),
  90. (FieldType : htReplyTo; FieldString : 'Reply-To'),
  91. (FieldType : htReturnPath; FieldString : 'Return-Path'),
  92. (FieldType : htSender; FieldString : 'Sender'),
  93. (FieldType : htSubject; FieldString : 'Subject'),
  94. (FieldType : htTo; FieldString : 'To'),
  95. (FieldType : htUserFields; FieldString : 'X-'),
  96. (FieldType : htXIpro; FieldString : 'X-Ipro'));
  97. type
  98. TIpHeaderCollection = class;
  99. TIpHeaderItem = class (TCollectionItem)
  100. private
  101. FCollection : TIpHeaderCollection;
  102. FName : string;
  103. FNameL : string;
  104. { Lower case version of FName. Used to speed up header searches. }
  105. FProperty : Boolean; {!!.13}
  106. FValue : TStringList;
  107. protected
  108. procedure SetName(const Name : string);
  109. procedure SetValue (v : TStringList);
  110. public
  111. constructor Create (Collection : TCollection); override;
  112. destructor Destroy; override;
  113. published
  114. property Collection : TIpHeaderCollection
  115. read FCollection write FCollection;
  116. property Name : string read FName write SetName;
  117. property NameL : string read FNameL;
  118. { Lower case version of Name property. }
  119. property IsProperty : Boolean read FProperty write FProperty; {!!.13}
  120. { Set to True if this header is exposed via an iPRO property. }{!!.13}
  121. property Value : TStringList read FValue write SetValue;
  122. end;
  123. TIpHeaderCollection = class (TCollection)
  124. private
  125. FOwner : TPersistent;
  126. protected
  127. function GetItem (Index : Integer) : TIpHeaderItem;
  128. function GetOwner : TPersistent; override;
  129. procedure SetItem (Index : Integer; Value : TIpHeaderItem);
  130. public
  131. constructor Create (AOwner : TPersistent);
  132. {$IFNDEF VERSION5}
  133. procedure Delete (Item : integer);
  134. {$ENDIF}
  135. function HasHeader (AName : string) : Integer;
  136. procedure HeaderByName (AName : string;
  137. Headers : TStringList);
  138. procedure LoadHeaders (AHeaderList : TStringList;
  139. Append : Boolean);
  140. property Items[Index : Integer] : TIpHeaderItem
  141. read GetItem write SetItem;
  142. end;
  143. {End !!.12}
  144. TIpMimeParts = class; { Forwards }
  145. TIpMimeEntity = class(TPersistent)
  146. protected {private}
  147. FProgress : Byte;
  148. PrevProgress : Byte;
  149. FMimeParts : TIpMimeParts;
  150. FParentBoundary : string;
  151. FBody : TIpAnsiTextStream;
  152. FEntityName : string;
  153. FBoundary : string;
  154. FCharacterSet : string;
  155. FContentDescription : string;
  156. FContentDispositionType : string;
  157. FContentID : string;
  158. FContentSubtype : string;
  159. FContentType : string;
  160. FCreationDate : string;
  161. FContentTransferEncoding : TIpMimeEncodingMethod;
  162. FFileName : string;
  163. FIsMime : Boolean;
  164. FIsMultipart : Boolean;
  165. FModificationDate : string;
  166. FMimeVersion : string;
  167. FOnCodingProgress : TIpCodingProgressEvent;
  168. FOriginalSize : Longint;
  169. FParent : TIpMimeEntity;
  170. FReadDate : string;
  171. FRelatedType : string; {!!.02}
  172. FRelatedSubtype : string; {!!.02}
  173. FRelatedStart : string; {!!.02}
  174. FRelatedStartInfo : string; {!!.02}
  175. FAttachmentCount : Integer; {!!.12}
  176. protected {methods}
  177. procedure Clear; virtual;
  178. procedure ClearBodyLargeAttach(const AttachmentSize : Longint); virtual; {!!.12}
  179. function ContainsSpecialChars(const Value : string) : Boolean; {!!.14}
  180. procedure DecodeContentDisposition(const aDisp : string);
  181. procedure DecodeContentType(const aType : string);
  182. function DecodeContentTransferEncoding(const aEncoding : string) :
  183. TIpMimeEncodingMethod;
  184. procedure DecodeMimeHeaders(RawHeaders : TStringlist);
  185. procedure DoOnCodingProgress(Count, TotalSize : Longint; var Abort : Boolean);
  186. procedure EncodeContentDisposition(RawHeaders : TStringList);
  187. procedure EncodeContentType(RawHeaders : TStringList);
  188. procedure EncodeContentTransferEncoding(RawHeaders : TStringList);
  189. procedure EncodeMimeHeaders(RawHeaders : TStringlist);
  190. procedure OctetStreamToHextetStream(InStream : TStream; OutStream : TIpAnsiTextStream;
  191. const Table; PadChar, Delim : AnsiChar);
  192. procedure Decode8Bit(OutStream : TStream);
  193. procedure DecodeBase64(OutStream : TStream);
  194. procedure DecodeBinHex(OutStream : TStream);
  195. procedure DecodeQuoted(OutStream : TStream);
  196. procedure DecodeUUEncode(OutStream : TStream);
  197. procedure Encode8Bit(InStream : TStream);
  198. procedure EncodeBase64(InStream : TStream);
  199. procedure EncodeBinHex(InStream : TStream; const aFileName : string);
  200. procedure EncodeQuoted(InStream : TStream);
  201. procedure EncodeUUEncode(InStream : TStream; const aFileName : string);
  202. function DecodeEntity(InStream : TIpAnsiTextStream) : string;
  203. function DecodeEntityAsAttachment(InStream : TIpAnsiTextStream) : string; {!!.01}
  204. function EncodeEntity(OutStream : TIpAnsiTextStream) : string;
  205. procedure ReadBody(InStream : TIpAnsiTextStream; const StartLine : string); {!!.12}
  206. protected {properties}
  207. property OnCodingProgress : TIpCodingProgressEvent
  208. read FOnCodingProgress write FOnCodingProgress;
  209. public {methods}
  210. constructor Create(ParentEntity : TIpMimeEntity); virtual;
  211. destructor Destroy; override;
  212. procedure ClearBody;
  213. procedure EncodeBodyFile(const InFile : string);
  214. procedure EncodeBodyStream(InStream : TStream; const aFileName : string);
  215. procedure EncodeBodyStrings(InStrings : TStrings; const aFileName : string);
  216. procedure ExtractBodyFile(const OutFile : string);
  217. procedure ExtractBodyStream(OutStream : TStream);
  218. procedure ExtractBodyStrings(OutStrings : TStrings);
  219. function FindNestedMimePart(const aType, aSubType, aContentID : string) : TIpMimeEntity; {!!.02}
  220. function GetMimePart(const aType, aSubType, aContentID : string;
  221. CanCreate : Boolean) : TIpMimeEntity;
  222. function NewMimePart : TIpMimeEntity;
  223. property AttachmentCount : Integer read FAttachmentCount; {!!.12}
  224. public {properties}
  225. property Body : TIpAnsiTextStream
  226. read FBody;
  227. property Boundary : string
  228. read FBoundary write FBoundary;
  229. property CharacterSet : string
  230. read FCharacterSet write FCharacterSet;
  231. property ContentDescription : string
  232. read FContentDescription write FContentDescription;
  233. property ContentDispositionType : string
  234. read FContentDispositionType write FContentDispositionType;
  235. property ContentID : string
  236. read FContentID write FContentID;
  237. property ContentSubtype : string
  238. read FContentSubtype write FContentSubtype;
  239. property ContentTransferEncoding : TIpMimeEncodingMethod
  240. read FContentTransferEncoding write FContentTransferEncoding;
  241. property ContentType : string
  242. read FContentType write FContentType;
  243. property CreationDate : string
  244. read FCreationDate write FCreationDate;
  245. property EntityName : string
  246. read FEntityName write FEntityName;
  247. property FileName : string
  248. read FFileName write FFileName;
  249. property IsMime : Boolean
  250. read FIsMime;
  251. property IsMultipart : Boolean
  252. read FIsMultipart;
  253. property MimeParts : TIpMimeParts
  254. read FMimeParts;
  255. property MimeVersion : string
  256. read FMimeVersion write FMimeVersion;
  257. property ModificationDate : string
  258. read FModificationDate write FModificationDate;
  259. property OriginalSize : Longint
  260. read FOriginalSize write FOriginalSize;
  261. property Parent : TIpMimeEntity
  262. read FParent;
  263. property ReadDate : string
  264. read FReadDate write FReadDate;
  265. property RelatedStart : string {!!.02}
  266. read FRelatedStart write FRelatedStart;
  267. property RelatedStartInfo : string {!!.02}
  268. read FRelatedStartInfo write FRelatedStartInfo;
  269. property RelatedSubtype : string {!!.02}
  270. read FRelatedSubtype write FRelatedSubtype;
  271. property RelatedType : string {!!.02}
  272. read FRelatedType write FRelatedType;
  273. end;
  274. { TIpMimeParts }
  275. TIpMimeParts = class
  276. protected {private}
  277. Entitys : TList;
  278. function GetCount : Integer;
  279. function GetPart(aIndex : Integer) : TIpMimeEntity;
  280. public {methods}
  281. constructor Create;
  282. destructor Destroy; override;
  283. function Add(aEntity : TIpMimeEntity) : Integer;
  284. function Remove(aEntity : TIpMimeEntity) : Integer;
  285. procedure Clear;
  286. procedure Delete(aIndex : Integer);
  287. function IndexOf(aEntity : TIpMimeEntity) : Integer;
  288. public {properties}
  289. property Count : Integer
  290. read GetCount;
  291. property Parts[aIndex : Integer] : TIpMimeEntity
  292. read GetPart; default;
  293. end;
  294. { TIpMessage }
  295. type
  296. TIpMessage = class(TIpMimeEntity)
  297. protected {private}
  298. MsgStream : TIpAnsiTextStream;
  299. protected {property variables}
  300. FBCC : TStringList;
  301. FCC : TStringList;
  302. FDate : string;
  303. FFrom : string;
  304. FInReplyTo : string;
  305. FKeywords : string;
  306. FFollowupTo : string; {!!.12}
  307. FControl : string; {!!.12}
  308. FMessageID : string;
  309. FMessageTag : Integer;
  310. FNewsgroups : TStringList;
  311. FNNTPPostingHost : string;
  312. FOrganization : string;
  313. FPath : TStringList;
  314. FPostingHost : string;
  315. FReceived : TStringList;
  316. FRecipients : TStringList;
  317. FReferences : TStringList;
  318. FReplyTo : string;
  319. FReturnPath : string;
  320. FSender : string;
  321. FSubject : string;
  322. FUserFields : TStringList;
  323. FHeaders : TIpHeaderCollection; {!!.12}
  324. FDispositionNotify: string;
  325. protected {methods}
  326. procedure CheckAllHeaders; {!!.12}
  327. procedure CheckHeaderType (HeaderInfo : TIpHeaderItem; {!!.12}
  328. HeaderType : TIpHeaderTypes); {!!.12}
  329. procedure Clear; override;
  330. procedure NewMessageStream;
  331. function GetPosition : Longint;
  332. function GetSize : Longint;
  333. procedure SetPosition(Value : Longint);
  334. procedure SetBCC(const Value: TStringList);
  335. procedure SetCC(const Value: TStringList);
  336. procedure SetNewsgroups(const Value: TStringList);
  337. procedure SetPath(const Value: TStringList);
  338. procedure SetReceived(const Value: TStringList);
  339. procedure SetRecipients(const Value: TStringList);
  340. procedure SetReferences(const Value: TStringlist);
  341. procedure SetUserFields(const Value: TStringList);
  342. public {methods}
  343. constructor CreateMessage; virtual;
  344. destructor Destroy; override;
  345. procedure AddDefaultAttachment(const aFileName : string); {!!.02}
  346. procedure AddDefaultAttachmentAs (const aFileName : string; {!!.12}
  347. const AttachmentName : string); {!!.12}
  348. procedure Assign(Source : TPersistent); override;
  349. function AtEndOfStream : Boolean;
  350. procedure DecodeMessage; virtual;
  351. procedure EncodeMessage; virtual;
  352. function GetBodyHtml(CanCreate : Boolean) : TIpMimeEntity;
  353. function GetBodyPlain(CanCreate : Boolean) : TIpMimeEntity;
  354. procedure LoadFromFile(const aFileName : string);
  355. procedure LoadFromStream(aStream : TStream); {!!.12}
  356. procedure NewMessage;
  357. function ReadLine : string;
  358. function ReadLineCRLF : string;
  359. procedure SaveToFile(const aFileName : string);
  360. procedure SaveToStream(Stream: TStream); {!!.12}
  361. procedure SetHeaders(Headers : TIpHeaderCollection); {!!.12}
  362. procedure WriteLine(const aSt : string);
  363. public {properties}
  364. property BCC : TStringList
  365. read FBCC write SetBCC; {!!.01}
  366. property CC : TStringList
  367. read FCC write SetCC; {!!.01}
  368. property Control : string {!!.12}
  369. read FControl write FControl; {!!.12}
  370. property Date : string
  371. read FDate write FDate;
  372. property DispositionNotification : string {!!.12}
  373. read FDispositionNotify write FDispositionNotify; {!!.12}
  374. property FollowupTo : String {!!.12}
  375. read FFollowupTo Write FFollowupTo; {!!.12}
  376. property From : string
  377. read FFrom write FFrom;
  378. property Headers : TIpHeaderCollection {!!.12}
  379. read FHeaders write SetHeaders; {!!.12}
  380. property InReplyTo : string
  381. read FInReplyTo write FInReplyTo;
  382. property Keywords : string
  383. read FKeywords write FKeywords;
  384. property MessageID : string
  385. read FMessageID write FMessageID;
  386. property MessageStream : TIpAnsiTextStream {!!.03}
  387. read MsgStream; {!!.03}
  388. property MessageTag : Integer
  389. read FMessageTag write FMessageTag;
  390. property Newsgroups : TStringList
  391. read FNewsgroups write SetNewsgroups; {!!.01}
  392. property NNTPPostingHost : string
  393. read FNNTPPostingHost write FNNTPPostingHost;
  394. property Organization : string
  395. read FOrganization write FOrganization;
  396. property Path : TStringList
  397. read FPath write SetPath; {!!.01}
  398. property Position : Longint
  399. read GetPosition write SetPosition;
  400. property PostingHost : string
  401. read FPostingHost write FPostingHost;
  402. property Received : TStringList
  403. read FReceived write SetReceived; {!!.01}
  404. property Recipients : TStringList
  405. read FRecipients write SetRecipients; {!!.01}
  406. property References : TStringlist
  407. read FReferences write SetReferences; {!!.01}
  408. property ReplyTo : string
  409. read FReplyTo write FReplyTo;
  410. property ReturnPath : string
  411. read FReturnPath write FReturnPath;
  412. property Sender : string
  413. read FSender write FSender;
  414. property Size : Longint
  415. read GetSize;
  416. property Subject : string
  417. read FSubject write FSubject;
  418. property UserFields : TStringList
  419. read FUserFields write SetUserFields; {!!.01}
  420. end;
  421. { TIpMailMessage}
  422. type
  423. TIpMailMessage = class(TIpMessage)
  424. published {properties}
  425. property BCC;
  426. property CC;
  427. property ContentDescription;
  428. property ContentTransferEncoding;
  429. property ContentType;
  430. property Date;
  431. property From;
  432. property Keywords;
  433. property MailTo : TStringList
  434. read FRecipients write SetRecipients; {!!.01}
  435. property OnCodingProgress;
  436. property References;
  437. property ReplyTo;
  438. property Sender;
  439. property Subject;
  440. property UserFields;
  441. end;
  442. { TIpNewsArticle }
  443. type
  444. TIpNewsArticle = class(TIpMessage)
  445. published {properties}
  446. property ContentDescription;
  447. property ContentTransferEncoding;
  448. property ContentType;
  449. property Date;
  450. property From;
  451. property Keywords;
  452. property Newsgroups;
  453. property NNTPPostingHost;
  454. property OnCodingProgress;
  455. property Path;
  456. property References;
  457. property ReplyTo;
  458. property Sender;
  459. property Subject;
  460. property UserFields;
  461. end;
  462. { TIpFormDataEntity }
  463. type
  464. TIpFormDataEntity = class(TIpMimeEntity)
  465. protected
  466. FFilesEntity : TIpMimeEntity;
  467. public {methods}
  468. constructor Create(ParentEntity : TIpMimeEntity); override;
  469. destructor Destroy; override;
  470. procedure AddFormData(const aName, aText : string);
  471. procedure AddFile(const aFileName, aContentType, aSubtype : string;
  472. aEncoding : TIpMimeEncodingMethod);
  473. procedure SaveToStream(aStream : TStream);
  474. end;
  475. {$IFNDEF IP_LAZARUS}
  476. { dummy class so this unit will be added to the uses clause when an }
  477. { IpPop3Client, IpSmtpClient or IpNntpClient component is dropped on the form }
  478. (*** //JMN
  479. TIpCustomEmailClass = class(TIpCustomClient)
  480. end;
  481. **)
  482. {$ENDIF}
  483. function IpBase64EncodeString(const InStr: string): string; {!!.02}{!!.03}
  484. {Begin !!.12}
  485. const
  486. IpLgAttachSizeBoundry = 5 * 1024 * 1024;
  487. { Attachments over this size will be encoded using a TIpMemMapStream for
  488. greatly improved performance. This boundary also applies to the final
  489. encoding of messages with large attachments. }
  490. implementation
  491. const
  492. { standard headers }
  493. strBCC = 'BCC: ';
  494. strCC = 'CC: ';
  495. strDate = 'Date: ';
  496. strDispositionNotify = 'Disposition-Notification-To: ';
  497. strFrom = 'From: ';
  498. strInReplyTo = 'In-Reply-To: ';
  499. strKeywords = 'Keywords: ';
  500. strMessageID = 'Message-ID: ';
  501. strNewsgroups = 'Newsgroups: ';
  502. strNNTPPostingHost = 'NNTP-Posting-Host: ';
  503. strOrganization = 'Organization: ';
  504. strPath = 'Path: ';
  505. strPostingHost = 'Posting-Host: ';
  506. strReceived = 'Received: ';
  507. strReferences = 'References: ';
  508. strReplyTo = 'Reply-To: ';
  509. strReturnPath = 'Return-Path: ';
  510. strSender = 'Sender: ';
  511. strSubject = 'Subject: ';
  512. strTo = 'To: ';
  513. strUserFields = 'X-';
  514. strXIpro = 'X-Ipro: ';
  515. strFollowUp = 'Followup-To: '; {!!.12}
  516. strControl = 'Control: '; {!!.12}
  517. {Begin !!.13}
  518. IpMimeHeaders : array [0..5] of string =
  519. { List of MIME headers that must be marked as public properties in
  520. the message's Headers collection. Marking them as a public property
  521. prevents them from being written out twice if the message is saved
  522. to a file or stream. }
  523. (
  524. 'Content-Type',
  525. 'MIME-Version',
  526. 'Content-Transfer-Encoding',
  527. 'Content-Description',
  528. 'Content-ID',
  529. 'Content-Disposition'
  530. );
  531. {End !!.13}
  532. { MIME headers }
  533. strMimeVersion = 'MIME-Version: ';
  534. strContent = 'Content-';
  535. strContentBase = strContent + 'Base: ';
  536. strContentDescription = strContent + 'Description: ';
  537. strContentDisposition = strContent + 'Disposition: ';
  538. strContentID = strContent + 'ID: ';
  539. strContentLanguage = strContent + 'Language: ';
  540. strContentLocation = strContent + 'Location: ';
  541. strContentTransferEncoding = strContent + 'Transfer-Encoding: ';
  542. strContentType = strContent + 'Type: ';
  543. { MIME content types }
  544. strApplication = 'application';
  545. strAudio = 'audio';
  546. strFiles = 'files';
  547. strFormData = 'form-data';
  548. strImage = 'image';
  549. strMessage = 'message';
  550. strMultiPart = 'multipart';
  551. strText = 'text';
  552. strVideo = 'video';
  553. { MIME content subtypes and parameters }
  554. strBoundary = 'boundary=';
  555. strCharSet = 'charset=';
  556. strMixed = 'mixed';
  557. strName = 'name=';
  558. strPlain = 'plain';
  559. strHTML = 'html';
  560. strOctetStream = 'octet-stream';
  561. strAlternative = 'alternative';
  562. strRelated = 'related'; {!!.02}
  563. { MIME content disposition parameters }
  564. strAttachment = 'attachment';
  565. strInline = 'inline';
  566. strCreationDate = 'creation-date=';
  567. strFilename = 'filename=';
  568. strModificationDate = 'modification-date=';
  569. strReadDate = 'read-date=';
  570. strStart = 'start='; {!!.02}
  571. strStartInfo = 'start-info='; {!!.02}
  572. strSize = 'size=';
  573. strType = 'type='; {!!.02}
  574. { MIME encoding methods }
  575. str7Bit = '7bit';
  576. str8Bit = '8bit';
  577. strBase64 = 'base64';
  578. strBinary = 'binary';
  579. strBinHex = 'binhex';
  580. strQuoted = 'quoted-printable';
  581. strUUEncode = 'uuencoded';
  582. { default MIME content type information }
  583. {$I IPDEFCT.INC}
  584. type
  585. TIp6BitTable = array[0..63] of AnsiChar;
  586. const {- BinHex encoding table }
  587. IpBinHexTable : TIp6BitTable = (
  588. '!', '"', '#', '$', '%', '&', '''', '(',
  589. ')', '*', '+', ',', '-', '0', '1', '2',
  590. '3', '4', '5', '6', '8', '9', '@', 'A',
  591. 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
  592. 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R',
  593. 'S', 'T', 'U', 'V', 'X', 'Y', 'Z', '[',
  594. '`', 'a', 'b', 'c', 'd', 'e', 'f', 'h',
  595. 'i', 'j', 'k', 'l', 'm', 'p', 'q', 'r');
  596. const {-BinHex decoding table }
  597. IpHexBinTable : array[33..114] of Byte = (
  598. $00, $01, $02, $03, $04, $05, $06, $07,
  599. $08, $09, $0A, $0B, $0C, $FF, $FF, $0D,
  600. $0E, $0F, $10, $11, $12, $13, $FF, $14,
  601. $15, $FF, $FF, $FF, $FF, $FF, $FF, $16,
  602. $17, $18, $19, $1A, $1B, $1C, $1D, $1E,
  603. $1F, $20, $21, $22, $23, $24, $FF, $25,
  604. $26, $27, $28, $29, $2A, $2B, $FF, $2C,
  605. $2D, $2E, $2F, $FF, $FF, $FF, $FF, $30,
  606. $31, $32, $33, $34, $35, $36, $FF, $37,
  607. $38, $39, $3A, $3B, $3C, $FF, $FF, $3D,
  608. $3E, $3F);
  609. const { Base64 encoding table }
  610. Ip64Table : TIp6BitTable = (
  611. #065, #066, #067, #068, #069, #070, #071, #072,
  612. #073, #074, #075, #076, #077, #078, #079, #080,
  613. #081, #082, #083, #084, #085, #086, #087, #088,
  614. #089, #090, #097, #098, #099, #100, #101, #102,
  615. #103, #104, #105, #106, #107, #108, #109, #110,
  616. #111, #112, #113, #114, #115, #116, #117, #118,
  617. #119, #120, #121, #122, #048, #049, #050, #051,
  618. #052, #053, #054, #055, #056, #057, #043, #047);
  619. const { Base64 decoding table }
  620. IpD64Table : array[#43..#122] of Byte = ( {!!.12}
  621. $3E, $7F, $7F, $7F, $3F, $34, $35, $36,
  622. $37, $38, $39, $3A, $3B, $3C, $3D, $7F,
  623. $7F, $7F, $7F, $7F, $7F, $7F, $00, $01,
  624. $02, $03, $04, $05, $06, $07, $08, $09,
  625. $0A, $0B, $0C, $0D, $0E, $0F, $10, $11,
  626. $12, $13, $14, $15, $16, $17, $18, $19,
  627. $7F, $7F, $7F, $7F, $7F, $7F, $1A, $1B,
  628. $1C, $1D, $1E, $1F, $20, $21, $22, $23,
  629. $24, $25, $26, $27, $28, $29, $2A, $2B,
  630. $2C, $2D, $2E, $2F, $30, $31, $32, $33);
  631. const { UUEncode encoding table }
  632. IpUUTable : TIp6BitTable = (
  633. #96, #33, #34, #35, #36, #37, #38, #39,
  634. #40, #41, #42, #43, #44, #45, #46, #47,
  635. #48, #49, #50, #51, #52, #53, #54, #55,
  636. #56, #57, #58, #59, #60, #61, #62, #63,
  637. #64, #65, #66, #67, #68, #69, #70, #71,
  638. #72, #73, #74, #75, #76, #77, #78, #79,
  639. #80, #81, #82, #83, #84, #85, #86, #87,
  640. #88, #89, #90, #91, #92, #93, #94, #95);
  641. const
  642. HexDigits : array[0..$F] of AnsiChar = '0123456789ABCDEF';
  643. RLEChar : Byte = $90;
  644. BinHexFileType : array[0..3] of Byte = ($49, $42, $4D, $3F); { "IBM?" }
  645. CRLF = #13#10;
  646. MaxLine = 1000; {!!.12}
  647. MaxLineEncode = 77; {!!.13}
  648. { Maximum line length for QuotablePrintable & Base64 encoding. } {!!.13}
  649. type
  650. BinHexHeader = packed record
  651. Version : Byte;
  652. FileType : array[0..3] of Byte;
  653. Creator : array[0..3] of Byte;
  654. Flags : Word;
  655. DFLong : Longint;
  656. RFLong : Longint;
  657. end;
  658. function IsSameString (Str1 : string; {!!.12}
  659. Str2 : string; {!!.12}
  660. CaseSensitive : Boolean) : Boolean; {!!.12}
  661. begin {!!.12}
  662. if CaseSensitive then {!!.12}
  663. Result := (Str1 = Str2) {!!.12}
  664. else {!!.12}
  665. Result := StrIComp (PChar (Str1), PChar (Str2)) = 0; {!!.12}
  666. end; {!!.12}
  667. { Parse string into string list }
  668. procedure Parse(const Line : string; Delim : AnsiChar; var List : TStringList);
  669. var
  670. iPos, jPos : Integer;
  671. Term : string;
  672. begin
  673. iPos := 1;
  674. jPos := IpUtils.CharPos(Delim, Line);
  675. while (jPos > 0) do begin
  676. Term := Copy(Line, iPos, jPos - iPos); {!!.02}
  677. if (Term <> '') then
  678. List.Add(Trim(Term));
  679. iPos := jPos + 1;
  680. jPos := IpUtils.CharPosIdx(Delim, Line, iPos);
  681. end;
  682. if (iPos < Length(Line)) then
  683. List.Add(Trim(Copy(Line, iPos, Length(Line))));
  684. end;
  685. { Return a particular parameter from a parsed header parameter list }
  686. procedure DecodeSingleParameter(const ParamName : string;
  687. RawParams : TStringList;
  688. var ParamFieldStr : string);
  689. var
  690. S : string;
  691. i, j : Integer;
  692. begin
  693. ParamFieldStr := '';
  694. {find the line containing the parameter field name}
  695. for i := 1 to RawParams.Count do begin
  696. S := RawParams[i-1];
  697. if StrLIComp(PChar(ParamName), PChar(S), Length(ParamName)) = 0 then begin
  698. {strip off the parameter field name and remove quotes }
  699. ParamFieldStr := Copy(S, Length(ParamName) + 1, Length(S));
  700. j := IpUtils.CharPos('"', ParamFieldStr);
  701. while (j > 0) do begin
  702. Delete(ParamFieldStr, j, 1);
  703. j := IpUtils.CharPos('"', ParamFieldStr);
  704. end;
  705. Break;
  706. end;
  707. end;
  708. end;
  709. { Return a particular header as string }
  710. procedure DecodeSingleHeader(const HeaderName : string;
  711. RawHeaders : TStringList;
  712. var HeaderFieldStr : string);
  713. var
  714. S, S2 : string;
  715. i, j : Integer;
  716. begin
  717. HeaderFieldStr := '';
  718. {find the line containing the header field name}
  719. for i := 1 to RawHeaders.Count do begin
  720. S := RawHeaders[i-1];
  721. if StrLIComp(PChar(HeaderName), PChar(S), Length(HeaderName)) = 0 then begin
  722. {strip off the header field name}
  723. S := Copy(S, Length(HeaderName) + 1, Length(S));
  724. {unfold the header if continued on more than one line}
  725. if (i < RawHeaders.Count) then
  726. for j := i to Pred(RawHeaders.Count) do begin
  727. S2 := RawHeaders[j];
  728. if (Length(S2) > 0) and (S2[1] <> #09) and (S2[1] <> ' ') then
  729. Break
  730. else
  731. S := S + S2;
  732. end;
  733. HeaderFieldStr := S;
  734. Break;
  735. end;
  736. end;
  737. end;
  738. { Return a particular header as string list }
  739. (*procedure DecodeListHeader(const HeaderName : string;
  740. RawHeaders, HeaderFieldList : TStringList);
  741. var
  742. S : string;
  743. i, j : Integer;
  744. begin
  745. {find the line containing the header field name}
  746. for i := 1 to RawHeaders.Count do begin
  747. S := RawHeaders[i-1];
  748. if StrLIComp(PChar(HeaderName), PChar(S), Length(HeaderName)) = 0 then begin
  749. {strip off the header field name}
  750. HeaderFieldList.Add(Copy(S, Length(HeaderName) + 1, Length(S)));
  751. {unfold the header if continued on more than one line}
  752. if (i < RawHeaders.Count) then
  753. for j := i to Pred(RawHeaders.Count) do begin
  754. S := RawHeaders[j];
  755. if (Length(S) > 0) and (S[1] <> #09) and (S[1] <> ' ') then
  756. Break
  757. else
  758. HeaderFieldList.Add(S);
  759. end;
  760. Break;
  761. end;
  762. end;
  763. end;*)
  764. { Return multiple instance headers as string list }
  765. (*procedure DecodeMultiHeader(const HeaderName : string;
  766. RawHeaders, HeaderFieldList : TStringList);
  767. var
  768. S, S2 : string;
  769. i, j : Integer;
  770. begin
  771. {find the next line containing the header field name}
  772. for i := 1 to RawHeaders.Count do begin
  773. S := RawHeaders[i-1];
  774. if StrLIComp(PChar(HeaderName), PChar(S), Length(HeaderName)) = 0 then begin
  775. if HeaderName <> strUserFields then begin {!!.11}
  776. {strip off the header field name}
  777. S := Copy(S, Length(HeaderName) + 1, Length(S));
  778. {unfold the header if continued on more than one line}
  779. if (i < RawHeaders.Count) then
  780. for j := i to Pred(RawHeaders.Count) do begin
  781. S2 := RawHeaders[j];
  782. if (Length(S2) > 0) and (S2[1] <> #09) and (S2[1] <> ' ') then
  783. Break
  784. else
  785. S := S + S2;
  786. end;
  787. end; {!!.11}
  788. HeaderFieldList.Add(S);
  789. end;
  790. end;
  791. end;*)
  792. { Add header string to raw headers }
  793. procedure EncodeSingleHeader(const HeaderName : string;
  794. RawHeaders : TStringList;
  795. HeaderFieldStr : string);
  796. begin
  797. if (HeaderFieldStr <> '') then
  798. RawHeaders.Add(HeaderName + HeaderFieldStr);
  799. end;
  800. { Unfold multiple line header and add to raw headers }
  801. procedure EncodeListHeader(const HeaderName : string;
  802. RawHeaders, HeaderFieldList : TStringList;
  803. const Delim : string;
  804. Fold : Boolean);
  805. var
  806. S : string;
  807. i : Integer;
  808. begin
  809. if (HeaderFieldList.Count > 0) then begin
  810. S := HeaderName;
  811. for i := 0 to Pred(HeaderFieldList.Count) do begin
  812. if (Length(S + HeaderFieldList[i]) > MaxLine) then begin
  813. RawHeaders.Add(S);
  814. S := #09;
  815. end;
  816. S := S + HeaderFieldList[i];
  817. if (i < HeaderFieldList.Count - 1) and (S <> '') then begin
  818. S := S + Delim; {!!.14}
  819. if Fold then begin
  820. RawHeaders.Add(S);
  821. S := #09;
  822. end;
  823. end;
  824. end;
  825. RawHeaders.Add(S);
  826. end;
  827. end;
  828. { Add multiple instance header to raw headers }
  829. procedure EncodeMultiHeader(const HeaderName : string;
  830. RawHeaders, HeaderFieldList : TStringList;
  831. Delim : AnsiChar;
  832. Fold : Boolean);
  833. var
  834. i, j : Integer;
  835. SL : TStringList;
  836. S : string;
  837. begin
  838. if (HeaderFieldList.Count > 0) then
  839. for j := 1 to HeaderFieldList.Count do begin
  840. if not Fold then
  841. RawHeaders.Add(HeaderName + HeaderFieldList[j-1])
  842. else begin
  843. SL := TStringList.Create;
  844. try
  845. Parse(HeaderFieldList[j-1], Delim, SL);
  846. S := HeaderName;
  847. for i := 1 to SL.Count do begin
  848. S := S + SL[i-1];
  849. if (i < SL.Count) and (S <> '') then begin
  850. {Begin !!.13}
  851. RawHeaders.Add(S);
  852. S := Delim;
  853. {End !!.13}
  854. end;
  855. end;
  856. finally
  857. SL.Free;
  858. end;
  859. RawHeaders.Add(S);
  860. end;
  861. end;
  862. end;
  863. { Generate "unique" boundary string }
  864. function GenerateBoundary : string;
  865. var
  866. Temp : TDateTime;
  867. begin
  868. Temp := Now;
  869. Randomize;
  870. Result := '_NextPart_' + IntToHex(Trunc(Temp), 8) + '-' +
  871. IntToHex(Trunc(Frac(Temp) * 10000), 8) + '-' +
  872. IntToHex(GetTickCount64, 8) + '-' + IntToHex(Random($FFFF), 4);
  873. end;
  874. { 16-bit CRC of stream between starting and ending offset }
  875. function BinHexCRC(Stream : TStream; StartOffset, EndOffset : Longint) : Word;
  876. var
  877. Crc : Word;
  878. InByte : Byte;
  879. ByteStream : TIpByteStream;
  880. procedure DoCRC(b : Byte);
  881. {- carry CRC division on with next byte }
  882. var
  883. j : Byte;
  884. t : Boolean;
  885. begin
  886. for j := 1 to 8 do begin
  887. t := (Crc and $8000) <> 0;
  888. Crc := (Crc shl 1) xor (b shr 7);
  889. if t then
  890. Crc := Crc xor $1021;
  891. b := b shl 1;
  892. end;
  893. end;
  894. begin
  895. if (StartOffset > Stream.Size) or (EndOffset > Stream.Size) then
  896. raise EIpBaseException.Create(SBadOffset);
  897. Crc := 0;
  898. Stream.Position := StartOffset;
  899. ByteStream := TIpByteStream.Create(Stream);
  900. try
  901. while (ByteStream.Position < EndOffset) do begin
  902. if ByteStream.Read(InByte) then
  903. DoCrc(InByte);
  904. end;
  905. finally
  906. ByteStream.Free;
  907. end;
  908. DoCrc(0);
  909. DoCrc(0);
  910. Result := Swap(Crc);
  911. end;
  912. { Reverse bytes and words }
  913. function htonl(HostLong : Longint) : Longint;
  914. var
  915. dw : Longint;
  916. wa : array[0..1] of Word absolute dw;
  917. w : Word;
  918. begin
  919. dw := HostLong;
  920. w := wa[0];
  921. wa[0] := Swap(wa[1]);
  922. wa[1] := Swap(w);
  923. Result := dw;
  924. end;
  925. {Begin !!.12}
  926. { TIpHeaderItem ****************************************************** }
  927. constructor TIpHeaderItem.Create (Collection : TCollection);
  928. begin
  929. inherited Create (Collection);
  930. FCollection := TIpHeaderCollection.Create (
  931. TIpHeaderCollection(Collection).FOwner);
  932. FValue := TStringList.Create;
  933. FName := '';
  934. FProperty := False; {!!.13}
  935. end;
  936. destructor TIpHeaderItem.Destroy;
  937. begin
  938. FCollection.Free;
  939. FCollection := nil;
  940. FValue.Free;
  941. FValue := nil;
  942. inherited Destroy;
  943. end;
  944. procedure TIpHeaderItem.SetName(const Name : string);
  945. begin
  946. FName := Name;
  947. FNameL := LowerCase(Name);
  948. end;
  949. procedure TIpHeaderItem.SetValue (v : TStringList);
  950. begin
  951. FValue.Assign (v);
  952. end;
  953. { TIpHeaderCollection ************************************************ }
  954. constructor TIpHeaderCollection.Create(AOwner : TPersistent);
  955. begin
  956. inherited Create (TIpHeaderItem);
  957. FOwner := AOwner;
  958. end;
  959. {$IFNDEF VERSION5}
  960. procedure TIpHeaderCollection.Delete(Item: integer);
  961. begin
  962. GetItem(Item).Free;
  963. end;
  964. {$ENDIF}
  965. function TIpHeaderCollection.GetItem (Index : Integer) : TIpHeaderItem;
  966. begin
  967. Result := TIpHeaderItem (inherited GetItem (Index));
  968. end;
  969. function TIpHeaderCollection.GetOwner : TPersistent;
  970. begin
  971. Result := FOwner;
  972. end;
  973. function TIpHeaderCollection.HasHeader (AName : string) : Integer;
  974. var
  975. i : Integer;
  976. begin
  977. Result := -1;
  978. AName := LowerCase(AName);
  979. for i := 0 to Count - 1 do
  980. if Items[i].NameL = AName then begin
  981. Result := i;
  982. Break;
  983. end;
  984. end;
  985. procedure TIpHeaderCollection.HeaderByName (AName : string;
  986. Headers : TStringList);
  987. var
  988. HeaderPos : Integer;
  989. begin
  990. Headers.Clear;
  991. HeaderPos := HasHeader (AName);
  992. if HeaderPos >= 0 then
  993. Headers.Assign (Items[HeaderPos].Value);
  994. end;
  995. procedure TIpHeaderCollection.LoadHeaders (AHeaderList : TStringList;
  996. Append : Boolean);
  997. var
  998. CurPos : Integer;
  999. function ExtractHeaderName (const AName : string) : string;
  1000. {!!.15 - replaced local variable i with inx in order to avoid confusion with
  1001. variable i in parent routine. }
  1002. var
  1003. inx : Integer;
  1004. NameLen : Integer;
  1005. begin
  1006. Result := '';
  1007. CurPos := 0;
  1008. inx := 0;
  1009. NameLen := Length (AName);
  1010. while (inx < NameLen) and (AName[inx + 1] <> ':') and
  1011. (AName[inx + 1] >= #33) and (AName[inx + 1] <= #126) do
  1012. Inc (inx);
  1013. if (inx > 0) then
  1014. Result := Copy (AName, 1, inx);
  1015. CurPos := inx + 2;
  1016. end;
  1017. function IsWrappedLine (AHeaderList : TStringList;
  1018. LineToCheck : Integer) : Boolean;
  1019. begin
  1020. if LineToCheck < AHeaderList.Count then begin
  1021. if Length (AHeaderList[LineToCheck]) > 0 then begin
  1022. if (AHeaderList[LineToCheck][1] = ' ') or
  1023. (AHeaderList[LineToCheck][1] = #09) then
  1024. Result := True
  1025. else
  1026. Result := False;
  1027. end else
  1028. Result := False;
  1029. end else
  1030. Result := False;
  1031. end;
  1032. procedure GetFieldValue ( AHeaderList : TStringList;
  1033. var CurLine : Integer;
  1034. var NewField : TIpHeaderItem);
  1035. var
  1036. WorkLine : string;
  1037. LineLen : Integer;
  1038. begin
  1039. if CurLine >= AHeaderList.Count then
  1040. Exit;
  1041. LineLen := Length (AHeaderList[CurLine]);
  1042. while (CurPos < LineLen) and
  1043. ((AHeaderList[CurLine][CurPos] = ' ') or
  1044. (AHeaderList[CurLine][CurPos] = #09)) do
  1045. Inc (CurPos);
  1046. WorkLine := Copy (AHeaderList[CurLine],
  1047. CurPos, LineLen - CurPos + 1);
  1048. {Begin !!.13}
  1049. Inc(CurLine);
  1050. while IsWrappedLine (AHeaderList, CurLine) do begin
  1051. WorkLine := WorkLine + #9 + Trim(AHeaderList[CurLine]);
  1052. Inc(CurLine);
  1053. end;
  1054. NewField.Value.Add (Trim (WorkLine));
  1055. {End !!.13}
  1056. end;
  1057. var
  1058. i : Integer;
  1059. HeaderName : string;
  1060. NewHeader : TIpHeaderItem;
  1061. begin
  1062. if not Append then
  1063. Clear;
  1064. i := 0;
  1065. while i < AHeaderList.Count do begin
  1066. HeaderName := ExtractHeaderName (AHeaderList[i]);
  1067. if HeaderName <> '' then begin
  1068. NewHeader := TIpHeaderItem (Add);
  1069. NewHeader.Name := HeaderName;
  1070. GetFieldValue (AHeaderList, i, NewHeader);
  1071. {Begin !!.15}
  1072. end
  1073. else
  1074. Inc(i);
  1075. {End !!.15}
  1076. end;
  1077. end;
  1078. procedure TIpHeaderCollection.SetItem (Index : Integer;
  1079. Value : TIpHeaderItem);
  1080. begin
  1081. inherited SetItem (Index, Value);
  1082. end;
  1083. {End !!.12}
  1084. { TIpMimeParts }
  1085. constructor TIpMimeParts.Create;
  1086. begin
  1087. inherited Create;
  1088. Entitys := TList.Create;
  1089. end;
  1090. destructor TIpMimeParts.Destroy;
  1091. begin
  1092. Clear;
  1093. Entitys.Free;
  1094. inherited Destroy;
  1095. end;
  1096. { Add Mime block to list }
  1097. function TIpMimeParts.Add(aEntity : TIpMimeEntity) : Integer;
  1098. begin
  1099. Result := Entitys.Add(aEntity);
  1100. end;
  1101. { Clear list }
  1102. procedure TIpMimeParts.Clear;
  1103. var
  1104. i : Integer;
  1105. begin
  1106. for i := Pred(Entitys.Count) downto 0 do
  1107. Delete(i);
  1108. end;
  1109. { Delete block from list }
  1110. procedure TIpMimeParts.Delete(aIndex : Integer);
  1111. begin
  1112. if (aIndex >= 0) and (aIndex < Entitys.Count) then begin
  1113. TIpMimeEntity(Entitys[aIndex]).Free;
  1114. end;
  1115. end;
  1116. { Remove block from list }
  1117. function TIpMimeParts.Remove(aEntity : TIpMimeEntity) : Integer;
  1118. begin
  1119. Result := Entitys.Remove(Pointer(aEntity));
  1120. end;
  1121. { Count property read access method }
  1122. function TIpMimeParts.GetCount : Integer;
  1123. begin
  1124. Result := Entitys.Count;
  1125. end;
  1126. { Parts property read access method }
  1127. function TIpMimeParts.GetPart(aIndex : Integer) : TIpMimeEntity;
  1128. begin
  1129. if (aIndex >= 0) and (aIndex < Entitys.Count) then
  1130. Result := TIpMimeEntity(Entitys[aIndex])
  1131. else
  1132. Result := nil;
  1133. end;
  1134. { Returns list index of specified Mime block }
  1135. function TIpMimeParts.IndexOf(aEntity : TIpMimeEntity) : Integer;
  1136. begin
  1137. Result := Entitys.IndexOf(aEntity);
  1138. end;
  1139. { TIpMimeEntity }
  1140. constructor TIpMimeEntity.Create(ParentEntity : TIpMimeEntity);
  1141. begin
  1142. inherited Create;
  1143. FBody := TIpAnsiTextStream.CreateEmpty;
  1144. FBody.Stream := TMemoryStream.Create;
  1145. FMimeParts := TIpMimeParts.Create;
  1146. FParent := ParentEntity;
  1147. if (FParent <> nil) then
  1148. FParentBoundary := FParent.Boundary;
  1149. end;
  1150. destructor TIpMimeEntity.Destroy;
  1151. begin
  1152. FMimeParts.Free;
  1153. FBody.FreeStream;
  1154. FBody.Free;
  1155. if (FParent <> nil) then
  1156. FParent.MimeParts.Remove(Self);
  1157. inherited Destroy;
  1158. end;
  1159. { Clear Body property }
  1160. procedure TIpMimeEntity.ClearBody;
  1161. begin
  1162. FBody.FreeStream;
  1163. FBody.Stream := TMemoryStream.Create;
  1164. end;
  1165. {Begin !!.12}
  1166. { Clear Body property in preparation for large attachment }
  1167. procedure TIpMimeEntity.ClearBodyLargeAttach(const AttachmentSize : Longint);
  1168. var
  1169. FileName : string;
  1170. Strm : TIpMemMapStream;
  1171. begin
  1172. FBody.FreeStream;
  1173. FileName := GetTemporaryFile(GetTemporaryPath);
  1174. if FileExistsUTF8(FileName) then
  1175. DeleteFileUTF8(FileName);
  1176. Strm := TIpMemMapStream.Create(FileName, False, True);
  1177. Strm.Size := Trunc(AttachmentSize * 1.3695);
  1178. Strm.Open;
  1179. FBody.Stream := Strm;
  1180. end;
  1181. {End !!.12}
  1182. { Clear all properties }
  1183. procedure TIpMimeEntity.Clear;
  1184. begin
  1185. ClearBody;
  1186. FMimeParts.Clear;
  1187. FBoundary := '';
  1188. FCharacterSet := '';
  1189. FContentDescription := '';
  1190. FContentDispositionType := '';
  1191. FContentID := '';
  1192. FContentSubtype := '';
  1193. FContentType := '';
  1194. FContentTransferEncoding := emUnknown;
  1195. FFileName := '';
  1196. FIsMime := False;
  1197. FIsMultipart := False;
  1198. FMimeVersion := '';
  1199. FEntityName := '';
  1200. FRelatedType := ''; {!!.02}
  1201. FRelatedSubtype := ''; {!!.02}
  1202. FRelatedStart := ''; {!!.02}
  1203. FRelatedStartInfo := ''; {!!.02}
  1204. end;
  1205. { Build Mime (and nested Mime) block(s) from incoming text stream }
  1206. function TIpMimeEntity.DecodeEntity(InStream : TIpAnsiTextStream) : string;
  1207. var
  1208. Blk : TIpMimeEntity;
  1209. RawHeaders : TStringList;
  1210. Decoded : Boolean; {!!.12}
  1211. i, {!!.13}
  1212. LeadingBlankLines : Integer; {!!.13}
  1213. begin
  1214. Decoded := False; {!!.12}
  1215. LeadingBlankLines := 0; {!!.13}
  1216. { skip blank lines in front of mime headers or body }
  1217. Result := InStream.ReadLine;
  1218. while (Result = '') and not InStream.AtEndOfStream do begin
  1219. inc(LeadingBlankLines);
  1220. Result := InStream.ReadLine;
  1221. end;
  1222. { decode mime headers if any }
  1223. {Begin !!.15}
  1224. if (StrLIComp(PChar(strContent), PChar(Result), Length(strContent)) = 0) or
  1225. (StrLIComp(PChar(strMimeVersion), PChar(Result),
  1226. Length(strMimeVersion)) = 0) then begin
  1227. {End !!.15}
  1228. RawHeaders := TStringList.Create;
  1229. try
  1230. repeat
  1231. RawHeaders.Add(Result);
  1232. Result := InStream.ReadLine;
  1233. until (Result = '') or (InStream.AtEndOfStream);
  1234. DecodeMimeHeaders(RawHeaders);
  1235. finally
  1236. RawHeaders.Free;
  1237. end;
  1238. Result := InStream.ReadLine;
  1239. { skip blank lines between mime headers and mime body }
  1240. while (Result = '') and not InStream.AtEndOfStream do
  1241. Result := InStream.ReadLine;
  1242. end;
  1243. { decode body - main loop }
  1244. {Begin !!.15}
  1245. if (FParentBoundary <> '') and
  1246. (Result = '--' + FParentBoundary) then
  1247. { The body of this entity is empty & we are now positioned at the boundary
  1248. marker for the next entity. }
  1249. Decoded := True
  1250. else
  1251. {End !!.15}
  1252. while not (((FParentBoundary <> '') and {!!.12}
  1253. (Result = '--' + FParentBoundary) {!!.12}
  1254. ) or InStream.AtEndOfStream) do begin {!!.12}
  1255. Decoded := True;
  1256. { check for ending boundary - in which case were done }
  1257. if (FParentBoundary <> '') then
  1258. if Pos('--' + FParentBoundary + '--', Result) = 1 {> 0} then begin
  1259. Result := InStream.ReadLine;
  1260. Exit;
  1261. end;
  1262. { decode any nested mime parts - recursively }
  1263. if IsMultiPart and (Boundary <> '') and {!!.03}
  1264. (Pos('--' + Boundary, Result) = 1) then begin
  1265. Blk := TIpMimeEntity.Create(Self);
  1266. Result := Blk.DecodeEntity(Instream);
  1267. FMimeParts.Add(Blk);
  1268. end else begin
  1269. { read raw text line into body }
  1270. for i := 1 to LeadingBlankLines do {!!.13}
  1271. Body.WriteLine(''); {!!.13}
  1272. Body.WriteLine(Result);
  1273. Result := InStream.ReadLine;
  1274. end;
  1275. if InStream.AtEndOfStream then break; {!!.12}
  1276. LeadingBlankLines := 0; {!!.13}
  1277. end;
  1278. {Begin !!.12}
  1279. { If did not find a MIME entity then assume the body is text &
  1280. read it into the Body property. }
  1281. if not Decoded then
  1282. ReadBody(InStream, Result)
  1283. else if (not (Pos('--' + FParentBoundary, Result) = 1)) then
  1284. { If the last line is not a MIME separator then add the last line
  1285. to the Body. }
  1286. Body.WriteLine(Result);
  1287. {End !!.12}
  1288. end;
  1289. {!!.01}
  1290. { Build Mime block as subpart from incoming text stream }
  1291. function TIpMimeEntity.DecodeEntityAsAttachment(InStream : TIpAnsiTextStream) : string;
  1292. var
  1293. Blk : TIpMimeEntity;
  1294. begin
  1295. Blk := TIpMimeEntity.Create(Self);
  1296. Blk.ContentType := FContentType;
  1297. Blk.ContentSubtype := FContentSubtype;
  1298. Blk.ContentDispositionType := FContentDispositionType;
  1299. Blk.ContentDescription := FContentDescription;
  1300. Blk.ContentTransferEncoding := FContentTransferEncoding;
  1301. Blk.CharacterSet := FCharacterSet;
  1302. Blk.CreationDate := FCreationDate;
  1303. Blk.FileName := FFileName;
  1304. Blk.EntityName := FEntityName;
  1305. Blk.FIsMime := True;
  1306. Blk.FIsMultipart := False;
  1307. Blk.ModificationDate := FModificationDate;
  1308. Blk.MimeVersion := FMimeVersion;
  1309. Blk.OriginalSize := FOriginalSize;
  1310. Blk.ReadDate := FReadDate;
  1311. Result := Blk.DecodeEntity(Instream);
  1312. FMimeParts.Add(Blk);
  1313. Body.Position := 0;
  1314. end;
  1315. { Decode Content-Disposition header field and sub-fields }
  1316. procedure TIpMimeEntity.DecodeContentDisposition(const aDisp : string);
  1317. var
  1318. RawParams : TStringList;
  1319. S : string;
  1320. begin
  1321. { split up parameters }
  1322. RawParams := TStringList.Create;
  1323. try
  1324. Parse(aDisp, ';', RawParams);
  1325. { decode disposition type and parameters }
  1326. if (RawParams.Count > 0) then begin
  1327. FContentDispositionType := RawParams[0];
  1328. if (RawParams.Count > 1) then begin
  1329. DecodeSingleParameter(strFileName, RawParams, FFileName);
  1330. DecodeSingleParameter(strCreationDate, RawParams, FCreationDate);
  1331. DecodeSingleParameter(strModificationDate, RawParams, FModificationDate);
  1332. DecodeSingleParameter(strReadDate, RawParams, FReadDate);
  1333. DecodeSingleParameter(strSize, RawParams, S);
  1334. FOriginalSize := StrToIntDef(S, 0);
  1335. end;
  1336. end else
  1337. FContentDispositionType := '';
  1338. finally
  1339. RawParams.Free;
  1340. end;
  1341. end;
  1342. { Decode Content-Type header field and sub-fields }
  1343. procedure TIpMimeEntity.DecodeContentType(const aType : string);
  1344. var
  1345. RawParams : TStringList;
  1346. S : string;
  1347. i : Integer;
  1348. begin
  1349. { split up parameters }
  1350. RawParams := TStringList.Create;
  1351. try
  1352. Parse(aType, ';', RawParams);
  1353. { decode type and subtype }
  1354. FContentType := '';
  1355. FContentSubType := '';
  1356. if (RawParams.Count > 0) then begin
  1357. S := RawParams[0];
  1358. i := IpUtils.CharPos('/', S);
  1359. if (i > 0) then begin
  1360. FContentType := Copy(S, 1, i - 1);
  1361. FContentSubType := Copy(S, i + 1, Length(S));
  1362. end else
  1363. FContentType := S;
  1364. end;
  1365. FIsMultipart := StrIComp(PChar(FContentType), PChar(strMultipart)) = 0;
  1366. { decode the parameters }
  1367. DecodeSingleParameter(strName, RawParams, FEntityName);
  1368. DecodeSingleParameter(strBoundary, RawParams, FBoundary);
  1369. DecodeSingleParameter(strCharSet, RawParams, FCharacterSet);
  1370. {!!.02}
  1371. { decode multipart/related parameters }
  1372. DecodeSingleParameter(strType, RawParams, S);
  1373. if (S <> '') then begin
  1374. i := IpUtils.CharPos('/', S);
  1375. if (i > 0) then begin
  1376. FRelatedType := Copy(S, 1, i - 1);
  1377. FRelatedSubType := Copy(S, i + 1, Length(S));
  1378. end else
  1379. FRelatedType := S;
  1380. DecodeSingleParameter(strStart, RawParams, FRelatedStart);
  1381. DecodeSingleParameter(strStartInfo, RawParams, FRelatedStartInfo);
  1382. end;
  1383. {!!.02}
  1384. finally
  1385. RawParams.Free;
  1386. end;
  1387. end;
  1388. { Decode Content-TranferEncoding header field }
  1389. function TIpMimeEntity.DecodeContentTransferEncoding(const aEncoding : string) :
  1390. TIpMimeEncodingMethod;
  1391. begin
  1392. if (UpperCase(aEncoding) = UpperCase(str7Bit)) then
  1393. Result := em7bit
  1394. else if (UpperCase(aEncoding) = UpperCase(str8Bit)) then
  1395. Result := em8bit
  1396. else if (UpperCase(aEncoding) = UpperCase(strBase64)) then
  1397. Result := emBase64
  1398. else if (UpperCase(aEncoding) = UpperCase(strBinary)) then
  1399. Result := emBinary
  1400. else if (UpperCase(aEncoding) = UpperCase(strBinHex)) then
  1401. Result := emBinHex
  1402. else if (UpperCase(aEncoding) = UpperCase(strQuoted)) then
  1403. Result := emQuoted
  1404. else if (UpperCase(aEncoding) = UpperCase(strUUEncode)) then
  1405. Result := emUUEncode
  1406. else
  1407. Result := emUnknown;
  1408. end;
  1409. { Decode Mime headers from raw header list }
  1410. procedure TIpMimeEntity.DecodeMimeHeaders(RawHeaders : TStringList);
  1411. var
  1412. S : string;
  1413. begin
  1414. { decode content type header }
  1415. DecodeSingleHeader(strContentType, RawHeaders, S);
  1416. if (S <> '') then begin
  1417. FIsMime := True;
  1418. DecodeContentType(S);
  1419. if FIsMultipart and (FBoundary = '') then
  1420. raise EIpBaseException.Create(SNoBoundary);
  1421. end else begin
  1422. FIsMime := False;
  1423. Exit;
  1424. end;
  1425. { decode the others }
  1426. DecodeSingleHeader(strMIMEVersion, RawHeaders, FMimeVersion);
  1427. DecodeSingleHeader(strContentTransferEncoding, RawHeaders, S);
  1428. FContentTransferEncoding := DecodeContentTransferEncoding(S);
  1429. DecodeSingleHeader(strContentDescription, RawHeaders, FContentDescription);
  1430. DecodeSingleHeader(strContentID, RawHeaders, FContentID);
  1431. DecodeSingleHeader(strContentDisposition, RawHeaders, S);
  1432. if (S <> '') then
  1433. DecodeContentDisposition(S);
  1434. if (FContentDispositionType = strAttachment) then {!!.12}
  1435. Inc (FParent.FAttachmentCount); {!!.12}{!!.15}
  1436. end;
  1437. { Compute attachment coding progress and fire OnCodingProgress event }
  1438. procedure TIpMimeEntity.DoOnCodingProgress(Count, TotalSize : Longint;
  1439. var Abort : Boolean);
  1440. { IMPORTANT: The progress event must only be fired by the root parent }
  1441. begin
  1442. if (Parent = nil) or (Parent = Self) then begin
  1443. FProgress := ((Count*100) div TotalSize);
  1444. if (FProgress > 100) then
  1445. FProgress := 100;
  1446. if (FProgress div 10) = 0 then
  1447. PrevProgress := 0;
  1448. { report progress in 10% increments }
  1449. if ((FProgress div 10) > (PrevProgress div 10)) then begin
  1450. PrevProgress := FProgress;
  1451. if Assigned(FOnCodingProgress) then
  1452. FOnCodingProgress(Self, FProgress, Abort);
  1453. end;
  1454. end else
  1455. Parent.DoOnCodingProgress(Count, TotalSize, Abort);
  1456. end;
  1457. { Generate Mime message stream from properties (and nested Mime blocks) }
  1458. function TIpMimeEntity.EncodeEntity(OutStream : TIpAnsiTextStream) : string;
  1459. var
  1460. i : Integer;
  1461. S : string;
  1462. RawHeaders : TStringList;
  1463. Ch : AnsiChar;
  1464. begin
  1465. Result := FParentBoundary;
  1466. { write out mime headers }
  1467. if (Result <> '') then begin
  1468. OutStream.WriteLine('--' + Result);
  1469. RawHeaders := TStringList.Create;
  1470. try
  1471. EncodeMimeHeaders(RawHeaders);
  1472. if (RawHeaders.Count > 0) then
  1473. for i := 0 to Pred(RawHeaders.Count) do
  1474. if (RawHeaders[i] <> '') then
  1475. OutStream.WriteLine(RawHeaders[i]);
  1476. OutStream.WriteLine('');
  1477. finally
  1478. RawHeaders.Free;
  1479. end;
  1480. end;
  1481. // flush to update underlaying memory streams
  1482. Body.Flush;
  1483. { write out mime body }
  1484. if (Body.FastSize > 0) then
  1485. begin
  1486. // presize stream for more speed
  1487. OutStream.Stream.Size := OutStream.Stream.Size + Body.FastSize;
  1488. // use optimal method depending on the source stream to copy the stream
  1489. if Body.Stream is TIpMemMapStream then
  1490. OutStream.Write((Body.Stream as TIpMemMapStream).Memory^, Body.FastSize)
  1491. else
  1492. if Body.Stream is TMemoryStream then
  1493. OutStream.Write((Body.Stream as TMemoryStream).Memory^, Body.Stream.Size)
  1494. else
  1495. OutStream.CopyFrom(Body, 0); // copy the entire stream from the beginning
  1496. { make sure the body is properly terminated } {!!.01}
  1497. OutStream.Position := OutStream.Size - 1; {!!.01}
  1498. TIpBufferedStream(OutStream).ReadChar(Ch); {!!.01}
  1499. if ((Ch <> #13) and (Ch <> #10)) then {!!.01}
  1500. OutStream.WriteLine(''); {!!.01}
  1501. end;
  1502. { encode nested mime parts - recursively }
  1503. if (FMimeParts.Count > 0) then begin
  1504. for i := 0 to Pred(FMimeParts.Count) do
  1505. S := FMimeParts[i].EncodeEntity(OutStream);
  1506. OutStream.WriteLine('--' + S + '--');
  1507. end;
  1508. end;
  1509. {Begin !!.14}
  1510. function TIpMimeEntity.ContainsSpecialChars(const Value : string) : Boolean;
  1511. var
  1512. Inx : Integer;
  1513. begin
  1514. Result := False;
  1515. for Inx := 1 to Length(Value) do
  1516. if (Ord(Value[Inx]) <= 32) or
  1517. (Value[Inx] in ['(', ')', '<', '>', '@',
  1518. ',', ';', ':', '\', '"',
  1519. '/', '[', ']', '?', '=']) then begin
  1520. Result := True;
  1521. Break;
  1522. end; { if }
  1523. end;
  1524. {End !!.14}
  1525. { Generate Content-Disposition header into raw header list }
  1526. procedure TIpMimeEntity.EncodeContentDisposition(RawHeaders : TStringList);
  1527. var
  1528. Params : TStringList;
  1529. begin
  1530. if (FContentDispositionType = '') then
  1531. Exit;
  1532. Params := TStringList.Create;
  1533. try
  1534. Params.Add(FContentDispositionType);
  1535. {Begin !!.14}
  1536. if (FFileName <> '') then begin
  1537. { If the filename contains spaces, control characters, or any of the
  1538. special characters identified in RFC 1521 then wrap the filename in
  1539. quotes.
  1540. Assumption: FFileName length is <= 78 characters. Future enhancement
  1541. is to support RFC 2184. }
  1542. if ContainsSpecialChars(FFileName) then
  1543. Params.Add(strFileName + '"' + FFileName + '"')
  1544. else
  1545. Params.Add(strFileName + FFileName);
  1546. end; { if }
  1547. {End !!.14}
  1548. if (FCreationDate <> '') then
  1549. Params.Add(strCreationDate + FCreationDate);
  1550. if (FModificationDate <> '') then
  1551. Params.Add(strModificationDate + FModificationDate);
  1552. if (FReadDate <> '') then
  1553. Params.Add(strReadDate + FReadDate);
  1554. if (FOriginalSize > 0) then
  1555. Params.Add(strSize + IntToStr(FOriginalSize));
  1556. EncodeListHeader(strContentDisposition, RawHeaders, Params, ';', False);
  1557. finally
  1558. Params.Free;
  1559. end;
  1560. end;
  1561. { Generate Content-Type header into raw header list }
  1562. procedure TIpMimeEntity.EncodeContentType(RawHeaders : TStringList);
  1563. var
  1564. S : string;
  1565. Params : TStringList;
  1566. begin
  1567. if (FContentType = '') then
  1568. Exit;
  1569. Params := TStringList.Create;
  1570. try
  1571. S := FContentType;
  1572. if (FContentSubType <> '') then
  1573. S := S + '/' + FContentSubType;
  1574. Params.Add(S);
  1575. if IsMultipart then
  1576. Params.Add(strBoundary + '"' + FBoundary + '"');
  1577. if (FEntityName <> '') then
  1578. Params.Add(strName + '"' + FEntityName + '"');
  1579. if (FCharacterSet <> '') then
  1580. Params.Add(strCharSet + FCharacterSet); {no quotes}
  1581. {!!.02}
  1582. { encode multipart/related parameters }
  1583. if (FRelatedType <> '') then begin
  1584. if (FRelatedSubtype <> '') then
  1585. Params.Add(strType + '"' + FRelatedType + '/' + FRelatedSubtype + '"')
  1586. else
  1587. Params.Add(strType + '"' + FRelatedType + '"');
  1588. if (FRelatedStart <> '') then
  1589. Params.Add(strStart + '"' + FRelatedStart + '"');
  1590. if (FRelatedStartInfo <> '') then
  1591. Params.Add(strStartInfo + '"' + FRelatedStartInfo + '"');
  1592. end;
  1593. {!!.02}
  1594. EncodeListHeader(strContentType, RawHeaders, Params, ';', False);
  1595. finally
  1596. Params.Free;
  1597. end;
  1598. end;
  1599. { Generate Content-TranferEncoding header into raw header list }
  1600. procedure TIpMimeEntity.EncodeContentTransferEncoding(RawHeaders : TStringList);
  1601. begin
  1602. case FContentTransferEncoding of
  1603. em7bit : EncodeSingleHeader(strContentTransferEncoding, RawHeaders, str7Bit);
  1604. em8bit : EncodeSingleHeader(strContentTransferEncoding, RawHeaders, str8Bit);
  1605. emBase64 : EncodeSingleHeader(strContentTransferEncoding, RawHeaders, strBase64);
  1606. emBinary : EncodeSingleHeader(strContentTransferEncoding, RawHeaders, strBinary);
  1607. emBinHex : EncodeSingleHeader(strContentTransferEncoding, RawHeaders, strBinHex);
  1608. emQuoted : EncodeSingleHeader(strContentTransferEncoding, RawHeaders, strQuoted);
  1609. emUUEncode : EncodeSingleHeader(strContentTransferEncoding, RawHeaders, strUUEncode);
  1610. end;
  1611. end;
  1612. { Generate Mime headers into raw header list }
  1613. procedure TIpMimeEntity.EncodeMimeHeaders(RawHeaders : TStringList);
  1614. begin
  1615. if (FContentType <> '') then begin
  1616. EncodeSingleHeader(strMimeVersion, RawHeaders, FMimeVersion);
  1617. EncodeContentType(RawHeaders);
  1618. EncodeSingleHeader(strContentDescription, RawHeaders, FContentDescription);
  1619. EncodeSingleHeader(strContentID, RawHeaders, FContentID);
  1620. EncodeContentTransferEncoding(RawHeaders);
  1621. EncodeContentDisposition(RawHeaders);
  1622. end;
  1623. end;
  1624. { Encode Mime body from TStream - file name is optional }
  1625. procedure TIpMimeEntity.EncodeBodyStream(InStream : TStream; const aFileName : string);
  1626. {Begin !!.12}
  1627. var
  1628. LargeAttachment : Boolean;
  1629. { Large attachments are handled with memory map streams in order to avoid
  1630. whacko memory issues with TMemoryStream. }
  1631. begin
  1632. if (Instream.Size > 0) then begin
  1633. LargeAttachment := (InStream.Size > IpLgAttachSizeBoundry);
  1634. if LargeAttachment then
  1635. ClearBodyLargeAttach(InStream.Size)
  1636. else
  1637. begin
  1638. ClearBody;
  1639. // presize stream for more speed
  1640. FBody.Stream.Size := Trunc(InStream.Size * 1.3695);
  1641. end;
  1642. {End !!.12}
  1643. case FContentTransferEncoding of
  1644. em7Bit : Encode8Bit(InStream);
  1645. em8Bit : Encode8Bit(InStream);
  1646. emBase64 : EncodeBase64(InStream);
  1647. emBinary : Encode8Bit(InStream);
  1648. emBinHex : EncodeBinHex(InStream, aFileName);
  1649. emQuoted : EncodeQuoted(InStream);
  1650. emUUEncode : EncodeUUEncode(InStream, aFileName);
  1651. emUnknown : Encode8Bit(InStream);
  1652. end;
  1653. {Begin !!.12}
  1654. FBody.Flush;
  1655. if LargeAttachment then
  1656. { This is a large attachment that was written to a memory map stream.
  1657. Memory map streams are usually created larger than necessary so shrink
  1658. it down to the correct size. }
  1659. TIpMemMapStream(FBody.Stream).Size := TIpMemMapStream(FBody.Stream).DataSize;
  1660. {End !!.12}
  1661. end;
  1662. FOriginalSize := InStream.Size;
  1663. FFileName := ExtractFileName(aFileName);
  1664. end;
  1665. { Encode Mime body from TStrings - file name is optional }
  1666. procedure TIpMimeEntity.EncodeBodyStrings(InStrings : TStrings; const aFileName : string);
  1667. var
  1668. MS : TMemoryStream;
  1669. begin
  1670. if (InStrings.Count > 0) then begin
  1671. MS := TMemoryStream.Create;
  1672. try
  1673. InStrings.SaveToStream(MS);
  1674. MS.Position := 0; {!!.03}
  1675. FOriginalSize := MS.Size;
  1676. FFileName := ExtractFileName(aFileName);
  1677. EncodeBodyStream(MS, aFileName);
  1678. finally
  1679. MS.Free;
  1680. end;
  1681. end;
  1682. end;
  1683. { Encode Mime body from file }
  1684. procedure TIpMimeEntity.EncodeBodyFile(const InFile : string);
  1685. var
  1686. FS : TIpMemMapStream; {!!.12}
  1687. i : Integer;
  1688. aExt, aTyp, aSub : string;
  1689. aEnc : TIpMimeEncodingMethod;
  1690. begin
  1691. { If content-type, has not been defined for this entity, }
  1692. { default values for that file extension will be used. }
  1693. { These values are defined in the include file, IPDEFCT.INC }
  1694. aTyp := strApplication;
  1695. aSub := strOctetStream;
  1696. aEnc := emBase64;
  1697. aExt := ExtractFileExt(InFile);
  1698. for i := 0 to High(DefExtensions) do
  1699. if (aExt = DefExtensions[i]) then begin
  1700. aTyp := DefContent[i].Typ;
  1701. aSub := DefContent[i].Sub;
  1702. aEnc := DefContent[i].Enc;
  1703. Break;
  1704. end;
  1705. if (FContentType = '') then begin
  1706. FContentType := aTyp;
  1707. FContentSubtype := aSub;
  1708. FContentTransferEncoding := aEnc;
  1709. end;
  1710. if (FContentTransferEncoding = emUnknown) then
  1711. FContentTransferEncoding := aEnc;
  1712. FS := TIpMemMapStream.Create(InFile, True, False); {!!.12}
  1713. try
  1714. FS.Open; {!!.12}
  1715. FOriginalSize := FS.Size;
  1716. FFileName := ExtractFileName(InFile);
  1717. EncodeBodyStream(FS, FFileName);
  1718. finally
  1719. FS.Free;
  1720. end;
  1721. end;
  1722. { Decode encoded Mime block body to TStream }
  1723. procedure TIpMimeEntity.ExtractBodyStream(OutStream : TStream);
  1724. var
  1725. MS : TMemoryStream;
  1726. begin
  1727. if (FBody.Size > 0) then begin
  1728. { We want to append the decoded data to the end of OutStream, }
  1729. { so a local memory stream is used since OutStream may be a }
  1730. { TIpAnsiTextStream, in which case the decoding algorithms }
  1731. { will overwrite its existing contents. }
  1732. MS := TMemoryStream.Create;
  1733. try
  1734. case FContentTransferEncoding of
  1735. em7Bit : Decode8Bit(MS);
  1736. em8Bit : Decode8Bit(MS);
  1737. emBase64 : DecodeBase64(MS);
  1738. emBinary : OutStream.CopyFrom(FBody, FBody.Size); {!!.14}
  1739. emBinHex : DecodeBinHex(MS);
  1740. emQuoted : DecodeQuoted(MS);
  1741. emUUEncode : DecodeUUEncode(MS);
  1742. emUnknown : Decode8Bit(MS);
  1743. end;
  1744. OutStream.CopyFrom(MS, 0);
  1745. finally
  1746. MS.Free;
  1747. end;
  1748. end;
  1749. end;
  1750. { Decode encoded Mime block body to TStrings }
  1751. procedure TIpMimeEntity.ExtractBodyStrings(OutStrings : TStrings);
  1752. var
  1753. MS : TMemoryStream;
  1754. begin
  1755. if (FBody.Size > 0) then begin
  1756. MS := TMemoryStream.Create;
  1757. try
  1758. ExtractBodyStream(MS);
  1759. MS.Position := 0;
  1760. OutStrings.LoadFromStream(MS);
  1761. finally
  1762. MS.Free;
  1763. end;
  1764. end;
  1765. end;
  1766. { Decode encoded Mime block body to file }
  1767. procedure TIpMimeEntity.ExtractBodyFile(const OutFile : string);
  1768. var
  1769. FS : TFileStream;
  1770. begin
  1771. if (FBody.Size > 0) then begin
  1772. FS := TFileStreamUTF8.Create(OutFile, fmCreate);
  1773. try
  1774. ExtractBodyStream(FS);
  1775. finally
  1776. FS.Free;
  1777. end;
  1778. end;
  1779. end;
  1780. { Access/create specified MIME part }
  1781. function TIpMimeEntity.GetMimePart(const aType, aSubType, aContentID : string;
  1782. CanCreate : Boolean) : TIpMimeEntity;
  1783. var
  1784. i : Integer;
  1785. begin
  1786. Result := nil;
  1787. if (MimeParts.Count > 0) then
  1788. for i := 0 to Pred(MimeParts.Count) do
  1789. { ContentID is primary search key }
  1790. if (aContentID <> '') then begin
  1791. if (MimeParts[i].ContentID = aContentID) then begin
  1792. Result := MimeParts[i];
  1793. Break;
  1794. end;
  1795. end else begin
  1796. if (MimeParts[i].ContentType = aType) and
  1797. (MimeParts[i].ContentSubtype = aSubType) then begin
  1798. Result := MimeParts[i];
  1799. Break;
  1800. end;
  1801. end;
  1802. if Assigned(Result) then
  1803. Result.Body.Position := 0
  1804. else if CanCreate then begin
  1805. Result := NewMimePart;
  1806. Result.ContentType := aType;
  1807. Result.ContentSubtype := aSubtype;
  1808. Result.ContentID := aContentID;
  1809. end;
  1810. end;
  1811. {!!.02}
  1812. { Search all nested levels for specified MIME part }
  1813. function TIpMimeEntity.FindNestedMimePart(const aType, aSubType, aContentID : string) : TIpMimeEntity;
  1814. var
  1815. i : Integer;
  1816. Blk : TIpMimeEntity;
  1817. begin
  1818. Result := nil;
  1819. if (MimeParts.Count > 0) then
  1820. for i := 0 to Pred(MimeParts.Count) do begin
  1821. { ContentID is primary search key }
  1822. if (aContentID <> '') and {!!.12}
  1823. (IsSameString (MimeParts[i].ContentID, {!!.12}
  1824. aContentID, False)) then begin {!!.12}
  1825. Result := MimeParts[i];
  1826. Break;
  1827. end else if (IsSameString (MimeParts[i].ContentType, {!!.12}
  1828. aType, False)) and {!!.12}
  1829. (IsSameString (MimeParts[i].ContentSubtype, {!!.12}
  1830. aSubType, False)) then begin {!!.12}
  1831. Result := MimeParts[i];
  1832. Break;
  1833. end else begin
  1834. Blk := MimeParts[i];
  1835. Result := Blk.FindNestedMimePart(aType, aSubType, aContentID);
  1836. if Assigned(Result) then
  1837. Break;
  1838. end;
  1839. end;
  1840. if Assigned(Result) then
  1841. Result.Body.Position := 0;
  1842. end;
  1843. { Create nested Mime block and add to list }
  1844. function TIpMimeEntity.NewMimePart : TIpMimeEntity;
  1845. begin
  1846. {parent Entity is now multipart}
  1847. FIsMime := True;
  1848. FIsMultipart := True;
  1849. FContentType := strMultipart;
  1850. if (FBoundary = '') then
  1851. FBoundary := GenerateBoundary;
  1852. Result := TIpMimeEntity.Create(Self);
  1853. FMimeParts.Add(Result);
  1854. end;
  1855. { Copy Instream to OutStream as is - no decoding }
  1856. procedure TIpMimeEntity.Decode8Bit(OutStream : TStream);
  1857. var
  1858. FS : TIpAnsiTextStream;
  1859. Abort : Boolean;
  1860. begin
  1861. Abort := False;
  1862. FS := TIpAnsiTextStream.Create(OutStream);
  1863. try
  1864. FBody.Position := 0;
  1865. while (FBody.Position < FBody.Size) and not Abort do begin
  1866. FS.WriteLine(FBody.ReadLine);
  1867. DoOnCodingProgress(OutStream.Position, FBody.Size, Abort);
  1868. end;
  1869. finally
  1870. FS.Free;
  1871. end;
  1872. end;
  1873. { Decode InStream to OutStream - Base64 }
  1874. procedure TIpMimeEntity.DecodeBase64(OutStream : TStream);
  1875. { rewritten } {!!.12}
  1876. var
  1877. I : Integer; {!!.16}
  1878. C : Char;
  1879. InBuf : array[0..3] of Char;
  1880. OutBuf : array[0..2] of Byte;
  1881. Done : Boolean;
  1882. Abort : Boolean;
  1883. BufStream : TIpBufferedStream;
  1884. begin
  1885. BufStream := (FBody as TIpBufferedStream);
  1886. BufStream.Position := 0;
  1887. Done := False;
  1888. Abort := False;
  1889. while not (Done or Abort) do begin
  1890. { read in the next 4 valid Base64 characters }
  1891. I := 0;
  1892. InBuf := '===='; {!!.15}
  1893. while (I < 4) do begin
  1894. if not BufStream.ReadChar(C) then begin
  1895. Done := True;
  1896. Break;
  1897. end;
  1898. { skip bad characters }
  1899. if (Low(IpD64Table) <= C) and (C <= High(IpD64Table)) then
  1900. if (IpD64Table[C] <> $7F) then begin
  1901. InBuf[I] := C;
  1902. Inc(I);
  1903. end;
  1904. end;
  1905. { Decode 4 characters to 3 bytes }
  1906. I := 0;
  1907. OutBuf[0] := ((IpD64Table[InBuf[0]] shl 2) or (IpD64Table[InBuf[1]] shr 4));
  1908. Inc(I);
  1909. if InBuf[2] <> '=' then begin
  1910. OutBuf[1] := ((IpD64Table[InBuf[1]] shl 4) or (IpD64Table[InBuf[2]] shr 2));
  1911. Inc(I);
  1912. if InBuf[3] <> '=' then begin
  1913. OutBuf[2] := ((IpD64Table[InBuf[2]] shl 6) or IpD64Table[InBuf[3]]);
  1914. Inc(I);
  1915. end else
  1916. Done := True;
  1917. end else
  1918. Done := True;
  1919. OutStream.Write(OutBuf, I);
  1920. DoOnCodingProgress(OutStream.Position, BufStream.FastSize, Abort); {!!.16}
  1921. end;
  1922. end;
  1923. { Decode InStream to OutStream - BinHex }
  1924. procedure TIpMimeEntity.DecodeBinHex(OutStream : TStream);
  1925. var
  1926. InBuf : array[1..4] of Byte;
  1927. OutBuf : array[1..3] of Byte;
  1928. i : Byte;
  1929. btThis, btLast, btNext : Byte;
  1930. ch : AnsiChar;
  1931. // headerlength is encoded as byte, HeaderFileName can only 256 bytes long
  1932. HeaderFileName : Array [0..MaxByte] of Byte; {!!.12}{!!.16}
  1933. HeaderLength : byte; {!!.12}
  1934. CRC : Word;
  1935. DataOffset, DataEnd, HeaderEnd : Longint;
  1936. WS1, WS2 : TMemoryStream;
  1937. Header : BinHexHeader;
  1938. Abort : Boolean;
  1939. BufStream : TIpBufferedStream;
  1940. function NextChar : AnsiChar;
  1941. {- skip past any CRLF's and return the next message stream char }
  1942. var
  1943. c : AnsiChar;
  1944. begin
  1945. c := #0;
  1946. repeat
  1947. BufStream.ReadChar(c);
  1948. until ((c <> #13) and (c <> #10)) or (BufStream.Position = BufStream.Size);
  1949. Result := c;
  1950. end;
  1951. function ValidChar(ch : AnsiChar) : Boolean;
  1952. {- test if ch is a valid BinHex encoded char }
  1953. var
  1954. b : Byte;
  1955. begin
  1956. Result := False;
  1957. b := Ord(ch);
  1958. if (b > 32) and (b < 115) then
  1959. if IpHexBinTable[b] <> $0FF then
  1960. Result := True;
  1961. end;
  1962. begin
  1963. Abort := False;
  1964. FBody.Position := 0;
  1965. if Pos('(This file must be converted with BinHex', FBody.ReadLine) = 0 then
  1966. raise EIpBaseException.Create(SBinHexBadFormat);
  1967. if (NextChar <> ':') then
  1968. raise EIpBaseException.Create(SBinHexColonExpected);
  1969. { decode attachment into working stream }
  1970. BufStream := (FBody as TIpBufferedStream);
  1971. WS1 := TMemoryStream.Create;
  1972. try
  1973. i := 0;
  1974. ch := NextChar;
  1975. while (ch <> ':') and (BufStream.Position < BufStream.Size) and not Abort do begin
  1976. if not ValidChar(ch) then
  1977. raise EIpBaseException.Create(SBinHexBadChar);
  1978. Inc(i);
  1979. InBuf[i] := IpHexBinTable[Ord(ch)];
  1980. { decode 4 characters into 3 bytes }
  1981. if (i = 4) then begin
  1982. i := 0;
  1983. { 1st : upper 6 lower 2 }
  1984. OutBuf[1] := (InBuf[1] shl 2) or ((InBuf[2] shr 4) and $03);
  1985. { 2nd : upper 4 lower 4 }
  1986. OutBuf[2] := (InBuf[2] shl 4) or ((InBuf[3] shr 2) and $0F);
  1987. { 3rd : upper 2 lower 6 }
  1988. OutBuf[3] := (InBuf[3] shl 6) or (InBuf[4] and $03F);
  1989. WS1.Write(OutBuf, SizeOf(OutBuf));
  1990. end;
  1991. ch := NextChar;
  1992. end;
  1993. { handle odd characters }
  1994. if (i > 0) then begin
  1995. if (i = 1) then
  1996. raise EIpBaseException.Create(SBinHexOddChar);
  1997. OutBuf[1] := (InBuf[1] shl 2) or ((InBuf[2] shr 4) and $03);
  1998. if (i = 2) then
  1999. WS1.Write(OutBuf, 1)
  2000. else begin
  2001. OutBuf[2] := (InBuf[2] shl 4) or ((InBuf[3] shr 2) and $0F);
  2002. WS1.Write(OutBuf, 2);
  2003. end;
  2004. DoOnCodingProgress(BufStream.Position, BufStream.Size, Abort);
  2005. end;
  2006. if Abort then
  2007. Exit;
  2008. { should be the end of file marker }
  2009. if (ch <> ':') then
  2010. raise EIpBaseException.Create(SBinHexColonExpected);
  2011. { expand RLE sequences }
  2012. WS2 := TMemoryStream.Create;
  2013. try
  2014. WS1.Position := 0;
  2015. btThis := 0;
  2016. while (WS1.Position < WS1.Size) and not Abort do begin
  2017. btLast := btThis;
  2018. WS1.Read(btThis, 1);
  2019. if (btThis <> RLEChar) then
  2020. WS2.Write(btThis, 1)
  2021. else begin
  2022. WS1.Read(btNext, 1);
  2023. if (btNext = 0) then
  2024. WS2.Write(btThis, 1)
  2025. else begin
  2026. btThis := btLast;
  2027. for i := 1 to (btNext - 1) do
  2028. WS2.Write(btThis, 1);
  2029. end;
  2030. end;
  2031. DoOnCodingProgress(WS1.Position, WS1.Size, Abort);
  2032. end;
  2033. if Abort then
  2034. WS2.Free;
  2035. { strip off header }
  2036. FillChar (HeaderFileName, SizeOf (HeaderFileName), $00); {!!.12}
  2037. FillChar(Header, SizeOf(Header), #0);
  2038. WS2.Position := 0;
  2039. WS2.Read(HeaderLength, SizeOf (Byte)); {!!.12}
  2040. WS2.Read(HeaderFileName, HeaderLength); {!!.12}
  2041. WS2.Read(Header, SizeOf(Header));
  2042. { check header CRC }
  2043. HeaderEnd := WS2.Position;
  2044. WS2.Read(CRC, 2);
  2045. DataOffset := WS2.Position;
  2046. if (CRC <> BinHexCRC(WS2, 0, HeaderEnd)) then
  2047. raise EIpBaseException.Create(SBinHexBadHeaderCRC);
  2048. DataEnd := DataOffset + htonl(Header.DFLong);
  2049. if (DataEnd > WS2.Size) then
  2050. raise EIpBaseException.Create(SBinHexLengthErr);
  2051. if (htonl(Header.RFLong) > 0) then
  2052. raise EIpBaseException.Create(SBinHexResourceForkErr);
  2053. { check data fork CRC - follows data fork }
  2054. WS2.Position := DataEnd;
  2055. WS2.Read(CRC, 2);
  2056. if (CRC <> BinHexCRC(WS2, DataOffset, DataEnd)) then
  2057. raise EIpBaseException.Create(SBinHexBadDataCRC);
  2058. { copy data fork to OutStream }
  2059. WS2.Position := DataOffset;
  2060. OutStream.CopyFrom(WS2, DataEnd - DataOffset);
  2061. finally
  2062. WS2.Free;
  2063. end;
  2064. finally
  2065. WS1.Free;
  2066. end;
  2067. end;
  2068. { Decode InStream to OutStream - QuotedPrintable }
  2069. procedure TIpMimeEntity.DecodeQuoted(OutStream : TStream);
  2070. var
  2071. O, Count, WS : Byte; {!!.12}
  2072. I : integer; {!!.12}
  2073. InBuf : array[0..pred (MaxLine)] of Byte; {!!.15}
  2074. OutBuf : array[0..pred (MaxLine)] of Byte; {!!.15}
  2075. Decoding : Boolean;
  2076. Keeper : Boolean;
  2077. Abort : Boolean;
  2078. BufStream : TIpBufferedStream;
  2079. begin
  2080. Abort := False;
  2081. FBody.Position := 0;
  2082. BufStream := FBody as TIpBufferedStream;
  2083. FillChar(InBuf, SizeOf(InBuf), #0);
  2084. WS := $FF;
  2085. Decoding := True;
  2086. Keeper := False;
  2087. { Skip any CR/LF's to get to the encoded stuff }
  2088. while True do begin
  2089. if not BufStream.ReadChar(Char(InBuf[0])) then
  2090. Exit;
  2091. if ((InBuf[0] <> $0D) and (InBuf[0] <> $0A)) then begin
  2092. Keeper := True;
  2093. Break;
  2094. end;
  2095. end;
  2096. while Decoding and not Abort do begin
  2097. { Initialize }
  2098. if Keeper then begin
  2099. I := 1;
  2100. Keeper := False;
  2101. end else begin
  2102. I := 0;
  2103. end;
  2104. O := 0;
  2105. { Read in one line at a time - skipping over bad characters }
  2106. while True do begin
  2107. if (I > High(InBuf)) then {!!.01}
  2108. raise EIpBaseException.Create(SLineLengthErr); {!!.01}
  2109. if not BufStream.ReadChar(Char(InBuf[I])) then
  2110. Break;
  2111. case InBuf[I] of
  2112. $0A : Continue;
  2113. $0D : begin
  2114. Inc(I);
  2115. Break;
  2116. end;
  2117. { Test for potential end of data }
  2118. { '--' is probably the next Mime boundary }
  2119. { $2D : if (I = 1) and (InBuf[0] = $2D) then Exit;} {!!.03}
  2120. end;
  2121. Inc(I);
  2122. end;
  2123. if I = 0 then Exit;
  2124. Count := I;
  2125. I := 0;
  2126. { Decode data to output stream }
  2127. while I < Count do begin
  2128. case InBuf[I] of
  2129. 9 : begin
  2130. if WS = $FF then
  2131. WS := O;
  2132. OutBuf[O] := InBuf[I];
  2133. Inc(O);
  2134. Inc(I);
  2135. end;
  2136. 13 : if WS = $FF then begin
  2137. OutBuf[O] := 13;
  2138. OutBuf[O+1] := 10;
  2139. Inc(O, 2);
  2140. Inc(I);
  2141. end else begin
  2142. OutBuf[WS] := 13;
  2143. OutBuf[WS+1] := 10;
  2144. O := WS+2;
  2145. Inc(I);
  2146. end;
  2147. 32 : begin
  2148. if WS = $FF then
  2149. WS := O;
  2150. OutBuf[O] := InBuf[I];
  2151. Inc(O);
  2152. Inc(I);
  2153. end;
  2154. 33..60 : begin
  2155. WS := $FF;
  2156. OutBuf[O] := InBuf[I];
  2157. Inc(O);
  2158. Inc(I);
  2159. end;
  2160. 61 : begin
  2161. WS := $FF;
  2162. if I+2 >= Count then Break;
  2163. case InBuf[I+1] of
  2164. 48 : OutBuf[O] := 0; {0}
  2165. 49 : OutBuf[O] := 16; {1}
  2166. 50 : OutBuf[O] := 32; {2}
  2167. 51 : OutBuf[O] := 48; {3}
  2168. 52 : OutBuf[O] := 64; {4}
  2169. 53 : OutBuf[O] := 80; {5}
  2170. 54 : OutBuf[O] := 96; {6}
  2171. 55 : OutBuf[O] := 112; {7}
  2172. 56 : OutBuf[O] := 128; {8}
  2173. 57 : OutBuf[O] := 144; {9}
  2174. 65 : OutBuf[O] := 160; {A}
  2175. 66 : OutBuf[O] := 176; {B}
  2176. 67 : OutBuf[O] := 192; {C}
  2177. 68 : OutBuf[O] := 208; {D}
  2178. 69 : OutBuf[O] := 224; {E}
  2179. 70 : OutBuf[O] := 240; {F}
  2180. 97 : OutBuf[O] := 160; {a}
  2181. 98 : OutBuf[O] := 176; {b}
  2182. 99 : OutBuf[O] := 192; {c}
  2183. 100 : OutBuf[O] := 208; {d}
  2184. 101 : OutBuf[O] := 224; {e}
  2185. 102 : OutBuf[O] := 240; {f}
  2186. end;
  2187. case InBuf[I+2] of
  2188. 48 : ; {0}
  2189. 49 : OutBuf[O] := OutBuf[O] + 1; {1}
  2190. 50 : OutBuf[O] := OutBuf[O] + 2; {2}
  2191. 51 : OutBuf[O] := OutBuf[O] + 3; {3}
  2192. 52 : OutBuf[O] := OutBuf[O] + 4; {4}
  2193. 53 : OutBuf[O] := OutBuf[O] + 5; {5}
  2194. 54 : OutBuf[O] := OutBuf[O] + 6; {6}
  2195. 55 : OutBuf[O] := OutBuf[O] + 7; {7}
  2196. 56 : OutBuf[O] := OutBuf[O] + 8; {8}
  2197. 57 : OutBuf[O] := OutBuf[O] + 9; {9}
  2198. 65 : OutBuf[O] := OutBuf[O] + 10; {A}
  2199. 66 : OutBuf[O] := OutBuf[O] + 11; {B}
  2200. 67 : OutBuf[O] := OutBuf[O] + 12; {C}
  2201. 68 : OutBuf[O] := OutBuf[O] + 13; {D}
  2202. 69 : OutBuf[O] := OutBuf[O] + 14; {E}
  2203. 70 : OutBuf[O] := OutBuf[O] + 15; {F}
  2204. 97 : OutBuf[O] := OutBuf[O] + 10; {a}
  2205. 98 : OutBuf[O] := OutBuf[O] + 11; {b}
  2206. 99 : OutBuf[O] := OutBuf[O] + 12; {c}
  2207. 100 : OutBuf[O] := OutBuf[O] + 13; {d}
  2208. 101 : OutBuf[O] := OutBuf[O] + 14; {e}
  2209. 102 : OutBuf[O] := OutBuf[O] + 15; {f}
  2210. end;
  2211. Inc(I, 3);
  2212. Inc(O);
  2213. end;
  2214. 62..126 : begin
  2215. WS := $FF;
  2216. OutBuf[O] := InBuf[I];
  2217. Inc(O);
  2218. Inc(I);
  2219. end;
  2220. else
  2221. Inc(I);
  2222. end;
  2223. end;
  2224. if O>0 then
  2225. OutStream.Write(OutBuf, O)
  2226. else
  2227. Break; { OutBuf is empty }
  2228. DoOnCodingProgress(OutStream.Position, FBody.Size, Abort);
  2229. end;
  2230. end;
  2231. { Decode InStream to OutStream - UUEncode }
  2232. procedure TIpMimeEntity.DecodeUUEncode(OutStream : TStream);
  2233. var
  2234. I, O, Len, Count : Byte;
  2235. InBuf : array[0..85] of Byte;
  2236. OutBuf : array[0..65] of Byte;
  2237. FirstLine : Boolean;
  2238. Abort : Boolean;
  2239. BufStream : TIpBufferedStream;
  2240. begin
  2241. Abort := False;
  2242. FBody.Position := 0;
  2243. BufStream := FBody as TIpBufferedStream;
  2244. FirstLine := True;
  2245. while True and not Abort do begin
  2246. { Initialize }
  2247. I := 0;
  2248. O := 0;
  2249. { Skip any CR/LF's to get to the encoded stuff }
  2250. while True do begin
  2251. if not BufStream.ReadChar(Char(InBuf[0])) then
  2252. Exit;
  2253. if FirstLine then begin
  2254. if ((InBuf[0] <> $0D) and (InBuf[0] <> $0A)) then begin
  2255. FirstLine := False;
  2256. Break;
  2257. end;
  2258. end else begin
  2259. if ((InBuf[0] = $0D) or (InBuf[0] = $0A)) then FirstLine := True;
  2260. end;
  2261. end;
  2262. { We're done }
  2263. if AnsiChar(InBuf[0]) = '`' then Exit;
  2264. { Get count for this line }
  2265. Len := (((InBuf[0] - $20) and $3F) * 4) div 3;
  2266. if (((InBuf[0] - $20) and $3F) * 4) mod 3 <> 0 then
  2267. Inc(Len);
  2268. Count := FBody.Read(InBuf, Len);
  2269. { Unexpected situation }
  2270. if (Count <> Len) or (Count > 63) then
  2271. raise EIpBaseException.Create(SUUEncodeCountErr);
  2272. { Decode buffer }
  2273. while (I < Count) do begin
  2274. if ((Count - I) >= 4) then begin
  2275. OutBuf[O] := (((InBuf[I] - $20) and $3F) shl 2) or
  2276. (((InBuf[I+1] - $20) and $3F) shr 4);
  2277. OutBuf[O+1] := (((InBuf[I+1] - $20) and $3F) shl 4) or
  2278. (((InBuf[I+2] - $20) and $3F) shr 2);
  2279. OutBuf[O+2] := (((InBuf[I+2] - $20) and $3F) shl 6) or
  2280. (((InBuf[I+3] - $20) and $3F));
  2281. Inc(O, 3);
  2282. end else begin
  2283. if (Count >= 2) then begin
  2284. OutBuf[O] := (((InBuf[I] - $20) and $3F) shl 2) or
  2285. (((InBuf[I+1] - $20) and $3F) shr 4);
  2286. Inc(O);
  2287. end;
  2288. if (Count >= 3) then begin
  2289. OutBuf[O+1] := (((InBuf[I+1] - $20) and $3F) shl 4) or
  2290. (((InBuf[I+2] - $20) and $3F) shr 2);
  2291. Inc(O);
  2292. end;
  2293. end;
  2294. Inc(I, 4);
  2295. end;
  2296. OutStream.Write(OutBuf, O);
  2297. DoOnCodingProgress(OutStream.Position, FBody.Size, Abort);
  2298. end;
  2299. end;
  2300. { Encode InStream to OutStream - as is, no encoding }
  2301. procedure TIpMimeEntity.Encode8Bit(InStream : TStream);
  2302. var
  2303. FS : TIpAnsiTextStream;
  2304. Abort : Boolean;
  2305. begin
  2306. Abort := False;
  2307. FS := TIpAnsiTextStream.Create(InStream);
  2308. try
  2309. while not (FS.AtEndOfStream or Abort) do begin
  2310. FBody.WriteLine(FS.ReadLine);
  2311. DoOnCodingProgress(FS.Position, FS.Size, Abort);
  2312. end;
  2313. finally
  2314. FS.Free;
  2315. end;
  2316. end;
  2317. { Encode InStream to OutStream - Base64 }
  2318. procedure TIpMimeEntity.EncodeBase64(InStream : TStream);
  2319. begin
  2320. OctetStreamToHextetStream(InStream, FBody, Ip64Table, '=', #0);
  2321. end;
  2322. { Encode InStream to OutStream - BinHex }
  2323. procedure TIpMimeEntity.EncodeBinHex(InStream : TStream;
  2324. const aFileName : string);
  2325. var
  2326. HeaderFileName : string; {!!.12}
  2327. CRC : Word;
  2328. DataOffset : DWord;
  2329. PrevByte, CurrByte, i : Byte;
  2330. Header : BinHexHeader;
  2331. WS1, WS2 : TMemoryStream;
  2332. Abort : Boolean;
  2333. begin
  2334. Abort := False;
  2335. WS1 := TMemoryStream.Create;
  2336. try
  2337. { start with file name }
  2338. if (Length(aFileName) < MaxLine) then
  2339. HeaderFileName := UpperCase(ExtractFileName(aFileName))
  2340. else
  2341. HeaderFileName := Copy(UpperCase(ExtractFileName(aFileName)), 1, MaxLine);
  2342. WS1.Write(HeaderFileName, Length(HeaderFileName) + 1);
  2343. { build rest of file header and header CRC and add to working stream }
  2344. FillChar(Header, SizeOf(Header), #0);
  2345. Move(BinHexFileType, Header.FileType, SizeOf(Header.FileType));
  2346. Move(BinHexFileType, Header.Creator, SizeOf(Header.Creator));
  2347. Header.DFLong := htonl(InStream.Size);
  2348. Header.RFLong := 0;
  2349. WS1.Write(Header, SizeOf(Header));
  2350. CRC := BinHexCRC(WS1, 0, WS1.Size);
  2351. WS1.Write(CRC, 2);
  2352. { append data fork and data CRC to working stream }
  2353. DataOffset := WS1.Position;
  2354. InStream.Position := 0;
  2355. WS1.CopyFrom(InStream, InStream.Size);
  2356. CRC := BinHexCRC(WS1, DataOffset, WS1.Size);
  2357. WS1.Write(CRC, 2);
  2358. { tack on resource fork CRC - not used but still required }
  2359. CRC := 0;
  2360. WS1.Write(CRC, 2);
  2361. { go back and compress RLE sequences }
  2362. WS2 := TMemoryStream.Create;
  2363. try
  2364. WS1.Position := 0;
  2365. CurrByte := 0;
  2366. while (WS1.Position < WS1.Size) and not Abort do begin
  2367. PrevByte := CurrByte;
  2368. WS1.Read(CurrByte, 1);
  2369. if (CurrByte <> PrevByte) then
  2370. WS2.Write(CurrByte, 1)
  2371. else begin
  2372. i := 1;
  2373. repeat
  2374. i := i + WS1.Read(CurrByte, 1);
  2375. until (CurrByte <> PrevByte) or (i = 255) or
  2376. (WS1.Position = WS1.Size);
  2377. if (i > 2) then begin
  2378. WS2.Write(RLEChar, 1);
  2379. WS2.Write(i, 1);
  2380. WS2.Write(CurrByte, 1);
  2381. end else begin
  2382. WS2.Write(PrevByte, 1);
  2383. WS2.Write(CurrByte, 1);
  2384. end;
  2385. end;
  2386. DoOnCodingProgress(WS1.Position, WS1.Size, Abort);
  2387. end;
  2388. if Abort then
  2389. Exit;
  2390. { write out preamble }
  2391. FBody.WriteLine('(This file must be converted with BinHex 4.0)');
  2392. { Encode compressed stream and stream it out }
  2393. WS2.Position := 0;
  2394. OctetStreamToHextetStream(WS2, FBody, IpBinHexTable, #0, ':');
  2395. finally
  2396. WS2.Free;
  2397. end;
  2398. finally
  2399. WS1.Free;
  2400. end;
  2401. end;
  2402. { Encode InStream to OutStream - QuotedPrintable }
  2403. procedure TIpMimeEntity.EncodeQuoted(InStream : TStream);
  2404. var
  2405. O, W : Integer;
  2406. WordBuf, OutBuf : array[0..80] of AnsiChar;
  2407. CurChar : AnsiChar;
  2408. Abort : Boolean;
  2409. ByteStream : TIpByteStream;
  2410. procedure SendLine;
  2411. begin
  2412. if (OutBuf[O-1] = #9) or (OutBuf[O-1] = #32) then begin
  2413. OutBuf[O] := '=';
  2414. Inc(O);
  2415. end;
  2416. FBody.WriteLineZ(OutBuf);
  2417. FillChar(OutBuf, SizeOf(OutBuf), #0);
  2418. O := 0;
  2419. end;
  2420. procedure AddWordToOutBuf;
  2421. var
  2422. J : Integer;
  2423. begin
  2424. if (O + W) > 74 then SendLine;
  2425. for J := 0 to (W - 1) do begin
  2426. OutBuf[O] := WordBuf[J];
  2427. Inc(O);
  2428. end;
  2429. W := 0;
  2430. end;
  2431. procedure AddHexToWord(B : Byte);
  2432. begin
  2433. if W > 73 then AddWordToOutBuf;
  2434. WordBuf[W] := '=';
  2435. WordBuf[W+1] := HexDigits[B shr 4];
  2436. WordBuf[W+2] := HexDigits[B and $F];
  2437. Inc(W, 3)
  2438. end;
  2439. begin
  2440. Abort := False;
  2441. O := 0;
  2442. W := 0;
  2443. FillChar(OutBuf, SizeOf(OutBuf), #0);
  2444. ByteStream := TIpByteStream.Create(InStream);
  2445. try
  2446. while ByteStream.Read(Byte(CurChar)) and not Abort do begin
  2447. if (Ord(CurChar) in [33..60, 62..126]) then begin
  2448. WordBuf[W] := CurChar;
  2449. Inc(W);
  2450. if W > 74 then AddWordToOutBuf;
  2451. end else if (CurChar = ' ') or (CurChar = #9) then begin
  2452. WordBuf[W] := CurChar;
  2453. Inc(W);
  2454. AddWordToOutBuf;
  2455. end else if (CurChar = #13) then begin
  2456. AddWordToOutBuf;
  2457. SendLine;
  2458. end else if (CurChar = #10) then begin
  2459. { Do nothing }
  2460. end else begin
  2461. AddHexToWord(Byte(CurChar));
  2462. end;
  2463. DoOnCodingProgress(ByteStream.Position, ByteStream.Size, Abort);
  2464. end;
  2465. finally
  2466. ByteStream.Free;
  2467. end;
  2468. end;
  2469. { Encode InStream to OutStream - UUEncode }
  2470. procedure TIpMimeEntity.EncodeUUEncode(InStream : TStream;
  2471. const aFileName : string);
  2472. var
  2473. I, O, Count, Temp : Byte;
  2474. InBuf : array[1..45] of Byte;
  2475. OutBuf : array[0..63] of AnsiChar;
  2476. Abort : Boolean;
  2477. begin
  2478. Abort := False;
  2479. FBody.WriteLine('begin 600 ' + aFileName);
  2480. { Encode and stream the attachment }
  2481. repeat
  2482. Count := InStream.Read(InBuf, SizeOf(InBuf));
  2483. if Count <= 0 then Break;
  2484. I := 1;
  2485. O := 0;
  2486. OutBuf[O] := AnsiChar(IpUUTable[Count and $3F]);
  2487. Inc(O);
  2488. while I+2 <= Count do begin
  2489. { Encode 1st byte }
  2490. Temp := (InBuf[I] shr 2);
  2491. OutBuf[O] := AnsiChar(IpUUTable[Temp and $3F]);
  2492. { Encode 1st/2nd byte }
  2493. Temp := (InBuf[I] shl 4) or (InBuf[I+1] shr 4);
  2494. OutBuf[O+1] := AnsiChar(IpUUTable[Temp and $3F]);
  2495. { Encode 2nd/3rd byte }
  2496. Temp := (InBuf[I+1] shl 2) or (InBuf[I+2] shr 6);
  2497. OutBuf[O+2] := AnsiChar(IpUUTable[Temp and $3F]);
  2498. { Encode 3rd byte }
  2499. Temp := (InBuf[I+2] and $3F);
  2500. OutBuf[O+3] := AnsiChar(IpUUTable[Temp]);
  2501. Inc(I, 3);
  2502. Inc(O, 4);
  2503. end;
  2504. { Are there odd bytes to add? }
  2505. if (I <= Count) then begin
  2506. Temp := (InBuf[I] shr 2);
  2507. OutBuf[O] := AnsiChar(IpUUTable[Temp and $3F]);
  2508. { One odd byte }
  2509. if (I = Count) then begin
  2510. Temp := (InBuf[I] shl 4) and $30;
  2511. OutBuf[O+1] := AnsiChar(IpUUTable[Temp and $3F]);
  2512. Inc(O, 2);
  2513. { Two odd bytes }
  2514. end else begin
  2515. Temp := ((InBuf[I] shl 4) and $30) or ((InBuf[I+1] shr 4) and $0F);
  2516. OutBuf[O+1] := AnsiChar(IpUUTable[Temp and $3F]);
  2517. Temp := (InBuf[I+1] shl 2) and $3C;
  2518. OutBuf[O+2] := AnsiChar(IpUUTable[Temp and $3F]);
  2519. Inc(O, 3);
  2520. end;
  2521. end;
  2522. { Add CR/LF }
  2523. OutBuf[O] := #13;
  2524. OutBuf[O+1] := #10;
  2525. { Write line to stream }
  2526. FBody.Write(OutBuf, (O + 2));
  2527. DoOnCodingProgress(InStream.Position, InStream.Size, Abort);
  2528. until (Count < SizeOf(InBuf)) or Abort;
  2529. { Add terminating end }
  2530. FBody.WriteLine('`');
  2531. FBody.WriteLine('end');
  2532. end;
  2533. { Translate each 3 bytes into 4 hextets and encode according to table }
  2534. procedure TIpMimeEntity.OctetStreamToHextetStream(InStream : TStream;
  2535. OutStream : TIpAnsiTextStream;
  2536. const Table;
  2537. PadChar, Delim : AnsiChar);
  2538. var
  2539. OutBuf: array[0..MaxLineEncode-1] of Char; {!!.12}{!!.13}
  2540. OutBufLen: Integer; {!!.12}
  2541. Abort : Boolean;
  2542. procedure FlushOutBuf;
  2543. {- write out encoded buffer to message stream }
  2544. begin
  2545. if OutBufLen > 0 then begin {!!.12}
  2546. OutStream.WriteLineArray(OutBuf, OutBufLen);
  2547. OutBufLen := 0; {!!.12}
  2548. end;
  2549. end;
  2550. procedure OutChar(ch : AnsiChar);
  2551. {- buffer the character to go out }
  2552. begin
  2553. if OutBufLen >= MaxLineEncode - 1 then {!!.12}{!!.13}
  2554. FlushOutBuf;
  2555. OutBuf[OutBufLen] := Ch; {!!.12}
  2556. inc(OutBufLen); {!!.12}
  2557. end;
  2558. type
  2559. TBuffer = array[0..MaxInt-1] of Byte;
  2560. var
  2561. Buffer: ^TBuffer;
  2562. I, Count: Cardinal;
  2563. begin
  2564. if InStream is TMemoryStream then
  2565. Buffer := (InStream as TMemoryStream).Memory
  2566. else
  2567. if InStream is TIpMemMapStream then
  2568. Buffer := (InStream as TIpMemMapStream).Memory
  2569. else
  2570. raise EIpBaseException.Create(SNoMemoryStreamErr);
  2571. Abort := False;
  2572. OutBufLen := 0; {!!.12}
  2573. if (Delim <> #0) then
  2574. OutChar(Delim);
  2575. { Encode and stream the attachment }
  2576. I := 0;
  2577. Count := InStream.Size div 3 * 3;
  2578. while I < Count do
  2579. begin
  2580. { Encode 1st byte }
  2581. OutBuf[OutBufLen] := Char(TIp6BitTable(Table)[Buffer[I] shr 2]);
  2582. { Encode 1st/2nd byte }
  2583. OutBuf[OutBufLen+1] := Char(TIp6BitTable(Table)[((Buffer[I] shl 4) or (Buffer[I+1] shr 4)) and $3F]);
  2584. { Encode 2nd/3rd byte }
  2585. OutBuf[OutBufLen+2] := Char(TIp6BitTable(Table)[((Buffer[I+1] shl 2) or (Buffer[I+2] shr 6)) and $3F]);
  2586. { Encode 3rd byte }
  2587. OutBuf[OutBufLen+3] := Char(TIp6BitTable(Table)[Buffer[I+2] and $3F]);
  2588. Inc(OutBufLen, 4);
  2589. if OutBufLen >= MaxLineEncode - 1 then {!!.12}{!!.13}
  2590. begin
  2591. FlushOutBuf;
  2592. if i mod 100 = 0 then
  2593. DoOnCodingProgress(I, Count, Abort);
  2594. if Abort then
  2595. break;
  2596. end;
  2597. Inc(I, 3);
  2598. end;
  2599. Count := InStream.Size;
  2600. { Are there odd bytes to add? }
  2601. if (I < Count) then begin
  2602. OutChar(TIp6BitTable(Table)[Buffer[I] shr 2]);
  2603. { One odd byte }
  2604. if I = Count-1 then begin
  2605. OutChar(TIp6BitTable(Table)[(Buffer[I] shl 4) and $30]);
  2606. if (PadChar <> #0) then
  2607. OutChar(PadChar);
  2608. { Two odd bytes }
  2609. end else begin
  2610. OutChar(TIp6BitTable(Table)[((Buffer[I] shl 4) and $30) or (((Buffer[I+1] shr 4) and $0F)) and $3F]);
  2611. OutChar(TIp6BitTable(Table)[(Buffer[I+1] shl 2) and $3C]);
  2612. end;
  2613. { Add padding }
  2614. if (PadChar <> #0) then
  2615. OutChar(PadChar);
  2616. end;
  2617. if (Delim <> #0) then
  2618. OutChar(Delim);
  2619. FlushOutBuf;
  2620. end;
  2621. {Begin !!.12}
  2622. procedure TIpMIMEEntity.ReadBody(InStream : TIpAnsiTextStream; const StartLine : string);
  2623. var
  2624. S : string;
  2625. begin
  2626. S := StartLine;
  2627. { read in message body up to message terminator '.' }
  2628. {while not ((S = '.') or AtEndOfStream) do begin}
  2629. while not InStream.AtEndOfStream do begin
  2630. Body.WriteLine(S);
  2631. S := InStream.ReadLine;
  2632. end;
  2633. { write final line }
  2634. Body.WriteLine(S);
  2635. end;
  2636. {End !!.12}
  2637. { TIpMessage }
  2638. constructor TIpMessage.CreateMessage;
  2639. begin
  2640. inherited Create(nil);
  2641. FBCC := TStringList.Create;
  2642. FCC := TStringList.Create;
  2643. FNewsgroups := TStringList.Create;
  2644. FPath := TStringList.Create;
  2645. FReceived := TStringList.Create;
  2646. FRecipients := TStringList.Create;
  2647. FReferences := TStringList.Create;
  2648. FUserFields := TStringList.Create;
  2649. FHeaders := TIpHeaderCollection.Create (Self); {!!.12}
  2650. MsgStream := TIpAnsiTextStream.CreateEmpty;
  2651. NewMessageStream;
  2652. end;
  2653. destructor TIpMessage.Destroy;
  2654. begin
  2655. Clear;
  2656. FBCC.Free;
  2657. FCC.Free;
  2658. FNewsgroups.Free;
  2659. FPath.Free;
  2660. FReceived.Free;
  2661. FRecipients.Free;
  2662. FReferences.Free;
  2663. FUserFields.Free;
  2664. FHeaders.Free; {!!.12}
  2665. MsgStream.FreeStream;
  2666. MsgStream.Free;
  2667. inherited Destroy;
  2668. end;
  2669. {Begin !!.13}
  2670. procedure TIpMessage.CheckAllHeaders;
  2671. var
  2672. i : Integer;
  2673. j : Integer;
  2674. HeaderNum : Integer;
  2675. begin
  2676. FAttachmentCount := 0;
  2677. { Roll through the list of headers specifically handled by iPRO.
  2678. When one is found, move it into the data structure specific to that
  2679. header field. }
  2680. for i := 0 to IpMaxHeaders - 1 do begin
  2681. if (IpHeaderXRef[i].FieldType = htUserFields) or
  2682. (IpHeaderXRef[i].FieldType = htReceived) then begin
  2683. for j := 0 to Headers.Count - 1 do begin
  2684. if StrLIComp (PChar (IpHeaderXRef[i].FieldString),
  2685. PChar (Headers.Items[j].Name),
  2686. Length (IpHeaderXRef[i].FieldString)) = 0 then
  2687. CheckHeaderType (Headers.Items[j],
  2688. IpHeaderXRef[i].FieldType);
  2689. end;
  2690. end else begin
  2691. HeaderNum := Headers.HasHeader (IpHeaderXRef[i].FieldString);
  2692. if HeaderNum >= 0 then
  2693. CheckHeaderType (Headers.Items[HeaderNum],
  2694. IpHeaderXRef[i].FieldType);
  2695. end;
  2696. end;
  2697. end;
  2698. procedure TIpMessage.CheckHeaderType (HeaderInfo : TIpHeaderItem;
  2699. HeaderType : TIpHeaderTypes);
  2700. function ExtractSingleHeader(HeaderInfo : TIpHeaderItem) : string;
  2701. begin
  2702. Result := Trim(HeaderInfo.Value.Text);
  2703. HeaderInfo.IsProperty := True; {!!.13}
  2704. end;
  2705. procedure ExtractCSVHeader(HeaderInfo : TIpHeaderItem;
  2706. var AList : TStringList);
  2707. var
  2708. WorkString : string;
  2709. begin
  2710. WorkString := ExtractSingleHeader(HeaderInfo);
  2711. Parse (WorkString, ',', AList);
  2712. HeaderInfo.IsProperty := True; {!!.13}
  2713. end;
  2714. procedure ExtractListHeader(HeaderInfo : TIpHeaderItem;
  2715. var AList : TStringList);
  2716. begin
  2717. AList.Assign (HeaderInfo.Value);
  2718. HeaderInfo.IsProperty := True; {!!.13}
  2719. end;
  2720. procedure ExtractAppendListHeader(HeaderInfo : TIpHeaderItem;
  2721. const IncludeName : Boolean; {!!.13}
  2722. var AList : TStringList);
  2723. var
  2724. i : Integer;
  2725. begin
  2726. for i := 0 to HeaderInfo.Value.Count - 1 do
  2727. {Begin !!.13}
  2728. if IncludeName then
  2729. AList.Add (HeaderInfo.Name + ': ' + HeaderInfo.Value[i])
  2730. else
  2731. AList.Add (HeaderInfo.Value[i]);
  2732. HeaderInfo.IsProperty := True;
  2733. {End !!.13}
  2734. end;
  2735. begin
  2736. case HeaderType of
  2737. htBCC :
  2738. ExtractCSVHeader(HeaderInfo, FBCC);
  2739. htCC :
  2740. ExtractCSVHeader(HeaderInfo, FCC);
  2741. htControl :
  2742. FControl := ExtractSingleHeader(HeaderInfo);
  2743. htDate :
  2744. FDate := ExtractSingleHeader(HeaderInfo);
  2745. htDispositionNotify :
  2746. FDispositionNotify := ExtractSingleHeader(HeaderInfo);
  2747. htFrom :
  2748. FFrom := ExtractSingleHeader(HeaderInfo);
  2749. htFollowUp :
  2750. FFollowUpTo := ExtractSingleHeader(HeaderInfo);
  2751. htInReplyTo :
  2752. FInReplyTo := ExtractSingleHeader(HeaderInfo);
  2753. htKeywords :
  2754. FKeywords := ExtractSingleHeader(HeaderInfo);
  2755. htMessageID :
  2756. FMessageID := ExtractSingleHeader(HeaderInfo);
  2757. htNewsgroups :
  2758. ExtractCSVHeader(HeaderInfo, FNewsgroups);
  2759. htNNTPPostingHost :
  2760. FNNTPPostingHost := ExtractSingleHeader(HeaderInfo);
  2761. htOrganization :
  2762. FOrganization := ExtractSingleHeader(HeaderInfo);
  2763. htPath :
  2764. ExtractListHeader(HeaderInfo, FPath);
  2765. htPostingHost :
  2766. FPostingHost := ExtractSingleHeader(HeaderInfo);
  2767. htReceived :
  2768. ExtractAppendListHeader(HeaderInfo, False, FReceived); {!!.13}
  2769. htReferences :
  2770. ExtractListHeader(HeaderInfo, FReferences);
  2771. htReplyTo :
  2772. FReplyTo := ExtractSingleHeader(HeaderInfo);
  2773. htReturnPath :
  2774. FReturnPath := ExtractSingleHeader(HeaderInfo);
  2775. htSender :
  2776. FSender := ExtractSingleHeader(HeaderInfo);
  2777. htSubject :
  2778. FSubject := ExtractSingleHeader(HeaderInfo);
  2779. htTo :
  2780. ExtractCSVHeader(HeaderInfo, FRecipients);
  2781. htUserFields :
  2782. ExtractAppendListHeader(HeaderInfo, True, FUserFields); {!!.13}
  2783. htXIpro : begin
  2784. end;
  2785. end;
  2786. end;
  2787. {End !!.12}
  2788. { Clear properties and free message stream }
  2789. procedure TIpMessage.Clear;
  2790. begin
  2791. inherited Clear;
  2792. FAttachmentCount := 0; {!!.12}
  2793. FMessageTag := 0; {!!.15}
  2794. FBCC.Clear;
  2795. FCC.Clear;
  2796. FDate := '';
  2797. FDispositionNotify := ''; {!!.12}
  2798. FFrom := '';
  2799. FInReplyTo := '';
  2800. FKeywords := '';
  2801. FFollowupTo := ''; {!!.15}
  2802. FControl := ''; {!!.15}
  2803. FMessageID := '';
  2804. FNewsgroups.Clear;
  2805. FNNTPPostingHost := '';
  2806. FOrganization := '';
  2807. FPath.Clear;
  2808. FPostingHost := '';
  2809. FReceived.Clear;
  2810. FRecipients.Clear;
  2811. FReferences.Clear;
  2812. FReplyTo := '';
  2813. FReturnPath := '';
  2814. FSender := '';
  2815. FSubject := '';
  2816. FUserFields.Clear;
  2817. FHeaders.Clear; {!!.15}
  2818. MsgStream.FreeStream;
  2819. end;
  2820. {Begin !!.12}
  2821. { Get headers, body, and MIME parts (if any) }
  2822. procedure TIpMessage.DecodeMessage;
  2823. var
  2824. AttDepth : Integer;
  2825. function IsAttachmentStart (const s : string) : Boolean;
  2826. type
  2827. TAttState = (asBegin, asHaveBegin,
  2828. asNumber1, asNumberSp,
  2829. asOpenCurly, asNumber2, asNumber2Sp, asCloseCurly,
  2830. asQuote1, asDblQuote1, AsAlnum1);
  2831. var
  2832. State : TAttState;
  2833. i : Integer;
  2834. SLen : Integer;
  2835. begin
  2836. Result := False;
  2837. State := asBegin;
  2838. i := 1;
  2839. SLen := Length (s);
  2840. while i < SLen do begin
  2841. case State of
  2842. asBegin : begin
  2843. if s[i] in [' ', #09] then
  2844. Inc (i)
  2845. else if LowerCase (Copy (s, i, 5)) = 'begin' then begin
  2846. State := asHaveBegin;
  2847. Inc (i, 5);
  2848. end else
  2849. Break;
  2850. end;
  2851. asHaveBegin : begin
  2852. if s[i] in [' ', #09] then
  2853. Inc (i)
  2854. else if s[i] = '{' then begin
  2855. Inc (i);
  2856. State := asNumber2;
  2857. end else if s[i] in ['0'..'9'] then begin
  2858. Inc (i);
  2859. State := asNumber1;
  2860. end else
  2861. Break;
  2862. end;
  2863. asNumber1 : begin
  2864. if s[i] in ['0'..'9'] then
  2865. Inc (i)
  2866. else if s[i] in [' ', #09] then begin
  2867. Inc (i);
  2868. State := asNumberSp;
  2869. end else
  2870. Break;
  2871. end;
  2872. asNumberSp : begin
  2873. if s[i] in [' ', #09] then
  2874. Inc (i)
  2875. else if s[i] = '"' then begin
  2876. Inc (i);
  2877. State := asDblQuote1;
  2878. end else if s[i] = '''' then begin
  2879. Inc (i);
  2880. State := asQuote1;
  2881. end else if s[i] in ['!'..'~'] then begin
  2882. Inc (i);
  2883. State := asAlNum1;
  2884. end else
  2885. Break;
  2886. end;
  2887. asOpenCurly : begin
  2888. if s[i] in [' ', #09] then
  2889. Inc (i)
  2890. else if s[i] in ['0'..'9'] then begin
  2891. Inc (i);
  2892. State := asNumber2;
  2893. end else
  2894. Break;
  2895. end;
  2896. asNumber2 : begin
  2897. if s[i] in ['0'..'9'] then
  2898. Inc (i)
  2899. else if s[i] in [' ', #09] then begin
  2900. Inc (i);
  2901. State := asNumber2Sp;
  2902. end else if s[i] = '}' then begin
  2903. State := asCloseCurly;
  2904. Inc (i);
  2905. end else
  2906. Break;
  2907. end;
  2908. asNumber2Sp : begin
  2909. if s[i] in [' ', #09] then
  2910. Inc (i)
  2911. else if s[i] = '}' then begin
  2912. Inc (i);
  2913. State := asCloseCurly;
  2914. end else
  2915. Break;
  2916. end;
  2917. asCloseCurly : begin
  2918. if s[i] in [' ', #09] then
  2919. Inc (i)
  2920. else if s[i] = '"' then begin
  2921. Inc (i);
  2922. State := asDblQuote1;
  2923. end else if s[i] = '''' then begin
  2924. Inc (i);
  2925. State := asQuote1;
  2926. end else
  2927. Break;
  2928. end;
  2929. asQuote1 : begin
  2930. if s[i] in [' '..'&', '('..'~'] then
  2931. Inc (i)
  2932. else if s[i] = '''' then begin
  2933. Result := True;
  2934. Break;
  2935. end else
  2936. Break;
  2937. end;
  2938. asDblQuote1 : begin
  2939. if s[i] in [' '..'!', '#'..'~'] then
  2940. Inc (i)
  2941. else if s[i] = '"' then begin
  2942. Result := True;
  2943. Break;
  2944. end else
  2945. Break;
  2946. end;
  2947. AsAlnum1 : begin
  2948. if s[i] in ['!'..'~'] then begin
  2949. Result := True;
  2950. Break;
  2951. end else
  2952. Break;
  2953. end;
  2954. end;
  2955. end;
  2956. end;
  2957. function IsAttachmentEnd (const s : string) : Boolean;
  2958. begin
  2959. if LowerCase (Copy (s, 1, 3)) = 'end' then
  2960. Result := True
  2961. else
  2962. Result := False;
  2963. end;
  2964. procedure CheckForAttachment (const s : string);
  2965. begin
  2966. if IsAttachmentStart (s) then begin
  2967. if AttDepth = 0 then
  2968. Inc (FAttachmentCount);
  2969. Inc (AttDepth);
  2970. end else if (IsAttachmentEnd (s)) and
  2971. (FAttachmentCount > 0) then
  2972. Dec (AttDepth);
  2973. end;
  2974. {End !!.12}
  2975. var
  2976. RawHeaders : TStringList;
  2977. S : string;
  2978. i, j : Integer; {!!.13}
  2979. begin
  2980. { get message headers}
  2981. Position := 0;
  2982. RawHeaders := TStringList.Create;
  2983. try
  2984. S := ReadLine;
  2985. repeat
  2986. if S <> '' then {!!.15}
  2987. RawHeaders.Add(S);
  2988. S := ReadLine;
  2989. until (S = '');
  2990. FHeaders.Clear; {!!.12}
  2991. FHeaders.LoadHeaders (RawHeaders, False); {!!.12}
  2992. CheckAllHeaders; {!!.12}
  2993. { decode MIME headers }
  2994. DecodeMimeHeaders(RawHeaders);
  2995. {Begin !!.13}
  2996. { If this is a MIME message, mark the MIME headers as being exposed via an
  2997. iPRO property. }
  2998. if FIsMime then
  2999. for i := Low(IpMimeHeaders) to High(IpMimeHeaders) do begin
  3000. j := FHeaders.HasHeader(IpMimeHeaders[i]);
  3001. if j > -1 then
  3002. FHeaders.Items[j].IsProperty := True;
  3003. end;
  3004. {End !!.13}
  3005. finally
  3006. RawHeaders.Free;
  3007. end;
  3008. { if message is mime, then decode mime parts }
  3009. if IsMime then begin {!!.01}
  3010. if (FContentDispositionType = strAttachment) then begin {!!.12}
  3011. Inc (FParent.FAttachmentCount); {!!.12}{!!.15}
  3012. DecodeEntityAsAttachment(MsgStream) {!!.01}
  3013. end else {!!.12}
  3014. DecodeEntity(MsgStream);
  3015. end else begin
  3016. { otherwise, just read in the message body. }
  3017. repeat { skip over blank lines between headers and body }
  3018. S := ReadLine;
  3019. until (S <> '') or AtEndOfStream;
  3020. { read in message body up to message terminator '.' }
  3021. {while not ((S = '.') or AtEndOfStream) do begin} {!!.10}
  3022. while not AtEndOfStream do begin {!!.10}
  3023. Body.WriteLine(S);
  3024. AttDepth := 0; {!!.12}
  3025. CheckForAttachment (S); {!!.12}
  3026. S := ReadLine;
  3027. end;
  3028. { write final line } {!!.10}
  3029. if S <> '' then {!!.13}
  3030. Body.WriteLine(S); {!!.10}
  3031. {Begin !!.12}
  3032. { Read the message body. }
  3033. {ReadBody(MsgStream, S); }
  3034. {End !!.12}
  3035. end;
  3036. Body.Position := 0;
  3037. end;
  3038. { Build message stream with headers, body, and MIME parts (if any) }
  3039. procedure TIpMessage.EncodeMessage;
  3040. var
  3041. i : Integer;
  3042. Size : Longint; {!!.12}
  3043. FileName : string; {!!.12}
  3044. Strm : TIpMemMapStream; {!!.12}
  3045. RawHeaders : TStringList;
  3046. begin
  3047. NewMessageStream;
  3048. {Begin !!.12}
  3049. { If we have some very large attachments then we need to use a memory mapped
  3050. file stream instead of TMemory, in order to improve performance. }
  3051. Size := 0;
  3052. for i := 0 to Pred(FMimeParts.Count) do
  3053. inc(Size, FMimeParts[i].FOriginalSize);
  3054. if Size > IpLgAttachSizeBoundry then begin
  3055. MsgStream.FreeStream;
  3056. FileName := GetTemporaryFile(GetTemporaryPath);
  3057. if FileExistsUTF8(FileName) then
  3058. DeleteFileUTF8(FileName);
  3059. Strm := TIpMemMapStream.Create(FileName, False, True);
  3060. Strm.Size := Trunc(Size * 1.5);
  3061. Strm.Open;
  3062. MsgStream.Stream := Strm;
  3063. end;
  3064. {End !!.12}
  3065. if (FContentType <> '') then begin
  3066. FIsMime := True;
  3067. FMimeVersion := '1.0';
  3068. end;
  3069. RawHeaders := TStringList.Create;
  3070. try
  3071. EncodeSingleHeader(strReturnPath, RawHeaders, FReturnPath);
  3072. EncodeMultiHeader(strReceived, RawHeaders, FReceived, #09, True);
  3073. EncodeListHeader(strPath, RawHeaders, FPath, ',', True);
  3074. EncodeListHeader(strNewsgroups, RawHeaders, FNewsgroups, ',', False); {!!.14}
  3075. EncodeSingleHeader(strMessageID, RawHeaders, FMessageID);
  3076. EncodeSingleHeader (strDispositionNotify, RawHeaders, {!!.12}
  3077. FDispositionNotify); {!!.12}
  3078. EncodeSingleHeader(strReplyTo, RawHeaders, FReplyTo);
  3079. EncodeSingleHeader(strFrom, RawHeaders, FFrom);
  3080. EncodeListHeader(strTo, RawHeaders, FRecipients, ',', True);
  3081. EncodeSingleHeader(strSubject, RawHeaders, FSubject);
  3082. EncodeSingleHeader(strDate, RawHeaders, FDate);
  3083. EncodeSingleHeader(strOrganization, RawHeaders, FOrganization);
  3084. EncodeListHeader(strCC, RawHeaders, FCC, ',', False);
  3085. EncodeListHeader(strBCC, RawHeaders, FBCC, ',', False);
  3086. EncodeSingleHeader(strInReplyTo, RawHeaders, FInReplyTo);
  3087. EncodeListHeader(strReferences, RawHeaders, FReferences, '', False);
  3088. EncodeSingleHeader(strSender, RawHeaders, FSender);
  3089. EncodeSingleHeader(strKeywords, RawHeaders, FKeywords);
  3090. EncodeMultiHeader('', RawHeaders, FUserFields, Char(0), False);
  3091. EncodeSingleHeader(strControl, RawHeaders, FControl); {!!.12}
  3092. EncodeSingleHeader(strFollowUp, RawHeaders, FFollowupTo); {!!.12}
  3093. {Begin !!.13}
  3094. for i := 0 to Pred(Headers.Count) do
  3095. { Write the header out only if it is not a header exposed via an iPRO
  3096. property. }
  3097. if (not Headers.Items[i].IsProperty) then begin
  3098. if Headers.Items[i].Value.Count = 1 then
  3099. EncodeSingleHeader(Headers.Items[i].Name + ': ', RawHeaders,
  3100. Headers.Items[i].Value[0])
  3101. else
  3102. EncodeMultiheader(Headers.Items[i].Name + ': ', RawHeaders,
  3103. Headers.Items[i].Value, #09, True);
  3104. end;
  3105. {End !!.13}
  3106. if IsMime then
  3107. EncodeMimeHeaders(RawHeaders);
  3108. if (RawHeaders.Count = 0) then
  3109. Exit;
  3110. for i := 0 to Pred(RawHeaders.Count) do
  3111. WriteLine(RawHeaders[i]);
  3112. finally
  3113. RawHeaders.Free;
  3114. end;
  3115. {Begin !!.13}
  3116. WriteLine('');
  3117. if IsMime then
  3118. EncodeEntity(MsgStream)
  3119. else if (FBody.Size > 0) then begin
  3120. FBody.Position := 0;
  3121. repeat
  3122. WriteLine(Body.ReadLine);
  3123. until FBody.AtEndOfStream;
  3124. end; { if }
  3125. {End !!.13}
  3126. end;
  3127. { Load message from file stream and decode }
  3128. procedure TIpMessage.LoadFromFile(const aFileName : string);
  3129. {Begin !!.12}
  3130. var
  3131. SourceStream : TIpMemMapStream;
  3132. {End !!.12}
  3133. begin
  3134. Clear;
  3135. NewMessageStream; {!!.03}
  3136. {Begin !!.12}
  3137. SourceStream := TIpMemMapStream.Create(aFileName, True, False);
  3138. try
  3139. SourceStream.Open;
  3140. {Begin !!.15}
  3141. if SourceStream.Size > IpLgAttachSizeBoundry then begin
  3142. MsgStream.FreeStream;
  3143. MsgStream.Stream := SourceStream;
  3144. end
  3145. else
  3146. MsgStream.CopyFrom(SourceStream, 0);
  3147. finally
  3148. if MsgStream.Stream <> SourceStream then
  3149. SourceStream.Free;
  3150. {End !!.15}
  3151. end;
  3152. {End !!.12}
  3153. try {!!.03}
  3154. DecodeMessage;
  3155. except {!!.03}
  3156. { just eat the exception, the messge might be corrupt, but the }
  3157. { raw text (MessageStream property) will still be available }
  3158. end; {!!.03}
  3159. end;
  3160. {Begin !!.12}
  3161. procedure TIpMessage.LoadFromStream(aStream : TStream);
  3162. var
  3163. FileName : string;
  3164. Strm : TIpMemMapStream;
  3165. begin
  3166. Clear;
  3167. NewMessageStream;
  3168. if aStream.Size > IpLgAttachSizeBoundry then begin
  3169. MsgStream.FreeStream;
  3170. FileName := GetTemporaryFile(GetTemporaryPath);
  3171. if FileExistsUTF8(FileName) then
  3172. DeleteFileUTF8(FileName);
  3173. Strm := TIpMemMapStream.Create(FileName, False, True);
  3174. Strm.Size := aStream.Size;
  3175. Strm.Open;
  3176. MsgStream.Stream := Strm;
  3177. end;
  3178. MsgStream.CopyFrom(aStream, 0);
  3179. try
  3180. DecodeMessage;
  3181. except
  3182. { just eat the exception, the messge might be corrupt, but the }
  3183. { raw text (MessageStream property) will still be available }
  3184. end;
  3185. end;
  3186. { Create new message stream but retain existing decoded message }
  3187. procedure TIpMessage.NewMessageStream;
  3188. begin
  3189. MsgStream.FreeStream;
  3190. MsgStream.Stream := TMemoryStream.Create;
  3191. MsgStream.bsInitForNewStream; {!!.02}
  3192. end;
  3193. { Clear all and create new empty message stream }
  3194. procedure TIpMessage.NewMessage;
  3195. begin
  3196. Clear;
  3197. NewMessageStream;
  3198. end;
  3199. { Position property read access method }
  3200. function TIpMessage.GetPosition : Longint;
  3201. begin
  3202. if Assigned(MsgStream) then
  3203. Result := MsgStream.Position
  3204. else
  3205. Result := 0;
  3206. end;
  3207. { Size property read access method }
  3208. function TIpMessage.GetSize : Longint;
  3209. begin
  3210. if Assigned(MsgStream) then
  3211. Result := MsgStream.Size
  3212. else
  3213. Result := 0;
  3214. end;
  3215. { Return next line from the message stream (CRLF stripped) }
  3216. function TIpMessage.ReadLine : string;
  3217. begin
  3218. if Assigned(MsgStream) then
  3219. Result := MsgStream.ReadLine
  3220. else
  3221. Result := '';
  3222. end;
  3223. { Return next line from the message stream (CRLF retained) }
  3224. function TIpMessage.ReadLineCRLF : string;
  3225. begin
  3226. if Assigned(MsgStream) then
  3227. Result := MsgStream.ReadLine + CRLF
  3228. else
  3229. Result := '';
  3230. end;
  3231. {- Save raw message stream to file }
  3232. procedure TIpMessage.SaveToFile(const aFileName : string);
  3233. var
  3234. FS : TFileStream;
  3235. begin
  3236. EncodeMessage;
  3237. Position := 0;
  3238. FS := TFileStreamUTF8.Create(aFileName, fmCreate);
  3239. try
  3240. FS.CopyFrom(MsgStream, MsgStream.Size);
  3241. finally
  3242. FS.Free;
  3243. end;
  3244. end;
  3245. {Begin !!.12}
  3246. {- Save raw message stream }
  3247. procedure TIpMessage.SaveToStream(Stream: TStream);
  3248. begin
  3249. Position := 0;
  3250. Stream.CopyFrom(MsgStream, MsgStream.Size);
  3251. end;
  3252. procedure TIpMessage.SetHeaders(Headers : TIpHeaderCollection);
  3253. begin
  3254. FHeaders.Assign(Headers);
  3255. end;
  3256. {End !!.12}
  3257. { Position property write access method }
  3258. procedure TIpMessage.SetPosition(Value : Longint);
  3259. begin
  3260. if Assigned(MsgStream) then
  3261. MsgStream.Position := Value;
  3262. end;
  3263. { Write string onto the message stream and append CRLF terminator }
  3264. procedure TIpMessage.WriteLine(const aSt : string);
  3265. begin
  3266. if Assigned(MsgStream) then
  3267. MsgStream.WriteLine(aSt);
  3268. end;
  3269. { Indicates whether or not we're at the end of the message stream }
  3270. function TIpMessage.AtEndOfStream : Boolean;
  3271. begin
  3272. if Assigned(MsgStream) then
  3273. Result := MsgStream.AtEndOfStream
  3274. else
  3275. Result := True;
  3276. end;
  3277. { Return 'alternative' text/plain mime part }
  3278. function TIpMessage.GetBodyPlain(CanCreate : Boolean) : TIpMimeEntity;
  3279. var
  3280. aParent : TIpMimeEntity;
  3281. begin
  3282. aParent := FindNestedMimePart(strMultipart, strAlternative, ''); {!!.02}
  3283. if not Assigned(aParent) then
  3284. aParent := Self;
  3285. {Begin !!.15}
  3286. Result := aParent.FindNestedMimePart(strText, strPlain, '');
  3287. if (Result = nil) and CanCreate then begin
  3288. Result := NewMimePart;
  3289. Result.ContentType := strText;
  3290. Result.ContentSubtype := strPlain;
  3291. end;
  3292. {End !!.15}
  3293. end;
  3294. { Return 'alternative' text/html mime part }
  3295. function TIpMessage.GetBodyHtml(CanCreate : Boolean) : TIpMimeEntity;
  3296. var
  3297. aParent : TIpMimeEntity;
  3298. begin
  3299. aParent := FindNestedMimePart(strMultipart, strAlternative, ''); {!!.02}
  3300. if not Assigned(aParent) then
  3301. aParent := Self;
  3302. {Begin !!.15}
  3303. Result := aParent.FindNestedMimePart(strText, strHtml, '');
  3304. if (Result = nil) and CanCreate then begin
  3305. Result := NewMimePart;
  3306. Result.ContentType := strText;
  3307. Result.ContentSubtype := strHTML;
  3308. end;
  3309. {End !!.15}
  3310. end;
  3311. { Add a file attachment using default types }
  3312. procedure TIpMessage.AddDefaultAttachment(const aFileName: string); {!!.02}
  3313. begin
  3314. with NewMimePart do begin
  3315. EntityName := ExtractFileName(aFileName);
  3316. ContentDispositionType := 'attachment';
  3317. EncodeBodyFile(aFileName);
  3318. end;
  3319. end;
  3320. procedure TIpMessage.AddDefaultAttachmentAs (const aFileName : string; {!!.12}
  3321. const AttachmentName : string); {!!.12}
  3322. begin {!!.12}
  3323. with NewMimePart do begin {!!.12}
  3324. EntityName := ExtractFileName (AttachmentName); {!!.12}
  3325. ContentDispositionType := 'attachment'; {!!.12}
  3326. EncodeBodyFile (aFileName); {!!.12}
  3327. end; {!!.12}
  3328. end; {!!.12}
  3329. { Set message properties from another TIpMessage }
  3330. procedure TIpMessage.Assign(Source: TPersistent);
  3331. var
  3332. SourcePos : Integer;
  3333. SourceMsg : TIpMessage;
  3334. begin
  3335. if Source is TIpMessage then begin
  3336. SourceMsg := TIpMessage(Source);
  3337. { clear our streams and properties }
  3338. NewMessage;
  3339. { ensure we are at the beginning of our streams }
  3340. Position := 0;
  3341. SourcePos := SourceMsg.Position;
  3342. SourceMsg.Position := 0;
  3343. MsgStream.CopyFrom(SourceMsg.MsgStream, 0);
  3344. Position := 0;
  3345. SourceMsg.Position := SourcePos;
  3346. try {!!.03}
  3347. DecodeMessage;
  3348. except {!!.03}
  3349. { just eat the exception, the messge might be corrupt, but the }
  3350. { raw text (MessageStream property) will still be available }
  3351. end; {!!.03}
  3352. end else
  3353. inherited Assign(Source);
  3354. end;
  3355. procedure TIpMessage.SetBCC(const Value: TStringList); {!!.01}
  3356. begin
  3357. FBCC.Assign(Value);
  3358. end;
  3359. procedure TIpMessage.SetCC(const Value: TStringList); {!!.01}
  3360. begin
  3361. FCC.Assign(Value);
  3362. end;
  3363. procedure TIpMessage.SetNewsgroups(const Value: TStringList); {!!.01}
  3364. begin
  3365. FNewsgroups.Assign(Value);
  3366. end;
  3367. procedure TIpMessage.SetPath(const Value: TStringList); {!!.01}
  3368. begin
  3369. FPath.Assign(Value);
  3370. end;
  3371. procedure TIpMessage.SetReceived(const Value: TStringList); {!!.01}
  3372. begin
  3373. FReceived.Assign(Value);
  3374. end;
  3375. procedure TIpMessage.SetRecipients(const Value: TStringList); {!!.01}
  3376. begin
  3377. FRecipients.Assign(Value);
  3378. end;
  3379. procedure TIpMessage.SetReferences(const Value: TStringlist); {!!.01}
  3380. begin
  3381. FReferences.Assign(Value);
  3382. end;
  3383. procedure TIpMessage.SetUserFields(const Value: TStringList); {!!.01}
  3384. begin
  3385. FUserFields.Assign(Value);
  3386. end;
  3387. { TIpFormDataEntity }
  3388. constructor TIpFormDataEntity.Create(ParentEntity : TIpMimeEntity);
  3389. begin
  3390. inherited Create(ParentEntity);
  3391. ContentType := strMultipart;
  3392. ContentSubType := strFormData;
  3393. Boundary := GenerateBoundary;
  3394. end;
  3395. destructor TIpFormDataEntity.Destroy;
  3396. begin
  3397. inherited Destroy;
  3398. end;
  3399. { Add file as nested Mime part of FilesEntity block }
  3400. procedure TIpFormDataEntity.AddFile(const aFileName,
  3401. aContentType,
  3402. aSubtype : string;
  3403. aEncoding : TIpMimeEncodingMethod);
  3404. var
  3405. Blk : TIpMimeEntity;
  3406. MS : TIpMemMapStream;
  3407. begin
  3408. if not Assigned(FFilesEntity) then begin
  3409. FFilesEntity := NewMimePart;
  3410. FFilesEntity.EntityName := strFiles;
  3411. FFilesEntity.ContentDispositionType := strFormData;
  3412. FFilesEntity.ContentType := strMultipart;
  3413. FFilesEntity.ContentSubtype := strMixed;
  3414. end;
  3415. Blk := FFilesEntity.NewMimePart;
  3416. Blk.ContentDispositionType := strAttachment;
  3417. Blk.ContentType := aContentType;
  3418. Blk.ContentSubtype := aSubtype;
  3419. Blk.ContentTransferEncoding := aEncoding;
  3420. MS := TIpMemMapStream.Create(aFileName, True, False);
  3421. try
  3422. MS.Open;
  3423. Blk.EncodeBodyStream(MS, aFileName);
  3424. finally
  3425. MS.Free;
  3426. end;
  3427. end;
  3428. { Add FormData Mime part }
  3429. procedure TIpFormDataEntity.AddFormData(const aName, aText : string);
  3430. var
  3431. Blk : TIpMimeEntity;
  3432. begin
  3433. Blk := NewMimePart;
  3434. Blk.EntityName := aName;
  3435. Blk.ContentDispositionType := strFormData;
  3436. Blk.Body.WriteLine(aText);
  3437. end;
  3438. { Generate raw Mime message and save to stream }
  3439. procedure TIpFormDataEntity.SaveToStream(aStream : TStream);
  3440. var
  3441. TS : TIpAnsiTextStream;
  3442. SL : TStringList;
  3443. begin
  3444. TS := TIpAnsiTextStream.Create(aStream);
  3445. try
  3446. SL := TStringList.Create;
  3447. try
  3448. EncodeMimeHeaders(SL);
  3449. SL.SaveToStream(TS);
  3450. EncodeEntity(TS);
  3451. finally
  3452. SL.Free;
  3453. end;
  3454. finally
  3455. TS.Free;
  3456. end;
  3457. end;
  3458. {HTTP Authentication Support -- .02}
  3459. function IpBase64EncodeString(const InStr: string): string; {!!.03}
  3460. {
  3461. encode a string into Base64, intended for producing short ( < 100 chars or so)
  3462. coded strings to be passed as part of HTTP authentications via HTTP headers.
  3463. NO LINE ORIENTED SMARTS: if you need to work with blocks of text use the
  3464. IpMsg class
  3465. }
  3466. var
  3467. CvtBuff: PChar;
  3468. I, Ct, Count, OutLen: Cardinal;
  3469. function CodeByte(byt : Byte) : char;
  3470. {- encode 6-bit value to BinHex char and send it }
  3471. begin
  3472. Result := Ip64Table[byt and $3F];
  3473. end;
  3474. begin
  3475. Result := '';
  3476. Count := Length(InStr);
  3477. if Count = 0 then // empty input string nothing to encode {!!.03}
  3478. Exit; {!!.03}
  3479. OutLen := Count * 2; // leave plenty of room for encoded string {!!.03}
  3480. GetMem(CvtBuff, OutLen + 1);
  3481. Ct := 0;
  3482. I := 1;
  3483. if Count >= 3 then begin {!!.03}
  3484. while I <= (Count - 2) do begin
  3485. { Encode 1st byte }
  3486. CvtBuff[Ct] := CodeByte(Ord(InStr[I]) shr 2);
  3487. Inc(Ct);
  3488. { Encode 1st/2nd byte }
  3489. CvtBuff[Ct] := CodeByte((Ord(InStr[I]) shl 4) or (Ord(InStr[I+1]) shr 4));
  3490. Inc(Ct);
  3491. { Encode 2nd/3rd byte }
  3492. CvtBuff[Ct] := CodeByte((Ord(InStr[I+1]) shl 2) or (Ord(InStr[I+2]) shr 6));
  3493. Inc(Ct);
  3494. { Encode 3rd byte }
  3495. CvtBuff[Ct] := CodeByte(Ord(InStr[I+2]) and $3F);
  3496. Inc(Ct);
  3497. Inc(I, 3);
  3498. end;
  3499. end; {!!.03}
  3500. { Are there odd bytes to add? }
  3501. if (I <= Count) then begin
  3502. CvtBuff[Ct] := CodeByte(Ord(InStr[I]) shr 2);
  3503. Inc(Ct);
  3504. { One odd byte }
  3505. if I = Count then begin
  3506. CvtBuff[Ct] := CodeByte((Ord(InStr[I]) shl 4) and $30);
  3507. Inc(Ct);
  3508. CvtBuff[Ct] := '='; // pad char
  3509. Inc(Ct);
  3510. { Two odd bytes }
  3511. end else begin
  3512. CvtBuff[Ct] := CodeByte(((Ord(InStr[I]) shl 4) and $30)
  3513. or ((Ord(InStr[I+1]) shr 4) and $0F));
  3514. Inc(Ct);
  3515. CvtBuff[Ct] := CodeByte((Ord(InStr[I+1]) shl 2) and $3C);
  3516. Inc(Ct);
  3517. end;
  3518. { Add padding }
  3519. CvtBuff[Ct] := '=';
  3520. Inc(Ct);
  3521. end;
  3522. CvtBuff[Ct] := #0;
  3523. Result := StrPas(CvtBuff);
  3524. FreeMem(CvtBuff, OutLen + 1);
  3525. end;
  3526. end.