/// <summary> /// ファイルから読込(宛先、送信元は読み込まない) /// </summary> /// <param name="filename">ファイル名</param> /// <returns>(件名, 本文)</returns> public static (string, string) LoadFromFile(string filename) { byte[] data = File.ReadAllBytes(filename); string subjectText = ""; string bodyText = ""; // ボディ部の先頭を見つける int indexBody = 0; for (int i = 0; i < data.Length; ++i) { if (CharCodeUtils.IsCrLf(data, i) && CharCodeUtils.IsCrLf(data, i + 2)) { indexBody = i + 4; break; } } if (indexBody == 0) { throw new Exception("形式が違います: ボディ部が存在しません。"); } string headers = Encoding.ASCII.GetString(data, 0, indexBody); subjectText = GetSubjectText(headers); bodyText = GetBodyText(data, indexBody); return(subjectText, bodyText); }
/// <summary> /// 全角文字の文字コード(JIS)から文字を取得する /// </summary> /// <param name="jiscode">JIS コード</param> /// <returns>文字(1文字): 取得できない場合は null</returns> public static string GetCharFromJisCode(int jiscode) { (byte high, byte low) = CharCodeUtils.SplitHL(jiscode); if (0x21 <= high && high <= 0x7e) { // OK } else { return(null); } if (0x21 <= low && low <= 0x7e) { // OK } else { return(null); } byte[] bistr = new byte[] { JIS_BEGIN_ESCAPE_SEQUENCE[0], JIS_BEGIN_ESCAPE_SEQUENCE[1], JIS_BEGIN_ESCAPE_SEQUENCE[2], high, low, JIS_END_ESCAPE_SEQUENCE[0], JIS_END_ESCAPE_SEQUENCE[1], JIS_END_ESCAPE_SEQUENCE[2] }; string result = null; try { result = jisEncoding.GetString(bistr); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex.Message); } return(result); }
/// <summary> /// 本文を JIS エンコーディングした MemoryStream を返す /// NOTE: 半角カタカナは処理できないので渡さないこと /// </summary> /// <param name="bodyText">本文</param> /// <returns>エンコーディング内容を格納した MemoryStream</returns> private static MemoryStream GetBodyStream(string bodyText) { int maxBodyCols; if (DataBags.Config.ForceInsertLineFeed) { maxBodyCols = DataBags.Config.MaxBodyCols * 2; } else { maxBodyCols = int.MaxValue; } MemoryStream stream = new MemoryStream(); int cols = 0; bool jisIn = false; for (int i = 0; i < bodyText.Length; ++i) { string sch = bodyText.Substring(i, 1); if (StringUtils.IsAsciiWithControl(sch)) { if (jisIn) { CharCodeUtils.WriteJisEndEscapeSequence(stream); } jisIn = false; if (sch[0] == '\n') { CharCodeUtils.WriteCrLf(stream); cols = 0; } else { char ascii = sch[0]; stream.WriteByte((byte)(ascii & 0xFF)); ++cols; } } else { if (!jisIn) { CharCodeUtils.WriteJisBeginEscapeSequence(stream); } jisIn = true; int jiscode; Emoji emoji = DataBags.Emojis.GetFromUnicode(sch[0]); if (emoji != null) { jiscode = emoji.Jiscode; } else { jiscode = CharCodeUtils.GetJisCodeFromChar(sch); if (jiscode == 0) { jiscode = CharCodeUtils.JIS_WHITE_SPACE_CODE; } } (byte high, byte low) = CharCodeUtils.SplitHL(jiscode); stream.WriteByte(high); stream.WriteByte(low); cols += 2; } if (maxBodyCols <= cols) { if (jisIn) { CharCodeUtils.WriteJisEndEscapeSequence(stream); } CharCodeUtils.WriteCrLfWithFlowed(stream); if (jisIn) { CharCodeUtils.WriteJisBeginEscapeSequence(stream); } cols = 0; } } if (jisIn) { CharCodeUtils.WriteJisEndEscapeSequence(stream); } return(stream); }
/// <summary> /// 件名を固定長に分け、BASE64 エンコーディングして返す /// NOTE: 改行は処理できないので渡さないこと /// NOTE: 半角カタカナは処理できないので渡さないこと /// </summary> /// <param name="subjectText">件名</param> /// <param name="maxCols">件名を分けるための長さ</param> /// <returns>複数の BASE64 文字列の一覧</returns> private static List <string> GetBase64EncodingBlocks(string subjectText, int maxCols) { List <string> base64EncodingBlocks = new List <string>(); MemoryStream stream = new MemoryStream(); int cols = 0; bool jisIn = false; for (int i = 0; i < subjectText.Length; ++i) { string sch = subjectText.Substring(i, 1); if (StringUtils.IsAsciiWithControl(sch)) { if (jisIn) { CharCodeUtils.WriteJisEndEscapeSequence(stream); } jisIn = false; char ascii = sch[0]; stream.WriteByte((byte)(ascii & 0xFF)); ++cols; } else { if (!jisIn) { CharCodeUtils.WriteJisBeginEscapeSequence(stream); } jisIn = true; int jiscode; Emoji emoji = DataBags.Emojis.GetFromUnicode(sch[0]); if (emoji != null) { jiscode = emoji.Jiscode; } else { jiscode = CharCodeUtils.GetJisCodeFromChar(sch); if (jiscode == 0) { jiscode = CharCodeUtils.JIS_WHITE_SPACE_CODE; } } (byte high, byte low) = CharCodeUtils.SplitHL(jiscode); stream.WriteByte(high); stream.WriteByte(low); ++cols; } if (maxCols <= cols) { if (jisIn) { CharCodeUtils.WriteJisEndEscapeSequence(stream); } base64EncodingBlocks.Add(GetBase64EncodingBlock(stream)); stream = new MemoryStream(); if (jisIn) { CharCodeUtils.WriteJisBeginEscapeSequence(stream); } cols = 0; } } if (jisIn) { CharCodeUtils.WriteJisEndEscapeSequence(stream); } if (0 < stream.Length) { base64EncodingBlocks.Add(GetBase64EncodingBlock(stream)); } return(base64EncodingBlocks); }
/// <summary> /// ヘッダ文字列から件名を取得する /// NOTE: 自分で出力した形式にしか対応していない /// </summary> /// <param name="headers">ヘッダ文字列</param> /// <returns>件名</returns> private static string GetSubjectText(string headers) { int startIndex = headers.IndexOf(MailMessage.SUBJECT, 0, StringComparison.CurrentCultureIgnoreCase); if (startIndex < 0) { return(""); } startIndex += MailMessage.SUBJECT.Length; int index = startIndex; int endIndex = startIndex; while (true) { index = headers.IndexOf("\r\n", index); if (index < 0) { break; } if (headers.Length <= index + 2) { break; } endIndex = index; if (headers[index + 2] != ' ' && headers[index + 2] != '\t') { break; } index += 2; } if (endIndex == startIndex) { return(""); } string subject = headers.Substring(startIndex, endIndex - startIndex); Regex regex = new Regex(@"=\?(?<charset>.*?)\?(?<encoding>.)\?(?<value>.*?)\?="); StringBuilder result = new StringBuilder(); MatchCollection matchCollection = regex.Matches(subject); foreach (Match match in matchCollection) { if (match.Success) { string charset = match.Groups["charset"].Value.ToLower(); string encoding = match.Groups["encoding"].Value.ToLower(); string val = match.Groups["value"].Value; if (charset == "iso-2022-jp" && encoding == "b") { // OK } else { // 未対応 return(""); } byte[] dataPart = Convert.FromBase64String(val); bool inJpn = false; int dataPartLength = dataPart.Length; for (int i = 0; i < dataPartLength;) { if (CharCodeUtils.IsBeginEscapeSequence(dataPart, i)) { inJpn = true; i += 3; continue; } if (CharCodeUtils.IsEndEscapeSequence(dataPart, i)) { inJpn = false; i += 3; continue; } if (inJpn) { if (i <= dataPartLength - 2) { int jiscode = CharCodeUtils.MergeHL(dataPart[i], dataPart[i + 1]); Emoji emoji = DataBags.Emojis.GetFromJiscode(jiscode); if (emoji != null) { result.Append(emoji.Unicode); } else { string sch = CharCodeUtils.GetCharFromJisCode(jiscode); if (sch != null) { result.Append(sch); } } i += 2; } else { i += 1; } } else { char ascii = (char)dataPart[i]; result.Append(ascii); i += 1; } } } } return(result.ToString()); }
/// <summary> /// ボディ部データから本文を取得する /// NOTE: 自分で出力した形式にしか対応していない /// </summary> /// <param name="data">ボディ部データ</param> /// <param name="offset">オフセット</param> /// <returns>本文</returns> private static string GetBodyText(byte[] data, int offset) { bool inJpn = false; int dataLength = data.Length; StringBuilder result = new StringBuilder(); for (int i = offset; i < dataLength;) { if (CharCodeUtils.IsCrLf(data, i)) { inJpn = false; i += 2; result.Append("\r\n"); continue; } if (CharCodeUtils.IsBeginEscapeSequence(data, i)) { inJpn = true; i += 3; continue; } if (CharCodeUtils.IsEndEscapeSequence(data, i)) { inJpn = false; i += 3; continue; } if (inJpn) { if (i <= dataLength - 2) { int jiscode = CharCodeUtils.MergeHL(data[i], data[i + 1]); Emoji emoji = DataBags.Emojis.GetFromJiscode(jiscode); if (emoji != null) { result.Append(emoji.Unicode); } else { string sch = CharCodeUtils.GetCharFromJisCode(jiscode); if (sch != null) { result.Append(sch); } } i += 2; } else { i += 1; } } else { char ascii = (char)data[i]; result.Append(ascii); i += 1; } } return(result.ToString()); }