Want to win a PS4? Go Premium and enter to win our High-Tech Treats giveaway. Enter to Win


Configuring sendmail to archive mail

Posted on 2000-03-13
Medium Priority
Last Modified: 2013-12-15
I'd like to have our mail server keep an archive copy of all email traffic.

I've got incomming mail covered (sending a copy to a dummy account using /etc/aliases), but I'm not sure how to keep a copy of outgoing mail.
Question by:sprinkmeier
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
LVL 40

Accepted Solution

jlevie earned 800 total points
ID: 2612073
There are two ways you can accomplish that.

The "-X some-log-file" argument to sendmail is the cheap and dirty way. The disadvantage is that it logs everything, the raw sendmail interactions, local delivery, the works. The data is there, but the format isn't very usable as as a message may be split up in the log file, interspersed with parts of other messages (it's a real time log).

The second and better way is to use Axel Reinhold's sendmail logging mod. It simply logs, in normal unix mailbox format, each message received by sendmail, both from external mailservers and from internal clients. The only disadvantage to it is that you do have to incorporate it into the the sendmail source (get it the 8.9.3 sources from www.sendmail.org) and compile your own sendmail, but it works really, really well. (I can probably help with compiling sendmail also)

On the chance that you are willing to use the latter method, I've put the mod below. Instructions for adding it to sendmail and using it are in the comments at the beginning. Paste it into a file named "logall.c" and follow its instructions.

