【SMTP】C语言实现smtp客户端发送mail+附件

I.数据结构 

{
   int     i4ServerIp;          //smtp服务器ip
   char    ServerDomainName[];  //服务器域名
   int     ServerIpType[];      //ip类型,ipv4 ipv6 dns
   int     ServerPort;        //smtp服务器端口号
   char    SendMail[];          //发送邮箱
   char    SendMailKey[];       //邮箱密码
   char    RecvMail[];          //收件邮箱
}

注:非完整代码,仅提供思路。

II.Telnet测试

1.发送邮件

以smtp.qq.com服务器为例:

在终端执行telnet smtp.qq.com 25命令连接smtp服务器,连接成功会返回220消息。以下任何步骤错误都会返回502错误消息。

1) 打招呼

向服务器发送Helo打招呼(即hello缩写),登录方式为auth login,还有其他的登录方式,这里不作展开。

2) 登录

服务器返回334 VXNlcm5hbWU6,即base64编码的'Username:' 所以我们发送用户名时也要使用base64编码的数据。334 UGFzc3dvcmQ6即'Password:',密码也要用base64编码发送。

这里password是从qq邮箱获取的授权码,教程网上搜索即可。

3) 指定发件人/收件人

MAIL FROM:<xxxx@qq.com>

RCPT TO:<xxxx@qq.com>

4) 发送数据:

首先要先发送DATA,会返回消息354 End data with <CR><LF>.<CR><LF>. 意思是接下来的正文键入回车时不会再发送,而是要以 <CR><LF>.<CR><LF>结尾才会发送出去。

正文要先输入发送邮箱/收件邮箱和标题

From:<xxxx@qq.com>

To:<xxxx@qq.com>

Subject: Test Mail

正文

.

结束发送QUIT即可。如果继续发送,将从3)开始继续输入发件人/收件人

2.C/S完整沟通
 
C:telnet smtp.qq.com 25
S:220 newxmesmtplogicsvrszb1-0.qq.com XMail Esmtp QQ Mail Server.
C:HELO SMTP
S:250 OK
C:AUTH LOGIN
S:334 VXNlcm5hbWU6
C:NDkzNDM0NDk2QHFxLmNvbQ==
S:334 UGFzc3dvcmQ6
C:cGxleG96emJmZ2p5Y2JiZg==
S:235 Authentication successful
C:MAIL FROM:<493434496@qq.com>
S:250 OK
C:RCPT TO:<466526618@qq.com>
S:250 OK
C:DATA
S:354 End data with <CR><LF>.<CR><LF>.
C:FROM:<493434496@qq.com>
C:TO:<466526618@qq.com>
C:Subject:Test Mail
C:Hello world!
C:.
S:250 OK: queued as.
C:QUIT
S:221 Bye.
3.测试及结果

 

III. 邮件附件及uuencode编码

1.文本发送附件原理

如果将纯文本内容进行uuencode编码写在正文中就会被识别为附件。

