Pop3cli
1#region License2//------------------------------------------------------------------------------
3// Copyright (c) Dmitrii Evdokimov
4// Source https://github.com/diev/
5//
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9// http://www.apache.org/licenses/LICENSE-2.0
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15//------------------------------------------------------------------------------
16
17// based on an article https://www.codeproject.com/Articles/15611/POP3-Email-Client-with-full-MIME-Support-NET-2-0
18
19// POP3 Email Client
20// =================
21//
22// copyright by Peter Huber, Singapore, 2006
23// this code is provided as is, bugs are probable, free for any use at own risk, no
24// responsibility accepted. All rights, title and interest in and to the accompanying content retained. :-)
25//
26// based on Standard for ARPA Internet Text Messages, http://rfc.net/rfc822.html
27// based on MIME Standard, Internet Message Bodies, http://rfc.net/rfc2045.html
28// based on MIME Standard, Media Types, http://rfc.net/rfc2046.html
29#endregion30
31using System;32using System.Collections.Generic;33using System.IO;34using System.Net.Mail;35using System.Net.Mime;36using System.Text;37
38namespace Mime39{
40/// <summary>41/// Stores all MIME decoded information of a received email. One email might consist of42/// several MIME entities, which have a very similar structure to an email. A MimeMessage43/// can be a top most level email or a MIME entity the emails contains.44///45/// According to various RFCs, MIME entities can contain other MIME entities46/// recursively. However, they usually need to be mapped to alternative views and47/// attachments, which are non recursive.48///49/// MimeMessage inherits from System.Net.MailMessage, but provides additional receiving related information50/// </summary>51public class MimeMessage : MailMessage52{53#region Constructors54
55/// <summary>56/// Default constructor57/// </summary>58public MimeMessage()59{60//for the moment, we assume to be at the top61//should this entity become a child, TopParent will be overwritten62TopParent = this;63Entities = new List<MimeMessage>();64UnknowHeaderlines = new List<string>();65}66
67#endregion Constructors68
69#region Public properties70
71/// <summary>72/// To whom the email was delivered to73/// </summary>74public MailAddress DeliveredTo { get; set; }75
76/// <summary>77/// To whom the email was78/// </summary>79public MailAddress ReturnPath { get; set; }80
81/// <summary>82/// Date when the email was received83/// </summary>84public DateTime DeliveryDate { get; set; }85
86/// <summary>87/// ID @ server88/// </summary>89public string MessageId { get; set; }90
91/// <summary>92/// Probably '1.0'93/// </summary>94public string MimeVersion { get; set; }95
96/// <summary>97/// It may be desirable to allow one body to make reference to another. Accordingly,98/// bodies may be labelled using the "Content-ID" header field.99/// </summary>100public string ContentId { get; set; }101
102/// <summary>103/// Some descriptive information for body104/// </summary>105public string ContentDescription { get; set; }106
107/// <summary>108/// ContentDisposition contains normally redundant information also stored in the109/// ContentType. Since ContentType is more detailed, it is enough to analyze ContentType110///111/// Something like:112/// inline113/// inline; filename="image001.gif114/// attachment; filename="image001.jpg"115/// </summary>116public ContentDisposition ContentDisposition { get; set; }117
118/// <summary>119/// Something like "7bit" / "8bit" / "binary" / "quoted-printable" / "base64"120/// </summary>121public string TransferType { get; set; }122
123/// <summary>124/// Similar as TransferType, but .NET supports only "7bit" / "quoted-printable"125/// / "base64" here, "bit8" is marked as "bit7" (i.e. no transfer encoding needed),126/// "binary" is illegal in SMTP127/// </summary>128public TransferEncoding ContentTransferEncoding { get; set; }129
130/// <summary>131/// The Content-Type field is used to specify the nature of the data in the body of a132/// MIME entity, by giving media type and subtype identifiers, and by providing133/// auxiliary information that may be required for certain media types. Examples:134/// text/plain;135/// text/plain; charset=ISO-8859-1136/// text/plain; charset=us-ascii137/// text/plain; charset=utf-8138/// text/html;139/// text/html; charset=ISO-8859-1140/// image/gif; name=image004.gif141/// image/jpeg; name="image005.jpg"142/// message/delivery-status143/// message/rfc822144/// multipart/alternative; boundary="----=_Part_4088_29304219.1115463798628"145/// multipart/related; boundary="----=_Part_2067_9241611.1139322711488"146/// multipart/mixed; boundary="----=_Part_3431_12384933.1139387792352"147/// multipart/report; report-type=delivery-status; boundary="k04G6HJ9025016.1136391237/carbon.singnet.com.sg"148/// application/pdf; name==?windows-1251?B?xODp5Obl8fIg7+7r/Ofu4uDy5ev88ero9SDq7uzo8uXy7uIgw/Dz7+/7?=149/// </summary>150public ContentType ContentType { get; set; }151
152/// <summary>153/// .NET framework combines MediaType (text) with subtype (plain) in one property, but154/// often one or the other is needed alone. MediaMainType in this example would be 'text'.155/// </summary>156public string MediaMainType { get; set; }157
158/// <summary>159/// .NET framework combines MediaType (text) with subtype (plain) in one property, but160/// often one or the other is needed alone. MediaSubType in this example would be 'plain'.161/// </summary>162public string MediaSubType { get; set; }163
164/// <summary>165/// MimeMessage can be used for any MIME entity, as a normal message body, an attachement or an alternative view. ContentStream166/// provides the actual content of that MIME entity. It's mainly used internally and later mapped to the corresponding167/// .NET types.168/// </summary>169public Stream ContentStream { get; set; }170
171/// <summary>172/// A MIME entity can contain several MIME entities. A MIME entity has the same structure173/// like an email.174/// </summary>175public List<MimeMessage> Entities { get; set; }176
177/// <summary>178/// This entity might be part of a parent entity179/// </summary>180public MimeMessage Parent { get; set; }181
182/// <summary>183/// The top most MIME entity this MIME entity belongs to (grand grand grand .... parent)184/// </summary>185public MimeMessage TopParent { get; set; }186
187/// <summary>188/// Headerlines not interpretable by this class189/// <example></example>190/// </summary>191public List<string> UnknowHeaderlines { get; set; } //192
193/// <summary>194/// Returns true if the message has attachments195/// </summary>196public bool HasAttachments => Attachments.Count > 0;197
198#endregion Public properties199
200#region Public methods201
202/// <summary>203/// Set all content type related fields204/// </summary>205public void SetContentTypeFields(string contentTypeString = null)206{207//set content type208if (string.IsNullOrEmpty(contentTypeString))209{210ContentType = new ContentType()211{212MediaType = MediaTypeNames.Text.Plain,213CharSet = "us-ascii"214};215}216else217{218ContentType = new ContentType(contentTypeString.Trim()219.Replace(" = ", "="));220}221
222//set encoding (character set)223if (ContentType.CharSet == null)224{225BodyEncoding = Encoding.ASCII;226}227else228{229BodyEncoding = Encoding.GetEncoding(ContentType.CharSet);230}231
232//set media main and sub type233if (string.IsNullOrEmpty(ContentType.MediaType))234{235//no mediatype found236ContentType.MediaType = MediaTypeNames.Text.Plain;237}238else239{240string mediaTypeString = ContentType.MediaType.Trim().ToLowerInvariant();241int slashPosition = ContentType.MediaType.IndexOf("/");242
243if (slashPosition < 1)244{245//only main media type found246MediaMainType = mediaTypeString;247System.Diagnostics.Debugger.Break(); //didn't have a sample email to test this248
249if (MediaMainType == "text")250{251MediaSubType = "plain";252}253else254{255MediaSubType = "";256}257}258else259{260//also submedia found261MediaMainType = mediaTypeString.Substring(0, slashPosition);262
263if (mediaTypeString.Length > slashPosition)264{265MediaSubType = mediaTypeString.Substring(slashPosition + 1);266}267else268{269if (MediaMainType == "text")270{271MediaSubType = "plain";272}273else274{275MediaSubType = "";276System.Diagnostics.Debugger.Break(); //didn't have a sample email to test this277}278}279}280}281
282IsBodyHtml = MediaSubType.Equals("html");283}284
285/// <summary>286/// Creates an empty child MIME entity from the parent MIME entity.287///288/// An email can consist of several MIME entities. A entity has the same structure289/// like an email, that is header and body. The child inherits few properties290/// from the parent as default value.291/// </summary>292public MimeMessage CreateChildEntity()293{294MimeMessage child = new MimeMessage295{296Parent = this,297TopParent = this.TopParent,298ContentTransferEncoding = this.ContentTransferEncoding299};300
301return child;302}303
304/// <summary>305/// Save this attachment to a file.306/// </summary>307/// <param name="attachment">Attachment to save.</param>308/// <param name="path">Path to a file.</param>309public void SaveAttachmentToFile(Attachment attachment, string path)310{311//byte[] allBytes = new byte[attachment.ContentStream.Length];312//int bytesRead = attachment.ContentStream.Read(allBytes, 0, (int)attachment.ContentStream.Length);313
314//string destinationFile = @"C:\" + attachment.Name;315
316//BinaryWriter writer = new BinaryWriter(new FileStream(destinationFile, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None));317//writer.Write(allBytes);318//writer.Close();319
320using (var file = File.OpenWrite(path))321{322attachment.ContentStream.CopyTo(file);323}324}325
326/// <summary>327/// Save all attachments to files in a folder.328/// </summary>329/// <param name="path">Path to a folder for files.</param>330public void SaveAttachmentsToFolder(string path)331{332foreach (var attachment in Attachments)333{334SaveAttachmentToFile(attachment, Path.Combine(path, attachment.Name));335}336}337
338#endregion Public methods339
340#region Debug info341
342private StringBuilder _mailStructure;343
344private void AppendLine(string format, object arg)345{346if (arg != null)347{348string argString = arg.ToString();349
350if (argString.Length > 0)351{352_mailStructure.AppendLine(string.Format(format, argString));353}354}355}356
357private void DecodeEntity(MimeMessage entity)358{359AppendLine("From : {0}", entity.From);360AppendLine("Sender: {0}", entity.Sender);361AppendLine("To : {0}", entity.To);362AppendLine("CC : {0}", entity.CC);363AppendLine("ReplyT: {0}", entity.ReplyToList);364AppendLine("Subj : {0}", entity.Subject);365AppendLine("S-Enc : {0}", entity.SubjectEncoding);366
367if (entity.DeliveryDate > DateTime.MinValue)368{369AppendLine("Date : {0}", entity.DeliveryDate);370}371
372if (entity.Priority != MailPriority.Normal)373{374AppendLine("Priory: {0}", entity.Priority);375}376
377if (entity.Body.Length > 0)378{379AppendLine("Body : {0} byte(s)", entity.Body.Length);380AppendLine("B-Enc : {0}", entity.BodyEncoding);381}382else383{384if (entity.BodyEncoding != Encoding.ASCII)385{386AppendLine("B-Enc : {0}", entity.BodyEncoding);387}388}389
390AppendLine("T-Type: {0}", entity.TransferType);391AppendLine("C-Type: {0}", entity.ContentType);392AppendLine("C-Desc: {0}", entity.ContentDescription);393AppendLine("C-Disp: {0}", entity.ContentDisposition);394AppendLine("C-Id : {0}", entity.ContentId);395AppendLine("M-ID : {0}", entity.MessageId);396AppendLine("Mime : Version {0}", entity.MimeVersion);397
398if (entity.ContentStream != null)399{400AppendLine("Stream: Length {0}", entity.ContentStream.Length);401}402
403//decode all child MIME entities404foreach (MimeMessage child in entity.Entities)405{406_mailStructure.AppendLine("------------------------------------");407DecodeEntity(child);408}409
410if (entity.ContentType != null &&411entity.ContentType.MediaType != null &&412entity.ContentType.MediaType.StartsWith("multipart"))413{414AppendLine("End {0}", entity.ContentType.ToString());415}416}417
418/// <summary>419/// Convert structure of message into a string420/// </summary>421/// <returns>Debug text</returns>422public string MailStructure()423{424_mailStructure = new StringBuilder(1000);425DecodeEntity(this);426_mailStructure.AppendLine("====================================");427
428return _mailStructure.ToString();429}430
431#endregion Debug info432}433}
434