#include <stdio.h>
#include <time.h>
#include <winsock.h>
#include <string.h>
#include <windows.h>
#pragma comment(lib, "ws2_32.lib")
/*****************************************************************************************************/
#define MAX_CONNECTION_NUM 50 //定义最大连接数
#define SMTP_SERVER_PORT 25 //定义服务器端口
#define BUFFER_SIZE 1024 //定义缓冲区大小
/*****************************************************************************************************/
u_short serverPort=SMTP_SERVER_PORT; //定义服务器端口为默认端口
char *readBuf; //定义接受数据缓冲区
WSAData wsaData; //定义WSAData结构
SOCKET clientSocket[MAX_CONNECTION_NUM]; //定义所有客户端socket
SOCKET serverSocket; //定义服务器监听socket
struct sockaddr_in clientSockAddr[MAX_CONNECTION_NUM]; //定义所有客户端信息
struct sockaddr_in server; //保存要设置的服务器信息
/*****************************************************************************************************/
struct UserInfo //定义用户信息结构
{
char username[20]; //用户名
char password[30]; //密码
};
struct ClientContent //客户端输入的信息
{
char command[10]; //保存命令字符串
char *content; //保存命令参数
};
struct MailContent //定义信笺结构
{
char from[50]; //写信人
char sender[50]; //信件发信人
char replyTo[50]; //信件回复地址
char to[50]; //信件的主收件人
char cc[50]; //信件的辅收件人
char bcc[50]; //信件的密件收件人
char date[30]; //信笺的创建日期
char Subject[20]; //信笺主题
char *data; //信笺内容
// char Received[200]; //MTA轨迹
// char Return_Path[50]; //发信人地址
// char Message_ID[60]; //信笺的唯一标识符
};
struct CommandFinished //命令执行状态结构,共8条命令
{
char HELO; //如果该命令已经成功执行的话
char MAIL; //就为字符'Y',否则为0
char RCPT;
char DATA;
char RSET;
char NOOP;
char QUIT;
char HELP;
};
/*****************************************************************************************************/
void MAIL_Handle(int index,struct CommandFinished *commandFinished,char *param,MailContent *mailContent);
void RCPT_Handle(int index,struct CommandFinished *commandFinished,char *param,MailContent *mailContent);
void DATA_Handle(int index,struct CommandFinished *commandFinished,char *param,MailContent *mailContent);
void RSET_Handle(int index,struct CommandFinished *commandFinished,char *param,MailContent *mailContent);
void NOOP_Handle(int index,struct CommandFinished *commandFinished,char *param,MailContent *mailContent);
void QUIT_Handle(int index,struct CommandFinished *commandFinished,char *param,MailContent *mailContent);
void HELP_Handle(int index,struct CommandFinished *commandFinished,char *param,MailContent *mailContent);
DWORD WINAPI SmtpBeginService(LPVOID lpParam); //对客户进行服务的函数,其中lpParam传入服务Socket序号
void WriteClientInfo(int index); //index为传入的客户Socket序号
void InitUserInfo();//初始化用户
void ReadUserInfo();//读出信息
void CommandHandle(int index); //对index位置的客户命令进行处理
int CheckDataEnd(char *dataBuf); //检查Data是否接受完成
void DataAlalysic(MailContent *mailContent); //分析邮data内容并将有用信息添到mailContent有关字段中
void SendMail(MailContent *mailContent,char *mailAddress,char *domainServer); //将mailContent邮件内容发送给mailAddress
void SaveMail(MailContent *mailContent,char *mailAddress); //目标邮件地址是本地服务器就直接保存
void ZiDuanAlalysic(char *ziDuan,MailContent *mailContent);//邮件头字段分析
void GetDomainServer(char *domain,char *dommainServer); //返回domain邮件域所在的服务器地址
void GetUser(char *mailAddress,char *userName); //得到邮件的用户名并保存到username中
int ExistUserName(char *userName); //查找本服务器上是否存在用户userName
void GetCommandAndContent(char *buf,ClientContent *clientContent);//得到客户发送信息的命令和内容
/*****************************************************************************************************/
/*****************************************************************************************************
Function_Name: main
Used To: 在25端口监听客户连接并创建新线程为客户服务
Date: 2007-4-1
*****************************************************************************************************/
int main(int argc,char *argv[])
{
/*****************************************************************************************************/
struct sockaddr_in tempSockAddr; //保存临时接受的客户信息
SOCKET tempSocket; //保存临时接受的SOCKET
for(int i=0;i<MAX_CONNECTION_NUM;i++)clientSocket[i]=NULL; //初始化所有客户端socket为无连接状态
server.sin_family=AF_INET; //填充服务器端信息
server.sin_port=htons(serverPort);
server.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
int sockAddrLen=sizeof(tempSockAddr);
/*****************************************************************************************************/
if(-1==WSAStartup(MAKEWORD(2,0),&wsaData))
{
printf("加载Winsock连接库失败!错误号为%d",WSAGetLastError());
WSACleanup();
return -1;
}
serverSocket=socket(AF_INET,SOCK_STREAM,0); //服务器建立socket并开始监听
if(INVALID_SOCKET ==serverSocket)
{
printf("服务器创建socket失败!错误号为%d",WSAGetLastError());
WSACleanup();
return -1;
}
if(SOCKET_ERROR==bind(serverSocket,(struct sockaddr*)&server,sizeof(server)))
{
printf("服务器套接字绑定发生错误!错误号为:%d",WSAGetLastError());
WSACleanup();
return -1;
}
if(SOCKET_ERROR==listen(serverSocket,MAX_CONNECTION_NUM+1))
{
printf("服务器套接字监听失败!错误号为:%d",WSAGetLastError());
WSACleanup();
return -1;
}
// InitUserInfo();
ReadUserInfo();
printf("SMTP Server is running.....");
/*****************************************************************************************************/
while(true) //服务器进入无限监听状态
{
int i=0;
tempSocket=accept(serverSocket,(struct sockaddr*)&tempSockAddr,&sockAddrLen);
for(i=0;i<MAX_CONNECTION_NUM;i++) //查找队列中有没有空位
{
if(NULL!=clientSocket[i])continue; //该位置不为空,被占用了,继续看下一个
else //有空位就把临时客户信息保存
{
clientSocket[i]=tempSocket;
clientSockAddr[i]=tempSockAddr;
tempSocket=NULL;
memset((void *)(struct sockaddr*)&tempSockAddr,0,sizeof(tempSockAddr));
int index=i; //保存空位号
DWORD dwThreadID;
HANDLE handle; //下面创建一个服务线程,SmtpBeginService为线程入口
handle=::CreateThread(NULL,NULL,SmtpBeginService,&index,CREATE_SUSPENDED,&dwThreadID);
::ResumeThread(handle);
break;
}
}
if(NULL!=tempSocket) //如果没有找到空位,即队列满了则发送人数满了并关闭连接
{
send(tempSocket,"Sorry,the server queue is full,please wait",sizeof("Sorry,the server queue is full,please wait"),0);
closesocket(tempSocket);
tempSocket=NULL;
}
}
}
/*****************************************************************************************************/
/*****************************************************************************************************
Function_Name: SmtpBeginService
Used To: 为客户服务的线程入口点
Params: lpParam--主线程传入的客户服务号
Date: 2007-4-2
*****************************************************************************************************/
DWORD WINAPI SmtpBeginService(LPVOID lpParam)
{
int *index=(int *)lpParam; //首先取得客户服务号,以下发送欢迎信息
//第一步
send(clientSocket[*index],"Welcome to LinLin SMTP Server.../r/n",sizeof("Welcome to LinLin SMTP Server.../r/n"),0);
WriteClientInfo(*index); //把第*index位置客户登录信息存入日志文件
CommandHandle(*index); //进入与客户命令响应函数
return 0;
}
/*****************************************************************************************************/
/*****************************************************************************************************
Function_Name: WriteClientIpInfo
Used To: 记录客户登录的ip信息和登录时间信息等
Param: index-客户服务号,用于访问客户socket和客户机信息
Date: 2004-4-2
*****************************************************************************************************/
void WriteClientInfo(int index)
{
char *timestr=(char *)malloc(sizeof(char)*40);
FILE *fp;
time_t ttime;
time(&ttime);//返回当前日历
tm *tmtime=localtime(&ttime); //将日历转换为当地时间
strftime(timestr,50,"%Y年%m月%d日%H时%M分%S秒",tmtime);
char *clientAddr=inet_ntoa(clientSockAddr[index].sin_addr);//inet_ntoa将网络地址转换成.间隔的字符串
char clf[3]={10,13,0};//定义回车换行
strcat(clientAddr,clf);
//打印出将要写进去什么
printf("/ntimestr=%s/nclientAddr=%s/r/n",timestr,clientAddr);
fp=fopen("ServerLog.txt","a");
fputs(clientAddr,fp);
fputs(timestr,fp);
fputs("/r/n",fp);
fclose(fp);
}
/*****************************************************************************************************/
/*****************************************************************************************************
Function_Name: InitUserInfo
Used To: 为了测试而先人工添加用户及密码,因为服务器暂时没提供注册功能
Date: 2004-4-2
*****************************************************************************************************/
void InitUserInfo()
{
UserInfo user[3];
strcpy(user[0].username,"wangjianlin1985");
strcpy(user[0].password,"111111");
strcpy(user[1].username,"lybbingxue");
strcpy(user[1].password,"111111");
strcpy(user[2].username,"make");
strcpy(user[2].password,"111111");
FILE *fp;
//打印出将要写进去什么
if((fp=fopen("user.txt","a"))==NULL)
return;
for(int i=0;i<3;i++)
{
if(fwrite(&user[i],sizeof(UserInfo),1,fp)!=1)
printf("file write error/n");
}
fclose(fp);
}
/*****************************************************************************************************/
/*****************************************************************************************************
Function_Name: ReadUserInfo
Used To: 读取信息
Date: 2004-4-2
*****************************************************************************************************/
void ReadUserInfo()
{
FILE *fp;
int fileLength; //定义文件长度
int usercount; //定义用户数量
int result=0;
UserInfo userInfo;
memset(&userInfo,0,sizeof(struct UserInfo));
if((fp=fopen("user.txt","r"))==NULL)
return;
fseek(fp,0,2);
fileLength=ftell(fp); //得到文件长度
usercount=fileLength/(sizeof(struct UserInfo));//得到用户数量
fseek(fp,0,0);
for(int i=0;i<usercount;i++)
{
fread(&userInfo,sizeof(UserInfo),1,fp);
printf("%s %s",userInfo.username ,userInfo.password );
printf("/n");
}
fclose(fp);
}
/*****************************************************************************************************/
/*****************************************************************************************************
Function_Name: GetCommandAndContent
Used To: 分析从客户接收的buf数据,分离出命令和参数并保存到clientContent中
Params: buf--传入从客户接收到数据的开始位置 clientContent--分析后保存命令和参数的结构
Date: 2007-4-4
*****************************************************************************************************/
void GetCommandAndContent(char *buf,ClientContent *clientContent)
{
if(0==strncmp(buf,"HELO",4))
{
strcpy(clientContent->command,"HELO");
clientContent->content=buf+4;
}
else if(0==strncmp(buf,"MAIL FROM:",10)) //如果buf接收数据的前10个字符为MAIL FROM:
{
strcpy(clientContent->command,"MAIL FROM:"); //保存命令为MAIL FROM:
clientContent->content=buf+10; //命令参数指向buf命令后的位置
}
else if(0==strncmp(buf,"RCPT TO:",8))
{
strcpy(clientContent->command,"RCPT TO:");
clientContent->content=buf+8;
}
else if(0==strncmp(buf,"DATA",4))
{
strcpy(clientContent->command,"DATA");
clientContent->content=NULL;
}
else if(0==strncmp(buf,"RSET",4))
{
strcpy(clientContent->command,"RSET");
clientContent->content=NULL;
}
else if(0==strncmp(buf,"NOOP",4))
{
strcpy(clientContent->command,"NOOP");
clientContent->content=NULL;
}
else if(0==strncmp(buf,"HELP",4))
{
strcpy(clientContent->command,"HELP");
clientContent->content=NULL;
}
else if(0==strncmp(buf,"QUIT",4))
{
strcpy(clientContent->command,"QUIT");
clientContent->content=NULL;
}
else
{
strcpy(clientContent->command,"UNKNOWN");
clientContent->content=NULL;
}
}
/**************检查dataBuf中是否有连续的/r/n./r/n字符,有就return 1,否则return 0; ***/
int CheckDataEnd( char *dataBuf )//待检查的字符串
{
int retValue=0;
for(int i=0;i<strlen(dataBuf)-4;i++)
{
if( '/r'==*(dataBuf+i)&&
'/n'==*(dataBuf+i+1)&&
'.'==*(dataBuf+i+2)&&
'/r'==*(dataBuf+i+3)&&
'/n'==*(dataBuf+i+4)
)
{
retValue=1;
break;
}
}
return retValue;
}
/*****************************************************************************************************/
/*****************************************************************************************************
Function_Name: DataAlalysic
Used To: 邮件数据分析 如果有邮件头,就分离出本服务器认识的字段
Params: buf--传入从客户接收到数据的开始位置 clientContent--分析后保存命令和参数的结构
Date: 2007-4-4
******************************************************************************************************/
void DataAlalysic(MailContent *mailContent)//分析邮data内容并将有用信息添到mailContent有关字段中
{
char *mailHead=NULL; //定义邮件头
char *mailBody=NULL; //定义邮件体
char ziDuan[200]; //定义一个字段
int datalength=strlen(mailContent->data)-4;
memset(ziDuan,0,200); //清空字段内容
for(int i=0;i<=datalength;i++)
{
if( '/r'==*(mailContent->data+i)&&
'/n'==*(mailContent->data+i+1)&&
'/r'==*(mailContent->data+i+2)&&
'/n'==*(mailContent->data+i+3)
)
{
//分离邮件头和邮件体
mailHead=(char *)malloc(sizeof(char)*(i+1));
memset(mailHead,0,i+1);
memcpy(mailHead,mailContent->data,i);//复制邮件头
printf("mailHead=%sAAAA/n",mailHead);//
mailBody=(char *)malloc(sizeof(char)*(strlen(mailContent->data)-i-3));
memset(mailBody,0,strlen(mailContent->data)-i-3);
memcpy(mailBody,mailContent->data+i+4,strlen(mailContent->data)-i-4);
printf("mailBody=%sBBBB/n",mailBody);//
memset(mailContent->data,0,strlen(mailContent->data));//清空邮件体内容以便以后从新填充
int i=0 , pos =0;
int mailHeadLength=strlen(mailHead)-1;
for( pos=0;pos<mailHeadLength;pos++)
{
if(!('/r'==*(mailHead+pos)&&'/n'==*(mailHead+1+pos)))
{
*(ziDuan+i)=*(mailHead+pos);
i++;
}
else //分析当前字段并pos指向下一个字段
{
printf("分析邮件头/n");//
ZiDuanAlalysic(ziDuan,mailContent);
memset(ziDuan,0,200);//清空字段内容以分析下个字段
i=0;
pos+=1;
}
}
*(ziDuan+i)=*(mailHead+pos);//复制最后一个字段的最后一个字节
ZiDuanAlalysic(ziDuan,mailContent);//
//定义信笺头和信笺体的空行和
mailContent->data[strlen(mailContent->data)]='/r';
mailContent->data[strlen(mailContent->data)]='/n';
mailContent->data[strlen(mailContent->data)]='/r';
mailContent->data[strlen(mailContent->data)]='/n';
memcpy(mailContent->data+strlen(mailContent->data),mailBody,strlen(mailBody));
}
}
}
/*****************************************************************************************************
Function Name: SendMail
Used to: 将待发送的邮件内容发送到目标地址mailAddress中
Parameters:
mailConten->data: <本服务器不认识的邮件头>+"/r/n/r/n"+<邮件体内容>
mailAddress: 标准email格式如user@domain.net
Date:2007-4-14
*****************************************************************************************************/
void SendMail( MailContent *mailContent,//待发送的邮件结构
char *mailAddress, //发送邮件的目标地址
char *domainServer //待发送的目标ip地址
)
{
SOCKET sendSocket; //定义发送socket
struct sockaddr_in server; //发送目标服务器信息
struct hostent *hp;
hp=gethostbyname(domainServer);
memcpy((char *)&server.sin_addr,(char *)hp->h_addr,hp->h_length);
server.sin_family=AF_INET;
server.sin_port=htons(25);
sendSocket=socket(AF_INET,SOCK_STREAM,0);
if(INVALID_SOCKET==sendSocket)return ;
if(0!=connect(sendSocket,(sockaddr *)&server,sizeof(struct sockaddr_in)))return ;
memset(readBuf,0,BUFFER_SIZE);
recv(sendSocket,readBuf,BUFFER_SIZE,0);
printf("readBuf=%sFFFFFF/n",readBuf);//???
//构造hello命令
char *hello=(char *)malloc(sizeof(char)*8+strlen(mailContent->from));
memset(hello,0,sizeof(char)*8+strlen(mailContent->from));
memcpy(hello,"HELO ",5);
memcpy(hello+5,mailAddress,strlen(mailContent->from));
memcpy(hello+5+strlen(mailContent->from),"/r/n",2);
printf("hello=%sGGGGGGG/n",hello);//???
send(sendSocket,hello,strlen(hello),0); //发送Hello命令
free(hello); //释放hello指向的内存
memset(readBuf,0,BUFFER_SIZE);
recv(sendSocket,readBuf,BUFFER_SIZE,0);
printf("%sHHHHHHHH/n",readBuf);///
if(0!=strncmp("250",readBuf,3)){closesocket(sendSocket);return ;}
//构造MAIL FROM命令
char *mailFrom=(char *)malloc(sizeof(char)*16+strlen(mailContent->from));
memset(mailFrom,0,sizeof(char)*16+strlen(mailContent->from));
strcat(mailFrom,"MAIL FROM: <");
strcat(mailFrom,mailContent->from);
strcat(mailFrom,">/r/n");
printf("mailFrom=%sMMMMMM/n",mailFrom);//
send(sendSocket,mailFrom,strlen(mailFrom),0);//发送MAIL FROM命令
free(mailFrom); //释放mailFrom指向的内存
memset(readBuf,0,BUFFER_SIZE);
recv(sendSocket,readBuf,BUFFER_SIZE,0);
printf("%sIIIIIIIII/n",readBuf);///
if(0!=strncmp("250",readBuf,3)){closesocket(sendSocket);return ;}
//构造RCPT TO命令
char *rcptTo=(char *)malloc(sizeof(char)*14+strlen(mailAddress));
memset(rcptTo,0,sizeof(char)*14+strlen(mailAddress));
strcat(rcptTo,"RCPT TO: <");
strcat(rcptTo,mailAddress);
strcat(rcptTo,">/r/n");
send(sendSocket,rcptTo,strlen(rcptTo),0); //发送rcpt to命令
free(rcptTo); //释放rcptTo指向的内存
memset(readBuf,0,BUFFER_SIZE);
recv(sendSocket,readBuf,BUFFER_SIZE,0);
printf("%sJJJJJ/n",readBuf);/
if(0!=strncmp("250",readBuf,3)){closesocket(sendSocket);return ;}
//构造DATA命令
char *data="DATA/r/n";
send(sendSocket,data,strlen(data),0);
memset(readBuf,0,BUFFER_SIZE);
recv(sendSocket,readBuf,BUFFER_SIZE,0);
printf("%sKKKKKKKK/n",readBuf);///
if(0!=strncmp("354",readBuf,3)){closesocket(sendSocket);return ;}
//计算邮件内容长度
int dataContentLen=0;
dataContentLen=strlen("From: ")+strlen(mailContent->from)+2;
dataContentLen+=strlen("To: ")+strlen(mailContent->to)+2;
if(NULL!=mailContent->date)dataContentLen+=strlen("Date: ")+strlen(mailContent->date)+2;
if(NULL!=mailContent->sender)dataContentLen+=strlen("Sender: ")+strlen(mailContent->sender)+2;
if(NULL!=mailContent->replyTo)dataContentLen+=strlen("Reply-To: ")+strlen(mailContent->replyTo)+2;
if(NULL!=mailContent->Subject)dataContentLen+=strlen("Subject: ")+strlen(mailContent->Subject)+2;
dataContentLen+=strlen(mailContent->data)+1;
char *dataContent=(char *)malloc(sizeof(char)*dataContentLen);
if(NULL==dataContent)return ;
memset(dataContent,0,dataContentLen);
//构造邮件体
strcat(dataContent,"From: ");
strcat(dataContent,mailContent->from);
strcat(dataContent,"/r/n");
strcat(dataContent,"To: ");
strcat(dataContent,mailContent->to);
strcat(dataContent,"/r/n");
if(NULL!=mailContent->date)
{
strcat(dataContent,"Date: ");
strcat(dataContent,mailContent->date);
strcat(dataContent,"/r/n");
}
if(NULL!=mailContent->sender)
{
strcat(dataContent,"Sender: ");
strcat(dataContent,mailContent->sender);
strcat(dataContent,"/r/n");
}
if(NULL!=mailContent->replyTo)
{
strcat(dataContent,"Reply-To: ");
strcat(dataContent,mailContent->replyTo);
strcat(dataContent,"/r/n");
}
if(NULL!=mailContent->Subject)
{
strcat(dataContent,"Subject: ");
strcat(dataContent,mailContent->Subject);
strcat(dataContent,"/r/n");
}
strcat(dataContent,mailContent->data);
printf("dataContent=%sGGGGGG/n",dataContent);//
send(sendSocket,dataContent,strlen(dataContent),0);
free(dataContent); //释放发送邮件内容分配的内存
memset(readBuf,0,BUFFER_SIZE);
recv(sendSocket,readBuf,BUFFER_SIZE,0);
printf("%sOOOOOOOOOOO/n",readBuf);
if(0!=strncmp("250",readBuf,3)){closesocket(sendSocket);return ;}
send(sendSocket,"QUIT/r/n",strlen("QUIT/r/n"),0);
closesocket(sendSocket);
return ;
}
/*****************************************************************************************************
Function_Name: MAIL_HANDLE
Used TO: 分析第index个客户MAIL FROM命令的邮件地址并保存
Date: 2007-4-5
/*****************************************************************************************************/
void MAIL_Handle( int index, //客户服务号
struct CommandFinished *commandFinished, //设置客户完成命令状态
char *param, //MAIL FROM:命令参数起始地址
MailContent *mailContent //邮件结构(输出参数)
) //处理Mail命令
{
bool mailModeIsOk=FALSE; //邮件地址格式是否正确变量
int addressLen; //邮件最长地址变量
if('/0'==param) //如果MAIL FROM命令没有带参数就是错误
{
send(clientSocket[index],"501 Syntax error in parameters or arguments",sizeof("501 Syntax error in parameters or arguments"),0);
return ;
}
int length=strlen(param);
printf("param=%s/n",param);///
for(int i=0;i<length;i++) //检查邮件地址中是否有'@'
{
if('@'==(*(param+i)))mailModeIsOk=TRUE;
}
if(FALSE==mailModeIsOk)
{
send(clientSocket[index],"501 Syntax error in parameters or arguments",sizeof("501 Syntax error in parameters or arguments"),0);
return ;
}
while(' '==(*param))param++; //删掉mail地址前多余的空格
if('<'==*param) //第二次检查邮件地址
{
param++; //去掉首尾'<' '>'
if('>'!=param[strlen(param)-3])
mailModeIsOk=FALSE;
else
param[strlen(param)-3]='/0';
}
if(FALSE==mailModeIsOk)
{
send(clientSocket[index],"501 Syntax error in parameters or arguments",sizeof("501 Syntax error in parameters or arguments"),0);
return ;
}
addressLen=strlen(param)>49?49:strlen(param);
printf("发信人地址:%s/n",param);///
memcpy(mailContent->from,param,addressLen); //将发件人地址保存
commandFinished->MAIL='Y'; //设置客户mail命令执行动作正确完成
send(clientSocket[index],"250 OK mail command finished",sizeof("250 OK mail command finished"),0);
}
/****************************************************************************************************/
/*****************************************************************************************************
Function_Name: RCPT_HANDLE
Used TO: 分析第index个客户RCPT TO:命令的邮件地址并保存
Date: 2007-4-5
/*****************************************************************************************************/
void RCPT_Handle( int index, //客户服务号
struct CommandFinished *commandFinished, //设置客户完成命令状态
char *param, //RCPT TO:命令参数开始地址
MailContent *mailContent //邮件结构(输出参数)
) //处理RCPT命令
{
bool mailModeIsOk=FALSE; //邮件地址格式是否正确变量
int addressLen; //邮件最长地址变量
if('/0'==param) //如果MAIL FROM命令没有带参数就是错误
{
send(clientSocket[index],"501 Syntax error in parameters or arguments",sizeof("501 Syntax error in parameters or arguments"),0);
return ;
}
int length=strlen(param);
for(int i=0;i<length;i++) //检查邮件地址中是否有'@'
{
if('@'==(*(param+i)))mailModeIsOk=TRUE;
}
if(FALSE==mailModeIsOk)
{
send(clientSocket[index],"501 Syntax error in parameters or arguments",sizeof("501 Syntax error in parameters or arguments"),0);
return ;
}
while(' '==(*param))param++; //删掉mail地址前多余的空格
if('<'==*param) //第二次检查邮件地址
{
param++; //去掉首尾'<' '>'
if('>'!=param[strlen(param)-3])
mailModeIsOk=FALSE;
else
param[strlen(param)-3]='/0';
}
if(FALSE==mailModeIsOk)
{
send(clientSocket[index],"501 Syntax error in parameters or arguments",sizeof("501 Syntax error in parameters or arguments"),0);
return ;
}
addressLen=strlen(param)>49?49:strlen(param);
printf("收信人地址:%s/n",param);///
memcpy(mailContent->to,param,addressLen);//将发件人地址保存
commandFinished->RCPT='Y';//设置客户mail命令执行动作正确完成
send(clientSocket[index],"250 OK mail command finished",sizeof("250 OK mail command finished"),0);
}
/*****************************************************************************************************/
/*****************************************************************************************************
Function_Name: DATA_HANDLE
Used TO: 分析第index个客户Data命令保存邮件的内容
备注:本服务器规定最多接受1M字节数据
Date: 2007-4-7
/*****************************************************************************************************/
void DATA_Handle( int index, //客户服务号
struct CommandFinished *commandFinished, //设置客户完成命令状态
char *param, //命令参数(输入参数)
MailContent *mailContent //邮件结构(输出参数)
) //处理DATA命令
{
int bytesNeeded; //定义接受数据需要的数据缓冲区大小
int bytesRecieved; //定义每次接受到的字节数目
long MAXRECIEVED=1024*1024; //定义最大接收1000000个字节
//先判断客户是否已经完成了MAIL和RCPT命令
if('Y'!=commandFinished->MAIL&&'Y'!=commandFinished->RCPT)
{
send(clientSocket[index],"503 Bad sequence of commands",sizeof("503 Bad sequence of commands"),0);
return ;
}
else
{
send(clientSocket[index],"354 Start mail input; end with <CRLF>.<CRLF>",sizeof("354 Start mail input; end with <CRLF>.<CRLF>"),0);
bytesNeeded=BUFFER_SIZE+1; //开始假设只需要BUFFER_SIZE+1,最后一个存放NULL
mailContent->data=(char *)malloc(sizeof(char)*bytesNeeded);
memset(mailContent->data,0,bytesNeeded);
//第6步接收邮件具体内容
bytesRecieved=recv(clientSocket[index],mailContent->data,BUFFER_SIZE,0);
while(BUFFER_SIZE==bytesRecieved) //缓冲区接收满了,证明还有要接收的数据
{
if(bytesNeeded<MAXRECIEVED)
{
bytesNeeded+=BUFFER_SIZE;
realloc(mailContent->data,bytesNeeded);
memset(mailContent->data+bytesNeeded-BUFFER_SIZE,0,BUFFER_SIZE);
bytesRecieved=recv(clientSocket[index],mailContent->data+bytesNeeded-BUFFER_SIZE-1,BUFFER_SIZE,0);
}
}
printf("邮件体共有%d个字符/n",strlen(mailContent->data));///
printf("%sPPPPPPPP/n",mailContent->data);//
if(CheckDataEnd(mailContent->data))
{
send(clientSocket[index],"250 OK mail command finished",sizeof("250 OK mail command finished"),0);
commandFinished->DATA='Y';
}
else
{
send(clientSocket[index],"501 Syntax error in parameters or arguments",sizeof("501 Syntax error in parameters or arguments"),0);
if(NULL!=mailContent->data)free(mailContent->data);
}
}
}
void RSET_Handle( int index,
struct CommandFinished *commandFinished,//设置客户完成命令状态
char *param,
MailContent *mailContent
)//处理RSET命令
{
commandFinished->DATA=0;
commandFinished->HELP=0;
commandFinished->MAIL=0;
commandFinished->NOOP=0;
commandFinished->QUIT=0;
commandFinished->RCPT=0;
commandFinished->RSET=0;
if(NULL!=mailContent->data) //如果信笺内容不为空,就先释放它占有的内存
free(mailContent->data);
memset(mailContent,0,sizeof(struct MailContent));//清空邮件体内容
send(clientSocket[index],"250 Requested mail action okay, completed",sizeof("250 Requested mail action okay, completed"),0);
}
void LOOP_Handle( int index,
struct CommandFinished *commandFinished,//设置客户完成命令状态
char *param,
MailContent *mailContent
)//处理NOOP命令
{
send(clientSocket[index],"250 connection is ok!",sizeof("250 connection is ok!"),0);
commandFinished->NOOP='Y';
}
/*****************************************************************************************************/
/*****************************************************************************************************
Function_Name: QUIT_HANDLE
Used TO: 处理客户退出服务器命令
Date: 2007-4-8
/*****************************************************************************************************/
void QUIT_Handle( int index, //客户服务号
struct CommandFinished *commandFinished, //设置客户完成命令状态
char *param, //命令参数(输入参数)
MailContent *mailContent //邮件结构(输出参数)
) //处理QUIT命令
{
char domainServer[16]; //定义邮件域服务器的地址
char *domain; //定义邮件的域
closesocket(clientSocket[index]); //关闭客户端的套接字
clientSocket[index]=NULL; //以备其他的客户使用本资源
memset(domainServer,0,16);
//以下判断MAIL RCPT DATA命令是否都成功完成了
if('Y'==commandFinished->MAIL&&'Y'==commandFinished->RCPT&&'Y'==commandFinished->DATA)
{
DataAlalysic(mailContent); //分析邮件结构的data字段并填充mailContent其他字段
domain=mailContent->to;
while('@'!=*domain)domain++; //让domain先指向邮件域字符串的开始地址
domain++;
GetDomainServer(domain,domainServer);
printf("domainServer=%s/n",domainServer);
if(0==strncmp("127.0.0.1",domainServer,9))
SaveMail(mailContent,mailContent->to);
else if(0!=strncmp("UNKNOWN",domainServer,7))
SendMail(mailContent,mailContent->to,domainServer);
if(0!=strlen(mailContent->cc))//存在信件的辅收件人,则继续发给信件的辅收件人
{
domain=mailContent->cc;
while('@'!=*domain)domain++;
domain++;
memset(domainServer,0,16);
GetDomainServer(domain,domainServer);
if(0==strncmp("127.0.0.1",domainServer,9))
SaveMail(mailContent,mailContent->cc);
else if(0!=strncmp("UNKNOWN",domainServer,7))
SendMail(mailContent,mailContent->cc,domainServer);
}
if(0!=strlen(mailContent->bcc))//存在信件的密件收件人,则继续发给信件的密件收件人
{
domain=mailContent->bcc;
while('@'!=*domain)domain++;
domain++;
memset(domainServer,0,16);
GetDomainServer(domain,domainServer);
if(0==strncmp("127.0.0.1",domainServer,9))
SaveMail(mailContent,mailContent->bcc);
else if(0!=strncmp("UNKNOWN",domainServer,7))
SendMail(mailContent,mailContent->bcc,domainServer);
}
}
}
void HELP_Handle( int index,
struct CommandFinished *commandFinished,//设置客户完成命令状态
char *param,
MailContent *mailContent
)//处理HELP命令
{
char *strMessage="214-Hello,Lin Lin SMTP Server 1.0/r/n214-/r/n214-Supported commands:/r/n214-HELO MAIL RCPT DATA RSET NOOP HELP";
send(clientSocket[index],strMessage,strlen(strMessage)+1,0);
}
/*****************************************************************************************************
FUNCTION_NAME:SaveMail
USED_TO: 将mailContent的邮件内容保存到mailAddress本地帐户中
Date: 2007-4-14
*****************************************************************************************************/
void SaveMail( MailContent *mailContent,
char *mailAddress
)
{
FILE *fp; //定义文件指针
int mailNum=0; //定义该该用户的收件箱的总邮件数
char userName[20]; //定义用户名,最多20个字符
char path[50]; //定义用户邮件保存地址
char *numStr;
// char *mail; //把邮件的内容全存放到这个字符串里
int dataContentLen=0;
/*
//计算邮件内容长度
dataContentLen=strlen("From: ")+strlen(mailContent->from)+2;
dataContentLen+=strlen("To: ")+strlen(mailContent->to)+2;
if(NULL!=mailContent->date)dataContentLen+=strlen("Date: ")+strlen(mailContent->date)+2;/
if(NULL!=mailContent->sender)dataContentLen+=strlen("Sender: ")+strlen(mailContent->sender)+2;
if(NULL!=mailContent->replyTo)dataContentLen+=strlen("Reply-To: ")+strlen(mailContent->replyTo)+2;//
if(NULL!=mailContent->Subject)dataContentLen+=strlen("Subject: ")+strlen(mailContent->Subject)+2;//
dataContentLen+=strlen(mailContent->data)+1;
mail=(char *)malloc(sizeof(char)*dataContentLen);//
if(NULL==mail)return ;
memset(mail,0,dataContentLen);/
*/
memset(userName,0,sizeof(char)*20);
memset(path,0,50);
// printf("mailAddress=%sTTTTTTTT/n",mailAddress);///
GetUser(mailAddress,userName); //从邮件中获得用户名
// printf("userName=%sQQQQ/n",userName);///
if(ExistUserName(userName))
{
WIN32_FIND_DATA FindFileData;
HANDLE hFind;
char *filter=(char *)malloc(sizeof(char)*40);
memset(filter,0,40);
strcat(filter,userName);
memcpy(filter+strlen(userName),"//RecieveBox//*.eml",17);
printf("filter=%s/n",filter);///
hFind=FindFirstFile(filter,&FindFileData);
if(INVALID_HANDLE_VALUE!=hFind)
{
mailNum++;
while(FindNextFile(hFind,&FindFileData))
{
mailNum++;
}
}
// printf("mailNum=%d/n",mailNum);/
free(filter);
FindClose(hFind);
mailNum++;
strcat(path,userName);
strcat(path,"//");
strcat(path,"RecieveBox//");
numStr=(char *)malloc(4);
memset(numStr,0,4);
itoa(mailNum,numStr,10);
strcat(path,numStr);
strcat(path,".eml");
printf("path=%s/n",path);///
if((fp=fopen(path,"w"))==NULL)///说明打开有问题
{
printf("error/n");
return ;
}
// strcat(mail,"From: ");/
// printf("mail=%s/n",mail);/
fputs("From: ",fp);
// strcat(mail,mailContent->from);/
fputs(mailContent->from,fp);
// strcat(mail,"/r/nTo: ");
fputs("/r/nTo: ",fp);
fputs(mailAddress,fp);
// strcat(mail,mailAddress);///
if(0!=strlen(mailContent->date))
{
// strcat(mail,"/r/nDate: ");
// strcat(mail,mailContent->date);
fputs("/r/nDate: ",fp);
fputs(mailContent->date,fp);
}
if(0!=strlen(mailContent->sender))
{
// strcat(mail,"/r/nSender: ");
// strcat(mail,mailContent->sender);
fputs("/r/nSender: ",fp);
fputs(mailContent->sender,fp);
}
if(0!=strlen(mailContent->replyTo))
{
// strcat(mail,"/r/nReply-To: ");
// strcat(mail,mailContent->replyTo);
fputs("/r/nReply-To: ",fp);
fputs(mailContent->replyTo,fp);
}
if(0!=strlen(mailContent->Subject))
{
// strcat(mail,"/r/nSubject: ");
// strcat(mail,mailContent->Subject);
fputs("/r/nSubject: ",fp);
fputs(mailContent->Subject,fp);
}
// strcat(mail,mailContent->data);
fputs(mailContent->data,fp);
fclose(fp);
// printf("mail=%sPPPPP/n",mail);/
printf("发来的邮件存进去了/n");///
// free(mail);
}
return ;
}
/*****************************************************************************************************
Function_Name: GetUser
Used_To: 从mailAddress中提取出用户名并保存到userName中
Param: mailAddress--待分析的标准mailAddress userName--保存用户名的开始地址
Date: 2007-4-14
*****************************************************************************************************/
void GetUser(char *mailAddress,char *userName)
{
int i=0;
while('@'!=mailAddress[i])
{
userName[i]=mailAddress[i];
i++;
}
}
/*****************************************************************************************************/
/*****************************************************************************************************
Function_Name: ExistUserName;
Used To: 从文件UserInfo.dat中查找userName是否存在
Return Value: 0--不存在 1--存在
Date: 2007-4-14
*****************************************************************************************************/
int ExistUserName(char *userName)
{
FILE *fp;
int fileLength; //定义文件长度
int usercount; //定义用户数量
int result=0;
UserInfo userInfo;
memset(&userInfo,0,sizeof(struct UserInfo));
if((fp=fopen("user.txt","r"))==NULL)
return result;
fseek(fp,0,2);
fileLength=ftell(fp); //得到文件长度
usercount=fileLength/(sizeof(struct UserInfo));//得到用户数量
fseek(fp,0,0);
for(int i=0;i<usercount;i++)
{
fread(&userInfo,sizeof(UserInfo),1,fp);
if(strlen(userInfo.username)!=strlen(userName))continue;
if(0==strncmp(userInfo.username,userName,strlen(userName)))
{
result=1;
printf("result=%d/n",result);
fclose(fp);
return result;
}
}
fclose(fp);
/*
fp=fopen("user.dat","r"); //以二进制读方式打开
if(NULL==fp) return NULL;
printf("打开成功/n");///
fseek(fp,0,2);
fileLength=ftell(fp); //得到文件长度
usercount=fileLength/(sizeof(struct UserInfo));//得到用户数量
for(int i=0;i<usercount;i++)
{
fseek(fp,i*sizeof(struct UserInfo),0);
fread(&userInfo,sizeof(struct UserInfo),1,fp);
printf("userInfo=%s/n",userInfo);
if(strlen(userInfo.username)!=strlen(userName))continue;
if(0==strncmp(userInfo.username,userName,strlen(userName)))
result=1;
}
fclose(fp);
*/
printf("result=%d/n",result);
return result;
}
/*****************************************************************************************************
Function_Name: GetDomainServer
Used To: 根据传入的domain邮件域字符串查找对应的服务器地址
Params: domain--要查找的邮件域 domainServer--输出查找到的服务器地址
Date: 2007-4-14
*****************************************************************************************************/
void GetDomainServer(char *domain,char *domainServer)
{
if(0==strncmp("sina.com",domain,8))
strcpy(domainServer,"smtp.sina.com.cn");
else if(0==strncmp("126.com",domain,7))
strcpy(domainServer,"smtp.126.com");
else if(0==strncmp("sohu.com",domain,8))
strcpy(domainServer,"smtp.sohu.com");//61.135.132.100
else if(0==strncmp("163.com",domain,7))
strcpy(domainServer,"smtp.163.com");
else if(0==strncmp("yahoo.com.cn",domain,12))
strcpy(domainServer,"smtp.mail.yahoo.com.cn");
else if(0==strncmp("qq",domain,2))
strcpy(domainServer,"smtp.qq.com");
else if(0==strncmp("gmail.com",domain,5))
strcpy(domainServer,"smtp.gmail.com");
else if(0==strncmp("linlin",domain,6))
strcpy(domainServer,"10.4.142.9");
else if(0==strncmp("binbin.com",domain,6))
strcpy(domainServer,"127.0.0.1");
else
strcpy(domainServer,"UNKNOWN");
}
/*****************************************************************************************************
Function_Name: ZiDuanAlalysic
Used To: 邮件头字段分析
Params: ziDuan--待分析的邮件头字段 clientContent--分析后保存命令和参数的结构
Date: 2007-4-4
******************************************************************************************************/
void ZiDuanAlalysic( char *ziDuan, //待分析的字段
MailContent *mailContent //分析后结果保存在这个结构的相应字段中
)
{
printf("ziDuan=%s/n",ziDuan);/
if(0==strncmp(ziDuan,"Subject:",8))
{
strncpy(mailContent->Subject,ziDuan+8,strlen(ziDuan)-8);
}
else if(0==strncmp(ziDuan,"Sender:",7))
{
strncpy(mailContent->sender,ziDuan+7,strlen(ziDuan)-7);
}
else if(0==strncmp(ziDuan,"Reply-To:",9))
{
strncpy(mailContent->replyTo,ziDuan+9,strlen(ziDuan)-9);
}
else if(0==strncmp(ziDuan,"Bcc:",4))
{
strncpy(mailContent->bcc,ziDuan+4,strlen(ziDuan)-4);
}
else if(0==strncmp(ziDuan,"Date:",5))
{
strncpy(mailContent->date,ziDuan+5,strlen(ziDuan)-5);
}
else if(0==strncmp(ziDuan,"Cc:",3))
{
strncpy(mailContent->cc,ziDuan+3,strlen(ziDuan)-3);
}
else //服务器不认识的字段放在新的mailContent->data中
{
strncpy(mailContent->data+strlen(mailContent->data),ziDuan,strlen(ziDuan));
}
}
/*****************************************************************************************************
Function_Name: CommandHandle
Used To: 接受客户命令并处理命令
Params: index--客户服务号
Date: 2007-4-3
*****************************************************************************************************/
void CommandHandle(int index)
{
/*****************************************************************************************************/
struct ClientContent clientContent; //保存接受的信息内容(包含命令和参数)
struct MailContent mailContent; //保存信件头的信息
struct CommandFinished commandFinished; //保存命令完成状态
memset(&commandFinished,0,sizeof(struct CommandFinished)); //初始化每个命令未执行
memset(&mailContent,0,sizeof(struct MailContent)); //开始时清空邮件体内容
memset(&clientContent,0,sizeof(struct ClientContent)); //清空命令字和参数
readBuf=(char *)malloc(BUFFER_SIZE+1); //分配接收数据的缓冲区,最后为/0结束符
memset(readBuf,0,BUFFER_SIZE+1); //清空接受数据的缓冲区
/*****************************************************************************************************/
//第2步
recv(clientSocket[index],readBuf,BUFFER_SIZE,0); //读取客户发送的命令信息
GetCommandAndContent(readBuf,&clientContent); //获得命令信息保存到clientContent中
while(0!=strcmp("HELO",clientContent.command))
{
send(clientSocket[index],"503 错误的命令序列,请先发送HELO/r/n",sizeof("503 错误的命令序列,请先发送HELO/r/n"),0);
memset(readBuf,0,BUFFER_SIZE+1); //清空接收缓冲区重新接受数据
//第3步
recv(clientSocket[index],readBuf,BUFFER_SIZE,0);
memset(&clientContent,0,sizeof(struct ClientContent)); //清空客户命令参数信息结构
GetCommandAndContent(readBuf,&clientContent); //重新获得命令信息
}
/*****************************************************************************************************/ //HELO命令成功执行后
send(clientSocket[index],"250 OK Lin Lin SMTP Server...",sizeof("250 OK Lin Lin SMTP Server..."),0);
memset(readBuf,0,BUFFER_SIZE+1); //readBuf清0准备重新接受数据
//第3步
recv(clientSocket[index],readBuf,BUFFER_SIZE,0);
printf("readBuf=%s/n",readBuf);///
memset(&clientContent,0,sizeof(struct ClientContent)); //清空客户命令参数信息结构
GetCommandAndContent(readBuf,&clientContent); //重新获得命令信息
while(0!=strcmp("QUIT",clientContent.command)) //当客户发送的命令不为QUIT时
{
if(0==strcmp("MAIL FROM:",clientContent.command)) //处理Mail命令
{
MAIL_Handle(index,&commandFinished,clientContent.content,&mailContent);
memset(readBuf,0,BUFFER_SIZE+1); //readBuf清0接受下个命令
//第4步
recv(clientSocket[index],readBuf,BUFFER_SIZE,0);
memset(&clientContent,0,sizeof(struct ClientContent)); //清空客户命令参数信息结构
GetCommandAndContent(readBuf,&clientContent); //重新获得命令信息
}
else if(0==strcmp("RCPT TO:",clientContent.command)) //处理RCPT TO命令
{
RCPT_Handle(index,&commandFinished,clientContent.content,&mailContent);
memset(readBuf,0,BUFFER_SIZE+1); //readBuf清0准备重新接受数据
//第5步
recv(clientSocket[index],readBuf,BUFFER_SIZE,0);
memset(&clientContent,0,sizeof(struct ClientContent)); //清空客户命令参数信息结构
GetCommandAndContent(readBuf,&clientContent); //重新获得命令信息
}
else if(0==strcmp("DATA",clientContent.command)) //处理DATA命令
{
DATA_Handle(index,&commandFinished,clientContent.content,&mailContent);
memset(readBuf,0,BUFFER_SIZE+1); //readBuf清0准备重新接受数据
//第7步具体内容已经接受结束,开始接受其他剩下的
recv(clientSocket[index],readBuf,BUFFER_SIZE,0);
memset(&clientContent,0,sizeof(struct ClientContent)); //清空客户命令参数信息结构
GetCommandAndContent(readBuf,&clientContent); //重新获得命令信息
}
else if(0==strcmp("RSET",clientContent.command)) //处理RSET命令
{
RSET_Handle(index,&commandFinished,clientContent.content,&mailContent);
memset(readBuf,0,BUFFER_SIZE+1); //readBuf清0准备重新接受数据
//第8步
recv(clientSocket[index],readBuf,BUFFER_SIZE,0);
memset(&clientContent,0,sizeof(struct ClientContent)); //清空客户命令参数信息结构
GetCommandAndContent(readBuf,&clientContent); //重新获得命令信息
}
else if(0==strcmp("LOOP",clientContent.command)) //处理LOOP命令
{
LOOP_Handle(index,&commandFinished,clientContent.content,&mailContent);
memset(readBuf,0,BUFFER_SIZE+1); //readBuf清0准备重新接受数据
//第8步
recv(clientSocket[index],readBuf,BUFFER_SIZE,0);
memset(&clientContent,0,sizeof(struct ClientContent)); //清空客户命令参数信息结构
GetCommandAndContent(readBuf,&clientContent); //重新获得命令信息
}
else if(0==strcmp("HELP",clientContent.command)) //处理HELP命令
{
MAIL_Handle(index,&commandFinished,clientContent.content,&mailContent);
memset(readBuf,0,BUFFER_SIZE+1); //readBuf清0准备重新接受数据
//第8步
recv(clientSocket[index],readBuf,BUFFER_SIZE,0);
memset(&clientContent,0,sizeof(struct ClientContent)); //清空客户命令参数信息结构
GetCommandAndContent(readBuf,&clientContent); //重新获得命令信息
}
else //处理不认识的命令;
{
send(clientSocket[index],"502 Command not implemented",sizeof("502 Command not implemented"),0);
memset(readBuf,0,BUFFER_SIZE+1); //readBuf清0准备重新接受数据
recv(clientSocket[index],readBuf,BUFFER_SIZE,0);
memset(&clientContent,0,sizeof(struct ClientContent)); //清空客户命令参数信息结构
GetCommandAndContent(readBuf,&clientContent); //重新获得命令信息
}
}
//以下处理QUIT命令
QUIT_Handle(index,&commandFinished,clientContent.content,&mailContent);
}