NCBI C++ ToolKit
ncbi_sendmail.c
Go to the documentation of this file.

Go to the SVN repository for this file.

1 /* $Id: ncbi_sendmail.c 101712 2024-01-30 19:09:27Z lavr $
2  * ===========================================================================
3  *
4  * PUBLIC DOMAIN NOTICE
5  * National Center for Biotechnology Information
6  *
7  * This software/database is a "United States Government Work" under the
8  * terms of the United States Copyright Act. It was written as part of
9  * the author's official duties as a United States Government employee and
10  * thus cannot be copyrighted. This software/database is freely available
11  * to the public for use. The National Library of Medicine and the U.S.
12  * Government have not placed any restriction on its use or reproduction.
13  *
14  * Although all reasonable efforts have been taken to ensure the accuracy
15  * and reliability of the software and data, the NLM and the U.S.
16  * Government do not and cannot warrant the performance or results that
17  * may be obtained by using this software or data. The NLM and the U.S.
18  * Government disclaim all warranties, express or implied, including
19  * warranties of performance, merchantability or fitness for any particular
20  * purpose.
21  *
22  * Please cite the author in any work or product based on this material.
23  *
24  * ===========================================================================
25  *
26  * Author: Anton Lavrentiev
27  *
28  * File Description:
29  * Send mail
30  *
31  */
32 
33 #include "ncbi_ansi_ext.h"
34 #include "ncbi_priv.h"
35 #include "ncbi_servicep.h"
36 #include <connect/ncbi_sendmail.h>
37 #include <ctype.h>
38 #include <errno.h>
39 #include <stdlib.h>
40 #include <time.h>
41 
42 #define NCBI_USE_ERRCODE_X Connect_SMTP
43 
44 #define MX_SENDMAIL_MAGIC 0xBA8ADEDA
45 #define MX_CRLF "\r\n"
46 
47 #define SMTP_READERR -1 /* Read error from socket */
48 #define SMTP_READTMO -2 /* Read timed out */
49 #define SMTP_RESPERR -3 /* Cannot read response prefix */
50 #define SMTP_NOCODE -4 /* No response code detected (letters?) */
51 #define SMTP_BADCODE -5 /* Response code doesn't match in lines */
52 #define SMTP_BADRESP -6 /* Malformed response */
53 
54 
55 /* Read SMTP response from the socket.
56  * Return the response in the buffer provided,
57  * and the response code (positive value) as a return value.
58  * Return a negative code in case of problem (protocol response
59  * read error or protocol violations).
60  * Return 0 in case of a call error.
61  */
62 static int s_SockRead(SOCK sock, char* response, size_t max_response_len,
63  int/*bool*/ savecode)
64 {
65  int/*bool*/ done = 0/*false*/;
66  size_t n = 0;
67  int code = 0;
68 
69  assert(response && max_response_len);
70  do {
71  EIO_Status status;
72  size_t m = 0;
73  char buf[4];
74 
75  status = SOCK_Read(sock, buf, 4, &m, eIO_ReadPersist);
76  if (status != eIO_Success) {
77  if (m == 3 && status == eIO_Closed)
78  buf[m++] = ' ';
79  else if (status == eIO_Timeout)
80  return SMTP_READTMO;
81  else if (m)
82  return SMTP_RESPERR;
83  else
84  return SMTP_READERR;
85  }
86  assert(m == 4);
87 
88  if (buf[3] == '-' || (done = isspace((unsigned char) buf[3]))) {
89  buf[3] = '\0';
90  if (!code) {
91  char* e;
92  errno = 0;
93  code = (int) strtol(buf, &e, 10);
94  if (errno || code <= 0 || e != buf + 3)
95  return SMTP_NOCODE;
96  if (savecode) {
97  n = 3;
98  if (n > max_response_len)
99  n = max_response_len;
100  memcpy(response, buf, n);
101  if (n < max_response_len)
102  response[n++] = ' ';
103  }
104  } else if (code != atoi(buf))
105  return SMTP_BADCODE;
106  } else
107  return SMTP_BADRESP;
108 
109  if (status == eIO_Success) {
110  do {
111  status = SOCK_Read(sock, buf, 1, &m, eIO_ReadPlain);
112  if (status == eIO_Closed) {
113  if (n < max_response_len)
114  response[n++] = '\n';
115  done = 1/*true*/;
116  break;
117  }
118  if (!m)
119  return status == eIO_Timeout ? SMTP_READTMO : SMTP_READERR;
120  if (*buf != '\r' && n < max_response_len)
121  response[n++] = *buf;
122  assert(status == eIO_Success);
123  } while (*buf != '\n');
124 
125  /* At least '\n' should sit in the buffer */
126  assert(n);
127  if (done)
128  response[n - 1] = '\0';
129  else if (n < max_response_len)
130  response[n] = ' ';
131  } else {
132  *response = '\0';
133  break;
134  }
135  } while (!done);
136 
137  assert(code > 0);
138  return code;
139 }
140 
141 
142 static int/*bool*/ s_SockReadResponse(SOCK sock, int code, int alt_code,
143  char* buf, size_t buf_size,
144  int/*bool*/ savecode)
145 {
146  int c = s_SockRead(sock, buf, buf_size, savecode);
147  if (c <= 0) {
148  const char* message = 0;
149  switch (c) {
150  case SMTP_READERR:
151  message = "Read error";
152  break;
153  case SMTP_READTMO:
154  message = "Read timed out";
155  break;
156  case SMTP_RESPERR:
157  message = "Cannot read response prefix";
158  break;
159  case SMTP_NOCODE:
160  message = "No response code detected";
161  break;
162  case SMTP_BADCODE:
163  message = "Response code mismatch";
164  break;
165  case SMTP_BADRESP:
166  message = "Malformed response";
167  break;
168  default:
169  message = "Unknown error";
170  assert(0);
171  break;
172  }
173  assert(message);
174  strncpy0(buf, message, buf_size - 1);
175  } else if (!code || c == code || (alt_code && c == alt_code))
176  return 1/*success*/;
177  return 0/*failure*/;
178 }
179 
180 
181 static int/*bool*/ s_SockWrite(SOCK sock, const char* buf, size_t len)
182 {
183  size_t n;
184 
185  if (!len)
186  len = strlen(buf);
187  if (SOCK_Write(sock, buf, len, &n, eIO_WritePersist) == eIO_Success) {
188  assert(n == len);
189  return 1/*success*/;
190  }
191  return 0/*failure*/;
192 }
193 
194 
195 static void s_MakeFrom(char* buf, size_t size, const char* from,
196  ECORE_Username user)
197 {
198  char x_buf[sizeof(((SSendMailInfo*) 0)->from)];
199  const char* at;
200  size_t len;
201 
202  if (from && *from) {
203  if (!(at = strchr(from, '@'))) {
204  /* no "@", verbatim copy */
205  if (buf != from)
206  strncpy0(buf, from, size - 1);
207  return;
208  }
209  if (at != from) {
210  /* "user@[host]" */
211  if (buf != from) {
212  len = (size_t)(at - from);
213  if (len < size) {
214  size_t tmp = len + strlen(at);
215  if (tmp < size)
216  len = tmp;
217  } else
218  len = size - 1;
219  strncpy0(buf, from, len);
220  }
221  if (*++at)
222  return; /* "user@host", all done */
223  /* (*at) == '\0' */
224  } else if (at[1]) {
225  /* "@host", save host if it fits the temp buffer */
226  if ((len = strlen(at)) < sizeof(x_buf)) {
227  memcpy(x_buf, at, len + 1);
228  at = x_buf;
229  } else
230  at = "@";
231  *buf = '\0';
232  /* (*at) != '\0' (=='@') */
233  } else {
234  /* "@" */
235  *buf = '\0';
236  return;
237  }
238  } else
239  at = 0;
240  if (!at || *at) {
241  if (!CORE_GetUsernameEx(buf, size, user) || !*buf)
242  strncpy0(buf, "anonymous", size - 1);
243  len = strlen(buf);
244  } else
245  len = strlen(buf) - 1/*'@'*/;
246  size -= len;
247  buf += len;
248  if (!at || !*at) {
249  if (size-- > 2) {
250  *buf++ = '@';
251  if ((!SOCK_gethostbyaddr(0, buf, size) || !strchr(buf, '.'))
252  && SOCK_gethostname(buf, size) != 0) {
253  const char* host;
255  if ((!(host = getenv("HOSTNAME")) && !(host = getenv("HOST")))
256  || (len = strlen(host)) >= size) {
257  *--buf = '\0';
258  } else
259  strcpy(buf, host);
260  CORE_UNLOCK;
261  }
262  } else
263  *buf = '\0';
264  } else if (1 < (len = strlen(at)) && len < size)
265  memcpy(buf, at, len + 1);
266 }
267 
268 
270 static char s_MxHost[CONN_HOST_LEN + 1];
271 static unsigned short s_MxPort;
272 
273 
274 static void x_Sendmail_InitEnv(void)
275 {
276  char buf[sizeof(s_MxHost)], *e;
277  unsigned int port;
278  double tmo;
279 
280  if (s_MxPort)
281  return;
282 
283  if (!ConnNetInfo_GetValueInternal(0, "MX_TIMEOUT", buf, sizeof(buf), 0)
284  || !*buf || (tmo = NCBI_simple_atof(buf, &e)) < 0.000001
285  || errno || !*e) {
286  tmo = 120.0/*2 min*/;
287  }
288  if (!ConnNetInfo_GetValueInternal(0, "MX_PORT", buf, sizeof(buf), 0)
289  || !(port = (unsigned int) atoi(buf)) || port > 65535) {
290  port = CONN_PORT_SMTP;
291  }
292  if (!ConnNetInfo_GetValueInternal(0, "MX_HOST", buf, sizeof(buf), 0)
293  || !*buf) {
294 #if defined(NCBI_OS_UNIX) && !defined(NCBI_OS_CYGWIN)
295  strcpy(buf, "localhost");
296 #else
297  strcpy(buf, "mailgw");
298 #endif /*NCBI_OS_UNIX && !NCBI_OS_CYGWIN*/
299  }
300 
301  s_MxTimeout.sec = (unsigned int) tmo;
302  s_MxTimeout.usec = (unsigned int)((tmo - s_MxTimeout.sec) * 1000000.0);
303  strcpy(s_MxHost, buf);
304  s_MxPort = (unsigned short) port;
305 }
306 
307 
309  const char* from,
310  ECORE_Username user)
311 {
312  if (info) {
314  info->cc = 0;
315  info->bcc = 0;
316  s_MakeFrom(info->from, sizeof(info->from), from, user);
317  info->header = 0;
318  info->body_size = 0;
319  info->mx_timeout = s_MxTimeout;
320  info->mx_host = s_MxHost;
321  info->mx_port = s_MxPort;
322  info->mx_options = 0;
323  info->magic = MX_SENDMAIL_MAGIC;
324  }
325  return info;
326 }
327 
328 
329 extern const char* CORE_SendMail(const char* to,
330  const char* subject,
331  const char* body)
332 {
333  return CORE_SendMailEx(to, subject, body, 0);
334 }
335 
336 
337 /* In two macros below the smartest (or, weak-minded?) Sun C compiler would
338  * have warned about unreachable end-of-loop condition (well, it thinks "a
339  * condition" is there, dumb!) -- so we had to add the "if (!sock)" thing.
340  */
341 #define SENDMAIL_RETURN(subcode, reason) \
342  do { \
343  if (sock) { \
344  SOCK_Close(sock); \
345  sock = 0; \
346  } \
347  CORE_LOGF_X(subcode, eLOG_Error, ("[SendMail] %s", reason)); \
348  if (!sock) { \
349  if (info->mx_options & fSendMail_ExtendedErrInfo) { \
350  char* retval = strdup(reason); \
351  return retval ? retval : ""; \
352  } \
353  return reason; \
354  } \
355  /*NOTREACHED*/ \
356  } while (0)
357 
358 #define SENDMAIL_RETURN2(subcode, reason, explanation) \
359  do { \
360  if (sock) { \
361  SOCK_Close(sock); \
362  sock = 0; \
363  } \
364  CORE_LOGF_X(subcode, eLOG_Error, \
365  ("[SendMail] %s: %s", reason, explanation)); \
366  if (!sock) { \
367  if (info->mx_options & fSendMail_ExtendedErrInfo) { \
368  size_t lenr = strlen(reason); \
369  size_t lene = strlen(explanation) + 1; \
370  char* retval = (char*) malloc(lene + 2 + lenr); \
371  if (!retval) \
372  return ""; \
373  memcpy(retval, reason, lenr); \
374  retval[lenr++] = ':'; \
375  retval[lenr++] = ' '; \
376  memcpy(retval + lenr, explanation, lene); \
377  return retval; \
378  } \
379  return reason; \
380  } \
381  /*NOTREACHED*/ \
382  } while (0)
383 
384 
385 static const char* s_SendRcpt(SOCK sock, const char* to,
386  char buf[], size_t buf_size,
387  const char what[],
388  const char write_error[],
389  const char proto_error[],
390  const SSendMailInfo* info)
391 {
392  char c;
393  while ((c = *to++) != '\0') {
394  char quote = 0;
395  size_t k = 0;
396  if (isspace((unsigned char) c))
397  continue;
398  while (k < buf_size) {
399  if (quote) {
400  if (c == quote)
401  quote = 0;
402  } else if (c == '"' || c == '<') {
403  quote = c == '<' ? '>' : c;
404  } else if (c == ',')
405  break;
406  buf[k++] = c == '\t' ? ' ' : c;
407  if (!(c = *to++))
408  break;
409  if (isspace((unsigned char) c)) {
410  while (isspace((unsigned char)(*to)))
411  to++;
412  }
413  }
414  if (k >= buf_size)
415  SENDMAIL_RETURN(3, "Recipient address is too long");
416  buf[k] = '\0'/*just in case*/;
417  if (quote) {
419  ("[SendMail] Unbalanced delimiters in"
420  " recipient %s for %s: \"%c\" expected",
421  buf, what, quote));
422  }
423  if (!s_SockWrite(sock, "RCPT TO: <", 10) ||
424  !s_SockWrite(sock, buf, k) ||
425  !s_SockWrite(sock, ">" MX_CRLF, sizeof(MX_CRLF))) {
426  SENDMAIL_RETURN(4, write_error);
427  }
428  if (!s_SockReadResponse(sock, 250, 251, buf, buf_size,
429  info->mx_options & fSendMail_ExtendedErrInfo)){
430  SENDMAIL_RETURN2(5, proto_error, buf);
431  }
432  if (!c)
433  break;
434  }
435  return 0;
436 }
437 
438 
439 static size_t s_FromSize(const SSendMailInfo* info)
440 {
441  const char* at, *dot;
442  size_t len = *info->from ? strlen(info->from) : 0;
443 
444  if (!len || !(info->mx_options & fSendMail_StripNonFQDNHost))
445  return len;
446  if (!(at = (const char*) memchr(info->from, '@', len))
447  || at == info->from + len - 1) {
448  return len - 1;
449  }
450  if (!(dot = (const char*) memchr(at + 1, '.',
451  len - (size_t)(at - info->from) - 1))
452  || dot == at + 1 || dot == info->from + len - 1) {
453  return (size_t)(at - info->from);
454  }
455  return len;
456 }
457 
458 
459 #define SENDMAIL_SENDRCPT(what, list, buffer) \
460  s_SendRcpt(sock, list, buffer, sizeof(buffer), what, \
461  "Write error in RCPT (" what ") command", \
462  "Protocol error in RCPT (" what ") command", \
463  info)
464 
465 #define SENDMAIL_READ_RESPONSE(code, buffer) \
466  s_SockReadResponse(sock, code, 0, buffer, sizeof(buffer), \
467  info->mx_options & fSendMail_ExtendedErrInfo)
468 
469 
470 extern const char* CORE_SendMailEx(const char* to,
471  const char* subject,
472  const char* body,
473  const SSendMailInfo* uinfo)
474 {
475  static const STimeout zero = {0, 0};
476  const SSendMailInfo* info;
477  SSendMailInfo ainfo;
478  EIO_Status status;
479  char buffer[1024];
481  SOCK sock = 0;
482 
483  info = uinfo ? uinfo : SendMailInfo_Init(&ainfo);
484  if (info->magic != MX_SENDMAIL_MAGIC)
485  SENDMAIL_RETURN(6, "SSendMailInfo inited improperly: Invalid magic");
486 
487  if ((!to || !*to) &&
488  (!info->cc || !*info->cc) &&
489  (!info->bcc || !*info->bcc)) {
490  SENDMAIL_RETURN(7, "At least one message recipient must be specified");
491  }
492 
493  /* Open connection to sendmail */
495  if ((status = SOCK_CreateEx(info->mx_host, info->mx_port,
496  &info->mx_timeout, &sock,
497  0, 0, log)) != eIO_Success) {
498  sprintf(buffer, "%.512s:%hu (%s)", info->mx_host, info->mx_port,
499  IO_StatusStr(status));
500  SENDMAIL_RETURN2(8, "Cannot connect to sendmail", buffer);
501  }
502  SOCK_SetTimeout(sock, eIO_ReadWrite, &info->mx_timeout);
503  SOCK_SetTimeout(sock, eIO_Close, &info->mx_timeout);
504  SOCK_DisableOSSendDelay(sock, 1/*true,disable*/);
505 
506  /* Follow the protocol conversation, RFC821 */
507  if (!SENDMAIL_READ_RESPONSE(220, buffer))
508  SENDMAIL_RETURN2(9, "Protocol error in connection init", buffer);
509 
510  if ((!(info->mx_options & fSendMail_StripNonFQDNHost)
511  || !SOCK_gethostbyaddr(0, buffer, sizeof(buffer)))
512  && SOCK_gethostname(buffer, sizeof(buffer)) != 0) {
513  SENDMAIL_RETURN(10, "Unable to get local host name");
514  }
515  if (!s_SockWrite(sock, "HELO ", 5) ||
516  !s_SockWrite(sock, buffer, 0) ||
517  !s_SockWrite(sock, MX_CRLF, sizeof(MX_CRLF)-1)) {
518  SENDMAIL_RETURN(11, "Write error in HELO command");
519  }
520  if (!SENDMAIL_READ_RESPONSE(250, buffer))
521  SENDMAIL_RETURN2(12, "Protocol error in HELO command", buffer);
522 
523  if (!s_SockWrite(sock, "MAIL FROM: <", 12) ||
524  !s_SockWrite(sock, info->from, s_FromSize(info)) ||
525  !s_SockWrite(sock, ">" MX_CRLF, sizeof(MX_CRLF))) {
526  SENDMAIL_RETURN(13, "Write error in MAIL command");
527  }
528  if (!SENDMAIL_READ_RESPONSE(250, buffer))
529  SENDMAIL_RETURN2(14, "Protocol error in MAIL command", buffer);
530 
531  if (to && *to) {
532  const char* error = SENDMAIL_SENDRCPT("To", to, buffer);
533  if (error)
534  return error;
535  }
536 
537  if (info->cc && *info->cc) {
538  const char* error = SENDMAIL_SENDRCPT("Cc", info->cc, buffer);
539  if (error)
540  return error;
541  }
542 
543  if (info->bcc && *info->bcc) {
544  const char* error = SENDMAIL_SENDRCPT("Bcc", info->bcc, buffer);
545  if (error)
546  return error;
547  }
548 
549  if (!s_SockWrite(sock, "DATA" MX_CRLF, 4 + sizeof(MX_CRLF)-1))
550  SENDMAIL_RETURN(15, "Write error in DATA command");
551  if (!SENDMAIL_READ_RESPONSE(354, buffer))
552  SENDMAIL_RETURN2(16, "Protocol error in DATA command", buffer);
553 
554  (void) SOCK_SetCork(sock, 1/*true*/);
555 
556  if (!(info->mx_options & fSendMail_NoMxHeader)) {
557  if (!(info->mx_options & fSendMail_Old822Headers)) {
558  /* Locale-independent month names per RFC5322 and older */
559  static const char* kMonth[] = { "Jan", "Feb", "Mar", "Apr",
560  "May", "Jun", "Jul", "Aug",
561  "Sep", "Oct", "Nov", "Dec" };
562  /* Skip DoW: it's optional yet must also be locale-independent */
563  static const char kDateFmt[] = "%d %%s %Y %H:%M:%S %z" MX_CRLF;
564  time_t now = time(0);
565  char datefmt[80];
566  struct tm* tm;
567 #if defined(NCBI_OS_SOLARIS)
568  /* MT safe */
569  tm = localtime(&now);
570 #elif defined(HAVE_LOCALTIME_R)
571  struct tm tmp;
572  localtime_r(&now, tm = &tmp);
573 #else
574  struct tm tmp;
576  tm = (struct tm*) memcpy(&tmp, localtime(&now), sizeof(tmp));
577  CORE_UNLOCK;
578 #endif /*NCBI_OS_SOLARIS*/
579  if (strftime(datefmt, sizeof(datefmt), kDateFmt, tm)) {
580  sprintf(buffer, datefmt, kMonth[tm->tm_mon]);
581  if (!s_SockWrite(sock, "Date: ", 6) ||
582  !s_SockWrite(sock, buffer, 0)) {
583  SENDMAIL_RETURN(32, "Write error in sending Date");
584  }
585  }
586  if (*info->from) {
587  if (!s_SockWrite(sock, "From: ", 6) ||
588  !s_SockWrite(sock, info->from, 0) ||
589  !s_SockWrite(sock, MX_CRLF, sizeof(MX_CRLF)-1)) {
590  SENDMAIL_RETURN(33, "Write error in sending From");
591  }
592  }
593  }
594  if (subject) {
595  if (!s_SockWrite(sock, "Subject: ", 9) ||
596  (*subject && !s_SockWrite(sock, subject, 0)) ||
597  !s_SockWrite(sock, MX_CRLF, sizeof(MX_CRLF)-1)) {
598  SENDMAIL_RETURN(17, "Write error in sending Subject");
599  }
600  }
601  if (to && *to) {
602  if (!s_SockWrite(sock, "To: ", 4) ||
603  !s_SockWrite(sock, to, 0) ||
604  !s_SockWrite(sock, MX_CRLF, sizeof(MX_CRLF)-1)) {
605  SENDMAIL_RETURN(18, "Write error in sending To");
606  }
607  }
608  if (info->cc && *info->cc) {
609  if (!s_SockWrite(sock, "Cc: ", 4) ||
610  !s_SockWrite(sock, info->cc, 0) ||
611  !s_SockWrite(sock, MX_CRLF, sizeof(MX_CRLF)-1)) {
612  SENDMAIL_RETURN(19, "Write error in sending Cc");
613  }
614  }
615  } else if (subject && *subject) {
617  "[SendMail] Subject ignored in as-is messages");
618  }
619 
620  if (!s_SockWrite(sock, "X-Mailer: CORE_SendMail (NCBI "
621 #ifdef NCBI_CXX_TOOLKIT
622  "CXX Toolkit"
623 #else
624  "C Toolkit"
625 #endif /*NCBI_CXX_TOOLKIT*/
626  ")" MX_CRLF, 0)) {
627  SENDMAIL_RETURN(20, "Write error in sending mailer information");
628  }
629 
630  assert(sizeof(buffer) > sizeof(MX_CRLF) && sizeof(MX_CRLF) >= 3);
631 
632  status = eIO_Timeout;
633  if (info->header && *info->header) {
634  size_t n = 0, m = strlen(info->header);
635  int/*bool*/ newline = 0/*false*/;
636  while (n < m) {
637  size_t k = 0;
638  if ((status = SOCK_Wait(sock, eIO_Read, &zero)) != eIO_Timeout)
639  break;
640  while (k < sizeof(buffer) - sizeof(MX_CRLF)) {
641  if (info->header[n] == '\n') {
642  memcpy(&buffer[k], MX_CRLF, sizeof(MX_CRLF)-1);
643  k += sizeof(MX_CRLF)-1;
644  newline = 1/*true*/;
645  } else {
646  if (info->header[n] != '\r' || info->header[n+1] != '\n')
647  buffer[k++] = info->header[n];
648  newline = 0/*false*/;
649  }
650  if (++n >= m)
651  break;
652  }
653  buffer[k] = '\0'/*just in case*/;
654  if (!s_SockWrite(sock, buffer, k))
655  SENDMAIL_RETURN(21, "Write error while sending custom header");
656  }
657  if (n < m) {
658  const char* error = "Custom header write error";
659  assert(status != eIO_Timeout);
660  if (status != eIO_Success)
661  strcpy(buffer, IO_StatusStr(status));
662  else if (SENDMAIL_READ_RESPONSE(0, buffer))
663  error = "Spurious response while writing custom header";
665  }
666  if (!newline && !s_SockWrite(sock, MX_CRLF, sizeof(MX_CRLF)-1))
667  SENDMAIL_RETURN(23, "Write error in finalizing custom header");
668  }
669 
670  assert(status == eIO_Timeout);
671  if (body) {
672  size_t n = 0, m = info->body_size ? info->body_size : strlen(body);
673  int/*bool*/ newline = 0/*false*/;
674  if (!(info->mx_options & fSendMail_NoMxHeader) && m) {
675  if (!s_SockWrite(sock, MX_CRLF, sizeof(MX_CRLF)-1))
676  SENDMAIL_RETURN(24, "Write error in message body delimiter");
677  }
678  while (n < m) {
679  size_t k = 0;
680  if ((status = SOCK_Wait(sock, eIO_Read, &zero)) != eIO_Timeout)
681  break;
682  while (k < sizeof(buffer) - sizeof(MX_CRLF)) {
683  if (body[n] == '\n') {
684  memcpy(&buffer[k], MX_CRLF, sizeof(MX_CRLF)-1);
685  k += sizeof(MX_CRLF)-1;
686  newline = 1/*true*/;
687  } else {
688  if (body[n] != '\r' || (n+1 < m && body[n+1] != '\n')){
689  if (body[n] == '.' && (newline || !n)) {
690  buffer[k++] = '.';
691  buffer[k++] = '.';
692  } else
693  buffer[k++] = body[n];
694  }
695  newline = 0/*false*/;
696  }
697  if (++n >= m)
698  break;
699  }
700  buffer[k] = '\0'/*just in case*/;
701  if (!s_SockWrite(sock, buffer, k))
702  SENDMAIL_RETURN(25, "Write error while sending message body");
703  }
704  if (n < m) {
705  const char* error = "Message body write error";
706  assert(status == eIO_Timeout);
707  if (status != eIO_Success)
708  strcpy(buffer, IO_StatusStr(status));
709  else if (SENDMAIL_READ_RESPONSE(0, buffer))
710  error = "Spurious response while writing message body";
712  }
713  if ((!newline && m && !s_SockWrite(sock,MX_CRLF,sizeof(MX_CRLF)-1))
714  || !s_SockWrite(sock, "." MX_CRLF, sizeof(MX_CRLF))) {
715  SENDMAIL_RETURN(27, "Write error while finalizing message body");
716  }
717  } else if (!s_SockWrite(sock, "." MX_CRLF, sizeof(MX_CRLF)))
718  SENDMAIL_RETURN(28, "Write error while finalizing message");
719 
720  (void) SOCK_SetCork(sock, 0/*false*/);
721 
722  if (!SENDMAIL_READ_RESPONSE(250, buffer))
723  SENDMAIL_RETURN2(29, "Protocol error in sending message", buffer);
724 
725  if (!s_SockWrite(sock, "QUIT" MX_CRLF, 4 + sizeof(MX_CRLF)-1))
726  SENDMAIL_RETURN(30, "Write error in QUIT command");
727  if (!SENDMAIL_READ_RESPONSE(221, buffer))
728  SENDMAIL_RETURN2(31, "Protocol error in QUIT command", buffer);
729 
730  SOCK_Close(sock);
731  return 0;
732 }
733 
734 #undef SENDMAIL_READ_RESPONSE
735 #undef SENDMAIL_SENDRCPT
736 #undef SENDMAIL_RETURN2
737 #undef SENDMAIL_RETURN
static char tmp[3200]
Definition: utf8.c:42
const char * CORE_SendMailEx(const char *to, const char *subject, const char *body, const SSendMailInfo *uinfo)
Send a message as in CORE_SendMail() but by explicitly specifying all additional parameters of the me...
SSendMailInfo * SendMailInfo_InitEx(SSendMailInfo *info, const char *from, ECORE_Username user)
Initialize SSendMailInfo structure, setting: 'cc', 'bcc', 'header' to NULL (means no recipients/addit...
#define SendMailInfo_Init(i)
const char * CORE_SendMail(const char *to, const char *subject, const char *body)
Send a simple message to recipient(s) defined in 'to', and having: 'subject', which may be empty (bot...
@ fSendMail_NoMxHeader
Don't add standard mail header, just use what user provided.
Definition: ncbi_sendmail.h:57
@ fSendMail_ExtendedErrInfo
Return extended error info that must be free()'d by caller.
Definition: ncbi_sendmail.h:67
@ fSendMail_LogOn
see: fSOCK_LogOn
Definition: ncbi_sendmail.h:55
@ fSendMail_StripNonFQDNHost
Strip host part off the "from" field if it does not look like an FQDN (i.e.
Definition: ncbi_sendmail.h:61
@ fSendMail_Old822Headers
Form "Date:" and "From:" hdrs (usually they are defaulted)
Definition: ncbi_sendmail.h:59
EIO_Status SOCK_SetTimeout(SOCK sock, EIO_Event event, const STimeout *timeout)
Specify timeout for the connection I/O (see SOCK_[Read|Write|Close]()).
Definition: ncbi_socket.c:7195
void SOCK_DisableOSSendDelay(SOCK sock, int on_off)
Control OS-defined send strategy by disabling/enabling the TCP Nagle algorithm (which is on by defaul...
Definition: ncbi_socket.c:7816
EIO_Status SOCK_Close(SOCK sock)
Close the SOCK handle, and destroy all relevant internal data.
Definition: ncbi_socket.c:6852
EIO_Status SOCK_Read(SOCK sock, void *buf, size_t size, size_t *n_read, EIO_ReadMethod how)
Read/peek up to "size" bytes from "sock" to a buffer pointed to by "buf".
Definition: ncbi_socket.c:7271
int SOCK_gethostname(char *name, size_t namelen)
Same as SOCK_gethostnameEx(,,<current API data logging>)
Definition: ncbi_socket.c:8775
void SOCK_SetCork(SOCK sock, int on_off)
Control OS-defined send strategy by disabling/enabling the TCP layer to send incomplete network frame...
Definition: ncbi_socket.c:7774
EIO_Status SOCK_Wait(SOCK sock, EIO_Event event, const STimeout *timeout)
Block on the socket until either the specified "event" is available or "timeout" expires (if "timeout...
Definition: ncbi_socket.c:6954
EIO_Status SOCK_CreateEx(const char *host, unsigned short port, const STimeout *timeout, SOCK *sock, const void *data, size_t size, TSOCK_Flags flags)
[CLIENT-side] Connect client to another(server-side, listening) socket (socket() + connect() [+ selec...
Definition: ncbi_socket.c:6649
EIO_Status SOCK_Write(SOCK sock, const void *data, size_t size, size_t *n_written, EIO_WriteMethod how)
Write "size" bytes of "data" to "sock".
Definition: ncbi_socket.c:7487
const char * SOCK_gethostbyaddr(unsigned int addr, char *name, size_t namelen)
Same as SOCK_gethostbyaddrEx(,,<current API data logging>)
Definition: ncbi_socket.c:8814
unsigned int TSOCK_Flags
bitwise "OR" of ESOCK_Flags
Definition: ncbi_socket.h:503
@ fSOCK_LogDefault
Definition: ncbi_socket.h:491
@ fSOCK_LogOn
Definition: ncbi_socket.h:490
ECORE_Username
Select which username is the most preferable to obtain from the system.
Definition: ncbi_util.h:458
EIO_Status
I/O status.
Definition: ncbi_core.h:132
unsigned int usec
microseconds (modulo 1,000,000)
Definition: ncbi_types.h:78
const char * IO_StatusStr(EIO_Status status)
Get the text form of an enum status value.
Definition: ncbi_core.c:56
const char * CORE_GetUsernameEx(char *buf, size_t bufsize, ECORE_Username username)
Obtain and store in the buffer provided, the best (as possible) user name that matches the requested ...
Definition: ncbi_util.c:805
unsigned int sec
seconds
Definition: ncbi_types.h:77
@ eLOG_Warning
Definition: ncbi_core.h:296
@ eIO_Timeout
timeout expired before any I/O succeeded
Definition: ncbi_core.h:134
@ eIO_Success
everything is fine, no error occurred
Definition: ncbi_core.h:133
@ eIO_ReadPlain
read readily available data only, wait if none
Definition: ncbi_core.h:90
@ eIO_ReadPersist
read exactly as much as requested, w/waits
Definition: ncbi_core.h:91
@ eIO_WritePersist
write exactly as much as specified, w/waits
Definition: ncbi_core.h:102
@ eIO_ReadWrite
eIO_Read | eIO_Write (also, eCONN_OnFlush)
Definition: ncbi_core.h:122
@ eIO_Close
also serves as an error indicator in SOCK_Poll
Definition: ncbi_core.h:123
@ eIO_Read
read
Definition: ncbi_core.h:120
unsigned int
A callback function used to compare two keys in a database.
Definition: types.hpp:1210
char * buf
yy_size_t n
int len
static MDB_envinfo info
Definition: mdb_load.c:37
const struct ncbi::grid::netcache::search::fields::SIZE size
double NCBI_simple_atof(const char *s, char **t)
Locale-independent ASCII-to-double conversion of string "s".
char * strncpy0(char *s1, const char *s2, size_t n)
Copy not more than "n" characters from string "s2" into "s1", and return the result,...
const char * ConnNetInfo_GetValueInternal(const char *service, const char *param, char *value, size_t value_size, const char *def_value)
#define CONN_PORT_SMTP
Definition: ncbi_connutil.h:94
#define CONN_HOST_LEN
#define CORE_LOGF_X(subcode, level, fmt_args)
Definition: ncbi_priv.h:150
#define CORE_LOCK_WRITE
Definition: ncbi_priv.h:269
#define CORE_UNLOCK
Definition: ncbi_priv.h:273
#define CORE_LOG_X(subcode, level, message)
Definition: ncbi_priv.h:146
#define CORE_LOCK_READ
Definition: ncbi_priv.h:271
#define SMTP_RESPERR
Definition: ncbi_sendmail.c:49
static char s_MxHost[CONN_HOST_LEN+1]
static void x_Sendmail_InitEnv(void)
static int s_SockReadResponse(SOCK sock, int code, int alt_code, char *buf, size_t buf_size, int savecode)
static int s_SockRead(SOCK sock, char *response, size_t max_response_len, int savecode)
Definition: ncbi_sendmail.c:62
#define SMTP_BADCODE
Definition: ncbi_sendmail.c:51
#define SENDMAIL_READ_RESPONSE(code, buffer)
#define SENDMAIL_RETURN2(subcode, reason, explanation)
static size_t s_FromSize(const SSendMailInfo *info)
#define MX_CRLF
Definition: ncbi_sendmail.c:45
static int s_SockWrite(SOCK sock, const char *buf, size_t len)
#define MX_SENDMAIL_MAGIC
Definition: ncbi_sendmail.c:44
#define SMTP_NOCODE
Definition: ncbi_sendmail.c:50
#define SMTP_READTMO
Definition: ncbi_sendmail.c:48
static const char * s_SendRcpt(SOCK sock, const char *to, char buf[], size_t buf_size, const char what[], const char write_error[], const char proto_error[], const SSendMailInfo *info)
#define SMTP_BADRESP
Definition: ncbi_sendmail.c:52
static void s_MakeFrom(char *buf, size_t size, const char *from, ECORE_Username user)
#define SENDMAIL_RETURN(subcode, reason)
#define SMTP_READERR
Definition: ncbi_sendmail.c:47
static STimeout s_MxTimeout
#define SENDMAIL_SENDRCPT(what, list, buffer)
static unsigned short s_MxPort
#define NCBI_CXX_TOOLKIT
Definition: ncbiconf_msvc.h:16
int isspace(Uchar c)
Definition: ncbictype.hpp:69
static uint8_t * buffer
Definition: pcre2test.c:1016
#define assert(x)
Definition: srv_diag.hpp:58
Define optional parameters for communication with sendmail.
Definition: ncbi_sendmail.h:75
Timeout structure.
Definition: ncbi_types.h:76
static string subject
Definition: inftrees.h:24
done
Definition: token1.c:1
Modified on Fri Sep 20 14:58:05 2024 by modify_doxy.py rev. 669887