【Hexler】Ubuntu下的简单Smtp Pop3邮件收发服务器

前段时间做了一个邮件服务器,其实操作都不难,这个里面的难点其实就是这个项目得需要用到一些技术,代码量会比较多,容易出错一点,只要在写代码的时候每实现一个功能就仔细测试一下,这样就会简单很多。
由于我只写了服务器端,注释也写的比较多,所以就不贴演示图了,客户端用的是微软比较老的客户端outlook。
直接贴代码了,如果有人有需要可以参考一下,有问题或者指点也可以评论问我哦。
在这里插入图片描述
x
在这里插入图片描述
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;

}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以帮你解答关于Ubuntu下Postfix +Dovecot邮件服务器搭建的问题。以下是大致的步骤: 1. 安装Postfix和Dovecot 在Ubuntu终端输入以下命令安装Postfix和Dovecot: ``` sudo apt-get update sudo apt-get install postfix dovecot-core dovecot-imapd dovecot-pop3d ``` 2. 配置Postfix 在终端中输入以下命令打开Postfix的主配置文件: ``` sudo nano /etc/postfix/main.cf ``` 在文件中搜索以下两行内容并进行修改: ``` myhostname = your_domain_name mydestination = your_domain_name, localhost.localdomain, localhost ``` 将“your_domain_name”替换为您的域名。 接下来,找到以下行并更改以允许邮件的发送和接收: ``` inet_interfaces = all ``` 最后,添加以下内容使Postfix能够与Dovecot一起工作: ``` home_mailbox = Maildir/ mailbox_command = ``` 保存并关闭文件。 3. 配置Dovecot 打开Dovecot的主配置文件: ``` sudo nano /etc/dovecot/dovecot.conf ``` 找到以下行并进行修改: ``` protocols = imap pop3 ``` 将其更改为: ``` protocols = imap pop3 lmtp ``` 然后,找到以下行并进行修改: ``` mail_location = mbox:~/mail:INBOX=/var/mail/%u ``` 将其更改为: ``` mail_location = maildir:~/Maildir ``` 保存并关闭文件。 4. 配置MySQL 如果您想要使用MySQL存储邮件,可以按照以下步骤进行配置: - 安装MySQL: ``` sudo apt-get install mysql-server ``` - 创建数据库和用户: ``` sudo mysql -u root -p ``` ``` CREATE DATABASE mailserver; CREATE USER 'mailuser'@'localhost' IDENTIFIED BY 'password'; GRANT ALL PRIVILEGES ON mailserver.* TO 'mailuser'@'localhost'; FLUSH PRIVILEGES; ``` - 创建表格: ``` sudo mysql -u mailuser -p mailserver < /etc/postfix/mysql_virtual_mailbox_domains.sql sudo mysql -u mailuser -p mailserver < /etc/postfix/mysql_virtual_mailbox_maps.sql sudo mysql -u mailuser -p mailserver < /etc/postfix/mysql_virtual_alias_maps.sql ``` 5. 重启服务 在终端中输入以下命令以重启Postfix和Dovecot: ``` sudo systemctl restart postfix sudo systemctl restart dovecot ``` 完成以上步骤后,您的Ubuntu邮件服务器将已经搭建完成。您可以测试是否正常工作,例如发送和接收一封电子邮件

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值