Expansions: add operators base32, base32d
[users/jgh/exim.git] / test / src / server.c
index f35d80ba2e308df759329338f785a42b59e9cbfd..b2f1bc754d95163d133f124093f276bf5cf55638 100644 (file)
@@ -1,5 +1,3 @@
-/* $Cambridge: exim/test/src/server.c,v 1.1 2006/02/06 16:24:05 ph10 Exp $ */
-
 /* A little hacked up program that listens on a given port and allows a script
 to play the part of a remote MTA for testing purposes. This scripted version is
 hacked from my original interactive version. A further hack allows it to listen
 /* A little hacked up program that listens on a given port and allows a script
 to play the part of a remote MTA for testing purposes. This scripted version is
 hacked from my original interactive version. A further hack allows it to listen
@@ -45,16 +43,21 @@ on all interfaces, unless the option -noipv6 is given. */
 #include <utime.h>
 
 #ifdef AF_INET6
 #include <utime.h>
 
 #ifdef AF_INET6
-#define HAVE_IPV6 1
+# define HAVE_IPV6 1
 #endif
 
 #ifndef S_ADDR_TYPE
 #endif
 
 #ifndef S_ADDR_TYPE
-#define S_ADDR_TYPE u_long
+# define S_ADDR_TYPE u_long
+#endif
+
+#ifndef CS
+# define CS (char *)
 #endif
 
 
 typedef struct line {
   struct line *next;
 #endif
 
 
 typedef struct line {
   struct line *next;
+  unsigned len;
   char line[1];
 } line;
 
   char line[1];
 } line;
 
@@ -121,6 +124,25 @@ return buffer;
 }
 
 
 }
 
 
+
+static void
+printit(char * s, int n)
+{
+while(n--)
+  {
+  unsigned char c = *s++;
+  if (c == '\\')
+    printf("\\\\");
+  else if (c >= ' ' && c <= '~')       /* assumes ascii */
+    putchar(c);
+  else
+    printf("\\x%02x", c);
+  }
+putchar('\n');
+}
+
+
+
 /*************************************************
 *                 Main Program                   *
 *************************************************/
 /*************************************************
 *                 Main Program                   *
 *************************************************/
@@ -141,6 +163,7 @@ int connection_count = 1;
 int count;
 int on = 1;
 int timeout = 5;
 int count;
 int on = 1;
 int timeout = 5;
+int initial_pause = 0;
 int use_ipv4 = 1;
 int use_ipv6 = 1;
 int debug = 0;
 int use_ipv4 = 1;
 int use_ipv6 = 1;
 int debug = 0;
@@ -149,6 +172,8 @@ line *script = NULL;
 line *last = NULL;
 line *s;
 FILE *in, *out;
 line *last = NULL;
 line *s;
 FILE *in, *out;
+int linebuf = 1;
+char *pidfile = NULL;
 
 char *sockname = NULL;
 unsigned char buffer[10240];
 
 char *sockname = NULL;
 unsigned char buffer[10240];
@@ -173,16 +198,31 @@ int len = sizeof(accepted);
 
 
 /* Sort out the arguments */
 
 
 /* Sort out the arguments */
