找回密码
 立即注册→加入我们

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 50218|回复: 79

【C】使用SMTP协议发送电子邮件源码,支持附件,支持OpenSSL加密,支持STARTTLS

  [复制链接]
发表于 2016-1-2 11:20:35 | 显示全部楼层 |阅读模式

欢迎访问技术宅的结界,请注册或者登录吧。

您需要 登录 才可以下载或查看,没有账号?立即注册→加入我们

×
呼。。终于完成了。

有关的内容大家可以参考我用Java写的轻量级电子邮件类smtpmail
其实这个C语言版的也差不多。区别在于,这个不需要自己保存数字签名文件,它反正就这么就能用。

效果图:
20160102114945.png

下面是通过SMTP和服务器交互的内容。想看?回帖后才能看到。
游客,如果您要查看本帖隐藏内容请回复
为了写这个东西,我需要解决以下三个问题:

写好它以后,大家可以看看我的DEMO,使用我定义的接口进行SMTP协议邮件发送过程。这用起来还是非常方便的。
  1. #include<stdio.h>
  2. #include<WinSock2.h>
  3. #include"smtpmail.h"

  4. #define DEMO_SERVER                "smtp.qq.com",465
  5. #define DEMO_ACCOUNT        "username","password"
  6. #define DEMO_FROMTO                "username@qq.com","receiver@qq.com"

  7. //=============================================================================
  8. //函数:SendTestMail
  9. //描述:测试函数,展示发送邮件
  10. //-----------------------------------------------------------------------------
  11. sm_exception_t SendTestMail()
  12. {
  13.         return sm_SendTextMail(sm_SSL,DEMO_SERVER,DEMO_ACCOUNT,DEMO_FROMTO,
  14.                 "这是测试邮件,来自smtpmail",
  15.                 "这是测试邮件的内容。");
  16. }

  17. //=============================================================================
  18. //函数:SendTestMailWithAttachment
  19. //描述:测试函数,展示发送带附件的邮件
  20. //-----------------------------------------------------------------------------
  21. sm_exception_t SendTestMailWithAttachment()
  22. {
  23.         smtpmail_t sm;
  24.         sm_exception_t ex;
  25.         FILE*fp = NULL;

  26.         sm_Init(&sm, sm_SSL);

  27.         ex = sm_CatchException(&sm);
  28.         if(ex)
  29.         {
  30.                 sm_Cleanup(&sm);
  31.                 if(fp)
  32.                         fclose(fp);
  33.                 return ex;
  34.         }

  35.         sm_StartSendMail(&sm,DEMO_SERVER,DEMO_ACCOUNT,DEMO_FROMTO,
  36.                 "这是测试邮件,来自smtpmail,测试附件",
  37.                 sm_false);

  38.         fp = fopen("attachment.gif","rb");
  39.         sm_SendAttachment(&sm, "image/gif", fp, "name", "filename.gif");
  40.         fclose(fp);fp=NULL;

  41.         sm_StartSendPlainText(&sm);
  42.         sm_SendPlainText(&sm, "这个邮件有一个GIF附件。");
  43.         sm_EndSendPlainText(&sm);

  44.         sm_FinishSendMail(&sm);
  45.         sm_Cleanup(&sm);

  46.         return sme_OK;
  47. }

  48. int main(int argc,char**argv)
  49. {
  50. #        ifdef _MSC_VER
  51.         {
  52.                 WSADATA wsaData;
  53.                 WSAStartup(WINSOCK_VERSION,&wsaData);
  54.         }
  55. #        endif // _MSC_VER
  56.        
  57.         fputs(sm_GetExceptionString(SendTestMail()),stderr);
  58.         fputs(sm_GetExceptionString(SendTestMailWithAttachment()),stderr);
  59.         return 0;
  60. }
复制代码



参考资料:
http://www.0xaa55.com/thread-1212-1-1.html
http://www.0xaa55.com/thread-1671-1-1.html
http://www.0xaa55.com/thread-1676-1-1.html
RFC 2821 (SMTP)
RFC 3207 (STARTTLS)
RFC 6409 (Port 587)
https://en.wikipedia.org/wiki/SMTPS

