libmsngr  1.1
 All Data Structures Files Functions Variables Enumerations Enumerator Macros Groups
msngr_mail.c
Go to the documentation of this file.
1 /*******************************************************************************
2 *
3 * COPYRIGHT (C) 2010 Battelle Memorial Institute. All Rights Reserved.
4 *
5 ********************************************************************************
6 *
7 * Author:
8 * name: Brian Ermold
9 * phone: (509) 375-2277
10 * email: brian.ermold@pnl.gov
11 *
12 ********************************************************************************
13 *
14 * REPOSITORY INFORMATION:
15 * $Revision: 6709 $
16 * $Author: ermold $
17 * $Date: 2011-05-16 23:28:15 +0000 (Mon, 16 May 2011) $
18 *
19 ********************************************************************************
20 *
21 * NOTE: DOXYGEN is used to generate documentation for this file.
22 *
23 *******************************************************************************/
24 
25 /** @file msngr_mail.c
26  * Mail Functions.
27  */
28 
29 #include <errno.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <sys/types.h>
33 #include <sys/uio.h>
34 #include <sys/wait.h>
35 #include <unistd.h>
36 
37 #include "messenger.h"
38 
39 /** Mail body growth size. */
40 #define MAIL_BODY_GROWTH_SIZE 1024
41 
42 /** Amount of allocated space to reserve at the end of the mail body. */
43 #define MAIL_BODY_RESERVED_SIZE 80
44 
45 /**
46  * @defgroup MAIL_MESSAGES Mail Messages
47  */
48 /*@{*/
49 
50 /*******************************************************************************
51  * Private Functions
52  */
53 /** @privatesection */
54 
55 /**
56  * PRIVATE: Full path to the sendmail program.
57  */
58 static char gSendMailPath[64];
59 
60 /*******************************************************************************
61  * Public Functions
62  */
63 /** @publicsection */
64 
65 /**
66  * Create a new mail message.
67  *
68  * The space pointed to by errstr should be large enough to
69  * hold MAX_MAIL_ERROR bytes. Any less than that and the error
70  * message could be truncated.
71  *
72  * If errlen is 0 then no error message is written to errstr
73  * and errstr can be NULL.
74  *
75  * @param from - who the message is from
76  * @param to - comma delimited list of recipients
77  * @param cc - comma delimited carbon copy list
78  * @param subject - message subject
79  * @param flags - control flags
80  * @param errlen - length of the error message buffer
81  * @param errstr - output: error message
82  *
83  * Control Flags:
84  *
85  * - MAIL_ADD_NEWLINE - Add a newline after every mail message added.
86  *
87  * @return
88  * - pointer to the new Mail message
89  * - NULL if:
90  * - the sendmail program could not be found
91  * - a memory allocation error occurred
92  */
94  const char *from,
95  const char *to,
96  const char *cc,
97  const char *subject,
98  int flags,
99  size_t errlen,
100  char *errstr)
101 {
102  Mail *mail;
103  int alloc_error;
104 
105  /* Find the sendmail program if it has not already been found */
106 
107  if (gSendMailPath[0] == '\0') {
108 
109  if (access("/usr/lib/sendmail", X_OK) == 0) {
110  strcpy(gSendMailPath, "/usr/lib/sendmail");
111  }
112  else {
113 
114  snprintf(errstr, errlen,
115  "Could not create mail message: '%s'\n"
116  " -> sendmail program not found\n", subject);
117 
118  return((Mail *)NULL);
119  }
120  }
121 
122  /* Create the Mail structure */
123 
124  mail = (Mail *)calloc(1, sizeof(Mail));
125 
126  if (!mail) {
127 
128  snprintf(errstr, errlen,
129  "Could not create mail message: '%s'\n"
130  " -> memory allocation error\n", subject);
131 
132  return((Mail *)NULL);
133  }
134 
135  alloc_error = 0;
136 
137  if (from) {
138 
139  mail->from = msngr_copy_string(from);
140  if (!mail->from) {
141  alloc_error = 1;
142  }
143  }
144 
145  if (to && !alloc_error) {
146 
147  mail->to = msngr_copy_string(to);
148  if (!mail->to) {
149  alloc_error = 1;
150  }
151  }
152 
153  if (cc && !alloc_error) {
154 
155  mail->cc = msngr_copy_string(cc);
156  if (!mail->cc) {
157  alloc_error = 1;
158  }
159  }
160 
161  if (subject && !alloc_error) {
162 
163  mail->subject = msngr_copy_string(subject);
164  if (!mail->subject) {
165  alloc_error = 1;
166  }
167  }
168 
169  if (!alloc_error) {
170 
171  mail->length = 0;
173 
174  mail->body = (char *)calloc(mail->nalloced, sizeof(char));
175  if (!mail->body) {
176  alloc_error = 1;
177  }
178  }
179 
180  if (!alloc_error) {
181 
182  mail->errstr = (char *)calloc(MAX_MAIL_ERROR, sizeof(char));
183  if (!mail->errstr) {
184  alloc_error = 1;
185  }
186  }
187 
188  if (alloc_error) {
189 
190  mail_destroy(mail);
191 
192  snprintf(errstr, errlen,
193  "Could not create mail message: '%s'\n"
194  " -> memory allocation error\n", subject);
195 
196  return((Mail *)NULL);
197  }
198 
199  mail->flags = flags;
200 
201  return(mail);
202 }
203 
204 /**
205  * Free all memory used by a Mail message.
206  *
207  * @param mail - pointer to the Mail message
208  */
209 void mail_destroy(Mail *mail)
210 {
211  if (mail) {
212 
213  if (mail->from) free(mail->from);
214  if (mail->to) free(mail->to);
215  if (mail->cc) free(mail->cc);
216  if (mail->subject) free(mail->subject);
217  if (mail->body) free(mail->body);
218  if (mail->errstr) free(mail->errstr);
219 
220  free(mail);
221  }
222 }
223 
224 /**
225  * Clear the last error message for a Mail message.
226  *
227  * @param mail - pointer to the Mail message
228  */
230 {
231  if (mail) {
232  mail->errstr[0] = '\0';
233  }
234 }
235 
236 /**
237  * Get the last error message for a Mail message.
238  *
239  * @param mail - pointer to the Mail message
240  *
241  * @return
242  * - the last error message that occurred
243  * - NULL if no errors have occurred
244  */
245 const char *mail_get_error(Mail *mail)
246 {
247  if (mail->errstr[0] == '\0') {
248  return((const char *)NULL);
249  }
250  else {
251  return((const char *)mail->errstr);
252  }
253 }
254 
255 /**
256  * Print a message to a mail message.
257  *
258  * This function will print a message to a mail body under the
259  * control of the format argument. Alternatively, an array of
260  * strings can be passed into this function by specifying
261  * MSNGR_MESSAGE_BLOCK for the format argument. In this case
262  * the next argument after format must be a pointer to a NULL
263  * terminted array of strings (char **).
264  *
265  * This function will print a message to a mail body
266  * under the control of the format argument.
267  *
268  * @param mail - pointer to the Mail message
269  * @param format - format string (see printf)
270  * @param ... - arguments for the format string
271  */
273  Mail *mail,
274  const char *format, ...)
275 {
276  va_list args;
277 
278  va_start(args, format);
279  mail_vprintf(mail, format, args);
280  va_end(args);
281 }
282 
283 /**
284  * Print a message to a mail message.
285  *
286  * This function will print a message to a mail body under the
287  * control of the format argument. Alternatively, an array of
288  * strings can be passed into this function by specifying
289  * MSNGR_MESSAGE_BLOCK for the format argument. In this case
290  * the first argument in the args list must be a pointer to
291  * a NULL terminted array of strings (char **).
292  *
293  * @param mail - pointer to the Mail message
294  * @param format - format string (see printf)
295  * @param args - arguments for the format string
296  */
298  Mail *mail,
299  const char *format,
300  va_list args)
301 {
302  int space_left;
303  char *msg_start;
304  char *msg_end;
305  int msg_length;
306  size_t min_size;
307  size_t new_size;
308  char *new_body;
309  char **msg_block;
310  va_list args_copy;
311  int i;
312 
313  /* Calculate the space left in the buffer, the start position
314  * in the buffer, and print the message to the buffer if there
315  * is enough space left. */
316 
317  space_left = mail->nalloced - mail->length - MAIL_BODY_RESERVED_SIZE;
318  msg_start = mail->body + mail->length;
319 
320  if (strcmp(format, "MSNGR_MESSAGE_BLOCK") == 0) {
321 
322  va_copy(args_copy, args);
323  msg_block = va_arg(args_copy, char **);
324  va_end(args_copy);
325 
326  msg_length = 0;
327 
328  for (i = 0; msg_block[i] != (char *)NULL; i++) {
329  msg_length += strlen(msg_block[i]);
330  }
331  }
332  else {
333  msg_block = (char **)NULL;
334  msg_length = msngr_vsnprintf(msg_start, space_left, format, args);
335  }
336 
337  /* Check if there was enough space left in the buffer */
338 
339  if (msg_length >= space_left) {
340 
341  /* Increase the size of the mail buffer */
342 
343  min_size = mail->length + msg_length + MAIL_BODY_RESERVED_SIZE;
344  new_size = mail->nalloced + MAIL_BODY_GROWTH_SIZE;
345 
346  while (new_size < min_size) {
347  new_size += MAIL_BODY_GROWTH_SIZE;
348  }
349 
350  new_body = (char *)realloc(mail->body, new_size * sizeof(char));
351 
352  /* Check if there was enough memory to resize the buffer */
353 
354  if (new_body) {
355 
356  /* Print message to the resized buffer */
357 
358  mail->nalloced = new_size;
359  mail->body = new_body;
360 
361  msg_start = mail->body + mail->length;
362 
363  if (!msg_block) {
364  msg_length = msngr_vsprintf(msg_start, format, args);
365  }
366  }
367  else { /* not enough memory to resize buffer */
368 
369  /* Flush the mail buffer */
370 
371  sprintf(msg_start,
372  "Could not increase mail buffer size, "
373  "sending mail and flushing buffer.\n\n");
374 
375  mail_send(mail);
376 
377  /* Recalculate the space left in the buffer
378  * and the start position in the buffer */
379 
380  space_left = mail->nalloced
381  - mail->length
383 
384  msg_start = mail->body + mail->length;
385 
386  if (msg_length < space_left) {
387  if (!msg_block) {
388  msg_length = msngr_vsprintf(msg_start, format, args);
389  }
390  }
391  else { /* mail message is lost */
392  msg_length = 0;
393  }
394  }
395  }
396 
397  if (msg_length && msg_block) {
398  for (i = 0; msg_block[i] != (char *)NULL; i++) {
399  msg_start += sprintf(msg_start, msg_block[i]);
400  }
401  }
402 
403  mail->length += msg_length;
404 
405  /* Make sure the message is terminated with a new line character */
406 
407  msg_end = mail->body + mail->length - 1;
408 
409  if (*msg_end != '\n') {
410  *(++msg_end) = '\n';
411  *(++msg_end) = '\0';
412  mail->length++;
413  }
414 
415  /* Check if we need to add a newline to the mail body */
416 
417  if (mail->flags & MAIL_ADD_NEWLINE) {
418  mail->body[mail->length] = '\n';
419  mail->length++;
420  mail->body[mail->length] = '\0';
421  }
422 }
423 
424 /**
425  * Send a mail message.
426  *
427  * If the mail message does not have a recipient or mail body,
428  * this function will do nothing and return successfully.
429  *
430  * @param mail - pointer to the Mail message
431  *
432  * @return
433  * - 1 if successful
434  * - 0 if an error occurred
435  *
436  * @see mail_get_error()
437  */
438 int mail_send(Mail *mail)
439 {
440  struct iovec iov[32];
441  int iovcnt;
442  int mail_pipe[2];
443  pid_t mail_pid;
444  int mail_exit_value;
445  int retval;
446  int i;
447 
448  /* Make sure we have a recipient and something to send */
449 
450  if (!mail->to || !mail->length) {
451  return(1);
452  }
453 
454  /* Create the iov array containing the complete mail message */
455 
456  iovcnt = 0;
457 
458  iov[iovcnt++].iov_base = "To: ";
459  iov[iovcnt++].iov_base = mail->to;
460  iov[iovcnt++].iov_base = "\n";
461 
462  if (mail->cc) {
463  iov[iovcnt++].iov_base = "Cc: ";
464  iov[iovcnt++].iov_base = mail->cc;
465  iov[iovcnt++].iov_base = "\n";
466  }
467 
468  if (mail->subject) {
469  iov[iovcnt++].iov_base = "Subject: ";
470  iov[iovcnt++].iov_base = mail->subject;
471  iov[iovcnt++].iov_base = "\n";
472  iov[iovcnt++].iov_base = "\n";
473  }
474 
475  iov[iovcnt++].iov_base = mail->body;
476 
477  for (i = 0; i < iovcnt; i++) {
478  iov[i].iov_len = strlen(iov[i].iov_base);
479  }
480 
481  /* Create the pipe */
482 
483  if (pipe(mail_pipe) == -1) {
484 
485  snprintf(mail->errstr, MAX_MAIL_ERROR,
486  "Could not create pipe to send mail message: '%s'\n"
487  " -> %s\n", mail->subject, strerror(errno));
488 
489  mail->length = 0;
490  mail->body[0] = '\0';
491 
492  return(0);
493  }
494 
495  /* Create the fork */
496 
497  mail_pid = fork();
498 
499  if (mail_pid == (pid_t)-1) {
500 
501  snprintf(mail->errstr, MAX_MAIL_ERROR,
502  "Could not create fork to send mail message: '%s'\n"
503  " -> %s\n", mail->subject, strerror(errno));
504 
505  mail->length = 0;
506  mail->body[0] = '\0';
507 
508  return(0);
509  }
510 
511  /* Child Process */
512 
513  if (mail_pid == 0) {
514 
515  dup2(mail_pipe[0], STDIN_FILENO);
516  close(mail_pipe[0]);
517  close(mail_pipe[1]);
518 
519  if (mail->from) {
520  execl(gSendMailPath, gSendMailPath,
521  "-f", mail->from, "-t", mail->to, NULL);
522  }
523  else {
524  execl(gSendMailPath, gSendMailPath,
525  "-t", mail->to, NULL);
526  }
527 
528  exit(errno);
529  }
530 
531  /* Parent Process */
532 
533  close(mail_pipe[0]);
534 
535  retval = 1;
536 
537  if (writev(mail_pipe[1], iov, iovcnt) == -1) {
538 
539  snprintf(mail->errstr, MAX_MAIL_ERROR,
540  "Could not write mail message: '%s'\n"
541  " -> %s\n", mail->subject, strerror(errno));
542 
543  retval = 0;
544  }
545 
546  /* Cleanup and return */
547 
548  close(mail_pipe[1]);
549  wait(&mail_exit_value);
550 
551  if (mail_exit_value) {
552 
553  int exit_value = mail_exit_value >> 8; /* upper 8 bits */
554  int signal_number = mail_exit_value & 127; /* lower 7 bits */
555  int core_dumped = mail_exit_value & 128; /* bit 8 */
556 
557  if (core_dumped) {
558 
559  snprintf(mail->errstr, MAX_MAIL_ERROR,
560  "Could not execute sendmail command: '%s'\n"
561  " -> core dumped with signal #%d\n",
562  gSendMailPath, signal_number);
563  }
564  else if (signal_number) {
565 
566  snprintf(mail->errstr, MAX_MAIL_ERROR,
567  "Could not execute sendmail command: '%s'\n"
568  " -> exited with signal #%d\n",
569  gSendMailPath, signal_number);
570  }
571  else {
572 
573  snprintf(mail->errstr, MAX_MAIL_ERROR,
574  "Could not execute sendmail command: '%s'\n"
575  " -> %s\n",
576  gSendMailPath, strerror(exit_value));
577  }
578 
579  retval = 0;
580  }
581 
582  mail->length = 0;
583  mail->body[0] = '\0';
584 
585  return(retval);
586 }
587 
588 /**
589  * Set control flags for a mail message.
590  *
591  * @param mail - pointer to the Mail message
592  * @param flags - flags to set
593  *
594  * Control Flags:
595  *
596  * - MAIL_ADD_NEWLINE - Add an extra newline character after every
597  * message added with dsmail_append().
598  */
599 void mail_set_flags(Mail *mail, int flags)
600 {
601  mail->flags |= flags;
602 }
603 
604 /**
605  * Unset control flags for a mail message.
606  *
607  * @param mail - pointer to the Mail message
608  * @param flags - flags to unset
609  *
610  * @see mail_set_flags()
611  */
612 void mail_unset_flags(Mail *mail, int flags)
613 {
614  mail->flags &= (0xffff ^ flags);
615 }
616 
617 /*@}*/