POP3命令码如下:
命令 参数 状态 描述
------------------------------------------
USER username 认可 此命令与下面的pass命令若成功,将导致状态转换
PASS password 认可
APOP Name,Digest 认可 Digest是MD5消息摘要
------------------------------------------
STAT None 处理 请求服务器发回关于邮箱的统计资料,如邮件总数和总字节数
UIDL [Msg#] 处理 返回邮件的唯一标识符,POP3会话的每个标识符都将是唯一的
LIST [Msg#] 处理 返回邮件数量和每个邮件的大小
RETR [Msg#] 处理 返回由参数标识的邮件的全部文本
DELE [Msg#] 处理 服务器将由参数标识的邮件标记为删除,由quit命令执行
RSET None 处理 服务器将重置所有标记为删除的邮件,用于撤消DELE命令
TOP [Msg#] 处理 服务器将返回由参数标识的邮件前n行内容,n必须是正整数
NOOP None 处理 服务器返回一个肯定的响应
如果POP3服务器是SSL的(比如GMAIL),连接服务器需要用 SslStream.
TcpClient tc = new TcpClient(server, port);
if (tc == null) {
NotifyMessageFun("错误", "尝试连接服务器" + server + ":" + port + "失败", NotifyMsgType.Error);
throw new Exception("连接服务器失败");
}
Stream st;
if (isSSL) {
SslStream sst = new SslStream(tc.GetStream());
sst.AuthenticateAsClient(server);
st = sst;
} else {
NetworkStream nst = tc.GetStream();
st = nst;
}
sw = new StreamWriter(st);
sr = new StreamReader(st);
连接之后,一定要先读一行,这一行是POP3服务器返回的欢迎信息,如果不读这一行,接着使用的USER 命令后在读,返回的还是这条欢迎信息,验证用户名是否正确的那条返回信息是在下一行才被返回,这样就影响了判断用户名是否正确。
如果提供的信息是正确的,返回的着3个字符是:+OK,否则是-ERR ,可以用这个来判断提供的信息是否正确,
LIST , RETR , UIDL, TOP 这几条命令返回的是多行,最后一行是一个英文的句号:“.”
UIDL返回的是邮件标识,格式如下:
+OK
1 GmailId11f312dd5ad8c1ad
2 GmailId11f312ee21e33479
3 GmailId11f36549f7112ade
.
GMAIL返回的顺序是最新的在最下面,其它的POP3服务器我没测,不知道是什么顺序。不过,同一台POP3服务器,返回的UIDL顺序一定是一致的。所以,只需要把UIDL存起来,下次在取出UIDL结果后,和以存的UIDL结果对比一下,就知道哪条是新邮件的标识了。
获取邮件头
不知道是不是所有POP3服务器 TOP 命令返回的信息顺序是不是一至,所以,只有约摸着取了。GMAIL的TOP 30 返回的信息足够取得邮件头的信息了。不过,不敢保存哪一行存的是 Subject, 哪一行存的是 Date 和 From ,所以,只有循环来判断。
Subject: 开头的就是标题,From开头的是发件人。
另外,如果包含中文,一般都会被加B编码或Q编码,不解码的话,你是看不懂什么意思的。
B编码是指 Base64, Q编码是指Quote_Printable
B编码是类似这样的:
Subject: =?gb2312?B?yq7Su8qx?=
Q编码是这个样子: =B3=C2=BF=A1=C7=E5=A3=AC=C4=FA=BA=C3=A3=A1
Quoted-Printable 加码规则(RFC 1341):
1. 字符用 =XX 形式表示,其中 XX 是该字符的十六进制值,
必须为 0-9 或者 A-F (使用大写字符),除非有可替换说明,
否则,此原则是强制性的。
2. 其中,十进制值 33-60 & 62-126(注意: 即不包含 '=' )
可以作为标准 ASCII 从而不进行转换。
3. 另外,十进制值 9-32 也可以作为制表和格式控制字符,
从而不进行转换。(注意,这个不是必须执行的,即也可以转换)
4. 由于在 RFC822 协议中规定主体 body 文本中各行均有最大字
符限制,因此,当主体文本中出现 CRLF 或者 LFCR 字符序列,
或者单独的 CR 以及 LF 字符的时候,必须转换成对应的
"=0D=0A","=0A=0D","=0D","=0A" 等编码来表示。
5. (关于软回车的问题) Quoted-Printable 编码要求编码后每行
最大字符数量不得超过 76 个字符。如果对大于该字符数量的行进
行编码,则必须使用软回车。所以,对于某个以编码行的最后加上
'='符号,则表示最后这个 '=' 是一个无意义的软回车。所以,如
果一个尚未编码的行的内容如下的话:
这里我用一个正则表达来取出编码(encoding)和编码方式(codeType),及需要取出的主体(ctx, ctx2):
private readonly Regex codingReg = new Regex(@"=\?(?<encoding>.*?)\?(?<codeType>.*?)\?(?<ctx>.*?)\?=(?<ctx2>.*)",RegexOptions.Compiled | RegexOptions.IgnoreCase);
/*
* =?gb2312?B?"或者"=?gb2312?Q?"或者没有
*/
private string parseString(string str){
Match ma = codingReg.Match(str);
if (ma.Success) {
string encoding = ma.Groups["encoding"].Value;
string codeType = ma.Groups["codeType"].Value;
string ctx = ma.Groups["ctx"].Value;
string ctx2 = ma.Groups["ctx2"].Value;
if (codeType == "B")
ctx = Encoding.GetEncoding(encoding).GetString(Convert.FromBase64String(ctx));
else if (codeType == "Q")
ctx = DecodeQ(ctx);
return ctx + " " + ctx2;
}
return str;
}
private string DecodeQ(string str) {
//string[] tmp = str.Split('=');
string[] tmp = str.Split(new char[] { '=' }, StringSplitOptions.RemoveEmptyEntries);
byte[] bt = new byte[tmp.Length];
for (int i = 0; i < tmp.Length; i++) {
bt[i] = (byte)int.Parse(tmp[i], System.Globalization.NumberStyles.HexNumber);
}
return Encoding.Default.GetString(bt);
}
B编码的,我测试过,没问题,但是Q编码的,我还没遇到,没测试,不过,DecodeQ是通过的。
| < Prev | Next > |
|---|
Last Updated ( Thursday, 09 July 2009 11:12 )