完整的代码:
smtpmail.h:
  1. //=============================================================================
  2. //作者:0xAA55
  3. //作者网站:http://www.0xaa55.com/
  4. //本源码完全免费开放,大家可以随意拿去使用、修改、用于任何用途。
  5. //但是,由于本源码带来的任何损失,均与本作者无关。请保留此信息。
  6. //-----------------------------------------------------------------------------
  7. #ifndef _SMTP_MAIL_SENDER_
  8. #define _SMTP_MAIL_SENDER_

  9. #include<stdint.h>
  10. #include<setjmp.h>
  11. #include<Winsock2.h>
  12. #include<openssl/ssl.h>

  13. typedef enum
  14. {
  15.         sm_false = 0,
  16.         sm_true
  17. }sm_bool_t,*sm_bool_p;

  18. //=============================================================================
  19. //smtpmail的异常号码。用于追踪错误。
  20. typedef enum
  21. {
  22.         sme_OK = 0,                                //没错
  23.         sme_InvalidParam,                //参数错误
  24.         sme_NoEnoughMemory,                //内存不足
  25.         sme_ServerNotFound,                //没找到服务器
  26.         sme_InitSSLFailed,                //初始化OpenSSL失败
  27.         sme_SSLHandShakeErr,        //OpenSSL握手失败
  28.         sme_ConnectFail,                //连接失败
  29.         sme_SendFail,                        //发送失败
  30.         sme_RecvFail,                        //接收失败
  31.         sme_IncorrectData,                //接收到了奇怪的数据
  32.         sme_BadStatusCode,                //服务器返回不好的状态码,不能再合作下去了
  33.         sme_STARTTLS_Failed,        //服务器不支持STARTTLS,但是用户要求必须支持
  34.         sme_LoginFailed,                //登录失败,错误的用户名和密码
  35.         sme_SetFromToError,                //设置收件人和发件人失败
  36.         sme_SendMailFail,                //最后服务器返回“发送邮件失败”
  37.         sme_ReadFileError,                //无法读取文件(无法发送附件)
  38. }sm_exception_t,*sm_exception_p;

  39. //=============================================================================
  40. //加密方式
  41. typedef enum
  42. {
  43.         sm_Plain = 0,                        //发送明文,不加密
  44.         sm_STARTTLS_Or_Plain,        //明文连接,如果服务器支持STARTTLS就加密,否则发送明文
  45.         sm_STARTTLS,                        //明文连接,服务器必须支持STARTTLS否则失败
  46.         sm_SSL                                        //SSL加密
  47. }sm_sectype_t,*sm_sectype_p;

  48. typedef struct smtpmail_s smtpmail_t,*smtpmail_p;
  49. typedef void(*Send_f)(smtpmail_p,const void*pBuf,size_t);
  50. typedef size_t(*Recv_f)(smtpmail_p,void*pBuf,size_t);

  51. #define MaxLine 255 /* 假定从服务器返回的字符串每行最大字符数 */

  52. struct smtpmail_s
  53. {
  54.         //传输工具
  55.         SOCKET Socket;
  56.         Send_f pfnSend;
  57.         Recv_f pfnRecv;

  58.         //安全模式
  59.         sm_sectype_t SecureMode;

  60.         SSL_CTX        *pCTX;
  61.         SSL                *pSSL;

  62.         //接收数据缓冲区
  63.         char        RecvBuf[MaxLine + 1];//添加一个行结尾
  64.         size_t        cbRecv;

  65.         char        szSeparator[MaxLine + 1];//不同MIME块之间的分隔行
  66.         size_t        cbSeparator;//分隔行的长度

  67.         //最后状态
  68.         uint32_t uLastStatus;
  69.         char         pLastRecvStr[MaxLine + 1];

  70.         //错误处理,这个必须设置
  71.         jmp_buf        OnError;
  72. };

  73. //=============================================================================
  74. //函数:sm_Init
  75. //描述:初始化邮件发送器
  76. //-----------------------------------------------------------------------------
  77. void sm_Init
  78. (
  79.         smtpmail_p ps,
  80.         sm_sectype_t SecureMode//安全模式
  81. );

  82. //=============================================================================
  83. //函数:sm_CatchException
  84. //描述:捕获异常处理。必须在调用sm_Init后直接使用。
  85. //    一旦出现异常,程序将从这里返回。切记返回完后调用sm_Cleanup进行清理。
  86. //-----------------------------------------------------------------------------
  87. #define sm_CatchException(ps) (sm_exception_t)setjmp((ps)->OnError)

  88. //=============================================================================
  89. //函数:sm_StartSendMail
  90. //描述:开始发送正文。调用前必须先调用sm_CatchException。
  91. //-----------------------------------------------------------------------------
  92. void sm_StartSendMail
  93. (
  94.         smtpmail_p        ps,
  95.         const char        *pszServerAddr,        //服务器地址,如“smtp.qq.com”
  96.         uint16_t        uPort,                        //服务器端口,如“465”
  97.         const char        *pUsername,                //用户名,如[QQ号]
  98.         const char        *pPassword,                //密码,如[QQ密码]
  99.         const char        *pMailFrom,                //发件人,如QQ号@qq.com
  100.         const char        *pRcptTo,                //收件人,如xxx@xx.com
  101.         const char        *pSubject,                //主题,如“测试邮件”
  102.         sm_bool_t        bHaveAttachment        //是否打算发送附件
  103. );

  104. //=============================================================================
  105. //函数:sm_StartSendMimeData
  106. //      sm_StartSendMimeDataAsAttachment
  107. //      sm_SendMimeData
  108. //      sm_SendTextMimeData
  109. //      sm_EndSendMimeData
  110. //描述:发送MIME内容。
  111. //有关MIME类型的资料:
  112. //http://www.sitepoint.com/web-foundations/mime-types-complete-list/
  113. //-----------------------------------------------------------------------------
  114. void sm_StartSendMimeData(smtpmail_p ps,const char*pszMimeType);
  115. void sm_StartSendMimeDataAsAttachment
  116. (
  117.         smtpmail_p ps,
  118.         const char*pszMimeType,
  119.         const char*pszName,                //附件名
  120.         const char*pszFilename        //附件文件名
  121. );
  122. void sm_SendMimeData(smtpmail_p ps,const void*pszData,size_t cbData);
  123. void sm_SendTextMimeData(smtpmail_p ps,const char*pszText);
  124. void sm_EndSendMimeData(smtpmail_p ps);

  125. #define sm_StartSendPlainText(ps) sm_StartSendMimeData(ps, "text/plain")
  126. #define sm_SendPlainText(ps, pszText) sm_SendTextMimeData(ps, pszText)
  127. #define sm_EndSendPlainText(ps) sm_EndSendMimeData(ps)

  128. #define sm_StartSendHTML(ps) sm_StartSendMimeData(ps, "text/html")
  129. #define sm_SendHTML(ps,pszText) sm_SendMimeData(ps, pszText)
  130. #define sm_EndSendHTML(ps) sm_EndSendMimeData(ps)

  131. //=============================================================================
  132. //函数:sm_SendAttachment
  133. //描述:发送附件
  134. //-----------------------------------------------------------------------------
  135. void sm_SendAttachment
  136. (
  137.         smtpmail_p ps,
  138.         const char*pszMimeType,        //MIME类型
  139.         FILE*fp,                                //已经打开的文件
  140.         const char*pName,                //附件名
  141.         const char*pFilename        //文件名,不含路径
  142. );

  143. //=============================================================================
  144. //函数:sm_FinishSendMail
  145. //描述:完成邮件的发送(最后一步)
  146. //-----------------------------------------------------------------------------
  147. void sm_FinishSendMail(smtpmail_p ps);

  148. //=============================================================================
  149. //函数:sm_Cleanup
  150. //描述:用完smtpmail后,清理内存。
  151. //-----------------------------------------------------------------------------
  152. void sm_Cleanup(smtpmail_p ps);

  153. //=============================================================================
  154. //函数:sm_SendTextMail
  155. //描述:一步完成——发送纯文本邮件
  156. //-----------------------------------------------------------------------------
  157. sm_exception_t sm_SendTextMail
  158. (
  159.         sm_sectype_t SecureMode,        //安全模式
  160.         const char        *pszServerAddr,        //服务器地址,如“smtp.qq.com”
  161.         uint16_t        uPort,                        //服务器端口,如“465”
  162.         const char        *pUsername,                //用户名,如[QQ号]
  163.         const char        *pPassword,                //密码,如[QQ密码]
  164.         const char        *pMailFrom,                //发件人,如QQ号@qq.com
  165.         const char        *pRcptTo,                //收件人,如xxx@xx.com
  166.         const char        *pSubject,                //主题,如“测试邮件”
  167.         const char        *pText                        //纯文本内容
  168. );

  169. //=============================================================================
  170. //函数:sm_GetExceptionString
  171. //描述:取得用于描述异常的字符串
  172. //-----------------------------------------------------------------------------
  173. char*sm_GetExceptionString(sm_exception_t e);

  174. #endif // !_SMTP_MAIL_SENDER_
