smtp-sequencer.c 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. /*
  2. * Abstract SMTP support for libwebsockets - SMTP sequencer
  3. *
  4. * Copyright (C) 2016-2019 Andy Green <[email protected]>
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation:
  9. * version 2.1 of the License.
  10. *
  11. * This library is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public
  17. * License along with this library; if not, write to the Free Software
  18. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  19. * MA 02110-1301 USA
  20. *
  21. * This sequencer sits above the abstract protocol, and manages queueing,
  22. * retrying mail transmission, and retry limits.
  23. *
  24. * Having the sequencer means that, eg, we can manage retries after complete
  25. * connection failure.
  26. *
  27. * Connections to the smtp server are serialized
  28. */
  29. #include "private-lib-core.h"
  30. #include "private-lib-abstract-protocols-smtp.h"
  31. typedef enum {
  32. LSMTPSS_DISCONNECTED,
  33. LSMTPSS_CONNECTING,
  34. LSMTPSS_CONNECTED,
  35. LSMTPSS_BUSY,
  36. } smtpss_connstate_t;
  37. typedef struct lws_smtp_sequencer {
  38. struct lws_dll2_owner emails_owner; /* email queue */
  39. lws_abs_t *abs, *instance;
  40. lws_smtp_sequencer_args_t args;
  41. struct lws_sequencer *seq;
  42. smtpss_connstate_t connstate;
  43. time_t email_connect_started;
  44. /* holds the HELO for the smtp protocol to consume */
  45. lws_token_map_t apt[3];
  46. } lws_smtp_sequencer_t;
  47. /* sequencer messages specific to this sequencer */
  48. enum {
  49. SEQ_MSG_CLIENT_FAILED = LWSSEQ_USER_BASE,
  50. SEQ_MSG_CLIENT_DONE,
  51. };
  52. /*
  53. * We're going to bind to the raw-skt transport, so tell that what we want it
  54. * to connect to
  55. */
  56. static const lws_token_map_t smtp_rs_transport_tokens[] = {
  57. {
  58. .u = { .value = "127.0.0.1" },
  59. .name_index = LTMI_PEER_V_DNS_ADDRESS,
  60. }, {
  61. .u = { .lvalue = 25 },
  62. .name_index = LTMI_PEER_LV_PORT,
  63. }, {
  64. }
  65. };
  66. static void
  67. lws_smtpc_kick_internal(lws_smtp_sequencer_t *s)
  68. {
  69. lws_smtp_email_t *e;
  70. lws_dll2_t *d;
  71. char buf[64];
  72. int n;
  73. lws_dll2_t *pd2;
  74. pd2 = lws_dll2_get_head(&s->emails_owner);
  75. if (!pd2)
  76. return;
  77. e = lws_container_of(pd2, lws_smtp_email_t, list);
  78. if (!e)
  79. return;
  80. /* Is there something to do? If so, we need a connection... */
  81. if (s->connstate == LSMTPSS_DISCONNECTED) {
  82. s->apt[0].u.value = s->args.helo;
  83. s->apt[0].name_index = LTMI_PSMTP_V_HELO;
  84. s->apt[1].u.value = (void *)e;
  85. s->apt[1].name_index = LTMI_PSMTP_V_LWS_SMTP_EMAIL_T;
  86. /*
  87. * create and connect the smtp protocol + transport
  88. */
  89. s->abs = lws_abstract_alloc(s->args.vhost, NULL, "smtp.raw_skt",
  90. s->apt, smtp_rs_transport_tokens,
  91. s->seq, NULL);
  92. if (!s->abs)
  93. return;
  94. s->instance = lws_abs_bind_and_create_instance(s->abs);
  95. if (!s->instance) {
  96. lws_abstract_free(&s->abs);
  97. lwsl_err("%s: failed to create SMTP client\n", __func__);
  98. goto bail1;
  99. }
  100. s->connstate = LSMTPSS_CONNECTING;
  101. lws_seq_timeout_us(s->seq, 10 * LWS_USEC_PER_SEC);
  102. return;
  103. }
  104. /* ask the transport if we have a connection to the server ongoing */
  105. if (s->abs->at->state(s->abs->ati)) {
  106. /*
  107. * there's a connection, it could be still trying to connect
  108. * or established
  109. */
  110. s->abs->at->ask_for_writeable(s->abs->ati);
  111. return;
  112. }
  113. /* there's no existing connection */
  114. lws_smtpc_state_transition(c, LGSSMTP_CONNECTING);
  115. if (s->abs->at->client_conn(s->abs)) {
  116. lwsl_err("%s: failed to connect\n", __func__);
  117. return;
  118. }
  119. e->tries++;
  120. e->last_try = lws_now_secs();
  121. }
  122. /*
  123. * The callback we get from the smtp protocol... we use this to drive
  124. * decisions about destroy email, retry and fail.
  125. *
  126. * Sequencer will handle it via the event loop.
  127. */
  128. static int
  129. email_result(void *e, void *d, int disp, void *b, size_t l)
  130. {
  131. lws_smtp_sequencer_t *s = (lws_smtp_sequencer_t *)d;
  132. lws_sequencer_event(s->seq, LWSSEQ_USER_BASE + disp, e);
  133. return 0;
  134. }
  135. static int
  136. cleanup(struct lws_dll2 *d, void *user)
  137. {
  138. lws_smtp_email_t *e;
  139. e = lws_container_of(d, lws_smtp_email_t, list);
  140. if (e->done)
  141. e->done(e, "destroying", 10);
  142. lws_dll2_remove(d);
  143. lws_free(e);
  144. return 0;
  145. }
  146. static lws_seq_cb_return_t
  147. smtp_sequencer_cb(struct lws_sequencer *seq, void *user, int event, void *data)
  148. {
  149. struct lws_smtp_sequencer_t *s = (struct lws_smtp_sequencer_t *)user;
  150. switch ((int)event) {
  151. case LWSSEQ_CREATED: /* our sequencer just got started */
  152. lwsl_notice("%s: %s: created\n", __func__,
  153. lws_sequencer_name(seq));
  154. s->connstate = LSMTPSS_DISCONNECTED;
  155. s->state = 0; /* first thing we'll do is the first url */
  156. goto step;
  157. case LWSSEQ_DESTROYED:
  158. lws_dll2_foreach_safe(&s->pending_owner, NULL, cleanup);
  159. break;
  160. case LWSSEQ_TIMED_OUT:
  161. lwsl_notice("%s: LWSSEQ_TIMED_OUT\n", __func__);
  162. break;
  163. case LWSSEQ_USER_BASE + LWS_SMTP_DISPOSITION_SENT:
  164. lws_smtpc_free_email(data);
  165. break;
  166. case LWSSEQ_WSI_CONNECTED:
  167. s->connstate = LSMTPSS_CONNECTED;
  168. lws_smtpc_kick_internal(s);
  169. break;
  170. case LWSSEQ_WSI_CONN_FAIL:
  171. case LWSSEQ_WSI_CONN_CLOSE:
  172. s->connstate = LSMTPSS_DISCONNECTED;
  173. lws_smtpc_kick_internal(s);
  174. break;
  175. case SEQ_MSG_SENT:
  176. break;
  177. default:
  178. break;
  179. }
  180. return LWSSEQ_RET_CONTINUE;
  181. }
  182. /*
  183. * Creates an lws_sequencer to manage the test sequence
  184. */
  185. lws_smtp_sequencer_t *
  186. lws_smtp_sequencer_create(const lws_smtp_sequencer_args_t *args)
  187. {
  188. lws_smtp_sequencer_t *s;
  189. struct lws_sequencer *seq;
  190. /*
  191. * Create a sequencer in the event loop to manage the SMTP queue
  192. */
  193. seq = lws_sequencer_create(args->vhost->context, 0,
  194. sizeof(lws_smtp_sequencer_t), (void **)&s,
  195. smtp_sequencer_cb, "smtp-seq");
  196. if (!seq) {
  197. lwsl_err("%s: unable to create sequencer\n", __func__);
  198. return NULL;
  199. }
  200. s->abs = *args->abs;
  201. s->args = *args;
  202. s->seq = seq;
  203. /* set defaults in our copy of the args */
  204. if (!s->args.helo[0])
  205. strcpy(s->args.helo, "default-helo");
  206. if (!s->args.email_queue_max)
  207. s->args.email_queue_max = 8;
  208. if (!s->args.retry_interval)
  209. s->args.retry_interval = 15 * 60;
  210. if (!s->args.delivery_timeout)
  211. s->args.delivery_timeout = 12 * 60 * 60;
  212. return s;
  213. }
  214. void
  215. lws_smtp_sequencer_destroy(lws_smtp_sequencer_t *s)
  216. {
  217. /* sequencer destruction destroys all assets */
  218. lws_sequencer_destroy(&s->seq);
  219. }
  220. int
  221. lws_smtpc_add_email(lws_smtp_sequencer_t *s, const char *payload,
  222. size_t payload_len, const char *sender,
  223. const char *recipient, void *data, lws_smtp_cb_t done)
  224. {
  225. lws_smtp_email_t *e;
  226. if (s->emails_owner.count > s->args.email_queue_max) {
  227. lwsl_err("%s: email queue at limit of %d\n", __func__,
  228. (int)s->args.email_queue_max);
  229. return 1;
  230. }
  231. if (!done)
  232. return 1;
  233. e = malloc(sizeof(*e) + payload_len + 1);
  234. if (!e)
  235. return 1;
  236. memset(e, 0, sizeof(*e));
  237. e->data = data;
  238. e->done = done;
  239. lws_strncpy(e->from, sender, sizeof(e->from));
  240. lws_strncpy(e->to, recipient, sizeof(e->to));
  241. memcpy((char *)&e[1], payload, payload_len + 1);
  242. e->added = lws_now_secs();
  243. e->last_try = 0;
  244. e->tries = 0;
  245. lws_dll2_clear(&e->list);
  246. lws_dll2_add_tail(&e->list, &s->emails_owner);
  247. lws_smtpc_kick_internal(s);
  248. return 0;
  249. }