+if (argc > 1 && (!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")))
+  {
+  printf("Usage: %s [options]\n", argv[0]);
+  puts("Options"
+       "\n\t-d       debug"
+       "\n\t-i n     n seconds initial delay"
+       "\n\t-noipv4  disable ipv4"
+       "\n\t-noipv6  disable ipv6"
+       "\n\t-oP file write PID to file"
+       "\n\t-t n     n seconds timeout"
+  );
+  exit(0);
+  }
 
 while (na < argc && argv[na][0] == '-')
   {
   if (strcmp(argv[na], "-d") == 0) debug = 1;
   else if (strcmp(argv[na], "-t") == 0) timeout = atoi(argv[++na]);
 
 while (na < argc && argv[na][0] == '-')
   {
   if (strcmp(argv[na], "-d") == 0) debug = 1;
   else if (strcmp(argv[na], "-t") == 0) timeout = atoi(argv[++na]);
+  else if (strcmp(argv[na], "-i") == 0) initial_pause = atoi(argv[++na]);
   else if (strcmp(argv[na], "-noipv4") == 0) use_ipv4 = 0;
   else if (strcmp(argv[na], "-noipv6") == 0) use_ipv6 = 0;
   else if (strcmp(argv[na], "-noipv4") == 0) use_ipv4 = 0;
   else if (strcmp(argv[na], "-noipv6") == 0) use_ipv6 = 0;
+  else if (strcmp(argv[na], "-oP") == 0) pidfile = argv[++na];
   else
     {
   else
     {
-    printf("server: unknown option %s\n", argv[na]);
+    printf("server: unknown option %s, try -h or --help\n", argv[na]);
     exit(1);
     }
   na++;
     exit(1);
     }
   na++;
@@ -211,11 +251,22 @@ na++;
 if (na < argc) connection_count = atoi(argv[na]);
 
 
 if (na < argc) connection_count = atoi(argv[na]);
 
 
+/* Initial pause (before creating listen sockets */
+if (initial_pause > 0)
+  {
+  if (debug)
+    printf("%d: Inital pause of %d seconds\n", time(NULL), initial_pause);
+  else
+    printf("Inital pause of %d seconds\n", initial_pause);
+  while (initial_pause > 0)
+    initial_pause = sleep(initial_pause);
+  }
+
 /* Create sockets */
 
 if (port == 0)  /* Unix domain */
   {
 /* Create sockets */
 
 if (port == 0)  /* Unix domain */
   {
-  if (debug) printf("Creating Unix domain socket\n");
+  if (debug) printf("%d: Creating Unix domain socket\n", time(NULL));
   listen_socket[udn] = socket(PF_UNIX, SOCK_STREAM, 0);
   if (listen_socket[udn] < 0)
     {
   listen_socket[udn] = socket(PF_UNIX, SOCK_STREAM, 0);
   if (listen_socket[udn] < 0)
     {
@@ -373,24 +424,57 @@ for (i = 0; i <= skn; i++)
   }
 
 
   }
 
 
+if (pidfile)
+  {
+  FILE * p;
+  if (!(p = fopen(pidfile, "w")))
+    {
+    fprintf(stderr, "pidfile create failed: %s\n", strerror(errno));
+    exit(1);
+    }
+  fprintf(p, "%ld\n", (long)getpid());
+  fclose(p);
+  }
+
 /* This program handles only a fixed number of connections, in sequence. Before
 waiting for the first connection, read the standard input, which contains the
 script of things to do. A line containing "++++" is treated as end of file.
 This is so that the Perl driving script doesn't have to close the pipe -
 because that would cause it to wait for this process, which it doesn't yet want
 to do. The driving script adds the "++++" automatically - it doesn't actually
 /* This program handles only a fixed number of connections, in sequence. Before
 waiting for the first connection, read the standard input, which contains the
 script of things to do. A line containing "++++" is treated as end of file.
 This is so that the Perl driving script doesn't have to close the pipe -
 because that would cause it to wait for this process, which it doesn't yet want
 to do. The driving script adds the "++++" automatically - it doesn't actually
-appear in the test script. */
+appear in the test script. Within lines we interpret \xNN and \\ groups */
 
 
-while (fgets(buffer, sizeof(buffer), stdin) != NULL)
+while (fgets(CS buffer, sizeof(buffer), stdin) != NULL)
   {
   line *next;
   {
   line *next;
-  int n = (int)strlen(buffer);
+  char * d;
+  int n = (int)strlen(CS buffer);
+
+  if (n > 1 && buffer[0] == '>' && buffer[1] == '>')
+    linebuf = 0;
   while (n > 0 && isspace(buffer[n-1])) n--;
   buffer[n] = 0;
   while (n > 0 && isspace(buffer[n-1])) n--;
   buffer[n] = 0;
-  if (strcmp(buffer, "++++") == 0) break;
+  if (strcmp(CS buffer, "++++") == 0) break;
   next = malloc(sizeof(line) + n);
   next->next = NULL;
   next = malloc(sizeof(line) + n);
   next->next = NULL;
-  strcpy(next->line, buffer);
+  d = next->line;
+    {
+    char * s = CS buffer;
+    do
+      {
+      char ch;
+      char cl = *s;
+      if (cl == '\\' && (cl = *++s) == 'x')
+       {
+       if ((ch = *++s - '0') > 9 && (ch -= 'A'-'9'-1) > 15) ch -= 'a'-'A';
+       if ((cl = *++s - '0') > 9 && (cl -= 'A'-'9'-1) > 15) cl -= 'a'-'A';
+       cl |= ch << 4;
+       }
+      *d++ = cl;
+      }
+    while (*s++);
+    }
+  next->len = d - next->line - 1;
   if (last == NULL) script = last = next;
     else last->next = next;
   last = next;
   if (last == NULL) script = last = next;
     else last->next = next;
   last = next;
@@ -466,7 +550,7 @@ for (count = 0; count < connection_count; count++)
   dup_accept_socket = dup(accept_socket);
 
   if (port > 0)
   dup_accept_socket = dup(accept_socket);
 
   if (port > 0)
-    printf("\nConnection request from [%s]\n", host_ntoa(&accepted, buffer));
+    printf("\nConnection request from [%s]\n", host_ntoa(&accepted, CS buffer));
   else
     {
     printf("\nConnection request\n");
   else
     {
     printf("\nConnection request\n");
@@ -514,7 +598,8 @@ for (count = 0; count < connection_count; count++)
     if (ss[0] == '>')
       {
       char *end = "\r\n";
     if (ss[0] == '>')
       {
       char *end = "\r\n";
-      printf("%s\n", ss++);
+      unsigned len = s->len;
+      printit(ss++, len--);
 
       if (strncmp(ss, "*eof", 4) == 0)
         {
 
       if (strncmp(ss, "*eof", 4) == 0)
         {
@@ -523,13 +608,14 @@ for (count = 0; count < connection_count; count++)
         }
 
       if (*ss == '>')
         }
 
       if (*ss == '>')
-        { end = ""; ss++; }
+        { end = ""; ss++; len--; }
       else if (strncmp(ss, "CR>", 3) == 0)
       else if (strncmp(ss, "CR>", 3) == 0)
-        { end = "\r"; ss += 3; }
+        { end = "\r"; ss += 3; len -= 3; }
       else if (strncmp(ss, "LF>", 3) == 0)
       else if (strncmp(ss, "LF>", 3) == 0)
-        { end = "\n"; ss += 3; }
+        { end = "\n"; ss += 3; len -= 3; }
 
 
-      fprintf(out, "%s%s", ss, end);
+      fwrite(ss, 1, len, out);
+      if (*end) fprintf(out, end);
       }
 
     else if (isdigit((unsigned char)ss[0]))
       }
 
     else if (isdigit((unsigned char)ss[0]))
@@ -554,47 +640,93 @@ for (count = 0; count < connection_count; count++)
     connection. Read command line or data lines; the latter are indicated
     by the expected line being just ".". If the line starts with '<', that
     doesn't form part of the expected input. (This allows for incoming data
     connection. Read command line or data lines; the latter are indicated
     by the expected line being just ".". If the line starts with '<', that
     doesn't form part of the expected input. (This allows for incoming data
-    starting with a digit.) */
+    starting with a digit.) If the line starts with '<<' we operate in
+    unbuffered rather than line mode and assume that a single read gets the
+    entire message. */
 
     else
       {
       int offset;
       int data = strcmp(ss, ".") == 0;
 
 
     else
       {
       int offset;
       int data = strcmp(ss, ".") == 0;
 
-      if (ss[0] == '<')
+      if (ss[0] != '<')
+       offset = 0;
+      else
         {
         buffer[0] = '<';
         {
         buffer[0] = '<';
-        offset = 1;
+       if (ss[1] != '<')
+         offset = 1;
+       else
+         {
+         buffer[1] = '<';
+         offset = 2;
+         }
         }
         }
-      else offset = 0;
 
       fflush(out);
 
 
       fflush(out);
 
-      for (;;)
-        {
-        int n;
-        alarm(timeout);
-        if (fgets(buffer+offset, sizeof(buffer)-offset, in) == NULL)
-          {
-          printf("%sxpected EOF read from client\n",
-            (strncmp(ss, "*eof", 4) == 0)? "E" : "Une");
-          s = s->next;
-          goto END_OFF;
-          }
-        alarm(0);
-        n = (int)strlen(buffer);
-        while (n > 0 && isspace(buffer[n-1])) n--;
-        buffer[n] = 0;
-        printf("%s\n", buffer);
-        if (!data || strcmp(buffer, ".") == 0) break;
-        }
-
-      if (strncmp(ss, buffer, (int)strlen(ss)) != 0)
-        {
-        printf("Comparison failed - bailing out\n");
-        printf("Expected: %s\n", ss);
-        break;
-        }
+      if (!linebuf)
+       {
+       int n;
+       char c;
+
+       alarm(timeout);
+       n = read(dup_accept_socket, CS buffer+offset, s->len - offset);
+       if (n == 0)
+         {
+         printf("%sxpected EOF read from client\n",
+           (strncmp(ss, "*eof", 4) == 0)? "E" : "Une");
+         s = s->next;
+         goto END_OFF;
+         }
+       if (offset != 2)
+         while (read(dup_accept_socket, &c, 1) == 1 && c != '\n') ;
+       alarm(0);
+       n += offset;
+
+       printit(buffer, n);
+
+       if (data) do
+         {
+         n = (read(dup_accept_socket, &c, 1) == 1 && c == '.');
+         while (c != '\n' && read(dup_accept_socket, &c, 1) == 1)
+           ;
+         } while (!n);
+       else if (memcmp(ss, buffer, n) != 0)
+         {
+         printf("Comparison failed - bailing out\nExpected: ");
+         printit(ss, n);
+         break;
+         }
+       }
+      else
+       {
+       for (;;)
+         {
+         int n;
+         alarm(timeout);
+         if (fgets(CS buffer+offset, sizeof(buffer)-offset, in) == NULL)
+           {
+           printf("%sxpected EOF read from client\n",
+             (strncmp(ss, "*eof", 4) == 0)? "E" : "Une");
+           s = s->next;
+           goto END_OFF;
+           }
+         alarm(0);
+         n = (int)strlen(CS buffer);
+         while (n > 0 && isspace(buffer[n-1])) n--;
+         buffer[n] = 0;
+         printf("%s\n", buffer);
+         if (!data || strcmp(CS buffer, ".") == 0) break;
+         }
+
+       if (strncmp(ss, CS buffer, (int)strlen(ss)) != 0)
+         {
+         printf("Comparison failed - bailing out\n");
+         printf("Expected: %s\n", ss);
+         break;
+         }
+       }
       }
     }
 
       }
     }