复制代码
smtpmail.c:回帖后可见。
游客,如果您要查看本帖隐藏内容请回复
SRC: smtpmail.7z (9.22 KB, 下载次数: 74)
不过由于论坛上传附件大小限制的关系,这个附件只包含了核心的代码。而OpenSSL则并没有包含在里面,所以不能编译通过。
完整的源码包请回帖后查看并下载。
游客,如果您要查看本帖隐藏内容请回复

本帖被以下淘专辑推荐:

回复

使用道具 举报

发表于 2016-1-2 11:28:25 | 显示全部楼层
必须支持啊,又学了一点知识
回复 赞! 靠!

使用道具 举报

发表于 2016-1-2 11:28:28 | 显示全部楼层
真是屌得不行!
回复 赞! 靠!

使用道具 举报

发表于 2016-1-2 20:30:53 | 显示全部楼层
经测试,只支持QQ邮箱,对126邮箱不支持,其他未知。
回复 赞! 靠!

使用道具 举报

 楼主| 发表于 2016-1-2 22:02:16 | 显示全部楼层
美俪女神 发表于 2016-1-2 20:30
经测试,只支持QQ邮箱,对126邮箱不支持,其他未知。

试试gmail邮箱吧。。
回复 赞! 靠!

使用道具 举报

 楼主| 发表于 2016-1-15 22:13:13 | 显示全部楼层
