Sun Java System Messaging Server 6 2005Q4 MTA Developer's Reference

Intermediate Channel Example

The sample program in this section, in Example 4–3, converts the body of each queued message and then re-enqueues the converted messages back to the MTA. The conversion process involves applying the “rot 13” encoding used by some news readers to encode potentially offensive message content.

To configure the MTA to run this channel, see Running Your Enqueue and Dequeue Programs. Also refer to Preventing Mail Loops when Re-enqueuing Mail, which discusses configuring special rewrite rules for programs re-enqueuing dequeued email.

Some lines of code in this example are immediately preceded by a comment of the format:

/* See explanatory comment N */

where N is a number.

The numbers are links to some corresponding explanatory text found in Explanatory Text for Numbered Comments in the Intermediate Channel Example.


Example 4–3 Intermediate Channel Example


/* intermediate_channel.c
 * A channel program that re-enqueues queued messages after first
 * transforming their content with the "rot13" transformation.
 */
#include <stdio.h>
#include <stdlib.h>
#include "mtasdk.h"

typedef struct {
     size_t  maxlen;
     char   *buf;
} rot13_buf_t;

static mta_dq_process_done_t process_done;
static mta_dq_process_message_t process_message;
static char rot13(char c);
static const char *rot13str(rot13_buf_t **dst, const char *src,
                            size_t srclen);

int main()
{
     int ires;

     /*
      * Initialize the MTA SDK
      */
     if ((ires = mtaInit(0)))
     {
         mtaLog(mtaInit() returned %d; %s\n, ires, 
                mtaStrError(ires, 0));
         return(1);
     }

     /*
      *  Start the dequeue loop
      *  See explanatory comment 1
      */
     ires = mtaDequeueStart(NULL, process_message, 
                            process_done, 0); 

      /*
       *  Check the return status
       * See explanatory comment 2
       */
      if (!ires)
           /*
            *  Success
            */
           return(0);
      /*
       *  Produce an error message
       * See explanatory comment 3 */
       */
      mtaLog("mtaDequeueStart() returned %d; %s", ires,
              mtaStrError(ires, 0));

      /*
       *  And exit with an error
       */
      return(1);
}

/* 
 *  process_done -- Clean up the private context my_ctx_2 used by
 *                  process_message.
 * See explanatory comment 4
 */
static void process_done(void *my_ctx_2, void *my_ctx_1)
{
     rot13_buf_t *rbuf;

     if (!my_ctx_2)
          return;
     rbuf = (rot13_buf_t *)my_ctx_2;
     if (rbuf->buf)
          free(rbuf->buf);
     free(rbuf);
}


/* 
 *  process_message -- Process a single message by re-enqueuing but 
 *                     with its message body converted to the rot13 
 *                     set. The private my_ctx_1 context is not 
 *                     used. The private my_ctx_2 context is used 
 *                     for a rot13 translation context.
 * See explanatory comment 5
 */

