剛開始作郵件服務器開發,一切都是茫然的。在書上網上都很難找到一套完整的郵件服務器開發教程。在我的的摸索中碰到了不少蛋疼得問題。現終於完成了,將個人開發經驗分享給你們。web
開發環境:vs2012 mfc正則表達式
注意事項:編程
一、 網絡環境:windows
做爲郵件服務器,要接收來自互聯網的郵件,就得有能映射到外網的服務器。至少映射25(SMTP)端口(pop3都暫時不重要)。對於沒有外網條件的的小夥伴,推薦如下幾種方法調試:服務器
A、若是你使用model上網,查看你的電腦的外網IP。看看是否和model的一致。一致則說明你有一個公網IP。若是沒有,就致電網絡運營商,叫他給你換回公網IP,語氣必定要強硬。有了公網IP,你就能把smtp的端口映射出去。具體方法請搜索。網絡
B、對於一些校園網或大型局域網的用戶,那就有點悲劇了,基本沒法直接將端口映射出去。但能夠參考C方法。dom
C、先在的網上的網絡穿透都作的比較好,甚至能媲美公網ip的直接映射。去網上下載一些穿透軟件:花生殼之類的。都有提供內網穿透。給你們推薦個免費的網絡穿透軟件:nat123 。這軟件很好有,up主不經意的發現有大量的小學生用它在開MC的服務器。curl
D、若是你暫不須要使用外部的郵件服務器來測試你的服務器,那就在內網用其餘的郵件服務器發送至你的郵箱進行測試,要注意端口衝突。socket
二、 編寫smtp郵件服務器:學習
對於擁有外網映射的,能夠先編寫smtp 的接收服務器。這樣的好處是,能接收識別它域的不一樣類型格式的郵件,到本身發送郵件的時候,對郵件格式能有一個很好的組織。 而對於只能使用內網的用戶,建議先編寫發送端。成功進行對各個郵件服務器的發送後(這裏比較蛋疼,後面會介紹到),就能夠在編寫郵件接收端的時候用來測試。
三、 郵件服務器域名(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地址。 總之,咱們發郵件必定要先獲取郵件接收服務器的域名。
四、 防止反垃圾郵件:
把郵件發往其餘郵件服務器,很容易被識別爲垃圾文件。新浪郵箱最討厭,直接不信任個人域名和IP地址。最初郵件格式不完善,也被企鵝斷斷續續封了幾天。就163最包容,個人郵件都慷慨地接收了。
如今發郵件,除了基本的格式,不用MINE根本不行,尤爲是當須要圖片或者附件的郵件。
五、 加密:
通常可用base64對郵件標題和內容進行加密。
六、 郵件存儲:
建議儲存爲eml格式的郵件文件,不只利於轉發或其餘應用查看,也便於以後的pop3服務器使用。
七、 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 發送一個郵件。應遵循如下步驟:
一、提取出域名後綴:qq.com。
二、DNS查詢該域名的MX記錄。
三、根據查詢到的MX域名或IP地址連接該郵件服務器
四、使用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等。