int main(int argc, const char* argv[]) { const char custom_body[] = "Subject: Custom sized body\n" "\n" "Custom sized body\n" "0123456789\n"; /* these 11 chars to ignore */ const char* body[] = { "This is a simple test", "This is a test with\n.", 0, ".", "\n.\n", ".\n", "", "a\nb\nc\nd\n.", "a\r\n\rb\r\nc\r\nd\r\n.", ".\na" }; const char* subject[] = { 0, "CORE_SendMail Test", "", }; const char* to[] = { "lavr", "lavr@pavo", " \"Anton Lavrentiev\" <lavr@pavo> , lavr, <lavr> ", }; size_t i, j, k, n, m; const char* mx_host; SSendMailInfo info; const char* retval; STimeout mx_tmo; char* huge_body; short mx_port; char val[32]; FILE* fp; g_NCBI_ConnectRandomSeed = (int) time(0) ^ NCBI_CONNECT_SRAND_ADDEND; srand(g_NCBI_ConnectRandomSeed); CORE_SetLOGFormatFlags(fLOG_None | fLOG_Level | fLOG_OmitNoteLevel | fLOG_DateTime); CORE_SetLOGFILE(stderr, 0/*false*/); ConnNetInfo_GetValue(0, REG_CONN_DEBUG_PRINTOUT, val, sizeof(val), DEF_CONN_DEBUG_PRINTOUT); if (ConnNetInfo_Boolean(val) || (*val && (strcasecmp(val, "all") == 0 || strcasecmp(val, "some") == 0 || strcasecmp(val, "data") == 0))) { SOCK_SetDataLoggingAPI(eOn); } strcpy(val, "@"); SendMailInfo_InitEx(&info, val, eCORE_UsernameCurrent); CORE_LOGF(eLOG_Note, ("@ - <%s>", info.from)); strcpy(info.from, "user0"); SendMailInfo_InitEx(&info, info.from, eCORE_UsernameCurrent); CORE_LOGF(eLOG_Note, ("user0 - <%s>", info.from)); strcpy(info.from, "user1@"); SendMailInfo_InitEx(&info, info.from, eCORE_UsernameCurrent); CORE_LOGF(eLOG_Note, ("user1@ - <%s>", info.from)); strcpy(val, "@host2.net"); SendMailInfo_InitEx(&info, val, eCORE_UsernameLogin); CORE_LOGF(eLOG_Note, ("@host2.net - <%s>", info.from)); strcpy(val, "*****@*****.**"); SendMailInfo_InitEx(&info, val, eCORE_UsernameReal); CORE_LOGF(eLOG_Note, ("[email protected] - <%s>", info.from)); strcpy(info.from, "*****@*****.**"); SendMailInfo_InitEx(&info, info.from, eCORE_UsernameReal); CORE_LOGF(eLOG_Note, ("[email protected] - <%s>", info.from)); SendMailInfo_InitEx(&info, 0, eCORE_UsernameReal); CORE_LOGF(eLOG_Note, ("NULL - <%s>", info.from)); if ((huge_body = malloc(TEST_HUGE_BODY_SIZE)) != 0) { strcpy(huge_body, "user5@"); for (i = 0; i < TEST_HUGE_BODY_SIZE - 6; i++) huge_body[i + 6] = "abcdefghijklmnopqrstuvwxyz."[rand() % 27]; huge_body[TEST_HUGE_BODY_SIZE - 1] = '\0'; SendMailInfo_InitEx(&info, huge_body, eCORE_UsernameCurrent); CORE_LOGF(eLOG_Note, ("HUGE user5@host - <%s>", info.from)); SendMailInfo_InitEx(&info, huge_body + 5, eCORE_UsernameLogin); CORE_LOGF(eLOG_Note, ("HUGE @host - <%s>", info.from)); huge_body[4] = '6'; huge_body[5] = '_'; for (i = 6; i < sizeof(info.from) + 1; i++) { if (huge_body[i] == '.') huge_body[i] = '_'; } huge_body[sizeof(info.from) + 1] = '@'; SendMailInfo_InitEx(&info, huge_body, eCORE_UsernameReal); CORE_LOGF(eLOG_Note, ("HUGE user6 - <%s>", info.from)); huge_body[4] = '7'; huge_body[sizeof(info.from) - 10] = '@'; SendMailInfo_InitEx(&info, huge_body, eCORE_UsernameReal); CORE_LOGF(eLOG_Note, ("LONG user7 - <%s>", info.from)); memcpy(huge_body + sizeof(info.from) - 10, "user8", 5); huge_body[sizeof(info.from) << 1] = '\0'; SendMailInfo_InitEx(&info, huge_body + sizeof(info.from) - 10, eCORE_UsernameReal); CORE_LOGF(eLOG_Note, ("LONG user8 - <%s>", info.from)); SendMailInfo_InitEx(&info, huge_body + sizeof(info.from) + 1, eCORE_UsernameReal); CORE_LOGF(eLOG_Note, ("LONG @host - <%s>", info.from)); free(huge_body); } if (argc > 1) { CORE_LOG(eLOG_Note, "Special test requested"); if ((fp = fopen(argv[1], "rb")) != 0 && fseek(fp, 0, SEEK_END) == 0 && (m = ftell(fp)) != (size_t)(-1) && fseek(fp, 0, SEEK_SET) == 0 && (huge_body = (char*) malloc(m + 1)) != 0 && fread(huge_body, m, 1, fp) == 1) { huge_body[m] = '\0'; CORE_LOGF(eLOG_Note, ("Sending file (%lu bytes)", (unsigned long) m)); retval = CORE_SendMail("lavr", "File", huge_body); if (retval) { CORE_LOGF(eLOG_Fatal, ("Test failed: %s", retval)); } else { CORE_LOG(eLOG_Note, "Test passed"); } } else CORE_LOG_ERRNO(eLOG_Error, errno, "Test failed"); return 0; } #if 1 CORE_LOG(eLOG_Note, "Phase 1 of 2: Testing CORE_SendMail"); n = (sizeof(to)/sizeof(to[0]))* (sizeof(subject)/sizeof(subject[0]))* (sizeof(body)/sizeof(body[0])); m = 0; for (i = 0; i < sizeof(to)/sizeof(to[0]); i++) { for (j = 0; j < sizeof(subject)/sizeof(subject[0]); j++) for (k = 0; k < sizeof(body)/sizeof(body[0]); k++) { CORE_LOGF(eLOG_Note, ("Test %u of %u", (unsigned)(++m), (unsigned)n)); retval = CORE_SendMail(to[i], subject[j], body[k]); if (retval != 0) CORE_LOGF(eLOG_Fatal, ("Test failed: %s", retval)); } } #else CORE_LOG(eLOG_Note, "Phase 1 of 2: Skipping CORE_SendMail tests"); #endif CORE_LOG(eLOG_Note, "Phase 2 of 2: Testing CORE_SendMailEx"); SendMailInfo_Init(&info); mx_host = info.mx_host; mx_port = info.mx_port; mx_tmo = info.mx_timeout; info.mx_host = "localhost"; CORE_LOG(eLOG_Note, "Testing bad port"); info.mx_port = 10/*BAD*/; retval = CORE_SendMailEx("lavr", "CORE_SendMailEx", "Bad port", &info); if (!retval) CORE_LOG(eLOG_Fatal, "Test failed"); CORE_LOGF(eLOG_Note, ("Test passed: %s", retval)); CORE_LOG(eLOG_Note, "Testing bad protocol"); info.mx_port = CONN_PORT_FTP; retval = CORE_SendMailEx("lavr", "CORE_SendMailEx", "Protocol", &info); if (!retval) CORE_LOG(eLOG_Fatal, "Test failed"); CORE_LOGF(eLOG_Note, ("Test passed: %s", retval)); CORE_LOG(eLOG_Note, "Testing timeout"); info.mx_host = "www.ncbi.nlm.nih.gov"; info.mx_timeout.sec = 5; info.mx_port = CONN_PORT_HTTP; retval = CORE_SendMailEx("lavr", "CORE_SendMailEx", "Timeout", &info); if (!retval) CORE_LOG(eLOG_Error, "Test failed"); else CORE_LOGF(eLOG_Note, ("Test passed: %s", retval)); info.mx_port = mx_port; info.mx_timeout = mx_tmo; CORE_LOG(eLOG_Note, "Testing bad host"); info.mx_host = "abrakadabra"; retval = CORE_SendMailEx("lavr", "CORE_SendMailEx", "Bad host", &info); if (!retval) CORE_LOG(eLOG_Fatal, "Test failed"); CORE_LOGF(eLOG_Note, ("Test passed: %s", retval)); info.mx_host = "localhost"; CORE_LOG(eLOG_Note, "Testing cc"); info.cc = "vakatov"; retval = CORE_SendMailEx("", "CORE_SendMailEx", "CC", &info); if (retval) CORE_LOGF(eLOG_Fatal, ("Test failed: %s", retval)); CORE_LOG(eLOG_Note, "Test passed"); CORE_LOG(eLOG_Note, "Testing bcc"); info.cc = 0; info.bcc = "vakatov"; retval = CORE_SendMailEx(0, "CORE_SendMailEx", "Bcc", &info); if (retval) CORE_LOGF(eLOG_Fatal, ("Test failed: %s", retval)); CORE_LOG(eLOG_Note, "Test passed"); CORE_LOG(eLOG_Note, "Testing huge body"); info.cc = 0; info.bcc = 0; if (!(huge_body = (char*) malloc(TEST_HUGE_BODY_SIZE))) CORE_LOG(eLOG_Fatal, "Test failed: Cannot allocate memory"); for (i = 0; i < TEST_HUGE_BODY_SIZE - 1; i++) huge_body[i] = "0123456789\nABCDEFGHIJKLMNOPQRSTUVWXYZ ."[rand() % 39]; huge_body[i] = 0; retval = CORE_SendMailEx("lavr", "CORE_SendMailEx", huge_body, &info); if (retval) CORE_LOGF(eLOG_Fatal, ("Test failed: %s", retval)); if (!(fp = fopen("test_ncbi_sendmail.out", "w")) || fwrite(huge_body, TEST_HUGE_BODY_SIZE - 1, 1, fp) != 1) { CORE_LOG(eLOG_Error, "Test failed: Cannot store huge body to file"); } else { fclose(fp); CORE_LOG(eLOG_Note, "Success: Check test_ncbi_sendmail.out"); } free(huge_body); CORE_LOG(eLOG_Note, "Testing custom headers"); info.header = "Organization: NCBI/NLM/NIH\nReference: abcdefghijk"; retval = CORE_SendMailEx("lavr", "CORE_SendMailEx", "Custom header",&info); if (retval) CORE_LOGF(eLOG_Fatal, ("Test failed: %s", retval)); CORE_LOG(eLOG_Note, "Test passed"); CORE_LOG(eLOG_Note, "Testing no recipients"); retval = CORE_SendMailEx(0, "CORE_SendMailEx", "No recipients", &info); if (!retval) CORE_LOG(eLOG_Fatal, "Test failed"); CORE_LOGF(eLOG_Note, ("Test passed: %s", retval)); CORE_LOG(eLOG_Note, "Testing AS-IS message"); info.mx_options = fSendMail_NoMxHeader; retval = CORE_SendMailEx("lavr", "BAD SUBJECT SHOULD NOT APPEAR BUT IGNORED", "From: yourself\n" "To: yourself\n" "Subject: AS-IS message\n" "\n" "AS-IS", &info); if (retval) CORE_LOGF(eLOG_Fatal, ("Test failed: %s", retval)); CORE_LOG(eLOG_Note, "Test passed"); CORE_LOG(eLOG_Note, "Testing AS-IS custom sized message"); info.body_size = strlen(custom_body) - 11/*to ignore*/; retval = CORE_SendMailEx("<lavr@pavo>", "BAD SUBJECT SHOULD NOT APPEAR BUT IGNORED", custom_body, &info); if (retval) CORE_LOGF(eLOG_Fatal, ("Test failed: %s", retval)); CORE_LOG(eLOG_Note, "Test passed"); info.body_size = 0; info.mx_options = 0; info.mx_host = mx_host; CORE_LOG(eLOG_Note, "Testing bad from"); strcpy(info.from, "blahblah@blahblah"); retval = CORE_SendMailEx("lavr", "CORE_SendMailEx", "Bad from",&info); if (!retval) CORE_LOG(eLOG_Error, "Test failed"); else CORE_LOGF(eLOG_Note, ("Test passed: %s", retval)); SendMailInfo_Init(&info); CORE_LOG(eLOG_Note, "Testing drop no FQDN option"); info.mx_options |= fSendMail_StripNonFQDNHost; retval = CORE_SendMailEx("lavr", "CORE_SendMailEx", "No FQDN", &info); if (retval) CORE_LOGF(eLOG_Error, ("Test failed: %s", retval)); else CORE_LOG(eLOG_Note, "Test passed"); CORE_LOG(eLOG_Note, "Testing bad magic"); info.magic_cookie = 0; retval = CORE_SendMailEx("lavr", "CORE_SendMailEx", "Bad Magic", &info); if (!retval) CORE_LOG(eLOG_Fatal, "Test failed"); CORE_LOGF(eLOG_Note, ("Test passed: %s", retval)); CORE_LOG(eLOG_Note, "TEST completed successfully"); CORE_SetLOG(0); return 0; }
const char* CORE_SendMailEx(const char* to, const char* subject, const char* body, const SSendMailInfo* uinfo) { static const STimeout zero = {0, 0}; const SSendMailInfo* info; SSendMailInfo ainfo; char buffer[1024]; SOCK sock = 0; info = uinfo ? uinfo : SendMailInfo_Init(&ainfo); if (info->magic_number != MX_MAGIC_NUMBER) SENDMAIL_RETURN(6, "Invalid magic number"); if ((!to || !*to) && (!info->cc || !*info->cc) && (!info->bcc || !*info->bcc)) { SENDMAIL_RETURN(7, "At least one message recipient must be specified"); } /* Open connection to sendmail */ if (SOCK_Create(info->mx_host, info->mx_port, &info->mx_timeout, &sock) != eIO_Success) { SENDMAIL_RETURN(8, "Cannot connect to sendmail"); } SOCK_SetTimeout(sock, eIO_ReadWrite, &info->mx_timeout); /* Follow the protocol conversation, RFC821 */ if (!SENDMAIL_READ_RESPONSE(220, 0, buffer)) SENDMAIL_RETURN2(9, "Protocol error in connection init", buffer); if ((!(info->mx_options & fSendMail_StripNonFQDNHost) || !SOCK_gethostbyaddr(0, buffer, sizeof(buffer))) && SOCK_gethostname(buffer, sizeof(buffer)) != 0) { SENDMAIL_RETURN(10, "Unable to get local host name"); } if (!s_SockWrite(sock, "HELO ", 0) || !s_SockWrite(sock, buffer, 0) || !s_SockWrite(sock, MX_CRLF, 2)) { SENDMAIL_RETURN(11, "Write error in HELO command"); } if (!SENDMAIL_READ_RESPONSE(250, 0, buffer)) SENDMAIL_RETURN2(12, "Protocol error in HELO command", buffer); if (!s_SockWrite(sock, "MAIL FROM: <", 0) || !s_SockWrite(sock, info->from, s_FromSize(info)) || !s_SockWrite(sock, ">" MX_CRLF, 1 + 2)) { SENDMAIL_RETURN(13, "Write error in MAIL command"); } if (!SENDMAIL_READ_RESPONSE(250, 0, buffer)) SENDMAIL_RETURN2(14, "Protocol error in MAIL command", buffer); if (to && *to) { const char* error = SENDMAIL_SENDRCPT("To", to, buffer); if (error) return error; } if (info->cc && *info->cc) { const char* error = SENDMAIL_SENDRCPT("Cc", info->cc, buffer); if (error) return error; } if (info->bcc && *info->bcc) { const char* error = SENDMAIL_SENDRCPT("Bcc", info->bcc, buffer); if (error) return error; } if (!s_SockWrite(sock, "DATA" MX_CRLF, 0)) SENDMAIL_RETURN(15, "Write error in DATA command"); if (!SENDMAIL_READ_RESPONSE(354, 0, buffer)) SENDMAIL_RETURN2(16, "Protocol error in DATA command", buffer); if (!(info->mx_options & fSendMail_NoMxHeader)) { /* Follow RFC822 to compose message headers. Note that * 'Date:'and 'From:' are both added by sendmail automagically. */ if (!s_SockWrite(sock, "Subject: ", 0) || (subject && !s_SockWrite(sock, subject, 0)) || !s_SockWrite(sock, MX_CRLF, 2)) SENDMAIL_RETURN(17, "Write error in sending subject"); if (to && *to) { if (!s_SockWrite(sock, "To: ", 0) || !s_SockWrite(sock, to, 0) || !s_SockWrite(sock, MX_CRLF, 2)) SENDMAIL_RETURN(18, "Write error in sending To"); } if (info->cc && *info->cc) { if (!s_SockWrite(sock, "Cc: ", 0) || !s_SockWrite(sock, info->cc, 0) || !s_SockWrite(sock, MX_CRLF, 2)) SENDMAIL_RETURN(19, "Write error in sending Cc"); } } else if (subject && *subject) CORE_LOG_X(2, eLOG_Warning, "[SendMail] Subject ignored in as-is messages"); if (!s_SockWrite(sock, "X-Mailer: CORE_SendMail (NCBI " NCBI_SENDMAIL_TOOLKIT " Toolkit)" MX_CRLF, 0)) { SENDMAIL_RETURN(20, "Write error in sending mailer information"); } assert(sizeof(buffer) > sizeof(MX_CRLF) && sizeof(MX_CRLF) >= 3); if (info->header && *info->header) { size_t n = 0, m = strlen(info->header); int/*bool*/ newline = 0/*false*/; while (n < m) { size_t k = 0; if (SOCK_Wait(sock, eIO_Read, &zero) != eIO_Timeout) break; while (k < sizeof(buffer) - sizeof(MX_CRLF)) { if (info->header[n] == '\n') { memcpy(&buffer[k], MX_CRLF, sizeof(MX_CRLF) - 1); k += sizeof(MX_CRLF) - 1; newline = 1/*true*/; } else { if (info->header[n] != '\r' || info->header[n+1] != '\n') buffer[k++] = info->header[n]; newline = 0/*false*/; } if (++n >= m) break; } buffer[k] = '\0'/*just in case*/; if (!s_SockWrite(sock, buffer, k)) SENDMAIL_RETURN(21, "Write error while sending custom header"); } if (n < m) SENDMAIL_RETURN(22, "Header write error"); if (!newline && !s_SockWrite(sock, MX_CRLF, 2)) SENDMAIL_RETURN(23, "Write error while finalizing custom header"); } if (body) { size_t n = 0, m = info->body_size ? info->body_size : strlen(body); int/*bool*/ newline = 0/*false*/; if (!(info->mx_options & fSendMail_NoMxHeader) && m) { if (!s_SockWrite(sock, MX_CRLF, 2)) SENDMAIL_RETURN(24, "Write error in message body delimiter"); } while (n < m) { size_t k = 0; if (SOCK_Wait(sock, eIO_Read, &zero) != eIO_Timeout) break; while (k < sizeof(buffer) - sizeof(MX_CRLF)) { if (body[n] == '\n') { memcpy(&buffer[k], MX_CRLF, sizeof(MX_CRLF) - 1); k += sizeof(MX_CRLF) - 1; newline = 1/*true*/; } else { if (body[n] != '\r' || (n+1 < m && body[n+1] != '\n')){ if (body[n] == '.' && (newline || !n)) { buffer[k++] = '.'; buffer[k++] = '.'; } else buffer[k++] = body[n]; } newline = 0/*false*/; } if (++n >= m) break; } buffer[k] = '\0'/*just in case*/; if (!s_SockWrite(sock, buffer, k)) SENDMAIL_RETURN(25, "Write error while sending message body"); } if (n < m) SENDMAIL_RETURN(26, "Body write error"); if ((!newline && m && !s_SockWrite(sock, MX_CRLF, 2)) || !s_SockWrite(sock, "." MX_CRLF, 1 + 2)) { SENDMAIL_RETURN(27, "Write error while finalizing message body"); } } else if (!s_SockWrite(sock, "." MX_CRLF, 1 + 2)) SENDMAIL_RETURN(28, "Write error while finalizing message"); if (!SENDMAIL_READ_RESPONSE(250, 0, buffer)) SENDMAIL_RETURN2(29, "Protocol error in sending message", buffer); if (!s_SockWrite(sock, "QUIT" MX_CRLF, 0)) SENDMAIL_RETURN(30, "Write error in QUIT command"); if (!SENDMAIL_READ_RESPONSE(221, 0, buffer)) SENDMAIL_RETURN2(31, "Protocol error in QUIT command", buffer); SOCK_Close(sock); return 0; }