static int process_message(void **my_ctx_2, void *my_ctx_1,
                           mta_dq_t *dq,
{
     size_t len;
     const char *line, *to;
     int in_header;
     mta_nq_t *nq;

     /*
      *  Start a message enqueue
      */
     nq = NULL;
     /* See explanatory comment 6 */
     if (mtaEnqueueStart(&nq, env_from, env_from_len, 
          MTA_DQ_CONTEXT, dq, 0)) 
             goto(defer);

     /*
      *  Process the envelope recipient list
      *  See explanatory comment 7 */
      */
     while (!mtaDequeueRecipientNext(dq, &to, &len, 0))
          /* See explanatory comment 7 */
          if (mtaEnqueueTo(nq, to, len, MTA_DQ_CONTEXT, dq, 
              MTA_ENV_TO, 0) ||
              /* See explanatory comment 8 */
              mtaDequeueRecipientDisposition(dq, to, len, 
                                            MTA_DISP_DELIVERED,0))
               /* See explanatory comment 9 */
               goto defer; 
      if (mta_errno != MTA_EOF)
           goto defer;

      /* 
       *  First, get the messages header and write it
       *  unchanged to the new message being enqueued.
       * See explanatory comment 10
       */
      in_header = 1;
      while (in_header && !mtaDequeueLineNext(dq, &line, &len))
      {
           if (mtaEnqueueWriteLine(nq, line, len, 0))
                goto defer;
           if (!len)
                in_header = 0;
      }

      /*
       *  Determine why we exited the while loop
       */
      if (in_header)
      {
          /*
           *  We exited before seeing the body of the message
           * See explanatory comment 12
           */
          if (mta_errno == MTA_EOF) 
               /*
                *  Message read completely: it must have no body
                */
               goto done;
          else
               /*
                *  Error condition of some sort
                */
               goto defer;
       }

       /* 
        *  Now rot13 the body of the message
        * See explanatory comment 13
        */
       while (!mtaDequeueLineNext(dq, &line, &len))
             if (mtaEnqueueWriteLine(nq,
                             rot13str((rot13_buf_t **)my_ctx_2, 
                             line, len), len, 0))
                  goto defer;

       /*
        * If mta_errno == MTA_EOF, then we exited the loop 
        * normally; otherwise, theres been an error of some sort
        */
       if (mta_errno != MTA_EOF)
            goto defer;

       /*
        *  All done, enqueue the new message
        * See explanatory comment 14
        */
  done:
       if (!mtaEnqueueFinish(nq, 0) && 
           !mtaDequeueMessageFinish(dq, 0))
            return(0);
       /*
        *  Fall through to defer the message
        */
       nq = NULL;

       /*
        *  A processing error of some sort has occurred: defer the
        *  message for a later delivery attempt
        * See explanatory comment 15
        */
  defer: 
       mtaDequeueMessageFinish(dq, MTA_ABORT, 0);
       if (nq)
            mtaEnqueueFinish(nq, MTA_ABORT, 0);
       return(MTA_NO);
}

/* 
 *  rot13 -- an implmentation of the rotate-by-13 translation
 * See explanatory comment 16
 */
static char rot13(char c)
{
     if (A <= c && c <= Z) 
        return (((c - A + 13) % 26) + A);
     else if (a <= c && c <= z)
        return (((c - a + 13) % 26) + a);
     else return (c);
}

/* 
 *  rot13str -- Perform a rot13 translation on a string of text
 *  See explanatory comment 17
 */
static const char *rot13str(rot13_buf_t **dst, const char *src, 
                            size_t srclen)
{
     size_t i;
     char *ptr;
     rot13_buf_t *rbuf = *dst;

     /*
      *  First call?  If so, then allocate a rot13_buf_t structure
      */
     if (!rbuf)
     {
          rbuf = calloc(1, sizeof(rot13_buf_t));
          if (!rbuf)
               return(NULL);
          *dst = rbuf;
     }

     /*
      *  Need a larger buffer? 
      *  If so, then increase the length of rbuf->buf
      */
     if (rbuf->maxlen < srclen || !rbuf->buf)
     {
          size_t l;
          char *tmp;
          /* Round size up to the nearest 2k */
          l = 2048 * (int)((srclen + 2047) / 2048);
          tmp = (char *)malloc(l);
          if (!tmp)
               return(NULL);
          if (rbuf->buf)
               free(rbuf->buf);
          rbuf->buf    = tmp;
          rbuf->maxlen = l;
      }
      /*
       *  Now rot13 our input
       */
      ptr = rbuf->buf;
      for (i = 0; i < srclen; i++)
           *ptr++ = rot13(*src++);

      /*
       *  All done
       */
      return(rbuf->buf);
}

Explanatory Text for Numbered Comments in the Intermediate Channel Example

  1. The dequeue processing is initiated by calling mtaDequeueStart(). In this example, no global context is used; hence, the first call argument to mtaDequeueStart() is NULL.

  2. If the call to mtaDequeueStart() succeeds, then the program exits normally.

  3. If the call to mtaDequeueStart() fails, a diagnostic error message is displayed and the program exits with an error status.

  4. Each dequeue thread calls process_done() as it exits. The intent is to allow the program to clean up and destroy any per-thread contexts created by the process_message() routine. In this case, the buffer used by rot13str() is deallocated.

  5. The mtaDequeueStart() routine calls process_message() once for each queued message to be processed. On the first call by a dequeue thread, the memory pointed at by my_ctx_2 is NULL.

  6. A message enqueue starts. The dequeue context, dq, is provided so that per-message envelope fields can be carried over to the new message from the message being dequeued.

  7. Each envelope recipient address is obtained, one at a time, with mtaDequeueRecipientNext(). When there are no more recipient addresses to obtain, mtaDequeueRecipientNext() returns the status MTA_EOF.

  8. Each envelope recipient address is added to the recipient list for the new message being enqueued. The MTA_ENV_TO option for mtaEnqueueTo() is specified so that the address is to be added to the new message’s envelope only. It should not also be added to the message’s RFC 822 header. The new message’s header will be a copy of the header of the message being dequeued. This copy is performed at the code location marked by comment 12.

  9. Each recipient is marked as delivered with mtaDequeueRecipientDisposition().

  10. In the event of an error returned from either mtaEnqueueTo() or mtaDequeueRecipientDisposition(), or an unexpected error return from mtaDequeueRecipientNext(), the ongoing enqueue is cancelled and the processing of the current message is deferred.

  11. Each line of the current message is read and then copied to the new message being enqueued. This copying continues until a blank line is read from the current message. (A blank line signifies the end of the RFC 822 message header and the start of the RFC 822 message content.)

  12. The code here needs to determine why it exited the read loop: because of an error, or because the transition from the message’s header to body was detected.

  13. The remainder of the current message is read line by line and copied to the new message being enqueued. However, the line enqueued is first transformed using the “rot13” transformation. The per-thread context my_ctx_2 is used to hold an output buffer used by the rot13str() routine.

  14. The enqueue of the new message is finished. If that step succeeds, then the message being dequeued is removed from the MTA queues.

  15. In the event of an error, the new message enqueue is cancelled and the current message left in the queues for later processing.

  16. The rot13 character transformation.

  17. A routine that applies the rot13 transformation to a character string.

Sample Input Message for the Intermediate Channel Example

The example that follows is a sample input message from the queue to be processed by the program found in Example 4–3.


Received: from frodo.west.siroe.com by frodo.west.siroe.com
 (Sun Java System Messaging Server 6 2004Q2(built Mar 24 2004))id
<0HCH00301E6GO700@frodo.west.siroe.com\> for sue@sesta.com; Fri,
 28 Mar 2003 14:51:52 -0800 (PST)
Date: Fri, 28 Mar 2003 14:51:52 -0800 (PST)
From: root@frodo.west.siroe.com
Subject: Testing
To: sue@sesta.com
Message-id: <0HCH00303E6GO700@frodo.west.siroe.com\>
MIME-version: 1.0

This is a test message.

Output from the Intermediate Channel Example

This example shows the output generated by the dequeue and re-enqueue program (Example 4–3).


Received: from sesta.com by frodo.west.siroe.com
 (Sun Java System Messaging Server 6 2004Q2 (built Mar 24 2003))id
<0HCH00301E7DOH00@frodo.west.wiroe.com\> for sue@sesta.com; Fri,
 28 Mar 2003 14:51:58 -0800 (PST)
Received: from frodo.west.siroe.com by frodo.west.siroe.com
 (Sun Java System Messaging Server 6 2004Q2 (built Mar 24 2003))id
<0HCH00301E7DOH00@frodo.west.wiroe.com\> for sue@sesta.com; Fri,
 28 Mar 2003 14:51:52 -0800 (PST)
Date: Fri, 28 Mar 2003 14:51:52 -0800 (PST)
From: root@frodo.west.siroe.com
Subject: Testing
To: sue@sesta.com
Message-id: <0HCH00303E6GO700@frodo.west.siroe.com\>
MIME-version: 1.0

Guvf vf n grfg zrffntr.