/// <summary> /// 初始化邮件体 /// </summary> public MailMessage() { var now = DateTime.Now; this.messageId = MailCommon.BuildMessageID(now); //this.boundary = MailCommon.BuildBoundary(now); this.boundary = this.messageId; }
/// <summary> /// 创建用于邮件体的邮件地址集合的数组 /// </summary> /// <param name="addressList">须要处理的集合</param> /// <param name="encoding"></param> /// <returns>返回字符串型式数组</returns> public static string JoinAddressList(List <MailAddress> addressList, Encoding encoding) { var array = new string[addressList.Count]; for (int i = 0, l = addressList.Count; i < l; i++) { var address = addressList[i]; array[i] = MailCommon.Base64EncodeAddress(address.Name, address.Address, encoding); } var result = string.Join(",", array); return(result); }
///// <summary> ///// 将一个数组合并成符合邮件的每行最大76字符字符串,结果追加至字符构建对象 ///// </summary> ///// <param name="list"></param> ///// <param name="builder"></param> ///// <param name="lineStart">每行初始字符</param> //public static void JoinLine76Break(StringBuilder builder, IList<string> list, string lineStart) //{ // var lineLength = builder.Length; // var lineStartLength = lineStart.Length; // if (lineStartLength > 76) // { // throw new ArgumentOutOfRangeException("lineStart", "length limit less than 76."); // } // for (int i = 0, l = list.Count; i < l; i++) // { // var itemLength = list[i].Length; // if (lineLength + itemLength > 76) // { // builder.Append(MailCommon.NewLine); // builder.Append(lineStart); // lineLength = itemLength + lineStartLength; // } // else // { // lineLength += itemLength; // } // builder.Append(list[i]); // } //} /// <summary> /// 编码邮件地址头 /// </summary> /// <param name="name"></param> /// <param name="address"></param> /// <param name="encoding"></param> /// <returns></returns> public static string Base64EncodeAddress(string name, string address, Encoding encoding) { //判断姓名是否为空 if (string.IsNullOrEmpty(name)) { name = address.Split('@')[0]; } //判断姓名是否为键盘可打印,如果是,则不进行编码,如果不是,则进行编码 if (MailCommon.IsAscii(name) == false) { name = MailCommon.Base64EncodHead(name, encoding); } return("\"" + name + "\" <" + address + ">"); }
private string GenerateBody() { if (string.IsNullOrEmpty(this.Body)) { throw new InvalidOperationException("message.Body is empty"); } //编码完成 var body = Convert.ToBase64String(this.encoding.GetBytes(this.Body)); var sb = new StringBuilder(); //begin sb.Append("--"); sb.Append(this.boundary); sb.Append(MailCommon.NewLine); //分隔完成 if (this.IsBodyHtml == true) { sb.Append("Content-Type: text/html; charset=" + this.encoding.HeaderName); } else { sb.Append("Content-Type: text/plain; charset=" + this.encoding.HeaderName); } sb.Append(MailCommon.NewLine); sb.Append("Content-Transfer-Encoding: base64"); sb.Append(MailCommon.NewLine); sb.Append(MailCommon.NewLine); sb.Append(MailCommon.Line76Break(body)); sb.Append(MailCommon.NewLine); sb.Append(MailCommon.NewLine); //end // "--" + this.Boundary + "--" + MailCommon.NewLine; sb.Append("--"); sb.Append(this.Boundary); sb.Append("--"); sb.Append(MailCommon.NewLine); return(sb.ToString()); }
/// <summary> /// 获取邮件头,并填充 OutputHeaders /// </summary> /// <exception cref="InvalidOperationException">message.From invalid or message.To is empty or subject is empty</exception> /// <returns></returns> public string GetHead() { if (this.From == null || string.IsNullOrEmpty(this.From.Address)) { throw new InvalidOperationException("message.From invalid"); } if (this.To == null || this.To.Count == 0) { throw new InvalidOperationException("message.To is empty"); } if (string.IsNullOrEmpty(this.Subject)) { throw new InvalidOperationException("subject is empty"); } this.outputHeaders = new NameValueCollection(15 + this.headers.Count); StringBuilder sb = new StringBuilder(); //message id var messageId = "<" + this.messageId + ">"; sb.Append("Message-ID: "); sb.Append(messageId); sb.Append(MailCommon.NewLine); outputHeaders["Message-ID"] = messageId; //from var from = MailCommon.Base64EncodeAddress(this.From.Name, this.From.Address, this.encoding); sb.Append("From: "); sb.Append(from); sb.Append(MailCommon.NewLine); outputHeaders["From"] = from; //replay if (this.ReplyTo != null) { var replayTo = MailCommon.Base64EncodeAddress(this.ReplyTo.Name, this.ReplyTo.Address, this.encoding); sb.Append("Replay-To: "); sb.Append(replayTo); sb.Append(MailCommon.NewLine); outputHeaders["Replay-To"] = replayTo; } //to var to = MailCommon.JoinAddressList(this.to, this.encoding); sb.Append("To: "); sb.Append(to); sb.Append(MailCommon.NewLine); outputHeaders["To"] = to; //cc if (this.cc != null && this.cc.Count > 0) { var cc = MailCommon.JoinAddressList(this.cc, this.encoding); sb.Append("CC: "); sb.Append(cc); sb.Append(MailCommon.NewLine); outputHeaders["CC"] = cc; } //Subject var subject = ""; sb.Append("Subject: "); if (MailCommon.IsAscii(this.Subject)) { subject = this.Subject; } else { subject = MailCommon.Base64EncodHead(this.Subject, this.encoding); } sb.Append(subject); sb.Append(MailCommon.NewLine); outputHeaders["Subject"] = subject; //Data var date = DateTime.UtcNow.ToString("R"); sb.Append("Date: "); sb.Append(date); sb.Append(MailCommon.NewLine); outputHeaders["Date"] = date; //Mime sb.Append(MailCommon.MimeVersion); sb.Append(MailCommon.NewLine); outputHeaders["MIME-Version"] = "1.0"; //priority if (this.Priority != MailPriority.Normal) { sb.Append("X-Priority: "); sb.Append((int)this.Priority); sb.Append(MailCommon.NewLine); outputHeaders["X-Priority"] = ((int)this.Priority).ToString(); } //Mailer sb.Append(MailCommon.Mailer); sb.Append(MailCommon.NewLine); outputHeaders["X-Mailer"] = "http://www.aooshi.org/adf/"; //Content-Type //sb.Append(CreateContentType()); var contentType = "multipart/alternative;\r\n\tboundary=\"" + this.boundary + "\""; sb.Append("Content-Type: "); sb.Append(contentType); sb.Append(MailCommon.NewLine); outputHeaders["Content-Type"] = contentType; //encoding sb.Append("Content-Transfer-Encoding: base64"); sb.Append(MailCommon.NewLine); outputHeaders["Content-Transfer-Encoding"] = "base64"; //custom headers foreach (var key in this.headers.AllKeys) { sb.Append(key); sb.Append(": "); sb.Append(this.headers[key]); sb.Append(MailCommon.NewLine); // outputHeaders[key] = this.headers[key]; } //dkim var dkim = this.dkim; if (dkim != null) { var dkimValue = dkim.Sign(this); // sb.Insert(0, dkimValue + MailCommon.NewLine); } // sb.Append(MailCommon.NewLine); return(sb.ToString()); }
/// <summary> /// DKIM message /// </summary> /// <param name="message"></param> /// <returns></returns> public string Sign(MailMessage message) { //http://dkimcore.org/specification.html#1 //DKIM-Signature: v=1;a=rsa-sha256;bh=BODYHASH;c=relaxed;d=TOKEN;h=HEADERS;s=SELECTOR;b=SIGNATURE //http://www.dkim.org/specs/rfc4871-dkimbase.html#dkim-sig-hdr /* * DKIM-Signature: v=1; a=rsa-sha256; c=simple/relaxed; d=domain.com; * h=From:To:Subject:Date; q=dns/txt; s=newsletter17100; t=1510020630; * bh=+5tUEqyyuolGsLc9usglUSkelpZtxEkEWKaGC21PSEM=; * b= */ var dsLength = "DKIM-Signature: ".Length; var build = new StringBuilder(128); build.Append("DKIM-Signature:"); build.Append(" v=1;"); // string algorithm = this.algorithm == DKIMAlgorithm.RSASha1 ? "rsa-sha1" : "rsa-sha256"; build.Append(" a=").Append(algorithm).Append(';'); //c=header type / bodey type; string htype = this.headerType == DKIMType.Simple ? "simple" : "relaxed"; string bType = this.bodyType == DKIMType.Simple ? "simple" : "relaxed"; //build.Append(" c=simple/relaxed;"); build.Append(" c=").Append(htype).Append('/').Append(bType).Append(';').Append(MailCommon.NewLine); //create time long unixtime = Adf.UnixTimestampHelper.ToInt64Timestamp(); build.Append("\tt=").Append(unixtime).Append(';'); build.Append(" q=dns/txt;").Append(MailCommon.NewLine); // domain build.Append("\td=").Append(domain).Append(';'); // DNS selector build.Append(" s=").Append(selector).Append(';').Append(MailCommon.NewLine); //headers build.Append("\th=").Append(string.Join(":", this.signHeaders)).Append(';').Append(MailCommon.NewLine); //body hash var body = ""; if (this.bodyType == DKIMType.Relaxed) { body = this.RelaxedBodyCanonicalization(message.GetBody()); } else { body = this.SimpleBodyCanonicalization(message.GetBody()); } var bodyBytes = message.Encoding.GetBytes(body); var bodySigned = this.Hash(bodyBytes); var bodyHash = Convert.ToBase64String(bodySigned); build.Append("\tbh=").Append(bodyHash).Append(';').Append(MailCommon.NewLine); //b build.Append("\tb="); //build string /* * DKIM-Signature: v=1; a=rsa-sha256; c=simple/relaxed; d=caping.co.id; * s=newsletter17800; q=dns/txt; t=1510023325; h=From:To:Subject:Date; * h=yklAmb1CNh0SAfmrk97PlZTEHrGKV94Ps6R7KDwLLoo=; * b= */ //set current DKIM-Signature var dkimHead = build.ToString(dsLength, build.Length - dsLength); message.OutputHeaders["DKIM-Signature"] = dkimHead; var headers = ""; if (this.headerType == DKIMType.Relaxed) { headers = this.RelaxedHeaderCanonicalization(message.OutputHeaders); } else { headers = this.SimpleHeaderCanonicalization(message.OutputHeaders); } var headerBytes = message.Encoding.GetBytes(headers); var headerSigned = this.Sign(headerBytes); // assumes signature ends with "b=" var b = Convert.ToBase64String(headerSigned); b = MailCommon.Line76Break("b=" + b, "\t"); //remove last b build.Length -= "\tb=".Length; //append b build.Append(b); //update DKIM-Signature dkimHead = build.ToString(dsLength, build.Length - dsLength); message.OutputHeaders["DKIM-Signature"] = dkimHead; var signed = build.ToString(); return(signed); }