刚开始做邮件服务器开发,一切都是茫然的。在书上网上都很难找到一套完整的邮件服务器开发教程。在个人的摸索中碰到了很多蛋疼得问题。现终于完成了,将我的开发经验分享给大家。

开发环境: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等。

转载于:https://www.cnblogs.com/wchrt/p/4195831.html

基于公网smtp协议实现邮件服务器相关推荐

  1. 深入理解SMTP协议之邮件客户端

    本文将使用Python从零实现一个简易的邮件客户端,通过本文你将对SMTP协议有更深入的了解,同时掌握使用Python实现标准协议的经验. 我们将开发一个简单的邮件客户端,将邮件发送给任意收件人.我们 ...

  2. linux环境下企业基于域名访问的web于电子邮件服务器 论文,基于Linux平台的企业邮件服务器搭建...

    我失骄杨君失柳,杨柳轻飏直上重霄九.得道多助,失道寡助.身后有余忘缩手,眼前无路想回头.鸟宿池边树,僧敲月下门.想当年,金戈铁马,气吞万里如虎. 本文由418133804贡献 pdf文档可能在WAP端 ...

  3. .NET 简单的smtp协议发邮件

    .NET 简单的smtp协议发邮件 好久没写东西了 闲的疼 突然想到没写过发邮件 查了下资料 写了个简单的 MailMessage mymm = new MailMessage();          ...

  4. 基于C#平台下利用POP3和SMTP协议的邮件归档系统

    今天对电脑进行整理,发现电脑上有一个帮同学做的毕设代码,挺简单的,但是当成做的时候吃了不少苦头,现在都毕业了,就放上来给新手分享一下.好了话不多说. 首先了解一下POP3和SMTP协议,他们是两个网络 ...

  5. exchange无法收发邮件_Python使用POP3和SMTP协议收发邮件!

    先来了解一下收/发邮件有哪些协议: SMTP协议 SMTP(Simple Mail Transfer Protocol),即简单邮件传输协议.相当于中转站,将邮件发送到客户端. POP3协议 POP3 ...

  6. 利用SMTP协议实现邮件的发送(以163和qq邮箱为例)

    1.实验的准备即环境 1.windows系统 2.telnet 客户端 win10除了需要开启客户端之外还不够,还需要自己下载 telnet server并安装,下载官网:GoodTech Syste ...

  7. 【Java】-【使用smtp协议发邮件】

    目前163和qq邮箱支持SMTP协议,本文以qq邮箱为例,163邮箱和这个思路一样 场景:使用qq邮箱给xx邮箱发一条邮件,那么你一定要获得qq邮箱的授权码,在设置-账户里找到以下内容,开启服务获得授 ...

  8. python pop3_Python使用POP3和SMTP协议收发邮件的示例代码

    先来了解一下收/发邮件有哪些协议: SMTP协议 SMTP(Simple Mail Transfer Protocol),即简单邮件传输协议.相当于中转站,将邮件发送到客户端. POP3协议 POP3 ...

  9. Exchange2007使用POP3/SMTP协议收发邮件

    一.首先准备两台虚拟机 1.Windows2008(Exchange2007.DNS.DC.IP:192.168.6.1) 2.WindowsXP(客户机.IP:192.168.6.2) 二.在EXC ...

  10. Python 使用SMTP协议发送邮件

    引言 问题基于<计算机网络自定向下>第二章的课后套接字编程作业:邮件客户 题目的下载链接:python 压缩包 题目如下: 这个实验结束时,您将能够更好地了解SMTP协议.您还将获得使用P ...

最新文章

  1. 青源LIVE第22期|旷视刘松涛:YOLOX,高性能目标检测的最新实践
  2. ubuntu下编译安装PHP
  3. 关于Spring Cloud Zuul网管上传文件乱码问题
  4. 成功者五大因素 奸的好人-笔记
  5. Solidity的Bytecode和Opcode简介
  6. oracle 自增1,oracle自增无法从1开始
  7. java 分析java死锁_Java死锁示例–如何分析死锁情况
  8. mysql入门到跑路_MySQL 24小时入门笔记(3),插入和删除,删库到跑路
  9. 【白皮书】2020在线教育用户增长闭环白皮书.pdf(附下载链接)
  10. IIS与Apache同时使用80端口
  11. Jmter安装和配置
  12. C#工程添加了DLL编译运行时却提示”无法加载DLL“的解决方案
  13. ManjaroLinux安装NVIDIA驱动
  14. 苹果零售店被指销售翻新机
  15. mxgraph 画布
  16. Python selenium —— 一定要会用selenium的等待,三种等待方式解读
  17. RabbitMQ核心功能介绍
  18. 做不了爱人,我们做什么
  19. 手机投屏电视html,oppor17手机投屏电视的详细操作步骤
  20. 【LeetCode题解】347_前K个高频元素(Top-K-Frequent-Elements)

热门文章

  1. RuntimeError: all elements of input should be between 0 and 1
  2. pytorch进度条
  3. 【论文笔记】基于2-channel network的图片相似度判别-CVPR 2015
  4. linux如何切换到root用户
  5. 2021-06-22列表样式与背景图片
  6. vba commondialog控件添加不上_MyVBA加载宏——添加自定义菜单03——功能分析
  7. python中的jieba分词使用手册_Python jieba结巴分词原理及用法解析
  8. IDEA java 显示build目录
  9. linux两个树莓派通信,Arduino与树莓派间的通信实践
  10. MyBatisPlus学习