刚开始做邮件服务器开发,一切都是茫然的。在书上网上都很难找到一套完整的邮件服务器开发教程。在个人的摸索中碰到了很多蛋疼得问题。现终于完成了,将我的开发经验分享给大家。
开发环境:vs2012 mfc
注意事项:
1、 网络环境:
作为邮件服务器,要接收来自互联网的邮件,就得有能映射到外网的服务器。至少映射25(SMTP)端口(pop3都暂时不重要)。对于没有外网条件的的小伙伴,推荐以下几种方法调试:
A、如果你使用model上网,查看你的电脑的外网IP。看看是否和model的一致。一致则说明你有一个公网IP。如果没有,就致电网络运营商,叫他给你换回公网IP,语气一定要强硬。有了公网IP,你就能把smtp的端口映射出去。具体方法请搜索。
B、对于一些校园网或大型局域网的用户,那就有点悲剧了,基本无法直接将端口映射出去。但可以参考C方法。
C、先在的网上的网络穿透都做的比较好,甚至能媲美公网ip的直接映射。去网上下载一些穿透软件:花生壳之类的。都有提供内网穿透。给大家推荐个免费的网络穿透软件:nat123 。这软件很好有,up主不经意的发现有大量的小学生用它在开MC的服务器。
D、如果你暂不需要使用外部的邮件服务器来测试你的服务器,那就在内网用其他的邮件服务器发送至你的邮箱进行测试,要注意端口冲突。
2、 编写smtp邮件服务器:
对于拥有外网映射的,可以先编写smtp 的接收服务器。这样的好处是,能接收识别它域的不同类型格式的邮件,到自己发送邮件的时候,对邮件格式能有一个很好的组织。 而对于只能使用内网的用户,建议先编写发送端。成功进行对各个邮件服务器的发送后(这里比较蛋疼,后面会介绍到),就可以在编写邮件接收端的时候用来测试。
3、 邮件服务器域名(MX)的获取:
最开始用telnet测试163的smtp。网上搜索各大邮件服务器的stmp服务器,当初搜索出来的结果把我着实误导了好一阵子。举个例子把,就常用的企鹅邮箱,搜索出来的结果是:smtp.qq.com 25 。
于是傻傻地telnet上去helo他,他告诉我他是ehlo服务器,要登陆验证遇到这种问题真是想哭,我一个发邮件的我给你登陆什么啊,其他smtp.jbjb.com邮箱也是这样?与是想了各种奇葩的情况与方法浪费了很多时间。最后自己去抓了一个邮件服务器的向企鹅邮箱发送的包,首先是几个dns的包。看到有几个dns包,自己心理顿时想到了smtp前缀的域名不是接收外域邮件的域名,看了下dns到的地址:mx1.qq.com mx2.qq.com... 原来这才是接收邮件的服务器域名。这时候才明白自己以前挂的域名时也配置有MX地址。 总之,我们发邮件一定要先获取邮件接收服务器的域名。
4、 防止反垃圾邮件:
把邮件发往其他邮件服务器,很容易被识别为垃圾文件。新浪邮箱最讨厌,直接不信任我的域名和IP地址。最初邮件格式不完善,也被企鹅断断续续封了几天。就163最包容,我的邮件都慷慨地接收了。
现在发邮件,除了基本的格式,不用MINE根本不行,尤其是当需要图片或者附件的邮件。
5、 加密:
一般可用base64对邮件标题和内容进行加密。
6、 邮件存储:
建议储存为eml格式的邮件文件,不仅利于转发或其他应用查看,也便于之后的pop3服务器使用。
7、 web端cms
超麻烦的东西,找个模板改改吧。
先粗略讲讲SMTP协议发送邮件的过程:
直接举个例子:
R: 220 mx.jb.com MX JB Mail Sever
S: helo wchrt.vicp.cc
R: 250 OK
S: mail from: <jb@wchrt.vicp.cc>
R: 250 OK
S: rcpt to: <414322153@qq.com>
R: 250 OK
S: data
R: 354 End data with<CR><LF>.<CR><LF>
S: mail from: jb@wchrt.vicp.cc
S: rcpt to: 414322153@qq.com
S: subject: 你好
S: 约不约?
S: <CR><LF>.<CR><LF>
R: 250 OK in queue
S: QUIT
R: 221 close
该次对话中首先我们链接服务器后服务器给我们返回了220 和服务器的域名信息,表示该邮件服务器正常工作,可以提供服务。
然后我们发送helo过去标示自己服务器的域名。
服务收到后250说明和helo成功
之后是mailfrom 说明发件人的email地址
250 ok后再rcptto 说明发送目标的Email地址
成功后,就可以发送data命令发送邮件内容了。
Data返回的值说明邮件以<CR><LF>.<CR><LF>为结束标记。<CR><LF>这个标示符用换行符\r\n即可。
最后返回250 标示邮件接收成功。QUIT退出。
以下是服务器返回的一些编号:
501 参数格式错误
502 命令不可实现
503错误的命令序列
504 命令参数不可实现
211系统状态或系统帮助响应
214帮助信息
220 <domain>服务就绪
221 <domain>服务关闭传输信道
421 <domain>服务未就绪,关闭传输信道(当必须关闭时,此应答可以作为对任何命令的响应)
250要求的邮件操作完成
251用户非本地,将转发向<forward-path>
450要求的邮件操作未完成,邮箱不可用(例如,邮箱忙)
550要求的邮件操作未完成,邮箱不可用(例如,邮箱未找到,或不可访问)
451放弃要求的操作;处理过程中出错
551用户非本地,请尝试<forward-path>
452 系统存储不足,要求的操作未执行
552 过量的存储分配,要求的操作未执行
553 邮箱名不可用,要求的操作未执行(例如邮箱格式错误)
354 开始邮件输入,以<CRLF>.<CRLF>结束
554 操作失败
具体的内容请自行搜索smtp协议
大致清楚协议后就可以开始邮件接收端的编程,协议的细节的在调试过成功会慢慢清楚的。
服务器接收端代码:
1 UINT mailsever::listenthread(LPVOID Param) 2 { 3 int ret; 4 SOCKET listensock;//接收邮件请求的套接字 5 listensock=socket(AF_INET,SOCK_STREAM,0); 6 TRACE("listensock: %d\n",listensock); 7 8 sockaddr_in saddr; 9 saddr.sin_family=AF_INET; 10 saddr.sin_port=htons(25);//25 smtp端口 11 saddr.sin_addr.S_un.S_addr=INADDR_ANY; 12 13 ret=bind(listensock,(sockaddr *)&saddr,sizeof(sockaddr_in)); 14 15 ret=listen(listensock,20);//一个小服务器,暂时就只设置20的链接上限 16 17 that->isseverstart=true; 18 CString str; 19 that->GetDlgItemTextA(IDC_LOG,str); 20 str+="收信服务开启\r\n";; 21 that->SetDlgItemTextA(IDC_LOG,str); 22 23 sockaddr_in newaddr; 24 int newlen; 25 while(1) 26 { 27 SOCKET newsock=accept(listensock,(sockaddr *)&newaddr,&newlen);//获取到新邮件请求的套接字 28 if(newsock!=SOCKET_ERROR) 29 { 30 AfxBeginThread(dealthread,&newsock);//开启新线程接收邮件 31 } 32 } 33 that->isseverstart=false; 34 return 0; 35 } 36 37 static bool strcom(const char *s1,const char *s2) 38 { 39 int len=strlen(s2); 40 if(strlen(s1)<len) 41 { 42 return false; 43 } 44 int py=0; 45 for(int i=0;i<len;i++) 46 { 47 if(s1[i]>='A'&&s1[i]<='Z') 48 { 49 py=32; 50 } 51 else 52 { 53 py=0; 54 } 55 if(s1[i]+py!=s2[i]) 56 { 57 return false; 58 } 59 } 60 61 return true; 62 } 63 64 static bool isdataend(const char *ss) 65 { 66 int len=strlen(ss); 67 for(int i=0;i<len-2;i++) 68 { 69 if(ss[i]=='\n'&&ss[i+1]=='.'&&ss[i+2]=='\r') 70 { 71 return true; 72 } 73 } 74 return false; 75 } 76 77 78 79 UINT mailsever::dealthread(LPVOID Param)//邮件接收线程 80 { 81 82 SOCKET sock=*(SOCKET *)Param; 83 CString sdata; 84 char rdata[2048]; 85 int rlen; 86 87 sdata.Format("220 wchrt.vicp.cc smtp WC Mail Server\r\n"); 88 send(sock,LPCTSTR(sdata),sdata.GetLength(),0);//回答本服务器状态 89 90 maildata rmail;//储存邮件的maildata类 91 92 bool ishelo=false; 93 bool ismail=false; 94 bool isrcpt=false; 95 bool isdata=false; 96 bool isenddata=false; 97 bool istitle=false; 98 bool isend=false; 99 while(!isend) 100 { 101 rlen=recv(sock,rdata,2047,0); 102 if(rlen==0&&isenddata) 103 { 104 break; 105 } 106 if(rlen>2048||rlen<0) 107 { 108 continue; 109 } 110 rdata[rlen]='\0'; 111 CString str; 112 CString ss; 113 114 115 if(strcom(rdata,"helo")||strcom(rdata,"ehlo"))//处理helo请求 116 { 117 sdata.Format("250-wchrt.vicp.cc\r\n250 OK\r\n"); 118 ishelo=true; 119 } 120 else if(strcom(rdata,"mail from"))//处理邮件来源信息 121 { 122 int i=0; 123 while(i<rlen&&rdata[i]!=':') 124 { 125 i++; 126 } 127 if(i<rlen) 128 { 129 rmail.from.Format("%s",rdata+i); 130 } 131 ismail=true; 132 sdata.Format("250 OK\r\n"); 133 } 134 else if(strcom(rdata,"rcpt to"))//处理邮件目的地信息(本地暂未按邮件用户区分,统一接收在一起) 135 { 136 int i=0; 137 while(i<rlen&&rdata[i]!=':') 138 { 139 i++; 140 } 141 if(i<rlen) 142 { 143 rmail.to.Format("%s",rdata+i); 144 } 145 isrcpt=true; 146 sdata.Format("250 OK\r\n"); 147 } 148 else if(strcom(rdata,"data"))//处理data请求 149 { 150 if(!ismail||!isrcpt) 151 { 152 sdata.Format("503 is not mail or rcpt\r\n"); 153 } 154 else 155 { 156 sdata.Format("354 end with <CRLF>.<CRLF>\r\n"); 157 isdata=true; 158 } 159 } 160 else if(strcom(rdata,"quit"))//处理退出服务请求 161 { 162 isend=true; 163 break; 164 } 165 else 166 { 167 if(isdata)//接收邮件内容 168 { 169 rmail.alldata+=rdata; 170 if(isdataend(rdata)) 171 { 172 rmail.alldata.Replace("\r\n.\r\n","\r\n"); 173 isdata=false; 174 sdata.Format("250 OK\r\n"); 175 isenddata=true; 176 } 177 else 178 { 179 continue; 180 } 181 } 182 else 183 { 184 sdata.Format("250 OK\r\n"); 185 } 186 } 187 send(sock,(LPCTSTR)sdata,sdata.GetLength(),0);//返回应答 188 } 189 190 // 开始处理并储存接收到的邮件,处理过程详见maildata.cpp 191 //maildata::getmailinfo(rmail); 192 CString mid; 193 mid.Format("%d",that->mailid+1); 194 if(maildata::saveeml("all",mid,rmail)) 195 { 196 that->mailid++; 197 that->mailnum++; 198 CFile file; 199 if(!file.Open("mail\\info",CFile::modeReadWrite)) 200 { 201 if(!file.Open("mail\\info",CFile::modeCreate|CFile::modeReadWrite)) 202 { 203 AfxMessageBox("makeinfo error"); 204 return false; 205 } 206 } 207 CString str; 208 str.Format("%d\r\n%d",that->mailid,that->mailnum); 209 file.Write(str.GetString(),str.GetLength()); 210 file.Close(); 211 212 that->GetDlgItemTextA(IDC_LOG,str); 213 str+="new mail\r\n"; 214 that->SetDlgItemTextA(IDC_LOG,str); 215 } 216 return 0; 217 }
邮件发送端:
举个例子,我们要往邮箱:414322153@qq.com 发送一个邮件。应遵循以下步骤:
1、提取出域名后缀:qq.com。
2、DNS查询该域名的MX记录。
3、根据查询到的MX域名或IP地址链接该邮件服务器
4、使用smtp协议发送邮件
因为要进行DNS查询,mfc没有提供关于dns查询的类。只好自己手动链接dns服务器根据dns协议查询mx记录。或者是调用windows自带的nslookup来查询dns。
为了防止大家一些接触太多东西,这里就给大家讲一下简单实用nslookup查询mx记录的方法。等有精力再去学习dns协议。
cmd输入:nslookup
将查询设置为mx:set q=mx
开始查询:qq.com
见截图:
为方便起见,我们就不做服务器的连通测试,直接使用第一个MX地址发送邮件。
以下是发送邮件的代码:
1 static bool getres(SOCKET sock,const char *s,const char *s2=NULL) 2 { 3 char *rdata=new char [2048]; 4 int rlen; 5 CString rr,str; 6 7 rlen=recv(sock,rdata,2047,0); 8 rdata[rlen]='\0'; 9 10 rr.Format("%s",rdata); 11 that->GetDlgItemTextA(IDC_LOG,str); 12 str+=rr+"\r\n";; 13 that->SetDlgItemTextA(IDC_LOG,str); 14 15 /*TRACE("%s\n",rdata); 16 CString ss=rdata; 17 AfxMessageBox(ss);*/ 18 19 if(!strcom(rdata,s)) 20 { 21 if(s2!=NULL) 22 { 23 if(!strcom(rdata,s2)) 24 { 25 return false; 26 } 27 } 28 else 29 { 30 return false; 31 } 32 } 33 return true; 34 } 35 UINT mailsever::sendthread(LPVOID Param) 36 { 37 CString mpath; 38 mpath.Format("%s",Param); 39 //AfxMessageBox(mpath); 40 CFile file; 41 if(!file.Open(mpath,CFile::modeRead)) 42 { 43 return -1; 44 } 45 46 maildata rmail; 47 char s[20480]; 48 memset(s,'\0',sizeof(s)); 49 file.Read(s,min(file.GetLength(),20470)); 50 rmail.alldata.Format(s); 51 52 maildata::getmailinfo(rmail); 53 54 rmail.gettoaddress(); 55 56 //AfxMessageBox(rmail.toaddress); 57 58 59 //dns获取域名mx记录 60 char *szDomainName= (char *)rmail.toaddress.GetString(); 61 std::vector<ULONG> veculIPList; 62 std::vector<std::string> vecstrIPList; 63 std::vector<std::string> vecMXList; 64 ULONG ulTimeSpent = 0; 65 CDNSLookup dnslookup; 66 //使用114.114.114.144 dns服务 67 BOOL bRet = dnslookup.DNSLookup(inet_addr("114.114.114.114"), szDomainName, &vecstrIPList, &vecMXList, 1000, &ulTimeSpent); 68 if(!bRet) 69 { 70 return -1; 71 } 72 vecMXList[0].c_str(); 73 CString ss; 74 ss.Format("%s",vecMXList[0].c_str());//获取第一条记录 75 76 //AfxMessageBox(ss); 77 78 SOCKET sock=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); 79 sockaddr_in saddr; 80 saddr.sin_family =AF_INET; 81 saddr.sin_port=htons(25); 82 saddr.sin_addr.S_un.S_addr=*(u_long *)gethostbyname(vecMXList[0].c_str())->h_addr_list[0]; 83 84 /*CString sd=inet_ntoa(saddr.sin_addr); 85 86 AfxMessageBox(sd);*/ 87 88 if(SOCKET_ERROR==connect(sock,(sockaddr*)&saddr,sizeof(saddr))) 89 { 90 return -1; 91 } 92 93 94 95 CString sdata; 96 97 98 if(!getres(sock,"220")) 99 { 100 return -1; 101 } 102 103 104 sdata.Format("HELO wchrt.vicp.cc\r\n"); 105 send(sock,sdata.GetString(),sdata.GetLength(),0); 106 if(!getres(sock,"250")) 107 { 108 return -1; 109 } 110 111 112 sdata.Format("MAIL FROM: <%s>\r\n",rmail.from.GetString()); 113 send(sock,sdata.GetString(),sdata.GetLength(),0); 114 if(!getres(sock,"250")) 115 { 116 return -1; 117 } 118 119 120 sdata.Format("RCPT TO: <%s>\r\n",rmail.to.GetString()); 121 send(sock,sdata.GetString(),sdata.GetLength(),0); 122 if(!getres(sock,"250")) 123 { 124 return -1; 125 } 126 127 128 129 sdata.Format("DATA\r\n"); 130 send(sock,sdata.GetString(),sdata.GetLength(),0); 131 if(!getres(sock,"2","3")) 132 { 133 return -1; 134 } 135 136 137 sdata=rmail.alldata; 138 139 140 141 142 for(int i=0;i<sdata.GetLength();i+=1024) 143 { 144 /*TRACE("%d %d \n",i,sdata.GetLength()); 145 char bb[1025]; 146 int j; 147 for(j=0;j<min(strlen(sdata.GetString()+i),1024);j++) 148 { 149 bb[j]=(sdata.GetString()+i)[j]; 150 }bb[j]='\0'; 151 TRACE("%s",bb); 152 CString ss=bb; 153 AfxMessageBox(ss);*/ 154 155 send(sock,sdata.GetString()+i,min(strlen(sdata.GetString()+i),1024),0); 156 } 157 sdata.Format("\r\n\r\n.\r\n"); 158 send(sock,sdata.GetString(),sdata.GetLength(),0); 159 if(!getres(sock,"2")) 160 { 161 return -1; 162 } 163 164 165 sdata.Format("QUIT\r\n"); 166 send(sock,sdata.GetString(),sdata.GetLength(),0); 167 if(!getres(sock,"2")) 168 { 169 return -1; 170 } 171 AfxMessageBox("ok"); 172 }
maildata类:
1 maildata.h 2 3 #pragma once 4 #define MAIL_TYPE_RECV 1 5 #define MAIL_TYPE_SEND 2 6 #define MAIL_TYPE_USEND 3 7 8 class maildata:public CObject 9 { 10 public: 11 maildata(void); 12 13 ~maildata(void); 14 15 USHORT type; 16 CString alldata; 17 18 CString date; 19 CString from; 20 CString to; 21 CString subject; 22 CString content; 23 CString contenttype; 24 25 CString toaddress; 26 27 //void operator = (const maildata &); 28 void Serialize(CArchive &); 29 DECLARE_SERIAL(maildata); 30 31 32 static void getmailbaseinfo(maildata &); 33 static void getmailinfo(maildata &); 34 static bool setmailforsend(maildata &); 35 36 static bool openeml(const CString,const CString,maildata &); 37 static bool saveeml(const CString,const CString,maildata &); 38 bool gettoaddress(); 39 };
1 maildata.cpp 2 3 #include "stdafx.h" 4 #include "maildata.h" 5 6 #include "include/atlrx.h" 7 #include "base64.h" 8 9 IMPLEMENT_SERIAL(maildata,CObject,VERSIONABLE_SCHEMA|2); 10 #ifdef _DEBUG 11 #define new DEBUG_NEW 12 #endif 13 14 maildata::maildata(void) 15 { 16 } 17 18 19 maildata::~maildata(void) 20 { 21 } 22 23 24 /*void maildata::operator = (const maildata &m) 25 { 26 }*/ 27 28 29 void maildata::Serialize(CArchive &ar) 30 { 31 if(ar.IsStoring()) 32 { 33 ar<<alldata; 34 } 35 else if(ar.IsLoading()) 36 { 37 ar>>alldata; 38 } 39 } 40 41 42 static bool strcom(const char *s1,const char *s2) 43 { 44 int len=strlen(s2); 45 if(strlen(s1)<len) 46 { 47 return false; 48 } 49 int py=0; 50 for(int i=0;i<len;i++) 51 { 52 if(s1[i]>='A'&&s1[i]<='Z') 53 { 54 py=32; 55 } 56 else 57 { 58 py=0; 59 } 60 if(s1[i]+py!=s2[i]) 61 { 62 return false; 63 } 64 } 65 66 return true; 67 } 68 static CString* strmake(const char *st,const char *ed) 69 { 70 if(ed<=st) 71 { 72 CString *str=new CString; 73 *str=""; 74 return str; 75 } 76 char *data=new char[ed-st+1]; 77 int i=0; 78 while(st+i<ed) 79 { 80 data[i]=st[i]; 81 i++; 82 } 83 data[i]='\0'; 84 CString *str; 85 str=new CString(data); 86 return str; 87 } 88 static void getcontent(CString getstr,CString &content,CString &data)//正则表达式获取内容 89 { 90 CAtlRegExp<> reurl; 91 REParseError statu=reurl.Parse(getstr); 92 if(REPARSE_ERROR_OK!=statu) 93 { 94 return; 95 } 96 CAtlREMatchContext<> mcurl; 97 if(!reurl.Match(content,&mcurl)) 98 { 99 return; 100 } 101 const CAtlREMatchContext<>::RECHAR *szstart=0; 102 const CAtlREMatchContext<>::RECHAR *szend=0; 103 if(mcurl.m_uNumGroups<=0) 104 { 105 return; 106 } 107 mcurl.GetMatch(0,&szstart,&szend); 108 data=*strmake(szstart,szend); 109 } 110 111 static void dealsbstr(CString &str)//解码单行的=??= base64编码字符串 112 { 113 char *s=str.GetBuffer(); 114 int len=str.GetLength(); 115 s[len]='\0'; 116 int i=0; 117 while(i<len) 118 { 119 if(s[i]=='='&&s[i+1]=='?') 120 { 121 char rep[64]; 122 int rlen=0; 123 rep[rlen++]=s[i++]; 124 rep[rlen++]=s[i++]; 125 while(i<len&&s[i]!='?') 126 { 127 rep[rlen++]=s[i++]; 128 } 129 rep[rlen++]=s[i++]; 130 if(s[i]=='B') 131 { 132 rep[rlen++]=s[i++]; 133 rep[rlen++]=s[i++]; 134 int j=0; 135 char ss[64]; 136 while(i<len&&(s[i]!='?'||s[i+1]!='=')) 137 { 138 ss[j]=s[i]; 139 rep[rlen++]=s[i++]; 140 j++; 141 } 142 ss[j]='\0'; 143 144 std::string jb=base64_decode(ss); 145 //str.Format("%s",jb.c_str()); 146 147 rep[rlen++]=s[i++]; 148 rep[rlen++]=s[i++]; 149 rep[rlen]='\0'; 150 str.Replace(rep,jb.c_str()); 151 } 152 } 153 i++; 154 } 155 } 156 157 void maildata::getmailbaseinfo(maildata &rmail) 158 { 159 getcontent("Date: {.*?}\r\n",rmail.alldata,rmail.date); 160 getcontent("From: {.*?}\r\n",rmail.alldata,rmail.from); 161 getcontent("To: {.*?}\r\n",rmail.alldata,rmail.to); 162 getcontent("Subject: {.*?}\r\n",rmail.alldata,rmail.subject); 163 dealsbstr(rmail.from); 164 dealsbstr(rmail.to); 165 dealsbstr(rmail.subject); 166 } 167 void maildata::getmailinfo(maildata &rmail) 168 { 169 getmailbaseinfo(rmail); 170 getcontent("Content-Type: text/plain;.*?Content-Transfer-Encoding: {.*?}\r\n",rmail.alldata,rmail.contenttype); 171 getcontent("Content-Type: text/plain;.*?\r\n\r\n{.*?}--",rmail.alldata,rmail.content); 172 //AfxMessageBox(rmail.titletype+"\r\n"+rmail.title); 173 rmail.content.Replace("\r\n",""); 174 if(strcom(rmail.contenttype.GetString(),"base64")) 175 { 176 std::string ss=rmail.content.GetString(); 177 ss=base64_decode(ss); 178 rmail.content=ss.c_str(); 179 180 } 181 } 182 183 bool maildata::setmailforsend(maildata &rmail)//生成可用于发送的邮件内容 184 { 185 if(rmail.from.GetLength()<1||rmail.to.GetLength()<1||rmail.subject.GetLength()<1) 186 { 187 return false; 188 } 189 190 rmail.type=MAIL_TYPE_USEND; 191 rmail.alldata.Format(""); 192 193 CString str; 194 195 /*str.Format("Date: Tue, 9 Dec 2014 11:20:55 +0800\r\n", 196 rmail.from 197 ); 198 rmail.alldata+=str;*/ 199 200 //格式化基本信息 201 202 str.Format("From: %s\r\n", 203 rmail.from 204 ); 205 rmail.alldata+=str; 206 207 str.Format("To: %s\r\n", 208 rmail.to 209 ); 210 rmail.alldata+=str; 211 212 213 str.Format("Subject: =?GBK?B?%s?=\r\n", 214 base64_encode((unsigned char *)rmail.subject.GetString(),rmail.subject.GetLength()).c_str() 215 ); 216 rmail.alldata+=str; 217 218 /*str.Format("X-Priority: 3\r\n"); 219 rmail.alldata+=str; 220 str.Format("X-Mailer: wchrt's pro mail sever 1.0.0\r\n"); 221 rmail.alldata+=str; 222 str.Format("X-Client-IP: 118.112.48.107\r\n"); 223 rmail.alldata+=str;*/ 224 225 //加入MIME格式的邮件内容 226 227 str.Format("Content-Type: multipart/alternative;\r\n boundary=\"--=_Part=\"\r\n"); 228 rmail.alldata+=str; 229 str.Format("MIME-Version: 1.0\r\n"); 230 rmail.alldata+=str; 231 str.Format("\r\nThis is a multi-part message in MIME format.\r\n\r\n----=_Part=\r\n"); 232 rmail.alldata+=str; 233 234 if(rmail.content.GetLength()>0) 235 { 236 str.Format("Content-Type: text/plain; charset=GBK\r\nContent-Transfer-Encoding: base64\r\n\r\n%s\r\n----=_Part=--\r\n", 237 base64_encode((unsigned char *)rmail.content.GetString(),rmail.content.GetLength()).c_str() 238 ); 239 rmail.alldata+=str; 240 } 241 242 //需要发送附件的话将文件读入后添加MIME部分即可 243 244 AfxMessageBox(rmail.alldata); 245 } 246 247 248 bool maildata::openeml(const CString uname,const CString mid,maildata &rmail)//打开邮件 249 { 250 CString mpath="mail"; 251 mpath+="\\"; 252 mpath+=uname; 253 mpath+="\\"; 254 mpath+=mid+".eml"; 255 CFile file; 256 if(!file.Open(mpath,CFile::modeRead)) 257 { 258 return false; 259 } 260 char temp[40960]; 261 UINT len=file.Read(temp,40900); 262 temp[len]='\0'; 263 rmail.alldata.Format(temp); 264 //AfxMessageBox(rmail.alldata); 265 return true; 266 } 267 268 bool maildata::saveeml(const CString uname,const CString mid,maildata &rmail)//邮件储存 269 { 270 CString mpath="mail"; 271 if(!PathIsDirectory(mpath)) 272 { 273 if(!CreateDirectory(mpath,NULL)) 274 { 275 return false; 276 } 277 } 278 mpath+="\\"; 279 mpath+=uname; 280 if(!PathIsDirectory(mpath)) 281 { 282 if(!CreateDirectory(mpath,NULL)) 283 { 284 return false; 285 } 286 } 287 mpath+="\\"; 288 mpath+=mid+".eml"; 289 290 CFile file; 291 if(!file.Open(mpath,CFile::modeWrite)) 292 { 293 if(!file.Open(mpath,CFile::modeCreate|CFile::modeWrite)) 294 { 295 return false; 296 } 297 } 298 file.Write(rmail.alldata.GetString(),rmail.alldata.GetLength()); 299 file.Close(); 300 return true; 301 } 302 303 bool maildata::gettoaddress()//获取后缀地址 304 { 305 getcontent("@{.*}",to,toaddress); 306 if(toaddress.GetLength()<1) 307 { 308 return false; 309 } 310 toaddress.Replace(" ",""); 311 return true; 312 }
基本的smtp邮件服务器就告一段落了。剩下的就是用户管理以及在smtp服务器基础上制作pop3服务器以及web等。