smtp.c 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. #include "smtp.h"
  2. #include "hsocket.h"
  3. #include "base64.h"
  4. const char* smtp_command_str(enum smtp_command cmd) {
  5. switch (cmd) {
  6. #define XX(name, string) case SMTP_##name: return #string;
  7. SMTP_COMMAND_MAP(XX)
  8. #undef XX
  9. default: return "<unknown>";
  10. }
  11. }
  12. const char* smtp_status_str(enum smtp_status status) {
  13. switch (status) {
  14. #define XXX(code, name, string) case SMTP_STATUS_##name: return #string;
  15. SMTP_STATUS_MAP(XXX)
  16. #undef XXX
  17. default: return "<unknown>";
  18. }
  19. }
  20. int smtp_build_command(enum smtp_command cmd, const char* param, char* buf, int buflen) {
  21. switch (cmd) {
  22. // unary
  23. case SMTP_DATA:
  24. case SMTP_QUIT:
  25. return snprintf(buf, buflen, "%s\r\n", smtp_command_str(cmd));
  26. // <address>
  27. case SMTP_MAIL:
  28. case SMTP_RCPT:
  29. return snprintf(buf, buflen, "%s <%s>\r\n", smtp_command_str(cmd), param);
  30. default:
  31. return snprintf(buf, buflen, "%s %s\r\n", smtp_command_str(cmd), param);
  32. }
  33. }
  34. // EHLO => AUTH PLAIN => MAIL => RCPT => DATA => data => EOB => QUIT
  35. int sendmail(const char* smtp_server,
  36. const char* username,
  37. const char* password,
  38. mail_t* mail) {
  39. char buf[1024] = {0};
  40. int buflen = sizeof(buf);
  41. int cmdlen = 0;
  42. int status_code = 0;
  43. char basic[256];
  44. int basiclen;
  45. int sockfd = ConnectTimeout(smtp_server, SMTP_PORT, DEFAULT_CONNECT_TIMEOUT);
  46. if (sockfd < 0) {
  47. return sockfd;
  48. }
  49. so_sndtimeo(sockfd, 5000);
  50. so_rcvtimeo(sockfd, 5000);
  51. int ret, nsend, nrecv;
  52. nrecv = recv(sockfd, buf, buflen, 0);
  53. if (nrecv <= 0) {
  54. ret = -30;
  55. goto error;
  56. }
  57. status_code = atoi(buf);
  58. if (status_code != SMTP_STATUS_READY) {
  59. ret = status_code;
  60. goto error;
  61. }
  62. // EHLO smtp.xxx.com\r\n
  63. cmdlen = smtp_build_command(SMTP_EHLO, smtp_server, buf, buflen);
  64. nsend = send(sockfd, buf, cmdlen, 0);
  65. if (nsend != cmdlen) {
  66. ret = -20;
  67. goto error;
  68. }
  69. nrecv = recv(sockfd, buf, buflen, 0);
  70. if (nrecv <= 0) {
  71. ret = -30;
  72. goto error;
  73. }
  74. status_code = atoi(buf);
  75. if (status_code != SMTP_STATUS_OK) {
  76. ret = status_code;
  77. goto error;
  78. }
  79. // AUTH PLAIN\r\n
  80. cmdlen = smtp_build_command(SMTP_AUTH, "PLAIN", buf, buflen);
  81. nsend = send(sockfd, buf, cmdlen, 0);
  82. if (nsend != cmdlen) {
  83. ret = -20;
  84. goto error;
  85. }
  86. nrecv = recv(sockfd, buf, buflen, 0);
  87. if (nrecv <= 0) {
  88. ret = -30;
  89. goto error;
  90. }
  91. status_code = atoi(buf);
  92. if (status_code != SMTP_STATUS_AUTH) {
  93. ret = status_code;
  94. goto error;
  95. }
  96. {
  97. // BASE64 \0username\0password
  98. int usernamelen = strlen(username);
  99. int passwordlen = strlen(password);
  100. basic[0] = '\0';
  101. memcpy(basic+1, username, usernamelen);
  102. basic[1+usernamelen] = '\0';
  103. memcpy(basic+1+usernamelen+1, password, passwordlen);
  104. basiclen = 1 + usernamelen + 1 + passwordlen;
  105. }
  106. base64_encode((unsigned char*)basic, basiclen, buf);
  107. cmdlen = BASE64_ENCODE_OUT_SIZE(basiclen);
  108. buf[cmdlen] = '\r';
  109. buf[cmdlen+1] = '\n';
  110. cmdlen += 2;
  111. nsend = send(sockfd, buf, cmdlen, 0);
  112. if (nsend != cmdlen) {
  113. ret = -20;
  114. goto error;
  115. }
  116. nrecv = recv(sockfd, buf, buflen, 0);
  117. if (nrecv <= 0) {
  118. ret = -30;
  119. goto error;
  120. }
  121. status_code = atoi(buf);
  122. if (status_code != SMTP_STATUS_AUTH_SUCCESS) {
  123. ret = status_code;
  124. goto error;
  125. }
  126. // MAIL FROM: <from>\r\n
  127. cmdlen = smtp_build_command(SMTP_MAIL, mail->from, buf, buflen);
  128. nsend = send(sockfd, buf, cmdlen, 0);
  129. if (nsend != cmdlen) {
  130. ret = -20;
  131. goto error;
  132. }
  133. nrecv = recv(sockfd, buf, buflen, 0);
  134. if (nrecv <= 0) {
  135. ret = -30;
  136. goto error;
  137. }
  138. status_code = atoi(buf);
  139. if (status_code != SMTP_STATUS_OK) {
  140. ret = status_code;
  141. goto error;
  142. }
  143. // RCPT TO: <to>\r\n
  144. cmdlen = smtp_build_command(SMTP_RCPT, mail->to, buf, buflen);
  145. nsend = send(sockfd, buf, cmdlen, 0);
  146. if (nsend != cmdlen) {
  147. ret = -20;
  148. goto error;
  149. }
  150. nrecv = recv(sockfd, buf, buflen, 0);
  151. if (nrecv <= 0) {
  152. ret = -30;
  153. goto error;
  154. }
  155. status_code = atoi(buf);
  156. if (status_code != SMTP_STATUS_OK) {
  157. ret = status_code;
  158. goto error;
  159. }
  160. // DATA\r\n
  161. cmdlen = smtp_build_command(SMTP_DATA, NULL, buf, buflen);
  162. nsend = send(sockfd, buf, cmdlen, 0);
  163. if (nsend != cmdlen) {
  164. ret = -20;
  165. goto error;
  166. }
  167. nrecv = recv(sockfd, buf, buflen, 0);
  168. if (nrecv <= 0) {
  169. ret = -30;
  170. goto error;
  171. }
  172. status_code = atoi(buf);
  173. // SMTP_STATUS_DATA
  174. if (status_code >= 400) {
  175. ret = status_code;
  176. goto error;
  177. }
  178. // From:
  179. cmdlen = snprintf(buf, buflen, "From:%s\r\n", mail->from);
  180. nsend = send(sockfd, buf, cmdlen, 0);
  181. if (nsend != cmdlen) {
  182. ret = -20;
  183. goto error;
  184. }
  185. // To:
  186. cmdlen = snprintf(buf, buflen, "To:%s\r\n", mail->to);
  187. nsend = send(sockfd, buf, cmdlen, 0);
  188. if (nsend != cmdlen) {
  189. ret = -20;
  190. goto error;
  191. }
  192. // Subject:
  193. cmdlen = snprintf(buf, buflen, "Subject:%s\r\n\r\n", mail->subject);
  194. nsend = send(sockfd, buf, cmdlen, 0);
  195. if (nsend != cmdlen) {
  196. ret = -20;
  197. goto error;
  198. }
  199. // body
  200. cmdlen = strlen(mail->body);
  201. nsend = send(sockfd, mail->body, cmdlen, 0);
  202. if (nsend != cmdlen) {
  203. ret = -20;
  204. goto error;
  205. }
  206. // EOB
  207. nsend = send(sockfd, SMTP_EOB, SMTP_EOB_LEN, 0);
  208. if (nsend != SMTP_EOB_LEN) {
  209. ret = -20;
  210. goto error;
  211. }
  212. nrecv = recv(sockfd, buf, buflen, 0);
  213. if (nrecv <= 0) {
  214. ret = -30;
  215. goto error;
  216. }
  217. status_code = atoi(buf);
  218. if (status_code != SMTP_STATUS_OK) {
  219. ret = status_code;
  220. goto error;
  221. }
  222. // QUIT\r\n
  223. cmdlen = smtp_build_command(SMTP_QUIT, NULL, buf, buflen);
  224. nsend = send(sockfd, buf, cmdlen, 0);
  225. if (nsend != cmdlen) {
  226. ret = -20;
  227. goto error;
  228. }
  229. nrecv = recv(sockfd, buf, buflen, 0);
  230. if (nrecv <= 0) {
  231. ret = -30;
  232. goto error;
  233. }
  234. /*
  235. status_code = atoi(buf);
  236. if (status_code != SMTP_STATUS_BYE) {
  237. ret = status_code;
  238. goto error;
  239. }
  240. */
  241. ret = SMTP_STATUS_OK;
  242. error:
  243. if (sockfd != INVALID_SOCKET) {
  244. closesocket(sockfd);
  245. }
  246. return ret;
  247. }