前段时间做了一个邮件服务器,其实操作都不难,这个里面的难点其实就是这个项目得需要用到一些技术,代码量会比较多,容易出错一点,只要在写代码的时候每实现一个功能就仔细测试一下,这样就会简单很多。
由于我只写了服务器端,注释也写的比较多,所以就不贴演示图了,客户端用的是微软比较老的客户端outlook。
直接贴代码了,如果有人有需要可以参考一下,有问题或者指点也可以评论问我哦。
common.h
#ifndef COMMON_H
#define COMMON_H
#define MAX_MAIL (1024)
#define MAX_ATTACHMENT (7 * 1024)
#define USERFILE "table.txt"
typedef struct mail
{
char send[80]; //发送者地址
char recv[80]; //接收者地址
char subject[80]; //主题字
char filename[80]; //文件名
char raw[MAX_MAIL]; //邮件内容
char atta[MAX_ATTACHMENT]; //附件内容
int len; //附件长度
} mail_t;
typedef struct subject_ctrl
{
char command[16]; //命令字 8LED 7SHU MOTO CHANGETABLE
int bulb; //灯的号数(1~8)
int signal; //七段数码管显示信号(0~99)
int revo; //电机转数(0,1000)
int bulb_ctrl; //控制信号(0关灯,1开灯)
int result; //0表示控制成功,-1表示控制失败
} ctrl_t;
typedef struct table
{
char username[10]; // 邮件用户名
char password[10]; //用户密码
}table_t;
#endif
central.c
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "net.h"
#include "mailrecv.h"
typedef struct Accept
{
struct sockaddr_in cid_addr;
int cid;
} Accept_t;
int Socket(int domain, int type, int protocol);
void Bind(int sockfd, const char *ip, short Port);
void Listen(int sockfd, int backlog);
void Print_cid(const struct sockaddr_in *addr);
void *pop3(void *arg);
void *smtp(void *arg);
char info[2048] = "";
mail_t *mail;
table_t table = {0};
struct subject_ctrl ctrl = {0};
void mail_guard();
/************************************************
* 函数名:int main()
* 功能:实现smtp与pop3服务
* 参数:
*
* 返回值:
* 成功:0
* 失败:-1
* *********************************************/
int main()
{
pid_t pid = fork();
if (pid < 0)
{
printf("进程创建失败\n");
return -1;
}
if (pid == 0) //子进程1
{
printf("子进程1\n");
int sid = Socket(AF_INET, SOCK_STREAM, 0);
int cid = -1;
Bind(sid, "192.168.206.129", 10086);
Listen(sid, 5);
struct sockaddr_in cid_addr;
socklen_t len = sizeof(cid_addr);
pthread_t newthread;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
Accept_t Accept;
while (1)
{
cid = accept(sid, (struct sockaddr *)&cid_addr, &len);
Accept.cid_addr = cid_addr;
Accept.cid = cid;
if (cid < 0)
{
perror("accept:");
exit(-1);
}
//Print_cid(&cid_addr);
pthread_create(&newthread, &attr, smtp, &Accept);
}
}
pid_t pid2 = fork();
if (pid2 < 0)
{
printf("进程创建失败\n");
return -1;
}
if (pid2 == 0) //子进程2
{
printf("子进程2\n");
int sid1 = Socket(AF_INET, SOCK_STREAM, 0);
int cid1 = -1;
Bind(sid1, "192.168.206.129", 10010);
Listen(sid1, 5);
struct sockaddr_in cid_addr1;
socklen_t len = sizeof(cid_addr1);
pthread_t newthread1;
pthread_attr_t attr1;
pthread_attr_init(&attr1);
pthread_attr_setdetachstate(&attr1, PTHREAD_CREATE_DETACHED);
Accept_t Accept1;
while (1)
{
cid1 = accept(sid1, (struct sockaddr *)&cid_addr1, &len);
Accept1.cid_addr = cid_addr1;
Accept1.cid = cid1;
if (cid1 < 0)
{
perror("accept:");
exit(-1);
}
//Print_cid(&cid_addr1);
pthread_create(&newthread1, &attr1, pop3, &Accept1);
}
}
int icuc = 0;
int fx = open("/usr/mail/icu", O_RDWR | O_CREAT, 0666);
if (fx <= 0)
{
perror("初始化icu文件打开失败:");
return -1;
}
write(fx, &icuc, sizeof(icuc));
if (pid != 0)
{
printf("杀死父进程\n");
exit(0);
}
if (setsid() < 0)
{
printf("创建会话失败\n");
}
chdir("/usr/mail");
umask(0);
for (size_t i = 0; i < getdtablesize(); i++)
{
close(i);
}
close(fx);
return 0;
}
/************************************************
* 函数名:void *smtp(void *arg)
* 功能:实现smtp服务
* 参数:
* arg:传递已经建立好连接的套接字
* 返回值:
* 无
* *********************************************/
void *smtp(void *arg)
{
Accept_t *Accept = (Accept_t *)arg;
struct sockaddr_in cid_addr = Accept->cid_addr;
int cid = Accept->cid;
int fd;
char filename[100] = "";
mail_t *mail;
int fx;
int icu;
fx = open("/usr/mail/icu", O_RDWR | O_CREAT, 0666);
if (fx <= 0)
{
perror("icu文件打开失败:");
exit(-1);
}
read(fx, &icu, sizeof(icu));
lseek(fx, 0, SEEK_SET);
mail = malloc(sizeof(mail_t));
handleconnection(cid, mail, &table);
parsemail(mail, &ctrl, info, &table);
icu++;
write(fx, &icu, sizeof(icu));
sprintf(filename, "//usr//mail//%s//%d%s", "letter", icu, table.username);
fd = open(filename, O_RDWR | O_CREAT, 0666);
if (fd <= 0)
{
perror("邮件文件打开失败:");
exit(-1);
}
write(fd, info, strlen(info));
free(mail);
mail = NULL;
close(fd);
close(fx);
close(cid);
}
/************************************************
* 函数名:void *pop3(void *arg)
* 功能:实现pop3服务
* 参数:
* arg:传递已经建立好连接的套接字
* 返回值:
* 无
* *********************************************/
void *pop3(void *arg)
{
int icu;
char yy;
Accept_t *Accept = (Accept_t *)arg;
struct sockaddr_in cid_addr = Accept->cid_addr;
int cid = Accept->cid;
table_t table={0};
int fx;
char icuc = '0';
fx = open("/usr/mail/icu", O_RDWR | O_CREAT, 0666);
if (fx <= 0)
{
perror("icu文件打开失败:");
exit(-1);
}
read(fx, &icu, sizeof(icu));
pop3Connection(cid, info, &table, icu);
if (icu > 0)
icu--;
lseek(fx, 0, SEEK_SET);
write(fx, &icu, sizeof(icu));
close(cid);
mail = NULL;
close(fx);
mail = NULL;
}
mailrecv.h
#include "base64.h"
int getusername(int sockfd, struct table *p); //SMTP
int getpassword(int sockfd, struct table *p);
int getfromaddress(int sockfd, struct mail *pmail);
int gettoaddress(int sockfd, struct mail *pmail);
int getbody(int sockfd, struct mail *pmail);
#endif
mailrecv.c
#include "mailrecv.h"
/************************************************
* 函数名:int getusername(int sockfd, struct table *p)
* 功能:获取用户名 并解码
* 参数:
* sockfd:套接字
* p:存放解析之后的用户名的结构体
* 返回值:
* 成功:0
* 失败:-1
* *********************************************/
int getusername(int sockfd, struct table *p)
{
if (sockfd < 2)
{
return -1;
}
char buf[128] = "";
read(sockfd, buf, sizeof(buf) - 1);
buf[strlen(buf) - 2] = '\0';
strcpy(buf, base64_decode(buf));
strcpy(p->username, buf);
return 0;
}
/************************************************
* 函数名:int getpassword(int sockfd, struct table *p)
* 功能:获取密码 并解码
* 参数:
* sockfd:套接字
* p:存放解析之后的密码的结构体
* 返回值:
* 成功:0
* 失败:-1
* *********************************************/
int getpassword(int sockfd, struct table *p)
{
if (sockfd < 2)
{
return -1;
}
char buf[128] = "";
read(sockfd, buf, sizeof(buf) - 1);
buf[strlen(buf) - 2] = '\0';
strcpy(buf, base64_decode(buf));
strcpy(p->password, buf);
return 0;
}
/************************************************
* 函数名:int getfromaddress(int sockfd, struct mail *pmail)
* 功能:获取邮件信息中的发送方的地址
* 参数:
* sockfd:套接字
* pmail:用来存放发送方地址的结构体
* 返回值:
* 成功:0
* 失败:-1
* *********************************************/
int getfromaddress(int sockfd, struct mail *pmail)
{
if (sockfd < 2)
{
return -1;
}
char buf[128] = "";
read(sockfd, buf, sizeof(buf) - 1);
strcpy(pmail->send, buf);
printf("地址:%s\n", buf);
return 0;
}
/************************************************
* 函数名:int gettoaddress(int sockfd, struct mail *pmail)
* 功能:获取邮件信息中的目的地址
* 参数:
* sockfd:套接字
* pmail:用来存放目的地址的结构体
* 返回值:
* 成功:0
* 失败:-1
* *********************************************/
int gettoaddress(int sockfd, struct mail *pmail)
{
if (sockfd < 2)
{
return -1;
}
char buf[128] = "";
read(sockfd, buf, sizeof(buf) - 1);
strcpy(pmail->recv, buf);
printf("地址:%s\n", buf);
return 0;
}
/************************************************
* 函数名:int getbody(int sockfd, struct mail *pmail)
* 功能:获取邮件信息中正文
* 参数:
* sockfd:套接字
* pmail:用来存放邮件正文的结构体
* 返回值:
* 成功:0
* 失败:-1
* *********************************************/
int getbody(int sockfd, struct mail *pmail)
{
if (sockfd < 2)
{
return -1;
}
char buf[4096] = "";
read(sockfd, buf, sizeof(buf) - 1);
printf("body数据为:%s\n", buf);
strcpy(pmail->raw, buf);
read(sockfd, buf, sizeof(buf) - 1);
return 0;
}
parsemail.h
#ifndef PARSEMAIL_H
#define PARSEMAIL_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sqlite3.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include "common.h"
#include "base64.h"
#define sqlite3_databas_filename "/usr/mail/data.db" //数据库名称
#define sqlite3_table_filename "user" //表名称
int vernamepass(table_t usertable);
int parsemail(struct mail *pmail, struct subject_ctrl *subject,char *info,table_t *table);
#endif
parsemail.c
#include "parsemail.h"
/************************************************
* 函数名:int vernamepass(table_t usertable)
* 功能:验证登录
* 参数:
* pmail:存放了邮件解析出的信息
* subject:控制硬件的结构体
* info:拼装邮件用到的缓存变量
* table:其存放了用户名与密码
* 返回值:
* 成功:0
* 失败:-1
* *********************************************/
int vernamepass(table_t usertable)
{
sqlite3 *ppDb; //数据库操作句柄
if (sqlite3_open(sqlite3_databas_filename, &ppDb) != 0)
{
printf("sqlite3_open error:%s\n", sqlite3_errmsg(ppDb));
exit(-1);
}
//printf("本地<%s>数据库打开成功\n", sqlite3_databas_filename);
char **pazResult; //用于存储数据返回的二维表格(降为打击)
int pnRow; //行
int pnColumn; //列
char *pzErrmsg; //错误码
int len;
char login[1024] = "select * from user where 用户名 = '%s' and 密码 = '%s' ;";
char loginbuf[1024] = "";
sprintf(loginbuf, login, usertable.username, usertable.password);
len = strlen(login);
if (sqlite3_get_table(ppDb, loginbuf, &pazResult, &pnRow, &pnColumn, &pzErrmsg) != 0)
{
printf("登录失败! sqlite3_get_table error:%s\n", pzErrmsg);
return 0;
}
else
{
if (pnRow >= 1)
{
sqlite3_close(ppDb);
return 1;
}
else
{
sqlite3_close(ppDb);
return 0;
}
}
return -1;
}
/************************************************
* 函数名:int parsemail(struct mail *pmail, struct subject_ctrl *subject, char *info, table_t *table)
* 功能:解析邮件 进行邮件的组装
* 参数:
* pmail:存放了邮件解析出的信息
* subject:控制硬件的结构体
* info:拼装邮件用到的缓存变量
* table:其存放了用户名与密码
* 返回值:
* 成功:0
* 失败:-1
* *********************************************/
int parsemail(struct mail *pmail, struct subject_ctrl *subject, char *info, table_t *table)
{
char *p;
sqlite3 *ppDb; //数据库操作句柄
char **pazResult; //用于存储数据返回的二维表格(降为打击)
int pnRow; //行
int pnColumn; //列
char *pzErrmsg; //错误码
if (sqlite3_open(sqlite3_databas_filename, &ppDb) != 0)
{
printf("拼接邮件时 数据库 打开失败!\n");
exit(-1);
}
const char *update = "update user set 密码 = '%s' where 用户名 = '%s' ; \n";
char updatebuf[1024] = "";
/***************获取时间 用来拼接邮件************************/
time_t t;
FILE *fa;
struct tm *tt;
char *time_str;
t = time(&t); //获取秒数
tt = localtime(&t); //转换成本地时间结构体
time_str = asctime(tt); //转换成字符串时间
/********************************************************/
char subjectbuf[1024] = "";
char tempbuf[1024] = "";
char username[50] = "";
char password[50] = "";
if (NULL == pmail || NULL == subject)
{
return -1;
}
printf("%s\n", pmail->raw);
const char *sub = "Subject:";
char *start = strstr(pmail->raw, sub) + 9;
if (NULL == start)
{
return -1;
}
char *end = strstr(start, "\r\n") - 1;
if (NULL == end)
{
return -1;
}
int len = end - start + 1;
char buf[128] = "";
strncpy(buf, start, len);
const char *command = strtok(buf, " ");
if (NULL == command)
{
return -1;
}
strcpy(subject->command, command);
if (!strcmp(command, "8LED"))
{
const char *blub = strtok(NULL, " ");
subject->bulb = atoi(blub);
const char *blub_ctrl = strtok(NULL, " ");
subject->bulb_ctrl = atoi(blub_ctrl);
sprintf(subjectbuf, "8LED %d %d OK\n", subject->bulb, subject->bulb_ctrl);
}
else if (!strcmp(command, "7SHU"))
{
const char *num = strtok(NULL, " ");
subject->signal = atoi(num);
sprintf(subjectbuf, "7SHU %d OK\n", subject->signal);
}
else if (!strcmp(command, "MOTO"))
{
const char *revo = strtok(NULL, " ");
subject->revo = atoi(revo);
sprintf(subjectbuf, "MOTO %d OK\n", subject->revo);
}
else if (!strcmp(command, "CHANGETABLE"))
{
const char *sub1 = "filename=";
char *start1 = strstr(pmail->raw, sub1);
//table.txt
if (start1 == NULL)
{
sprintf(subjectbuf, "CHANGETABLE FAIL!!NO FILE\n");
}
else
{
strcpy(start1, start1 + 24);
printf("\n附件信息为%s\n", start1);
p = start1;
while (*(p++) != '-')
;
*(p - 1) = '\0';
printf("\n附件信息为%s\n", start1);
fa = fopen("/usr/mail/usertemp", "w+");
if (fa == NULL)
{
printf("账户缓存打开失败");
exit(-1);
}
fwrite(start1, strlen(start1), 1, fa);
fseek(fa, 0, SEEK_SET);
while (fscanf(fa, "%s %s\n", username, password) != EOF)
{
sprintf(updatebuf, update, password, username);
printf("%s",updatebuf);
if (sqlite3_get_table(ppDb, updatebuf, &pazResult, &pnRow, &pnColumn, &pzErrmsg) != 0)
{
printf("执行sql语句失败! sqlite3_get_table error:%s\n", pzErrmsg);
return 0;
}
else
{
printf("账号更新成功\n");
}
}
sprintf(subjectbuf, "CHANGETABLE OK\n");
}
}
else
{
sprintf(subjectbuf, "Parse command error!!!!!\n");
//return;
}
sqlite3_close(ppDb);
memset(pmail->raw, 0, sizeof(pmail->raw));
/*********************拼接邮件***********************************/
// Message-ID: <001101c93cc4$1ed2ae30$5400a8c0@hexlerforever>\r\n
// From: %s <%s>\r\n
// To: <%s>\r\n
// Subject:%s \r\n OK
// Date: Sun Nov 2 14:26:30 2008
// MIME-Version: 1.0\r\n
// Content-Type: multipart/alternative; \r\n
// X-Priority: 3..X-MSMail-Priority: Normal\r\n
// X-Mailer: Microsoft Outlook Express 6.00.2900.3138\r\n
// X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.3198\r\n
// \r\n
// This is response of %s device!\r\n
strcpy(info, "Message-ID: <001101c93cc4$1ed2ae30$5400a8c0@hexlerforever>\r\n");
sprintf(tempbuf, "From: Ubuntu1604 <192.168.206.129@qq.com>\r\n");
strcat(info, tempbuf);
sprintf(tempbuf, "To: %s <%s@qq.com>\r\n", table->username, table->username);
strcat(info, tempbuf);
subjectbuf[strlen(subjectbuf) - 1] = '\0';
sprintf(tempbuf, "Subject:%s \r\n", subjectbuf);
strcat(info, tempbuf);
sprintf(tempbuf, "Date: %s", time_str);
strcat(info, tempbuf);
strcat(info, "MIME-Version: 1.0\r\nContent-Type: multipart/alternative;\r\n\
X-Priority: 3..X-MSMail-Priority: Normal\r\nX-Mailer: Microsoft Outlook Express 6.00.2900.3138\r\n\
X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.3198\r\n\r\n");
sprintf(tempbuf, "This is response of %s device!\r\n", table->username);
strcat(info, tempbuf);
return 0;
}
net.h
#ifndef NET_H
#define NET_H
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "common.h"
#include "base64.h"
#include "parsemail.h"
#include "mailrecv.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <netinet/in.h>
#include "common.h"
#include "parsemail.h"
int handleconnection(int sockfd, struct mail *pmail,table_t *table);
void pop3Connection(int cfd, char *info,table_t *table,int letx);
int Socket(int domain, int type, int protocol);
void Bind(int sockfd, const char *ip, short Port);
void Listen(int sockfd, int backlog);
void Print_cid(const struct sockaddr_in *addr);
#endif
net.c
#include "net.h"
/************************************************
* 函数名:int handleconnection(int cfd, struct mail *pmail, table_t *table)
* 功能:SMTP服务 锁步流程
* 参数:
* cfd:套接字
* pmail:解析邮件内容后 存放的结构体
* table:获取到用户名和密码之后存放的结构体
* 返回值:
* 成功:0
* 失败:-1
* *********************************************/
int handleconnection(int cfd, struct mail *pmail, table_t *table)
{
char *bufa;
if (cfd < 2 || NULL == pmail)
{
return -1;
}
char *response220 = "220 192.168.206.132 ok\r\n";
char *response250_HELO = "250-192.168.206.132\r\n250-PIPELINING\r\n250-SIZE 52428800\r\n250-AUTH LOGIN PLAIN\r\n250-AUTH=LOGIN\r\n250-MAILCOMPRESS\r\n250 BITMIME\r\n";
char *response334_user = "334 VXNlcm5hbWU6\r\n";
char *response334_pass = "334 UGFzc3dvcmQ6\r\n";
char *response235 = "235 Authenticatin successful\r\n";
char *response250_ok = "250 OK\r\n";
char *response_354 = "354 End data with <CR><LF>.<CR><LF>\r\n";
char *response_221 = "221 Bye\r\n";
char buf[4096] = "";
while (write(cfd, response220, strlen(response220)))
{
if (read(cfd, buf, sizeof(buf) - 2) != -1)
{
sleep(1);
if (!strncmp(buf, "HELO", 4) || !strncmp(buf, "EHLO", 4))
{
//printf("读取到客户端 开始接收文件\n");
break;
}
}
}
//写入250_HELO
write(cfd, response250_HELO, strlen(response250_HELO));
read(cfd, buf, sizeof(buf) - 2);
//printf("登录验证 处理的字符串为%s\n", buf);
if (strncmp(buf, "AUTH LOGIN", 10))
{
perror("AUTH LOGIN error");
return -1;
}
//写入334_user
write(cfd, response334_user, strlen(response334_user));
//printf("获取用户名\n");
if (getusername(cfd, table))
{
perror("getusername error");
return -1;
}
//printf("获取密码\n");
//写入334_pass
write(cfd, response334_pass, strlen(response334_pass));
if (getpassword(cfd, table))
{
perror("getpassword error");
return -1;
}
//写入235_ok
write(cfd, response235, strlen(response235));
//printf("来源地址\n");
写入250_ok
write(cfd, response250_ok, strlen(response250_ok));
//当邮件是用户在客户端选择重发的时候 这里会增加一个REST字段 需要用到一个判断进行解决
read(cfd, buf, sizeof(buf) - 1);
if (strncmp(buf, "REST", 4))
{
strcpy(pmail->send, buf);
//发送方地址已输入至结构体
//已经输入到结构体
//printf("地址:%s\n", buf);
}
else
{
write(cfd, response250_ok, strlen(response250_ok));
getfromaddress(cfd, pmail);
perror("getFromaddress error");
return -1;
}
//写入250_ok
write(cfd, response250_ok, strlen(response250_ok));
//printf("发送地址\n");
if (gettoaddress(cfd, pmail))
{
perror("getToAddress error");
return -1;
}
//写入250_ok
write(cfd, response250_ok, strlen(response250_ok));
//printf("data附件数据\n");
read(cfd, buf, sizeof(buf) - 1);
//printf("\n附件数据%s\n", buf);
//写入354
write(cfd, response_354, strlen(response_354));
//printf("body正文:\n");
if (getbody(cfd, pmail))
{
perror("getbody error");
return -1;
}
//写入250_ok
write(cfd, response250_ok, strlen(response250_ok));
//printf("quit指令\n");
read(cfd, buf, sizeof(buf) - 1);
if (strncmp(buf, "QUIT", 4))
{
printf("接收到的quit指令 字符串部分为%s\n\n\n", buf);
perror("QUIT error");
return -1;
}
//printf("最后发送一个221\n\n");
//写入221
write(cfd, response_221, strlen(response_221));
//close(cfd);
return 0;
}
/************************************************
* 函数名:void pop3Connection(int cfd, char *info)
* 功能:pop3服务 发送邮件
* 参数:
* cid:套接字
* info:无用
* table:用来存放用户名与密码的结构体
* letx:信件数
* 返回值:
* 无
* *********************************************/
void pop3Connection(int cid, char *info, table_t *table, int letx)
{
char delfilename[50] = "rm ";
char filename[100] = "";
char *p;
int fd;
char buf[1024] = "";
//+OK POP3 server ready
strcpy(buf, "+OK POP3 mail server ready\r\n");
write(cid, buf, strlen(buf));
//printf("写入OK Pop3 cid完成\n");
read(cid, buf, sizeof(buf) - 1);
strcpy(buf, buf + 5);
p = buf;
do
{
if (*p == '\r')
*p = '\0';
} while (*(p++));
strcpy(table->username, buf);
//printf("读取到用户名信息为:%s\n", table->username);
//+OK hexler is welcome here
strcpy(buf, "+OK\r\n");
write(cid, buf, strlen(buf));
//printf("写入OK\n");
read(cid, buf, sizeof(buf) - 1);
strcpy(buf, buf + 5);
p = buf;
do
{
if (*p == '\r')
*p = '\0';
} while (*(p++));
strcpy(table->password, buf);
//printf("读取到密码信息为:%s\n", table->password);
while (vernamepass(*table) != 1)
;
//OK
strcpy(buf, "+OK\r\n");
write(cid, buf, strlen(buf));
//printf("写入OK\n");
read(cid, buf, sizeof(buf) - 1);
//printf("\n客户机发来的信息%s\n", buf);
//OK 1 300
if (letx <= 0)
{
//printf("没有邮件\n");
strcpy(buf, "+OK 0 0\r\n");
}
else
{
//printf("有邮件\n");
strcpy(buf, "+OK 1 300\r\n");
}
write(cid, buf, strlen(buf));
//printf("写入OK 1 300 or OK 0 0\n");
read(cid, buf, sizeof(buf) - 1);
//printf("\n客户机发来的信息%s\n", buf);
//OK 1 message\r\n1 300\r\n.\r\n
if (letx <= 0)
{
strcpy(buf, "+OK 0 message\r\n0 0\r\n.\r\n");
}
else
{
strcpy(buf, "+OK 1 message\r\n1 300\r\n.\r\n");
}
write(cid, buf, strlen(buf));
//printf("写入OK 1 message\n");
read(cid, buf, sizeof(buf) - 1);
//printf("\n客户机发来的信息%s\n", buf);
//OK 120 octets\r\n
strcpy(buf, "+OK 120 octets\r\n");
write(cid, buf, strlen(buf));
//printf("写入OK 120 octets\n");
//写入邮件信息
//printf("这里接收到的邮件信息:\n%s", info);
if (letx <= 0)
{
//printf("没有邮件\n");
strcpy(buf, "+OK 0 0\r\n");
}
else
{
sprintf(filename, "/usr/mail/%s/%d%s", "letter", letx, table->username);
fd = open(filename, O_RDWR | O_CREAT, 0666);
if (fd <= 0)
{
perror("写入邮件信息 文件打开失败:");
exit(-1);
}
strcat(delfilename, filename);
printf("\n%s\n", delfilename);
system(delfilename);
}
read(fd, buf, 1024);
//printf("%s读取到的文件内容为:%s\n", filename, buf);
write(cid, buf, strlen(buf));
//printf("写入邮件\n");
memset(buf, 0, sizeof(buf));
//写入结束符
strcpy(buf, "\r\n.\r\n");
write(cid, buf, strlen(buf));
//printf("写入rn\n");
read(cid, buf, sizeof(buf) - 1);
//printf("\n客户机发来的信息%s\n", buf);
//OK
strcpy(buf, "+OK\r\n");
write(cid, buf, strlen(buf));
//printf("写入OK\n");
read(cid, buf, sizeof(buf) - 1);
//printf("\n客户机发来的信息%s\n", buf);
//OK
strcpy(buf, "+OK\r\n");
write(cid, buf, strlen(buf));
//printf("写入OK\n");
}
/************************************************
* int Socket(int domain, int type, int protocol)
* 功能:建立socket套接字对象
* 参数:
* domain:域:用作基于什么类型操作
* Name Purpose Man page
* AF_UNIX, AF_LOCAL 局部通信 unix(7) 重点
* AF_INET IPv4网络协议 ip(7) 重点
* AF_INET6 IPv6网络协议 ipv6(7) 重点
* AF_IPX IPX - Novell协议
* AF_NETLINK 内核用户界面设备 netlink(7)
* AF_X25 ITU-T X.25 / ISO-8208协议 x25(7)
* AF_AX25 业余无线电AX.25协议
* AF_ATMPVC 访问原始ATM pvc
* AF_APPLETALK 可路由协议组 ddp(7)
* AF_PACKET 低层包接口 packet(7)
* AF_ALG 内核加密API接口
* type:套接字类型
* SOCK_STREAM:流式套接字 唯一对应 TCP协议
* SOCK_DGRAM:数据报套接字 唯一对应 UDP协议
* SOCK_RAW:原始套接字
* protocol:根据套接字类型,一般默认填0,如果式原始套接字等则需要
* 返回值:
* 成功:返回特殊的文件描述符
* 失败:-1,且errno存储错误类型
* *********************************************/
int Socket(int domain, int type, int protocol)
{
int sid = socket(domain, type, protocol);
if (sid < 0)
{
perror("socket:");
exit(-1);
}
return sid;
}
/************************************************
* 函数名:void Bind(int sockfd, const char *ip, short Port)
* 功能:为任务绑定ip和端口号
* 参数:
* sockfd:通过socket函数拿到的文件特殊描述符
* addr:struct sockaddr的结构体变量的首地址
* addrlen:struct sockaddr的结构体变量的长度
* 返回值:
* 成功:0
* 失败:-1,并且填写errno
* *********************************************/
void Bind(int sockfd, const char *ip, short Port)
{
struct sockaddr_in addr;
addr.sin_family = AF_INET | SO_REUSEADDR;
addr.sin_port = htons(Port);
addr.sin_addr.s_addr = inet_addr(ip);
if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
{
perror("bind:");
exit(-1);
}
}
/************************************************
* 函数名:void Listen(int sockfd, int backlog)
* 功能:监听套接字,将主动套接字变成被动套接字
* 参数:
* sockfd:通过socket函数拿到的文件特殊描述符
* backlog:定义了队列的最大长度,最多客户端同时访问,一般填5
* 如果一个连接请求当队列已满时到达,客户端可能会收到一个错误指示ECO
* NNREFUSED或,如果底层 协议支持重传时,请求可能会被忽略,以便稍
* 后重试连接成功。
* 返回值:
* 成功:0
* 失败:-1,并且填写errno
* *********************************************/
void Listen(int sockfd, int backlog)
{
if (listen(sockfd, backlog) == -1)
{
perror("listen:");
exit(-1);
}
}
/************************************************
* 函数名:Print_cid(const struct sockaddr_in *addr)
* 功能:输出套接字
* 参数:
* addr:套接字地址
* 返回值:
* 无
* *********************************************/
void Print_cid(const struct sockaddr_in *addr)
{
printf("类型是%s\n", addr->sin_family == AF_INET ? "AFINET" : "AF_INET6");
printf("ip地址为:%s\n端口号:%d\n", inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
}
下面解码的这个功能不是我写的,我直接用的,上面所有的功能都基本上是我写的吧,注释也都写好了,很容易就能看懂,分享不易,希望多多支持。
base64.h
#ifndef BASE64_H
#define BASE64_H
#include <stdio.h>
#include <string.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#define BASE64_PAD64 '='
char* base64_encode(const char *data);
char* base64_decode(const char *bdata);
#endif
base64.c
#include"base64.h"
#define BASE64_PAD64 '='
unsigned int base64_alphabet[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a',
'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1',
'2', '3', '4', '5', '6', '7', '8', '9', '+',
'/'};
unsigned int base64_suffix_map[256] = {
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 62, 255, 255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255,
255, 255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255, 255, 26, 27, 28,
29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255};
static int cmove_bits(unsigned char src, unsigned lnum, unsigned rnum)
{
src <<= lnum; src >>= rnum;
return src;
}
char* base64_encode(const char *data)
{
char *ret, *retpos;
int m, padnum = 0, retsize, dlen = strlen(data);
if(dlen == 0) return NULL;
/* Account the result buffer size and alloc the memory for it. */
if((dlen % 3) != 0)
padnum = 3 - dlen % 3;
retsize = (dlen + padnum) + ((dlen + padnum) * 1/3) + 1;
if((ret = malloc(retsize)) == NULL)
return NULL;
retpos = ret;
/* Starting to convert the originality characters to BASE64 chracaters.
Converting process keep to 4->6 principle. */
for(m = 0; m < (dlen + padnum); m += 3) {
/* When data is not suffice 24 bits then pad 0 and the empty place pad '='. */
*(retpos) = base64_alphabet[cmove_bits(*data, 0, 2)];
if(m == dlen + padnum - 3 && padnum != 0) { /* Whether the last bits-group suffice 24 bits. */
if(padnum == 1) { /* 16bit need pad one '='. */
*(retpos + 1) = base64_alphabet[cmove_bits(*data, 6, 2) + cmove_bits(*(data + 1), 0, 4)];
*(retpos + 2) = base64_alphabet[cmove_bits(*(data + 1), 4, 2)];
*(retpos + 3) = BASE64_PAD64;
} else if(padnum == 2) { /* 8bit need pad two'='. */
*(retpos + 1) = base64_alphabet[cmove_bits(*data, 6, 2)];
*(retpos + 2) = BASE64_PAD64;
*(retpos + 3) = BASE64_PAD64;
}
} else { /* 24bit normal. */
*(retpos + 1) = base64_alphabet[cmove_bits(*data, 6, 2) + cmove_bits(*(data + 1), 0, 4)];
*(retpos + 2) = base64_alphabet[cmove_bits(*(data + 1), 4, 2) + cmove_bits(*(data + 2), 0, 6)];
*(retpos + 3) = base64_alphabet[*(data + 2) & 0x3f];
}
retpos += 4;
data += 3;
}
ret[retsize - 1] =0;
return ret;
}
char* base64_decode(const char *bdata)
{
char *ret = NULL, *retpos;
int m, padnum = 0, retsize, bdlen = strlen(bdata);
if(bdlen == 0) return NULL;
if(bdlen % 4 != 0) return NULL;
/* Whether the data have invalid base-64 characters? */
for(m = 0; m < bdlen; ++m) {
if(bdata[m] != BASE64_PAD64 && base64_suffix_map[(int)(bdata[m])] == 255)
goto LEND;
}
/* Account the output size. */
if(bdata[bdlen - 1] == '=') padnum = 1;
if(bdata[bdlen - 1] == '=' && bdata[bdlen - 2] == '=') padnum = 2;
retsize = (bdlen - 4) - (bdlen - 4) / 4 + (3 - padnum) + 1;
ret = malloc(retsize);
if(ret == NULL)
return NULL;
retpos = ret;
/* Begging to decode. */
for(m = 0; m < bdlen; m += 4) {
*retpos = cmove_bits(base64_suffix_map[(int)(*bdata)], 2, 0) + cmove_bits(base64_suffix_map[(int)(*(bdata + 1))], 0, 4);
if(m == bdlen - 4 && padnum != 0) { /* Only deal with last four bits. */
if(padnum == 1) /* Have one pad characters, only two availability characters. */
*(retpos + 1) = cmove_bits(base64_suffix_map[(int)(*(bdata + 1))], 4, 0) + cmove_bits(base64_suffix_map[(int)(*(bdata + 2))], 0, 2);
/*
Have two pad characters, only two availability characters.
if(padnum == 2) {
}
*/
retpos += 3 - padnum;
} else {
*(retpos + 1) = cmove_bits(base64_suffix_map[(int)(*(bdata + 1))], 4, 0) + cmove_bits(base64_suffix_map[(int)(*(bdata + 2))], 0, 2);
*(retpos + 2) = cmove_bits(base64_suffix_map[(int)(*(bdata + 2))], 6, 0) + base64_suffix_map[(int)(*(bdata + 3))];
retpos += 3;
}
bdata += 4;
}
ret[retsize - 1] = 0;
LEND: return ret;
}