smtp.c 6.5 KB

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