IdMessageParts.pas 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. {
  2. $Project$
  3. $Workfile$
  4. $Revision$
  5. $DateUTC$
  6. $Id$
  7. This file is part of the Indy (Internet Direct) project, and is offered
  8. under the dual-licensing agreement described on the Indy website.
  9. (http://www.indyproject.org/)
  10. Copyright:
  11. (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved.
  12. }
  13. {
  14. $Log$
  15. }
  16. {
  17. Rev 1.8 9/30/2004 5:04:20 PM BGooijen
  18. Self was not initialized
  19. Rev 1.7 01/06/2004 00:28:46 CCostelloe
  20. Minor bug fix
  21. Rev 1.6 5/30/04 11:29:36 PM RLebeau
  22. Added OwnerMessage property to TIdMessageParts for use with
  23. TIdMessagePart.ResolveContentType() under Delphi versions prior to v6,
  24. where the TCollection.Owner method does not exist.
  25. Rev 1.5 16/05/2004 18:55:46 CCostelloe
  26. New TIdText/TIdAttachment processing
  27. Rev 1.4 2004.02.03 5:44:06 PM czhower
  28. Name changes
  29. Rev 1.3 10/17/03 12:06:04 PM RLebeau
  30. Updated TIdMessagePart.Assign() to copy all available header values
  31. rather than select ones.
  32. Rev 1.2 10/17/2003 12:43:12 AM DSiders
  33. Added localization comments.
  34. Rev 1.1 26/09/2003 01:07:18 CCostelloe
  35. Added FParentPart, so that nested MIME types
  36. (like multipart/alternative nested in multipart/related and vica-versa)
  37. can be encoded and decoded (when encoding, need to know this so the
  38. correct boundary is emitted) and so the user can properly define which
  39. parts belong to which sections.
  40. Rev 1.0 11/13/2002 07:57:32 AM JPMugaas
  41. 24-Sep-2003 Ciaran Costelloe
  42. - Added FParentPart, so that nested MIME types (like multipart/alternative
  43. nested in multipart/related and vica-versa) can be encoded and decoded
  44. (when encoding, need to know this so the correct boundary is emitted)
  45. and so the user can properly define which parts belong to which sections.
  46. 2002-08-30 Andrew P.Rybin
  47. - ExtractHeaderSubItem
  48. - virtual methods. Now descendant can add functionality.
  49. Ex: TIdText.GetContentType = GetContentType w/o charset
  50. }
  51. unit IdMessageParts;
  52. interface
  53. {$i IdCompilerDefines.inc}
  54. uses
  55. Classes,
  56. IdHeaderList,
  57. IdExceptionCore,
  58. IdGlobal;
  59. type
  60. TOnGetMessagePartStream = procedure(AStream: TStream) of object;
  61. TIdMessagePartType = (mptText, mptAttachment);
  62. // if you add to this, please also adjust the case statement in
  63. // TIdMessageParts.CountParts;
  64. TIdMessageParts = class;
  65. TIdMessagePart = class(TCollectionItem)
  66. protected
  67. FContentMD5: string;
  68. FCharSet: string;
  69. FEndBoundary: string;
  70. FExtraHeaders: TIdHeaderList;
  71. FFileName: String;
  72. FName: String;
  73. FHeaders: TIdHeaderList;
  74. FIsEncoded: Boolean;
  75. FOnGetMessagePartStream: TOnGetMessagePartStream;
  76. FParentPart: Integer;
  77. //
  78. function GetContentDisposition: string; virtual;
  79. function GetContentType: string; virtual;
  80. function GetContentTransfer: string; virtual;
  81. function GetContentID: string; virtual;
  82. function GetContentLocation: string; virtual;
  83. function GetContentDescription: string; virtual;
  84. function GetMessageParts: TIdMessageParts;
  85. function GetOwnerMessage: TPersistent;
  86. procedure SetContentDisposition(const Value: string); virtual;
  87. procedure SetContentType(const Value: string); virtual;
  88. procedure SetContentTransfer(const Value: string); virtual;
  89. procedure SetExtraHeaders(const Value: TIdHeaderList);
  90. procedure SetContentID(const Value: string); virtual;
  91. procedure SetContentDescription(const Value: string); virtual;
  92. procedure SetContentLocation(const Value: string); virtual;
  93. public
  94. constructor Create(Collection: TCollection); override;
  95. destructor Destroy; override;
  96. procedure Assign(Source: TPersistent); override;
  97. function GetCharSet(AHeader: string): String;
  98. function ResolveContentType(AContentType: string): string; //Fixes up ContentType
  99. class function PartType: TIdMessagePartType; virtual;
  100. //
  101. property IsEncoded: Boolean read FIsEncoded;
  102. property MessageParts: TIdMessageParts read GetMessageParts;
  103. property OwnerMessage: TPersistent read GetOwnerMessage;
  104. property OnGetMessagePartStream: TOnGetMessagePartStream read FOnGetMessagePartStream write FOnGetMessagePartStream;
  105. property Headers: TIdHeaderList read FHeaders;
  106. published
  107. property CharSet: string read FCharSet write FCharSet;
  108. property ContentDescription: string read GetContentDescription write SetContentDescription;
  109. property ContentDisposition: string read GetContentDisposition write SetContentDisposition;
  110. property ContentID: string read GetContentID write SetContentID;
  111. property ContentLocation: string read GetContentLocation write SetContentLocation;
  112. property ContentTransfer: string read GetContentTransfer write SetContentTransfer;
  113. property ContentType: string read GetContentType write SetContentType;
  114. property ExtraHeaders: TIdHeaderList read FExtraHeaders write SetExtraHeaders;
  115. property FileName: String read FFileName write FFileName;
  116. property Name: String read FName write FName;
  117. property ParentPart: integer read FParentPart write FParentPart;
  118. end;
  119. TIdMessagePartClass = class of TIdMessagePart;
  120. TIdMessageParts = class(TOwnedCollection)
  121. protected
  122. FAttachmentEncoding: string;
  123. FAttachmentCount: integer;
  124. FMessageEncoderInfo: TObject;
  125. FRelatedPartCount: integer;
  126. FTextPartCount: integer;
  127. //
  128. function GetItem(Index: Integer): TIdMessagePart;
  129. function GetOwnerMessage: TPersistent;
  130. procedure SetAttachmentEncoding(const AValue: string);
  131. procedure SetItem(Index: Integer; const Value: TIdMessagePart);
  132. public
  133. function Add: TIdMessagePart;
  134. procedure CountParts;
  135. constructor Create(AOwner: TPersistent); reintroduce;
  136. //
  137. property AttachmentCount: integer read FAttachmentCount;
  138. property AttachmentEncoding: string read FAttachmentEncoding write SetAttachmentEncoding;
  139. property Items[Index: Integer]: TIdMessagePart read GetItem write SetItem; default;
  140. property MessageEncoderInfo: TObject read FMessageEncoderInfo;
  141. property OwnerMessage: TPersistent read GetOwnerMessage;
  142. property RelatedPartCount: integer read FRelatedPartCount;
  143. property TextPartCount: integer read FTextPartCount;
  144. end;
  145. EIdCanNotCreateMessagePart = class(EIdMessageException);
  146. implementation
  147. uses
  148. IdMessage, IdGlobalProtocols, IdResourceStringsProtocols, IdMessageCoder, IdCoderHeader,
  149. SysUtils;
  150. { TIdMessagePart }
  151. procedure TIdMessagePart.Assign(Source: TPersistent);
  152. var
  153. mp: TIdMessagePart;
  154. begin
  155. if Source is TIdMessagePart then begin
  156. mp := TIdMessagePart(Source);
  157. // RLebeau 10/17/2003
  158. Headers.Assign(mp.Headers);
  159. ExtraHeaders.Assign(mp.ExtraHeaders);
  160. CharSet := mp.CharSet;
  161. FileName := mp.FileName;
  162. Name := mp.Name;
  163. end else begin
  164. inherited Assign(Source);
  165. end;
  166. end;
  167. constructor TIdMessagePart.Create(Collection: TCollection);
  168. begin
  169. inherited;
  170. if ClassType = TIdMessagePart then begin
  171. raise EIdCanNotCreateMessagePart.Create(RSTIdMessagePartCreate);
  172. end;
  173. FIsEncoded := False;
  174. FHeaders := TIdHeaderList.Create(QuoteRFC822);
  175. FExtraHeaders := TIdHeaderList.Create(QuoteRFC822);
  176. FParentPart := -1;
  177. end;
  178. destructor TIdMessagePart.Destroy;
  179. begin
  180. FHeaders.Free;
  181. FExtraHeaders.Free;
  182. inherited Destroy;
  183. end;
  184. function TIdMessagePart.GetContentDisposition: string;
  185. begin
  186. Result := Headers.Values['Content-Disposition']; {do not localize}
  187. end;
  188. function TIdMessagePart.GetContentID: string;
  189. begin
  190. Result := Headers.Values['Content-ID']; {do not localize}
  191. end;
  192. function TIdMessagePart.GetContentDescription: string;
  193. begin
  194. Result := Headers.Values['Content-Description']; {do not localize}
  195. end;
  196. function TIdMessagePart.GetContentLocation: string;
  197. begin
  198. Result := Headers.Values['Content-Location']; {do not localize}
  199. end;
  200. function TIdMessagePart.GetContentTransfer: string;
  201. begin
  202. Result := Headers.Values['Content-Transfer-Encoding']; {do not localize}
  203. end;
  204. function TIdMessagePart.GetCharSet(AHeader: string): String;
  205. begin
  206. Result := ExtractHeaderSubItem(AHeader, 'charset', QuoteMIME); {do not localize}
  207. end;
  208. function TIdMessagePart.ResolveContentType(AContentType: string): string;
  209. var
  210. LMsg: TIdMessage;
  211. LParts: TIdMessageParts;
  212. begin
  213. //This extracts 'text/plain' from 'text/plain; charset="xyz"; boundary="123"'
  214. //or, if '', it finds the correct default value for MIME messages.
  215. if AContentType <> '' then begin
  216. Result := AContentType;
  217. end else begin
  218. //If it is MIME, then we need to find the correct default...
  219. LParts := MessageParts;
  220. if Assigned(LParts) then begin
  221. LMsg := TIdMessage(LParts.OwnerMessage);
  222. if Assigned(LMsg) and (LMsg.Encoding = meMIME) then begin
  223. //There is an exception if we are a child of multipart/digest...
  224. if ParentPart <> -1 then begin
  225. AContentType := LParts.Items[ParentPart].Headers.Values['Content-Type']; {do not localize}
  226. if IsHeaderMediaType(AContentType, 'multipart/digest') then begin {do not localize}
  227. Result := 'message/rfc822'; {do not localize}
  228. Exit;
  229. end;
  230. end;
  231. //The default type...
  232. Result := 'text/plain'; {do not localize}
  233. Exit;
  234. end;
  235. end;
  236. Result := ''; //Default for non-MIME messages
  237. end;
  238. end;
  239. function TIdMessagePart.GetContentType: string;
  240. begin
  241. Result := Headers.Values['Content-Type']; {do not localize}
  242. end;
  243. function TIdMessagePart.GetMessageParts: TIdMessageParts;
  244. begin
  245. if Collection is TIdMessageParts then begin
  246. Result := TIdMessageParts(Collection);
  247. end else begin
  248. Result := nil;
  249. end;
  250. end;
  251. function TIdMessagePart.GetOwnerMessage: TPersistent;
  252. var
  253. LParts: TIdMessageParts;
  254. begin
  255. LParts := MessageParts;
  256. if Assigned(LParts) then begin
  257. Result := LParts.OwnerMessage;
  258. end else begin
  259. Result := nil;
  260. end;
  261. end;
  262. class function TIdMessagePart.PartType: TIdMessagePartType;
  263. begin
  264. Result := mptAttachment;
  265. end;
  266. procedure TIdMessagePart.SetContentID(const Value: string);
  267. begin
  268. Headers.Values['Content-ID'] := Value; {do not localize}
  269. end;
  270. procedure TIdMessagePart.SetContentDescription(const Value: string);
  271. begin
  272. Headers.Values['Content-Description'] := Value; {do not localize}
  273. end;
  274. procedure TIdMessagePart.SetContentDisposition(const Value: string);
  275. var
  276. LFileName: string;
  277. begin
  278. Headers.Values['Content-Disposition'] := RemoveHeaderEntry(Value, 'filename', LFileName, QuoteMIME); {do not localize}
  279. {RLebeau: override the current value only if the header specifies a new one}
  280. if LFileName <> '' then begin
  281. LFileName := DecodeHeader(LFileName);
  282. end;
  283. if LFileName <> '' then begin
  284. FFileName := LFileName;
  285. end;
  286. end;
  287. procedure TIdMessagePart.SetContentLocation(const Value: string);
  288. begin
  289. Headers.Values['Content-Location'] := Value; {do not localize}
  290. end;
  291. procedure TIdMessagePart.SetContentTransfer(const Value: string);
  292. begin
  293. Headers.Values['Content-Transfer-Encoding'] := Value; {do not localize}
  294. end;
  295. procedure TIdMessagePart.SetContentType(const Value: string);
  296. var
  297. LTmp, LCharSet, LName: string;
  298. begin
  299. LTmp := RemoveHeaderEntry(Value, 'charset', LCharSet, QuoteMIME);{do not localize}
  300. LTmp := RemoveHeaderEntry(LTmp, 'name', LName, QuoteMIME);{do not localize}
  301. Headers.Values['Content-Type'] := LTmp;
  302. {RLebeau: override the current values only if the header specifies new ones}
  303. if LCharSet <> '' then begin
  304. FCharSet := LCharSet;
  305. end;
  306. if LName <> '' then begin
  307. FName := LName;
  308. end;
  309. end;
  310. procedure TIdMessagePart.SetExtraHeaders(const Value: TIdHeaderList);
  311. begin
  312. FExtraHeaders.Assign(Value);
  313. end;
  314. { TMessageParts }
  315. function TIdMessageParts.Add: TIdMessagePart;
  316. begin
  317. // This helps prevent TIdMessagePart from being added
  318. Result := nil;
  319. end;
  320. procedure TIdMessageParts.CountParts;
  321. //TODO: Make AttCount, etc maintained on the fly
  322. var
  323. i: integer;
  324. begin
  325. FAttachmentCount := 0;
  326. FRelatedPartCount := 0;
  327. FTextPartCount := 0;
  328. for i := 0 to Count - 1 do begin
  329. if TIdMessagePart(Items[i]).ContentID <> '' then begin
  330. Inc(FRelatedPartCount);
  331. end;
  332. case TIdMessagePart(Items[i]).PartType of
  333. mptText :
  334. begin
  335. Inc(FTextPartCount)
  336. end;
  337. mptAttachment:
  338. begin
  339. Inc(FAttachmentCount);
  340. end;
  341. end;
  342. end;
  343. end;
  344. constructor TIdMessageParts.Create(AOwner: TPersistent);
  345. begin
  346. inherited Create(AOwner, TIdMessagePart);
  347. // Must set prop and not variable so it will initialize it
  348. AttachmentEncoding := 'MIME'; {do not localize}
  349. end;
  350. function TIdMessageParts.GetItem(Index: Integer): TIdMessagePart;
  351. begin
  352. Result := TIdMessagePart(inherited GetItem(Index));
  353. end;
  354. function TIdMessageParts.GetOwnerMessage: TPersistent;
  355. var
  356. LOwner: TPersistent;
  357. begin
  358. LOwner := inherited GetOwner;
  359. if LOwner is TIdMessage then begin
  360. Result := LOwner;
  361. end else begin
  362. Result := nil;
  363. end;
  364. end;
  365. procedure TIdMessageParts.SetAttachmentEncoding(const AValue: string);
  366. begin
  367. FMessageEncoderInfo := TIdMessageEncoderList.ByName(AValue);
  368. FAttachmentEncoding := AValue;
  369. end;
  370. procedure TIdMessageParts.SetItem(Index: Integer; const Value: TIdMessagePart);
  371. begin
  372. inherited SetItem(Index, Value);
  373. end;
  374. end.