-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMimeParser.cs
157 lines (122 loc) · 5.16 KB
/
MimeParser.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
using System;
using System.IO;
using System.Text;
using System.Linq;
using System.Net.Mime;
using System.Collections.Generic;
namespace POP
{
class MimeParser
{
private readonly List<string> lines;
private readonly string boundary;
public MimeParser(List<string> messageLines)
{
lines = messageLines;
boundary = ExtractBoundary();
}
public string GetBody()
{
var body = new List<string>();
foreach (List<string> section in FindSections())
body.Add(Decode(section));
return string.Concat(body);
}
private List<List<string>> FindSections()
{
var sections = new List<List<string>>();
var indexes = new List<int>();
for (int i = 0; i < lines.Count; i++)
{
if (lines[i].StartsWith(boundary))
indexes.Add(i);
}
var boundaries = new Dictionary<int, int>();
for (int i = 0; i < indexes.Count - 1; i++)
{
boundaries.Add(indexes[i], indexes[i + 1]);
}
foreach (var kv in boundaries)
sections.Add(lines.GetRange(kv.Key, kv.Value - kv.Key));
// Sometimes there is a plaintext and a html section
if (sections.Count < 2)
return sections;
// If the first section is text and the second is HTML then delete the first section because it is
// most likely the same thing but in a different format.
if (sections[0][1].Contains("Content-Type: text/plain;") &&
sections[1][1].Contains("Content-Type: text/html;"))
sections.RemoveAt(0);
return sections;
}
private string Decode(List<string> section)
{
int encodingIndex = (from line in section
where line.Contains("Content-Transfer-Encoding:")
select section.IndexOf(line)).FirstOrDefault();
string contentEncoding = section[encodingIndex].Split(' ')[1];
var encoding = TransferEncoding.Unknown;
if (contentEncoding == "7bit")
encoding = TransferEncoding.SevenBit;
//if (contentEncoding == "8bit") Need Framework 4.5
// encoding = TransferEncoding.EightBit;
if (contentEncoding == "quoted-printable")
encoding = TransferEncoding.QuotedPrintable;
if (contentEncoding.ToLower() == "base64")
encoding = TransferEncoding.Base64;
int space = (from line in section
where string.IsNullOrWhiteSpace(line)
select section.IndexOf(line)).FirstOrDefault();
section.RemoveRange(0, space + 1);
// Remove '=' at the end of each line
if (encoding != TransferEncoding.Base64)
{
for (int i = 0; i < section.Count; i++)
{
string current = section[i];
if (current.EndsWith("="))
section[i] = current.Substring(0, current.Length - 1);
}
}
MemoryStream ms;
string lsection = string.Join(string.Empty, section);
switch (encoding)
{
case TransferEncoding.Base64:
ms = new MemoryStream(Convert.FromBase64String(lsection), false);
break;
case TransferEncoding.QuotedPrintable:
ms = new MemoryStream(Encoding.UTF8.GetBytes(
string.Join(string.Empty, (from line in section
select POP.MailDecoder.DecodeSpecialChars(line)).ToList<string>())
));
break;
//case TransferEncoding.EightBit: Need framework 4.5
case TransferEncoding.SevenBit:
case TransferEncoding.Unknown:
default:
ms = new MemoryStream(Encoding.UTF8.GetBytes(lsection), false);
break;
}
string body;
using (var sr = new StreamReader(ms))
body = sr.ReadToEnd();
ms.Dispose();
return body;
}
private string ExtractBoundary()
{
int boundaryIndex = (from l in lines
where l.StartsWith("Content-Type:")
select lines.IndexOf(l)).FirstOrDefault();
if (!lines[boundaryIndex].Contains("boundary=\""))
boundaryIndex++;
string boundaryLine = lines[boundaryIndex];
if (boundaryLine.StartsWith("\t"))
boundaryLine = boundaryLine.Substring(1);
boundaryLine = boundaryLine.Substring(boundaryLine.IndexOf('"') + 1);
boundaryLine = boundaryLine.Remove(boundaryLine.Length - 1);
boundaryLine = string.Concat("--", boundaryLine); // MIME logic
return boundaryLine;
}
}
}