---snip, snip---begin logall.c---
/* ------------------------------------------------------------
 *      "LogAll" feature to log any message through this MTA
 *      (c) /ARX cleanware 1998
#define LOGALLHEADER      "X-Logged"
#define LOGALLLEVEL      12

      char logallrcsid[] =
            "$Id: logall.c,v 1.11 1998/09/19 10:12:04 root Exp root $";
 *      $Log: logall.c,v $
 *      Revision 1.11  1998/09/19 10:12:04  root
 *      published
 *      Revision 1.7  1998/09/17 07:12:49  root
 *      clobbered qtimestr
 *      ABSTRACT
 *      This routine logs every message in a mail folder
 *      before it is actually transmitted. The log is
 *      "envelope-oriented" which means it logs every message
 *      once - not for every recipient. You must also see
 *      the sendmail syslog for actual delivery. The log
 *      includes full message bodys.
 *      The logging code is compiled into the sendmail binary
 *      with the documented and often overlooked checkcompat()
 *      routine:
 *      see "Bryan Costales & Eric Allman: sendmail 2nd Edition
 *            §20 `The checkcompat() Cookbook' p.285ff"
 *      To check whether a message was already logged it adds
 *      a new header "X-Logged" to the message with the logging
 *      host, the envelope id and queued date/time as values.
 *      As long as this info doesn't change the message is no
 *      more logged.
 *      The logfile is configuerd in a new macro:
 *      D{LogAll}path
 *      where path is the full pathname of the logfile, which
 *      should be mode 600. REMEMBER THIS IS A FULL BODY LOG.
 *      So be prepared for a huge file on site with much traffic.
 *      The file must exist and every mail is appended to it.
 *      The macro should be inserted into a m4-config-file for
 *      example like this:
 *            LOCAL_CONFIG
 *            D{LogAll}/var/log/mail.log
 *      This source fragment must be included into the source-file:
 *      .../sendmail-8.x.y/src/conf.c
 *      at the following position (with the full path name of its
 *      current location - here "/root/adm/mail/sendmail/logall.c"):
 *>>old            if (tTd(49, 1))
 *>>old                  printf("checkcompat(to=%s, from=%s)\n",            
 *>>old                        to->q_paddr, e->e_from.q_paddr);          
 *>>new            #include "/root/adm/mail/sendmail/logall.c"
 *>>old            # ifdef EXAMPLE_CODE
 *      the sendmail binary must be remaked and reinstalled at
 *      its proper position (normally /usr/sbin/sendmail).
 *      Since the logfile produced by LogAll is a standard Unix-mailfolder,
 *      the logged mails can be managed with every Unix mail-reader or MUA.
 *      The mails inside the log can be deleted, remailed, read with any
 *      mail user agent. If the logfile is the standard mail folder of a
 *      special "LogAll-user" this is especially easy and even "mail" can
 *      or a Windows-based IMAP-client can manage the logfile.
 *      LogAll is tested with sendmail-8.8.5 and sendmail-8.9.1
 *      under Linux 2.0.29.
 *      AUTHOR
 *      Axel Reinhold - arx@hof.baynet.de
 *      The software is provided "AS IS" without warranties of any kind,
 *      either expressed or implied, including, but not limited to the
 *      implied warranties of merchantability and fitness for a particular
 *      purpose. The entire risc of the software is with you. In no event
 *      we will be liable for any damages, including any lost profits,
 *      lost savings or other incidental damages arising out of the use
 *      or inability to use the software, even if we have been advised
 *      of the possibility of such damages, or for any claim by another party.
 *   ------------------------------------------------------------

      int mid;                  /* sendmail macro id                */
      char *logall = NULL;            /* log file path from macro LogAll  */
      char bfpath[512];            /* body file path                */
      char *hlogged = LOGALLHEADER;      /* header field                      */
      char hlogval[MAXLINE];            /* header value                      */
      char *hlogvalp;                  /* header value pointer                */
      char qtimestr[80];            /* queued time string from asctime  */
      FILE *lf, *bf;                  /* log and body files                */
      ADDRESS *a;                  /* address structure                */
      HDR *h;                        /* header structure                */
      int toflag = 0;                  /* to header flag                */
      int fromflag = 0;            /* from header flag                */
      long bfpos;                  /* remember file pos                */
      size_t b_read;                  /* bytes read counter                */
      unsigned char b_buf[512];      /* buffer for read/write body          */

      mid = macid("{LogAll}", NULL);      /* get our config macro                */
      logall = macvalue(mid, e);      /* to check whether and where to log*/

      /* Use logging if macro is set and we can open the logfile          */
      if ((logall!=NULL) && ((lf = fopen(logall, "a"))!=NULL)) {
          if (tTd(49, 1))            /* debug use of logging                */
            printf("checkcompat: LogAll=%s e_id=%s\n", logall, e->e_id);

          /* construct the "X-Logged" header field */
          strcpy(qtimestr, asctime(localtime(&e->e_ctime))); /* get time  */
          if (qtimestr[strlen(qtimestr)-1]=='\n')      /* remove trailing  */
                qtimestr[strlen(qtimestr)-1]='\0';      /* line-feed asctime*/
          sprintf(hlogval, "Logged by %s as %s at %s",
            MyHostName, e->e_id, qtimestr);            /* our header field */

          /* add the header if it doesn't already exist                */
          hlogvalp = hvalue(hlogged, e->e_header);      /* get actual field */
          if (hlogvalp==NULL) addheader(hlogged, hlogval, &e->e_header);

          /* Log the message if our header didn't exist or was not from us*/
          if ((hlogvalp==NULL) || (strcmp(hlogvalp, hlogval)!=0)) {

            /* lock the logfile exclusive                            */
            if (lockfile(fileno(lf), logall, NULL, LOCK_EX)) ;
            fseek(lf, 0, SEEK_END);      /* go the eof for appending message */

            /* syslog the usage of LogAll                            */
            if (LogLevel >= LOGALLLEVEL) sm_syslog(LOG_INFO, e->e_id,
                  "LogAll to %s at %s", logall, qtimestr);

            /* print the Unix From line to separate messages in log file*/
            fprintf(lf, "From %s %s\n", e->e_from.q_user, qtimestr);

            /* scan all headers                                  */
            for (h = e->e_header; h != NULL; h = h->h_link) {

                  /* check if our header is from us and recent          */
                  /* if not change the field value to our field          */
                  if (strcasecmp(h->h_field, hlogged)==0) {
                        if (hlogvalp!=NULL) {
                              h->h_value =
                               realloc(h->h_value, strlen(hlogval)+1);
                              strcpy(h->h_value, hlogval);
                        h->h_flags &= ~H_DEFAULT;      /* set flag */

                  /* log the header if appropiate                      */
                  if ((h->h_value!=NULL) && ((h->h_flags & H_RESENT)==0)) {
                        fprintf(lf, "%s: %s\n", h->h_field, h->h_value);

                  if (h->h_flags & H_RCPT) toflag = 1;      /* to-header*/

            if (!toflag) {      /* if no to-header construct from envelope  */
                  fprintf(lf, "To:");
                  for (a = e->e_sendqueue; a != NULL; a = a->q_next)
                        fprintf(lf, " %s", a->q_paddr);
                  fprintf(lf, "\n");
            fprintf(lf, "\n");      /* separate headers from body          */

            /* now add the body from temp file or queued data file          */
            sprintf(bfpath, "%s/df%s", QueueDir, e->e_id); /* body file */
            if (e->e_dfp != NULL) {      /* if body temp file is open use it */
                  bfpos = ftell(e->e_dfp);/* remember old position    */
                  rewind(e->e_dfp);      /* read from beginning          */
                  while ((b_read = fread(b_buf, 1L, sizeof(b_buf), e->e_dfp)) != 0)
                        fwrite(b_buf, 1L, b_read, lf);
                  fseek(e->e_dfp, bfpos, SEEK_SET); /* set old pos    */
            else if ((bf = fopen(bfpath, "r")) != NULL) { /* body file  */
                  while ((b_read = fread(b_buf, 1, sizeof(b_buf), bf)) != 0)
                        fwrite(b_buf, 1, b_read, lf);
            else      { /* no body could be opened                      */
                  if (tTd(49, 1))
                      printf("checkcompat: fopen(%s) failed\n", bfpath);
            fprintf(lf, "\n");

            if (lockfile(fileno(lf), logall, NULL, LOCK_UN)) ; /* unlock*/

/*   ------------------------------------------------------------
 *      LogAll End
 *   ------------------------------------------------------------

Author Comment

ID: 2625871
Thanks, worked exactly as required!

Featured Post

What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

I. Introduction There's an interesting discussion going on now in an Experts Exchange Group — Attachments with no extension (http://www.experts-exchange.com/discussions/210281/Attachments-with-no-extension.html). This reminded me of questions tha…
Fine Tune your automatic Updates for Ubuntu / Debian
Learn how to find files with the shell using the find and locate commands. Use locate to find a needle in a haystack.: With locate, check if the file still exists.: Use find to get the actual location of the file.:
Learn how to navigate the file tree with the shell. Use pwd to print the current working directory: Use ls to list a directory's contents: Use cd to change to a new directory: Use wildcards instead of typing out long directory names: Use ../ to move…
Suggested Courses

609 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question