MimeEntity.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Collections.Specialized;
  4. using System.Text;
  5. using System.IO;
  6. using System.Net.Mime;
  7. using System.Net.Mail;
  8. namespace Ant.Service.Utilities
  9. {
  10. /// <summary>
  11. /// This class represents a Mime entity.
  12. /// 这个类表示一个MIME实体
  13. /// </summary>
  14. public class MimeEntity
  15. {
  16. private StringBuilder _encodedMessage;
  17. /// <summary>
  18. /// Gets the encoded message.
  19. /// 获取编码的消息。
  20. /// </summary>
  21. /// <value>
  22. /// 编码的消息
  23. /// The encoded message.
  24. /// </value>
  25. public StringBuilder EncodedMessage
  26. {
  27. get { return _encodedMessage; }
  28. }
  29. private List<MimeEntity> _children;
  30. /// <summary>
  31. /// Gets the children.
  32. /// </summary>
  33. /// <value>The children.</value>
  34. public List<MimeEntity> Children
  35. {
  36. get
  37. {
  38. return _children;
  39. }
  40. }
  41. private ContentType _contentType;
  42. /// <summary>
  43. /// Gets the type of the content.
  44. /// </summary>
  45. /// <value>The type of the content.</value>
  46. public ContentType ContentType
  47. {
  48. get { return _contentType; }
  49. }
  50. private string _mediaSubType;
  51. /// <summary>
  52. /// Gets the type of the media sub.
  53. /// </summary>
  54. /// <value>The type of the media sub.</value>
  55. public string MediaSubType
  56. {
  57. get { return _mediaSubType; }
  58. }
  59. private string _mediaMainType;
  60. /// <summary>
  61. /// Gets the type of the media main.
  62. /// </summary>
  63. /// <value>The type of the media main.</value>
  64. public string MediaMainType
  65. {
  66. get { return _mediaMainType; }
  67. }
  68. private NameValueCollection _headers;
  69. /// <summary>
  70. /// Gets the headers.
  71. /// </summary>
  72. /// <value>The headers.</value>
  73. public NameValueCollection Headers
  74. {
  75. get { return _headers; }
  76. }
  77. private string _mimeVersion;
  78. /// <summary>
  79. /// Gets or sets the MIME version.
  80. /// </summary>
  81. /// <value>The MIME version.</value>
  82. public string MimeVersion
  83. {
  84. get
  85. {
  86. return _mimeVersion;
  87. }
  88. set
  89. {
  90. _mimeVersion = value;
  91. }
  92. }
  93. private string _contentId;
  94. /// <summary>
  95. /// Gets or sets the content id.
  96. /// </summary>
  97. /// <value>The content id.</value>
  98. public string ContentId
  99. {
  100. get
  101. {
  102. return _contentId;
  103. }
  104. set
  105. {
  106. _contentId = value;
  107. }
  108. }
  109. private string _contentDescription;
  110. /// <summary>
  111. /// Gets or sets the content description.
  112. /// </summary>
  113. /// <value>The content description.</value>
  114. public string ContentDescription
  115. {
  116. get
  117. {
  118. return _contentDescription;
  119. }
  120. set
  121. {
  122. _contentDescription = value;
  123. }
  124. }
  125. private ContentDisposition _contentDisposition;
  126. /// <summary>
  127. /// Gets or sets the content disposition.
  128. /// </summary>
  129. /// <value>The content disposition.</value>
  130. public ContentDisposition ContentDisposition
  131. {
  132. get
  133. {
  134. return _contentDisposition;
  135. }
  136. set
  137. {
  138. _contentDisposition = value;
  139. }
  140. }
  141. private string _transferEncoding;
  142. /// <summary>
  143. /// Gets or sets the transfer encoding.
  144. /// </summary>
  145. /// <value>The transfer encoding.</value>
  146. public string TransferEncoding
  147. {
  148. get
  149. {
  150. return _transferEncoding;
  151. }
  152. set
  153. {
  154. _transferEncoding = value;
  155. }
  156. }
  157. private TransferEncoding _contentTransferEncoding;
  158. /// <summary>
  159. /// Gets or sets the content transfer encoding.
  160. /// </summary>
  161. /// <value>The content transfer encoding.</value>
  162. public TransferEncoding ContentTransferEncoding
  163. {
  164. get
  165. {
  166. return _contentTransferEncoding;
  167. }
  168. set
  169. {
  170. _contentTransferEncoding = value;
  171. }
  172. }
  173. /// <summary>
  174. /// Gets a value indicating whether this instance has boundary.
  175. /// </summary>
  176. /// <value>
  177. /// <c>true</c> if this instance has boundary; otherwise, <c>false</c>.
  178. /// </value>
  179. internal bool HasBoundary
  180. {
  181. get
  182. {
  183. return (!string.IsNullOrEmpty(_contentType.Boundary))
  184. || (!string.IsNullOrEmpty(_startBoundary));
  185. }
  186. }
  187. private string _startBoundary;
  188. /// <summary>
  189. /// Gets the start boundary.
  190. /// </summary>
  191. /// <value>The start boundary.</value>
  192. public string StartBoundary
  193. {
  194. get
  195. {
  196. if (string.IsNullOrEmpty(_startBoundary) || !string.IsNullOrEmpty(_contentType.Boundary))
  197. {
  198. return string.Concat("--", _contentType.Boundary);
  199. }
  200. return _startBoundary;
  201. }
  202. }
  203. /// <summary>
  204. /// Gets the end boundary.
  205. /// </summary>
  206. /// <value>The end boundary.</value>
  207. public string EndBoundary
  208. {
  209. get
  210. {
  211. return string.Concat(StartBoundary, "--");
  212. }
  213. }
  214. private MimeEntity _parent;
  215. /// <summary>
  216. /// Gets or sets the parent.
  217. /// </summary>
  218. /// <value>The parent.</value>
  219. public MimeEntity Parent
  220. {
  221. get { return _parent; }
  222. set { _parent = value; }
  223. }
  224. private MemoryStream _content;
  225. /// <summary>
  226. /// Gets or sets the content.
  227. /// </summary>
  228. /// <value>The content.</value>
  229. public MemoryStream Content
  230. {
  231. get { return _content; }
  232. internal set { _content = value; }
  233. }
  234. /// <summary>
  235. /// Initializes a new instance of the <see cref="MimeEntity"/> class.
  236. /// </summary>
  237. public MimeEntity()
  238. {
  239. _children = new List<MimeEntity>();
  240. _headers = new NameValueCollection();
  241. _contentType = MimeReader.GetContentType(string.Empty);
  242. _parent = null;
  243. _encodedMessage = new StringBuilder();
  244. }
  245. /// <summary>
  246. /// Initializes a new instance of the <see cref="MimeEntity"/> class.
  247. /// </summary>
  248. /// <param name="parent">The parent.</param>
  249. public MimeEntity(MimeEntity parent)
  250. : this()
  251. {
  252. if (parent == null)
  253. {
  254. throw new ArgumentNullException("parent");
  255. }
  256. _parent = parent;
  257. _startBoundary = parent.StartBoundary;
  258. }
  259. /// <summary>
  260. /// Sets the type of the content.
  261. /// </summary>
  262. /// <param name="contentType">Type of the content.</param>
  263. internal void SetContentType(ContentType contentType)
  264. {
  265. _contentType = contentType;
  266. _contentType.MediaType = MimeReader.GetMediaType(contentType.MediaType);
  267. _mediaMainType = MimeReader.GetMediaMainType(contentType.MediaType);
  268. _mediaSubType = MimeReader.GetMediaSubType(contentType.MediaType);
  269. }
  270. /// <summary>
  271. /// Toes the mail message ex.
  272. /// </summary>
  273. /// <returns></returns>
  274. public MailMessageEx ToMailMessageEx()
  275. {
  276. return ToMailMessageEx(this);
  277. }
  278. /// <summary>
  279. /// Toes the mail message ex.
  280. /// </summary>
  281. /// <param name="entity">The entity.</param>
  282. /// <returns></returns>
  283. private MailMessageEx ToMailMessageEx(MimeEntity entity)
  284. {
  285. if (entity == null)
  286. {
  287. //throw new ArgumentNullException("entity");
  288. return null;
  289. }
  290. //parse standard headers and create base email.
  291. MailMessageEx message = MailMessageEx.CreateMailMessageFromEntity(entity);
  292. if (!string.IsNullOrEmpty(entity.ContentType.Boundary))
  293. {
  294. message = MailMessageEx.CreateMailMessageFromEntity(entity);
  295. BuildMultiPartMessage(entity, message);
  296. }//parse multipart message into sub parts.
  297. else if (string.Equals(entity.ContentType.MediaType, MediaTypes.MessageRfc822, StringComparison.InvariantCultureIgnoreCase))
  298. {
  299. //use the first child to create the multipart message.
  300. if (entity.Children.Count < 0)
  301. {
  302. throw new Pop3Exception("Invalid child count on message/rfc822 entity.");
  303. }
  304. //create the mail message from the first child because it will
  305. //contain all of the mail headers. The entity in this state
  306. //only contains simple content type headers indicating, disposition, type and description.
  307. //This means we can't create the mail message from this type as there is no
  308. //internet mail headers attached to this entity.
  309. message = MailMessageEx.CreateMailMessageFromEntity(entity.Children[0]);
  310. BuildMultiPartMessage(entity, message);
  311. } //parse nested message.
  312. else
  313. {
  314. message = MailMessageEx.CreateMailMessageFromEntity(entity);
  315. BuildSinglePartMessage(entity, message);
  316. } //Create single part message.
  317. return message;
  318. }
  319. /// <summary>
  320. /// Builds the single part message.
  321. /// </summary>
  322. /// <param name="entity">The entity.</param>
  323. /// <param name="message">The message.</param>
  324. private void BuildSinglePartMessage(MimeEntity entity, MailMessageEx message)
  325. {
  326. SetMessageBody(message, entity);
  327. }
  328. /// <summary>
  329. /// Gets the body encoding.
  330. /// </summary>
  331. /// <param name="contentType">Type of the content.</param>
  332. public Encoding GetEncoding()
  333. {
  334. if (string.IsNullOrEmpty(this.ContentType.CharSet))
  335. {
  336. return Encoding.ASCII;
  337. }
  338. else
  339. {
  340. try
  341. {
  342. return Encoding.GetEncoding(this.ContentType.CharSet);
  343. }
  344. catch (ArgumentException)
  345. {
  346. return Encoding.ASCII;
  347. }
  348. }
  349. }
  350. /// <summary>
  351. /// Builds the multi part message.
  352. /// </summary>
  353. /// <param name="entity">The entity.</param>
  354. /// <param name="message">The message.</param>
  355. private void BuildMultiPartMessage(MimeEntity entity, MailMessageEx message)
  356. {
  357. foreach (MimeEntity child in entity.Children)
  358. {
  359. if (child == null)
  360. {
  361. continue;
  362. }
  363. if (string.Equals(child.ContentType.MediaType, MediaTypes.MultipartAlternative, StringComparison.InvariantCultureIgnoreCase)
  364. || string.Equals(child.ContentType.MediaType, MediaTypes.MultipartMixed, StringComparison.InvariantCultureIgnoreCase))
  365. {
  366. BuildMultiPartMessage(child, message);
  367. } //if the message is mulitpart/alternative or multipart/mixed then the entity will have children needing parsed.
  368. else if (!IsAttachment(child) &&
  369. (string.Equals(child.ContentType.MediaType, MediaTypes.TextPlain)
  370. || string.Equals(child.ContentType.MediaType, MediaTypes.TextHtml)))
  371. {
  372. message.AlternateViews.Add(CreateAlternateView(child));
  373. SetMessageBody(message, child);
  374. } //add the alternative views.
  375. else if (string.Equals(child.ContentType.MediaType, MediaTypes.MessageRfc822, StringComparison.InvariantCultureIgnoreCase)
  376. && string.Equals(child.ContentDisposition.DispositionType, DispositionTypeNames.Attachment, StringComparison.InvariantCultureIgnoreCase))
  377. {
  378. message.Children.Add(ToMailMessageEx(child));
  379. } //create a child message and
  380. else if (IsAttachment(child))
  381. {
  382. message.Attachments.Add(CreateAttachment(child));
  383. }
  384. }
  385. }
  386. private static bool IsAttachment(MimeEntity child)
  387. {
  388. return (child.ContentDisposition != null)
  389. && (string.Equals(child.ContentDisposition.DispositionType, DispositionTypeNames.Attachment, StringComparison.InvariantCultureIgnoreCase));
  390. }
  391. /// <summary>
  392. /// Sets the message body.
  393. /// </summary>
  394. /// <param name="message">The message.</param>
  395. /// <param name="child">The child.</param>
  396. private void SetMessageBody(MailMessageEx message, MimeEntity child)
  397. {
  398. Encoding encoding = child.GetEncoding();
  399. message.Body = DecodeBytes(child.Content.ToArray(), encoding);
  400. message.BodyEncoding = encoding;
  401. message.IsBodyHtml = string.Equals(MediaTypes.TextHtml,
  402. child.ContentType.MediaType, StringComparison.InvariantCultureIgnoreCase);
  403. }
  404. /// <summary>
  405. /// Decodes the bytes.
  406. /// </summary>
  407. /// <param name="buffer">The buffer.</param>
  408. /// <param name="encoding">The encoding.</param>
  409. /// <returns></returns>
  410. private string DecodeBytes(byte[] buffer, Encoding encoding)
  411. {
  412. if (buffer == null)
  413. {
  414. return null;
  415. }
  416. if (encoding == null)
  417. {
  418. encoding = Encoding.UTF7;
  419. } //email defaults to 7bit.
  420. return encoding.GetString(buffer);
  421. }
  422. /// <summary>
  423. /// Creates the alternate view.
  424. /// </summary>
  425. /// <param name="view">The view.</param>
  426. /// <returns></returns>
  427. private AlternateView CreateAlternateView(MimeEntity view)
  428. {
  429. AlternateView alternateView = new AlternateView(view.Content, view.ContentType);
  430. alternateView.TransferEncoding = view.ContentTransferEncoding;
  431. alternateView.ContentId = TrimBrackets(view.ContentId);
  432. return alternateView;
  433. }
  434. /// <summary>
  435. /// Trims the brackets.
  436. /// </summary>
  437. /// <param name="value">The value.</param>
  438. /// <returns></returns>
  439. public static string TrimBrackets(string value)
  440. {
  441. if (value == null)
  442. {
  443. return value;
  444. }
  445. if (value.StartsWith("<") && value.EndsWith(">"))
  446. {
  447. return value.Trim('<', '>');
  448. }
  449. return value;
  450. }
  451. /// <summary>
  452. /// Creates the attachment.
  453. /// </summary>
  454. /// <param name="entity">The entity.</param>
  455. /// <returns></returns>
  456. private Attachment CreateAttachment(MimeEntity entity)
  457. {
  458. Attachment attachment = new Attachment(entity.Content, entity.ContentType);
  459. if (entity.ContentDisposition != null)
  460. {
  461. attachment.ContentDisposition.Parameters.Clear();
  462. foreach (string key in entity.ContentDisposition.Parameters.Keys)
  463. {
  464. attachment.ContentDisposition.Parameters.Add(key, entity.ContentDisposition.Parameters[key]);
  465. }
  466. attachment.ContentDisposition.CreationDate = entity.ContentDisposition.CreationDate;
  467. attachment.ContentDisposition.DispositionType = entity.ContentDisposition.DispositionType;
  468. attachment.ContentDisposition.FileName = entity.ContentDisposition.FileName;
  469. attachment.ContentDisposition.Inline = entity.ContentDisposition.Inline;
  470. attachment.ContentDisposition.ModificationDate = entity.ContentDisposition.ModificationDate;
  471. attachment.ContentDisposition.ReadDate = entity.ContentDisposition.ReadDate;
  472. attachment.ContentDisposition.Size = entity.ContentDisposition.Size;
  473. }
  474. if (!string.IsNullOrEmpty(entity.ContentId))
  475. {
  476. attachment.ContentId = TrimBrackets(entity.ContentId);
  477. }
  478. attachment.TransferEncoding = entity.ContentTransferEncoding;
  479. return attachment;
  480. }
  481. }
  482. }