uuencode编码都是以'begin 644 文件名'开始,644为文件操作权限;编码的内容每行为60字节,行首一般都是以M开头,在编码的结尾以` end结尾。

例如:

begin 644 test.txt

MQ*K,_;2IP=:T\M*VR?FCK+K.M\'2]]"EQ]+0[-#0H:/6\=7(PZ+0K,?ARJ3"
M[:.LRZW%PJ._(-*[R_+1S-/JR,[&O<GZH:,*P<_'S;2ZM^>TM;[&T-&CK,ZB
MP.2CK,F]S;?0L=75R+3/X-.MH:.[V,K7S_+`M,_TR:JTIJ.LN>G(I:.LTK+.
.WK?GT^K2LL[>Q^>AHPH`

`

end

如果在正文中加入这段编码,收到的邮件就会有一个test.txt的文件,文件内容就是解码后的内容:

莫听穿林打叶声,何妨吟啸且徐行。竹杖芒鞋轻胜马,谁怕? 一蓑烟雨任平生。
料峭春风吹酒醒,微冷,山头斜照却相迎。回首向来萧瑟处,归去,也无风雨也无晴。 

2.uuencode编码原理 

uuencode将连续3个字节扩展成4个字节的编码,扩展后的4个字符将落在uuencode编码表中。

扩展规则如下:

s[3];  //源字节
d[4];  //目标字节
//tbl为uuencode编码表
d[0] = tbl[s[0]>>2];
d[1] = tbl[((s[0] & 3) << 4) + (s[1] >> 4)];
d[2] = tbl[((s[1] & 0xf) << 2) + (s[2] >> 6)];
d[3] = tbl[s[2] & 0x3f];
//如果s[]不足3个字节时,d[]后面补空格

//uuencode编码表
tbl[] = {
    '`', '!', '"', '#', '$', '%', '&', '\'',
    '(', ')', '*', '+', ',', '-', '.', '/',
    '0', '1', '2', '3', '4', '5', '6', '7',
    '8', '9', ':', ';', '<', '=', '>', '?',
    '@', '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', '[', '\\', ']', '^', '_',
    '`' 
};

行首字节是用来表示本行有多少个字节,通常以满行60字节(源数据45字节)时首字节就是tbl(45),及在tbl中是M字符,所以通常行首都是以M开头,只有最后一行不是。而且通常最后行的结尾有时因为不足3个字节要补空格,所以经常出现多个'`'符号的情况。

注:uuencode编码表和ASCII表唯一不同的是32号space换成了96号的'`'符号,因此补空格出现的是'`'。也因为uuencode编码表和ASCII码表基本相同,也可以用宏转换字符

#define ENCODE_BYTE(x) (((x) == 0) ? 0x60 : ((x) + 0x20))

d[0] = ENCODE_BYTE(s[0]>>2);
d[1] = ENCODE_BYTE(((s[0] & 3) << 4) + (s[1] >> 4));
d[2] = ENCODE_BYTE(((s[1] & 0xf) << 2) + (s[2] >> 6));
d[3] = ENCODE_BYTE(s[2] & 0x3f);
3. uuencode代码 
void uuencode (char* pu1DstBuf, const char * pu1SrcBuf, int i4Length)
{
    const UINT1 *s = pu1SrcBuf;

    /* Transform the 3x8 bits to 4x6 bits */
    while (i4Length > 0) {
        UINT1 s1;
        UINT1 s2;

        /* Are s[1], s[2] valid or should be assumed 0? */
        s1 = s2 = 0;
        i4Length -= 3; /* can be >=0, -1, -2 */
        if (i4Length >= -1) {
            s1 = s[1];
            if (i4Length >= 0)
                s2 = s[2];
        }
        *pu1DstBuf++ = tbl[s[0] >> 2];
        *pu1DstBuf++ = tbl[((s[0] & 3) << 4) + (s1 >> 4)];
        *pu1DstBuf++ = tbl[((s1 & 0xf) << 2) + (s2 >> 6)];
        *pu1DstBuf++ = tbl[s2 & 0x3f];
        s += 3;
    }
    /* Zero-terminate */
    *pu1DstBuf = '\0';
    /* If length is -2 or -1, pad last char or two */
    while (i4Length) {
        *--pu1DstBuf = tbl[64];
        i4Length++;
    }

}

顺带说一句,如果将tbl换成以下的表,那就是base64编码。

//base64编码表
tbl[] = {
    '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', '+', '/',
    '=' ,
};

IV.smtp加密连接

1. smtp端口号

摘自知乎:smtp端口号

163.com:

接收邮件服务器: http://pop.163.com

接收端口: 110或995(使用ssl时)

接收邮件服务器: http://imap.163.com

接收端口: 143或993(使用ssl时)

发送邮件服务器: http://smtp.163.com

发送端口: 25或465/994(使用ssl时)

126邮箱:

接收邮件服务器: http://pop.126.com

接收端口: 110

发送邮件服务器: http://smtp.126.com

发送端口: 25

139邮箱:

POP3服务器地址:http://POP.139.com(端口:110)

SMTP服务器地址:http://SMTP.139.com(端口:25)

QQ邮箱

接收邮件服务器: http://pop.qq.com

接收端口: 110或995(使用ssl时)

接收邮件服务器: http://imap.qq.com

接收端口: 143或993(使用ssl时)

发送邮件服务器: http://smtp.qq.com

发送端口: 25或465/587(使用ssl时)

QQ企业邮箱 :

POP3服务器地址:http://pop.exmail.qq.com (SSL启用 端口:995)

SMTP服务器地址:http://smtp.exmail.qq.com【端口:25或465/587(使用ssl时)】

gmail(http://google.com) :

POP3服务器地址:http://pop.gmail.com(SSL启用 端口:995)

SMTP服务器地址:http://smtp.gmail.com(SSL启用 端口:587)

Foxmail:

POP3服务器地址:http://POP.foxmail.com(端口:110)

SMTP服务器地址:http://SMTP.foxmail.com(端口:25)

sina.com:

接收邮件服务器: http://pop.sina.com

接收端口: 110

发送邮件服务器: http://smtp.sina.com

发送端口: 25

sinaVIP:

POP3服务器:http://pop3.vip.sina.com (端口:110)

SMTP服务器:http://smtp.vip.sina.com (端口:25)

sohu.com:

POP3服务器地址:http://pop3.sohu.com(端口:110)

SMTP服务器地址:http://smtp.sohu.com(端口:25)

雅虎邮箱:

接收邮件服务器: http://pop.mail.yahoo.cn

接收端口: 110或995(使用ssl时)

接收邮件服务器: http://imap.mail.yahoo.cn

接收端口: 143或993(使用ssl时)

发送邮件服务器: http://smtp.mail.yahoo.cn

发送端口: 25或465(使用ssl时)

HotMail :

接收邮件服务器: http://pop3.live.com

接收端口: 995

发送邮件服务器: http://smtp.live.com

发送端口: 25

263.net:

POP3服务器地址:http://pop3.263.net(端口:110)

SMTP服务器地址:http://smtp.263.net(端口:25)

263.net.cn:

POP3服务器地址:http://pop.263.net.cn(端口:110)

SMTP服务器地址:http://smtp.263.net.cn(端口:25)

x263.net:

POP3服务器地址:http://pop.x263.net(端口:110)

SMTP服务器地址:http://smtp.x263.net(端口:25)

21cn.com:

POP3服务器地址:http://pop.21cn.com(端口:110)

SMTP服务器地址:http://smtp.21cn.com(端口:25)

china.com:

POP3服务器地址:http://pop.china.com(端口:110)

SMTP服务器地址:http://smtp.china.com(端口:25)


tom.com:

POP3服务器地址:http://pop.tom.com(端口:110)

SMTP服务器地址:http://smtp.tom.com(端口:25)


etang.com:

POP3服务器地址:http://pop.etang.com

SMTP服务器地址:http://smtp.etang.com

2. smtp跳转加密端口

根据以上各服务器的端口号能看出,非加密端口都是25,加密端口有465,587,994等。

以smtp.qq.com为例,如果直接连465端口,需要进行ssl连接。

如果连接25端口,但需要加密连接,则可以发送STARTTLS跳转到加密端口。

C:telnet smtp.qq.com 25
S:220 newxmesmtplogicsvrszb1-0.qq.com XMail Esmtp QQ Mail Server.
C:HELO SMTP
S:250 OK
C:STARTTLS
S:220 Ready to start TLS from 180.108.251.243 to newxmesmtplogicsvrszb1-0.qq.com.
...
之后要发送数字签名等,没法手写,所以telnet只能到这一步
3.SSL编程基础

我们通常进行tcp连接后都会返回一个sockfd,在之后读写数据时通过sockfd即可向远端进行通信。ssl加密连接相当于在sock上套了一层加密解密协议。简单来说,我们将sockfd作为参数传入SSL_Connect函数后,之后会返回一个(SSL *)pConnId,作用与sockfd一样,读写时使用SSL_Read和SSL_Write即可。以下给出SSL_Connect代码:

VOID* SmtpSslConnect(int i4SockFd)
{
    SSL_CTX *pSslCtx;
    SSL     *pSslConnId = NULL;
    BIO     *pSockBio;
    X509    *X509_cert_peer;
    const char *pu1Cafile = NULL; //这里不需要Cafile

    SSL_library_init();

    /* 初始化ssl ctx */
    pSmtpSslCtx = SSL_CTX_new(SSLv23_client_method());

    /* 选择版本,去掉SSLv2,SSLv3,TLSv1版本 */
    SSL_CTX_set_options(pSmtpSslCtx,
                        SSL_OP_NO_SSLv2 |
                        SSL_OP_NO_SSLv3 |
                        SSL_OP_NO_TLSv1);

    /* 配置数字签名等 */
    SSL_CTX_set_mode(SslCtx, SSL_MODE_AUTO_RETRY);
    if(SMTP_SSL_CERT_VERIFY)  /* 该宏需要自定义,这里未定义可视为0 */
    {
        SSL_CTX_set_verify(SslCtx, SSL_VERIFY_PEER, NULL);
    }

    /*
     * Set the path to the user-provided CA file or use the default cert paths
     * if not provided.
     */
    if(pu1Cafile)
    {
        if(SSL_CTX_load_verify_locations(SslCtx, NULL, NULL) != 1)
        {
            SSL_CTX_free(SslCtx);
            return (NULL);
        }
    }
    else
    {
        X509_STORE_set_default_paths(SSL_CTX_get_cert_store(SslCtx));
        if(ERR_peek_error() != 0)
        {
            SSL_CTX_free(SslCtx);
            return (NULL);
        }
    }

    /* 创建BIO socket */
    pSockBio = BIO_new_socket(i4SockFd, 0))

    SSL_set_bio (pSslConnId, pSockBio);
    SSL_set_connect_state(pSslConnId);

    /* ssl connect */
    SSL_connect(pSslConnId)

    return (VOID *)pSslConnId;
}

V. c语言实现smtp客户端代码 

注意:非可直接运行的代码,只有函数实现,仅提供思路。

1.smtp客户端
#define SMTP_SRV_READY_CODE             "220" 
#define SMTP_SRV_CLOSE_CODE             "221" 
#define SMTP_SRV_OK_CODE                "250" 
#define SMTP_SRV_START_MAIL             "354" 
#define SMTP_SRV_AUTH_CODE              "334" 
#define SMTP_SRV_AUTH_SUCCESS           "235" 

int SmtpSslReadResponse (void *pConnId, const char *pu1Code)
{
    char               au1RecvBuf[SMTP_RCV_BUF_LEN];

    memset(au1RecvBuf, 0, SMTP_RCV_BUF_LEN);
    if ((SSL_read(pConnId, (UINT1 *)au1RecvBuf, SMTP_RCV_BUF_LEN)) > 0)
    {
        if ((strstr (au1RecvBuf, pu1CheckCode)) != NULL)
        {
            return (SMTP_SUCCESS);
        }
        else
        {
            return (SMTP_FAILURE);
        }
    }

    strcpy (au1ErrorBuf, "Connection Closed ");
    return (SMTP_FAILURE);
}

int SmtpSslSendCmd (void *pConnId, int u4Len)
{
    int                i4BytesWritten;

    if ((i4BytesWritten = SSL_write(pConnId, (const char *)au1CmdBuf, u4Len)) > 0)
    {
        return i4BytesWritten;
    }
    else
    {
        return SMTP_FAILURE;
    }
}

void SmtpSendEmail(void)
{
    int  i4SockFd;
    void *pConnId;

    //TCP Connect to Server
    MailServAddr.sin_addr.s_addr = OSIX_HTONL (gsSmtpAlertParams.u4MailServerIp);
    MailServAddr.sin_family = AF_INET;
    MailServAddr.sin_port = OSIX_HTONS (gsSmtpAlertParams.u4SMTPPort);

    i4SockFd = (socket (AF_INET, SOCK_STREAM, IPPROTO_TCP));

    connect (i4SockFd, (struct sockaddr *) &MailServAddr, sizeof (MailServAddr);

    /* 如果不是25端口,就认为是加密端口 */
    if (gsSmtpAlertParams.u4SMTPPort != 25)
    {
        pConnId = SmtpSslConnect(i4SockFd);

        if (SmtpSslReadResponse(pConnId,SMTP_SRV_READY_CODE) != SMTP_SUCCESS)
        {
            /* 连接之后会收到220消息,如果不是说明连接发生错误 */
            close (i4SockFd);
            return ;
        }
    }
    else  /* 发生STARTTLS */
    {
        /* 这里还没有加密,使用read而不是ssl_read */
        if (SmtpReadResponse (i4SockFd, (const UINT1 *)SMTP_SRV_READY_CODE) != SMTP_SUCCESS)
        {
            close (i4SockFd);
            return ;
        }

        memset (au1CmdBuf, 0, SMTP_CMD_BUF_LEN);
        u4Len = sprintf ((char *) au1CmdBuf, "STARTTLS\r\n");
        SmtpSendCmd (i4SockFd, u4Len);

        if (SmtpReadResponse (i4SockFd,
                              const char *)SMTP_SRV_READY_CODE) == SMTP_FAILURE)
        {
            close (i4SockFd);
            return;
        }

        pConnId = SmtpSslConnect(i4SockFd);
    }

    /* HELO SMTP */
    memset (au1CmdBuf, 0, SMTP_CMD_BUF_LEN);
    u4Len = sprintf ((CHR1 *) au1CmdBuf, "HELO SMTP\r\n");
    SmtpSslSendCmd (pConnId, u4Len);

    if (SmtpSslReadResponse(pConnId, SMTP_SRV_OK_CODE) == SMTP_FAILURE)
    {
        close (i4SockFd);
        return SMTP_FAILURE;
    }

    /* AUTH LOGIN */
    memset (au1CmdBuf, 0, SMTP_CMD_BUF_LEN);
    u4Len = sprintf ((char *) au1CmdBuf, "AUTH LOGIN\r\n");
    SmtpSslSendCmd (pConnId, u4Len);

    if (SmtpSslReadResponse(pConnId, SMTP_SRV_AUTH_CODE) == SMTP_FAILURE)
    {
        close (i4SockFd);
        return SMTP_FAILURE;
    }

    /* Username(base64) */
    memset (au1CmdBuf, 0, SMTP_CMD_BUF_LEN);
    memset (au1BaseBuf,0, SMTP_DOMAIN_NAME_LEN);
    strcpy(au1BaseBuf, gsSmtpAlertParams.au1SysDomainName);
    b64_ntop(au1BaseBuf, strlen(au1BaseBuf), (char*)au1CmdBuf, SMTP_CMD_BUF_LEN);
    strcat(au1CmdBuf,"\r\n");
    u4Len = STRLEN(au1CmdBuf);
    SmtpSslSendCmd (pConnId, u4Len);

    if (SmtpSslReadResponse(pConnId, SMTP_SRV_AUTH_CODE) == SMTP_FAILURE)
    {
        close (i4SockFd);
        return SMTP_FAILURE;
    }

    /* Key(base64) */
    memset (au1CmdBuf, 0, SMTP_CMD_BUF_LEN);
    memset (au1BaseBuf,0, SMTP_DOMAIN_NAME_LEN);
    strcpy(au1BaseBuf, gsSmtpAlertParams.au1SysDomainNameKey);
    b64_ntop(au1BaseBuf, strlen(au1BaseBuf), (char*)au1CmdBuf, SMTP_CMD_BUF_LEN);
    strcat(au1CmdBuf,"\r\n");
    u4Len = strlen(au1CmdBuf);
    SmtpSslSendCmd (pConnId, u4Len);

    if (SmtpSslReadResponse(pConnId, SMTP_SRV_AUTH_SUCCESS) == SMTP_FAILURE)
    {
        close (i4SockFd);
        return SMTP_FAILURE;
    }

    /* MAIL FROM:<> */
    memset (au1CmdBuf, 0, SMTP_CMD_BUF_LEN);
    u4Len = sprintf ((CHR1 *) au1CmdBuf, "MAIL FROM:<%s> \r\n",
                     gsSmtpAlertParams.au1SysDomainName);
    SmtpSslSendCmd (pConnId, u4Len);

    if (SmtpSslReadResponse(pConnId, SMTP_SRV_OK_CODE) == SMTP_FAILURE)
    {
        close (i4SockFd);
        return SMTP_FAILURE;
    }

    /* RCPT TO:<> */
    memset (au1CmdBuf, 0, SMTP_CMD_BUF_LEN);
    u4Len = sprintf ((char *) au1CmdBuf, "RCPT TO:<%s> \r\n",
                     gsSmtpAlertParams.au1RcvDomainName);
    SmtpSslSendCmd (pConnId, u4Len);

    if (SmtpSslReadResponse(pConnId, SMTP_SRV_OK_CODE) == SMTP_FAILURE)
    {
        close (i4SockFd);
        return SMTP_FAILURE;
    }

    /* DATA */
    memset (au1CmdBuf, 0, SMTP_CMD_BUF_LEN);
    u4Len = sprintf ((char *) au1CmdBuf, "DATA\r\n");
    SysLogSmtpSslSendCmd (pConnId, u4Len);

    if (SysLogSmtpSslReadResponse(pConnId, SMTP_SRV_START_MAIL) == SMTP_FAILURE)
    {
        close (i4SockFd);
        return SMTP_FAILURE;
    }

    /* data从flash中读取 */
    SmtpSslSendData(pConnId, NULL, SYSLOG_ZERO);

    /* QUIT */
    memset (au1CmdBuf, 0, SMTP_CMD_BUF_LEN);
    u4Len = sprintf ((char *) au1CmdBuf, "QUIT\r\n");
    SysLogSmtpSslSendCmd (pConnId, u4Len);

    if (SysLogSmtpSslReadResponse(pConnId, SMTP_SRV_CLOSE_CODE) == SMTP_FAILURE)
    {
        close (i4SockFd);
        return SMTP_FAILURE;
    }

    return;
}
2.正文及附件处理
#difine TEST_FILE  "test.txt"
enum {
    UUENC_SRC_BUF_SIZE = 15*3,
    UUENC_DST_BUF_SIZE = 4 * (UUENC_SRC_BUF_SIZE + 2) / 3,
}

int SmtpSslSendData (void *pConnId, char *pu1Data, int u4DataLen)
{
    int                i4Len;
    char               au1Subject[100];
    char               au1SendBuf[MAX_FLASH_FILESIZE];
    char               au1Str[SMTP_DOMAIN_NAME_LEN + SMTP_DOMAIN_NAME_LEN + SYSLOG_FIFTY];
    char               au1SrcBuf[UUENC_SRC_BUF_SIZE];
    char               au1DstBuf[UUENC_DST_BUF_SIZE + 1];
    int                i4Filefd = 0;
    int                i4Size = 0;
    int                u4Pos = 0;
    int                au1DataTermStr[] = "\r\n.\r\n";

    /* From:
     * To: */
    memset (au1Str, 0, sizeof (au1Str));
    sprintf ((char *) au1Str, "From: %s\r\nTo: %s\r\n",
             gsSmtpAlertParams.au1SysDomainName,
             gsSmtpAlertParams.au1RcvDomainName);
    if (SSL_write (pConnId, (const char *)au1Str, (int) strlen (au1Str), 0) <= 0)
    {
        return SMTP_FAILURE;
    }

    /* Subject */
    memset (au1Subject, 0, sizeof (au1Subject));
    sprinft ((char *) au1Subject, "Subject: Email Alert \r\n");
    if (SSL_write (pConnId, (const char *)au1Subject, (int) strlen (au1Subject), 0) <= 0)
    {
        return SMTP_FAILURE;
    }

    /* Main text and annexes */
    memset (au1SendBuf, 0, MAX_FLASH_FILESIZE);
    u4Pos = sprintf((char *)au1SendBuf, "Syslog.\r\n");
    if (u4DataLen == 0)
    {
        u4Pos += sprintf((char *)au1SendBuf + u4Pos, "begin 644 %s", TEST_FILE);
        i4Filefd = fileopen(TEST_FILE, FILE_RO);
        while (1)
        {
            i4Size = fileread(i4Filefd, (char *)au1SrcBuf, UUENC_SRC_BUF_SIZE);
            if (!i4Size)
                break;
            if (i4Size < 0)
            {
                fileclose (i4Filefd);
                return SMTP_FAILURE;
            }
            uuencode(au1DstBuf, (const char *)au1SrcBuf, i4Size);
            /* \n */
            au1SendBuf[u4Pos++] = '\n';
            /* Character representation length in ASCII */
            au1SendBuf[u4Pos++] = UUENCODE_BYTE(i4Size);
            /* 60 characters per line */
            u4Pos += SPRINTF((CHR1 *)au1SendBuf + u4Pos, "%s", au1DstBuf);
        }
        u4Pos += sprintf((char *)au1SendBuf + u4Pos, "\n`\nend\n");
    }
    else
    {
        u4Pos += sprintf((char *)au1SendBuf + u4Pos, "%s", pu1Data);
    }

    i4Len = strlen (au1SendBuf);
    if (SSL_write (pConnId, (const char *)au1SendBuf, i4Len, 0) <= 0)
    {
        fileclose (i4Filefd);
        return SMTP_FAILURE;
    }

    /* \r\n.\r\n */
    i4Len = strlen (au1DataTermStr);
    if (SSL_write (pConnId, (const char *)au1DataTermStr, i4Len, 0) <= 0)
    {
        file (i4Filefd);
        return SMTP_FAILURE;
    }

    fileclose (i4Filefd);
    return SMTP_SUCCESS;
}

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python 中可以使用 smtplib 模块来实现 Email 客户端,具体步骤如下: 1. 导入 smtplib 模块: ```python import smtplib ``` 2. 创建 SMTP 对象: ```python smtpObj = smtplib.SMTP( [host [, port [, local_hostname]]] ) ``` 其中,host 为 SMTP 服务器主机名,port 为 SMTP 服务器端口号,local_hostname 为本地主机名(可选)。 3. 连接 SMTP 服务器: ```python smtpObj.connect(host, port) ``` 4. 登录 SMTP 服务器: ```python smtpObj.login(user, password) ``` 其中,user 为登录 SMTP 服务器的用户名,password 为登录 SMTP 服务器的密码。 5. 发送邮件: ```python smtpObj.sendmail(sender, receivers, message) ``` 其中,sender 为发件人邮箱地址,receivers 为收件人邮箱地址(可以是一个列表),message 为邮件内容。 6. 退出 SMTP 服务器: ```python smtpObj.quit() ``` 完整代码示例: ```python import smtplib from email.mime.text import MIMEText from email.header import Header # SMTP 服务器地址和端口号 smtp_server = 'smtp.qq.com' smtp_port = 587 # 发件人邮箱地址和密码 sender = 'your_email@qq.com' password = 'your_password' # 收件人邮箱地址 receivers = ['receiver1@qq.com', 'receiver2@qq.com'] # 邮件内容 message = MIMEText('邮件内容', 'plain', 'utf-8') message['From'] = Header('发件人名称', 'utf-8') message['To'] = Header('收件人名称', 'utf-8') message['Subject'] = Header('邮件主题', 'utf-8') # 创建 SMTP 对象并连接 SMTP 服务器 smtpObj = smtplib.SMTP(smtp_server, smtp_port) smtpObj.starttls() # 开启安全连接 smtpObj.login(sender, password) # 登录 SMTP 服务器 # 发送邮件 smtpObj.sendmail(sender, receivers, message.as_string()) # 退出 SMTP 服务器 smtpObj.quit() ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值