1/* 2 * lws-unit-tests-smtp-client 3 * 4 * Written in 2010-2019 by Andy Green <andy@warmcat.com> 5 * 6 * This file is made available under the Creative Commons CC0 1.0 7 * Universal Public Domain Dedication. 8 * 9 * This performs unit tests for the SMTP client abstract protocol 10 */ 11 12#include <libwebsockets.h> 13 14#include <signal.h> 15 16static int interrupted, results[10], count_tests, count_passes; 17 18static int 19email_sent_or_failed(struct lws_smtp_email *email, void *buf, size_t len) 20{ 21 free(email); 22 23 return 0; 24} 25 26/* 27 * The test helper calls this on the instance it created to prepare it for 28 * the test. In our case, we need to queue up a test email to send on the 29 * smtp client abstract protocol. 30 */ 31 32static int 33smtp_test_instance_init(lws_abs_t *instance) 34{ 35 lws_smtp_email_t *email = (lws_smtp_email_t *) 36 malloc(sizeof(*email) + 2048); 37 38 if (!email) 39 return 1; 40 41 /* attach an email to it */ 42 43 memset(email, 0, sizeof(*email)); 44 email->data = NULL /* email specific user data */; 45 email->email_from = "noreply@warmcat.com"; 46 email->email_to = "andy@warmcat.com"; 47 email->payload = (void *)&email[1]; 48 49 lws_snprintf((char *)email->payload, 2048, 50 "From: noreply@example.com\n" 51 "To: %s\n" 52 "Subject: Test email for lws smtp-client\n" 53 "\n" 54 "Hello this was an api test for lws smtp-client\n" 55 "\r\n.\r\n", "andy@warmcat.com"); 56 email->done = email_sent_or_failed; 57 58 if (lws_smtpc_add_email(instance, email)) { 59 lwsl_err("%s: failed to add email\n", __func__); 60 return 1; 61 } 62 63 return 0; 64} 65 66/* 67 * from https://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol 68 * 69 * test vector sent to protocol 70 * test vector received from protocol 71 */ 72 73static lws_unit_test_packet_t test_send1[] = { 74 { 75 "220 smtp.example.com ESMTP Postfix", 76 smtp_test_instance_init, 34, LWS_AUT_EXPECT_RX 77 }, { 78 "HELO lws-test-client\x0a", 79 NULL, 21, LWS_AUT_EXPECT_TX 80 }, { 81 "250 smtp.example.com, I am glad to meet you", 82 NULL, 43, LWS_AUT_EXPECT_RX 83 }, { 84 "MAIL FROM: <noreply@warmcat.com>\x0a", 85 NULL, 33, LWS_AUT_EXPECT_TX 86 }, { 87 "250 Ok", 88 NULL, 6, LWS_AUT_EXPECT_RX 89 }, { 90 "RCPT TO: <andy@warmcat.com>\x0a", 91 NULL, 28, LWS_AUT_EXPECT_TX 92 }, { 93 "250 Ok", 94 NULL, 6, LWS_AUT_EXPECT_RX 95 }, { 96 "DATA\x0a", 97 NULL, 5, LWS_AUT_EXPECT_TX 98 }, { 99 "354 End data with <CR><LF>.<CR><LF>\x0a", 100 NULL, 35, LWS_AUT_EXPECT_RX 101 }, { 102 "From: noreply@example.com\n" 103 "To: andy@warmcat.com\n" 104 "Subject: Test email for lws smtp-client\n" 105 "\n" 106 "Hello this was an api test for lws smtp-client\n" 107 "\r\n.\r\n", 108 NULL, 27 + 21 + 39 + 1 + 47 + 5, LWS_AUT_EXPECT_TX 109 }, { 110 "250 Ok: queued as 12345\x0a", 111 NULL, 23, LWS_AUT_EXPECT_RX 112 }, { 113 "quit\x0a", 114 NULL, 5, LWS_AUT_EXPECT_TX 115 }, { 116 "221 Bye\x0a", 117 NULL, 7, LWS_AUT_EXPECT_RX | 118 LWS_AUT_EXPECT_LOCAL_CLOSE | 119 LWS_AUT_EXPECT_DO_REMOTE_CLOSE | 120 LWS_AUT_EXPECT_TEST_END 121 }, { /* sentinel */ 122 123 } 124}; 125 126 127static lws_unit_test_packet_t test_send2[] = { 128 { 129 "220 smtp.example.com ESMTP Postfix", 130 smtp_test_instance_init, 34, LWS_AUT_EXPECT_RX 131 }, { 132 "HELO lws-test-client\x0a", 133 NULL, 21, LWS_AUT_EXPECT_TX 134 }, { 135 "250 smtp.example.com, I am glad to meet you", 136 NULL, 43, LWS_AUT_EXPECT_RX 137 }, { 138 "MAIL FROM: <noreply@warmcat.com>\x0a", 139 NULL, 33, LWS_AUT_EXPECT_TX 140 }, { 141 "500 Service Unavailable", 142 NULL, 23, LWS_AUT_EXPECT_RX | 143 LWS_AUT_EXPECT_DO_REMOTE_CLOSE | 144 LWS_AUT_EXPECT_TEST_END 145 }, { /* sentinel */ 146 147 } 148}; 149 150static lws_unit_test_t tests[] = { 151 { "sending", test_send1, 3 }, 152 { "rejected", test_send2, 3 }, 153 { } /* sentinel */ 154}; 155 156static void 157sigint_handler(int sig) 158{ 159 interrupted = 1; 160} 161 162/* 163 * set the HELO our SMTP client will use 164 */ 165 166static const lws_token_map_t smtp_ap_tokens[] = { 167 { 168 .u = { .value = "lws-test-client" }, 169 .name_index = LTMI_PSMTP_V_HELO, 170 }, { /* sentinel */ 171 } 172}; 173 174void 175tests_completion_cb(const void *cb_user) 176{ 177 interrupted = 1; 178} 179 180int main(int argc, const char **argv) 181{ 182 int n = 1, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; 183 struct lws_context_creation_info info; 184 lws_test_sequencer_args_t args; 185 struct lws_context *context; 186 lws_abs_t *abs = NULL; 187 struct lws_vhost *vh; 188 const char *p; 189 190 /* the normal lws init */ 191 192 signal(SIGINT, sigint_handler); 193 194 if ((p = lws_cmdline_option(argc, argv, "-d"))) 195 logs = atoi(p); 196 197 lws_set_log_level(logs, NULL); 198 lwsl_user("LWS API selftest: SMTP client unit tests\n"); 199 200 memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ 201 info.port = CONTEXT_PORT_NO_LISTEN; 202 info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS; 203 204 context = lws_create_context(&info); 205 if (!context) { 206 lwsl_err("lws init failed\n"); 207 return 1; 208 } 209 210 vh = lws_create_vhost(context, &info); 211 if (!vh) { 212 lwsl_err("Failed to create first vhost\n"); 213 goto bail1; 214 } 215 216 /* create the abs used to create connections */ 217 218 abs = lws_abstract_alloc(vh, NULL, "smtp.unit_test", 219 &smtp_ap_tokens[0], NULL); 220 if (!abs) 221 goto bail1; 222 223 /* configure the test sequencer */ 224 225 args.abs = abs; 226 args.tests = tests; 227 args.results = results; 228 args.results_max = LWS_ARRAY_SIZE(results); 229 args.count_tests = &count_tests; 230 args.count_passes = &count_passes; 231 args.cb = tests_completion_cb; 232 args.cb_user = NULL; 233 234 if (lws_abs_unit_test_sequencer(&args)) { 235 lwsl_err("%s: failed to create test sequencer\n", __func__); 236 goto bail1; 237 } 238 239 /* the usual lws event loop */ 240 241 while (n >= 0 && !interrupted) 242 n = lws_service(context, 0); 243 244 /* describe the overall test results */ 245 246 lwsl_user("%s: %d tests %d fail\n", __func__, count_tests, 247 count_tests - count_passes); 248 for (n = 0; n < count_tests; n++) 249 lwsl_user(" test %d: %s\n", n, 250 lws_unit_test_result_name(results[n])); 251 252bail1: 253 lwsl_user("Completed: %s\n", 254 !count_tests || count_passes != count_tests ? "FAIL" : "PASS"); 255 256 lws_context_destroy(context); 257 258 lws_abstract_free(&abs); 259 260 return !count_tests || count_passes != count_tests; 261} 262