587端口+STARTTLS邮件机制:(正常情况)
1、用户连接到smtp.qq.com:587
2、服务器返回:“220 欢迎!qq邮箱”
3、用户发送:  “ehlo smtp.qq.com”
4、服务器返回:(“250-”开头的功能列表,验证什么的)
5、用户发送:  “STARTTLS”
6、服务器返回:“220 OK”
7、用户初始化OpenSSL,然后SSL_connect
8、用户通过SSL发送:  “ehlo smtp.qq.com”
9、服务器通过SSL返回:(“250-”开头的功能列表,验证什么的,但是没有“STARTTLS”)
10、用户通过SSL发送:  “AUTH LOGIN”
11、服务器通过SSL返回:“334 VXNlcm5hbWU6”
12、用户通过SSL发送:  (Base64编码的用户名,QQ邮箱的话,就是QQ号了)
13、服务器通过SSL返回:“334 UGFzc3dvcmQ6”
14、用户通过SSL发送:  (Base64编码的用户名,QQ邮箱的话,就是QQ密码了)
15、服务器通过SSL返回:“235 验证成功。”
16、用户通过SSL发送:  “MAIL FROM:<发件人@qq.com>”(发件人不能乱填,没有经过允许的话不认的)
17、服务器通过SSL返回:“250 设置发件人成功。”
18、用户通过SSL发送:  “RCPT TO:<收件人>”(收件人不能完全等于发件人)
19、服务器通过SSL返回:“250 设置收件人成功。”
20、用户通过SSL发送:  “DATA”
21、服务器通过SSL返回:“354用<cr><lf>.<cr><lf>结束
22、用户通过SSL发送:  (邮件内容,用“\r\n.\r\n”来表示结束信号)
23、服务器通过SSL返回:“250 已加入队列。”
24、用户通过SSL发送:  “QUIT”
25、服务器通过SSL返回:“221 Bye”
26、服务器断开SSL连接
27、服务器断开socket连接
回复 赞! 靠!

使用道具 举报

发表于 2016-1-30 22:17:08 | 显示全部楼层
想看看代码
回复 赞! 靠!

使用道具 举报

发表于 2016-1-31 10:34:18 | 显示全部楼层
真是来得太及时了.. 以后终于可以自动交作业了
回复 赞! 靠!

使用道具 举报

发表于 2016-5-5 13:36:27 | 显示全部楼层
呜 正好遇到这个问题了
回复 赞! 靠!

使用道具 举报

发表于 2016-5-14 22:41:30 | 显示全部楼层
回复 Down完整代码包~
回复 赞! 靠!

使用道具 举报

发表于 2016-6-4 02:48:50 | 显示全部楼层
后生可畏啊~!
回复 赞! 靠!

使用道具 举报

发表于 2016-11-14 16:05:06 | 显示全部楼层
必须支持啊,又学了一点知识
回复 赞! 靠!

使用道具 举报

发表于 2016-11-17 10:25:11 | 显示全部楼层
支持    !!
回复 赞! 靠!

使用道具 举报

发表于 2017-1-13 22:27:14 | 显示全部楼层
必须支持一下
回复 赞! 靠!

使用道具 举报

发表于 2017-2-6 03:49:15 | 显示全部楼层
刚好需要这功能..下载来研究看看
回复 赞! 靠!

使用道具 举报

发表于 2017-2-15 14:23:20 | 显示全部楼层
美俪女神 发表于 2016-1-2 20:30
经测试,只支持QQ邮箱,对126邮箱不支持,其他未知。

126需要手动配置邮箱支持的协议
回复 赞! 靠!

使用道具 举报

发表于 2017-2-27 12:37:10 | 显示全部楼层
感谢楼主分享哈
回复 赞! 靠!

使用道具 举报

发表于 2017-3-7 21:02:22 | 显示全部楼层
真的是太神拉~~
回复 赞! 靠!

使用道具 举报

发表于 2017-5-6 10:53:25 | 显示全部楼层
特别需要,万分感谢
回复 赞! 靠!

使用道具 举报

发表于 2017-5-14 00:11:29 | 显示全部楼层
。。急用。。感谢楼主
回复 赞! 靠!

使用道具 举报

本版积分规则

QQ|Archiver|小黑屋|技术宅的结界 ( 滇ICP备16008837号 )|网站地图

GMT+8, 2024-12-22 11:50 , Processed in 0.044957 second(s), 29 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表