home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume3 / dnamail / dnamail.c < prev    next >
C/C++ Source or Header  |  1989-02-03  |  12KB  |  454 lines

  1. /*************************************************************
  2.  * DNAMAIL - Deliver mail to remote DECnet nodes.
  3.  * Use interactively or with sendmail interface
  4.  *
  5.  * Copyright
  6.  *  Darin Johnson, Lockheed Missiles and Space
  7.  *
  8.  * Permission to copy and/or modify as long as reference is made
  9.  * to the authors.  This program may not be sold.
  10.  *************************************************************/
  11.  
  12. #include <stdio.h>
  13. #include <fcntl.h>
  14. #include <pwd.h>
  15. #include <ctype.h>
  16. #include <sysexits.h>        /* the 'proper' exits to return to sendmail */
  17. #include <sys/ioctl.h>
  18. #include <netdna/dna.h>
  19. #include <sys/time.h>
  20. #include "dnamail.h"
  21.  
  22. /* list of strings */
  23. struct clist {
  24.   char *str;
  25.   struct clist *next;
  26. };
  27.  
  28. extern int errno;
  29.  
  30. char *use_str = "USAGE: %s [-d] [-s subject] [-n node] [address-list]\n";
  31. #define usage() printf(use_str, argv[0])
  32.   
  33. char buff[MAXLINE];
  34. char *subject;
  35. char *node;
  36. char debug;
  37. char ttyflag;
  38. char *from;
  39. char *from_o;
  40. int num_addr;
  41. char *to_line;
  42. int baduser_flag;
  43.  
  44. char **to;    /* char *to[] */
  45.  
  46. int ll;            /* DECnet file descriptor */
  47.  
  48. /* open DECnet connection to mail protocol.  Descriptor is in 'll' */
  49. get_connection() {
  50.   OpenBlock ob;            /* info for opening DECnet link */
  51.   int ret;            /* return status code */
  52.  
  53.   if (debug)
  54.     fprintf(stderr, "Trying to open /dev/dna\n");
  55.  
  56.   if ((ll = open("/dev/dna", O_RDWR)) < 0) {
  57.     dnaerror("Open failed");
  58.     exit(EX_SOFTWARE);
  59.   }
  60.  
  61.   if (debug)
  62.     fprintf(stderr, "Trying to get logical link\n");
  63.  
  64.   if (ioctl(ll, SES_GET_LINK, 0)) {
  65.     dnaerror("Error getting logical link");
  66.     exit_with_status();
  67.   }
  68.  
  69.   if (debug)
  70.     fprintf(stderr, "Trying to get link to mail server on remote node\n");
  71.   
  72.     /* set up open block with access information */
  73.   strcpy(ob.op_node_name, node);  /* node we want to send mail to */
  74.   ob.op_object_nbr = MAIL_OBJECT;
  75.   ob.op_userid[0] = ob.op_account[0] = ob.op_password[0] = NULL;
  76.   ob.op_opt_data.im_length = 0;
  77.   if (ioctl(ll, SES_LINK_ACCESS, &ob) < 0) {
  78.     dnaerror("link access");
  79.     exit_with_status();
  80.   }
  81.   
  82.   if (debug)
  83.     fprintf(stderr, "Link established...\n");
  84.   /* ll now contains descriptor to open DECnet connection to MAIL.EXE on
  85.      node 'node' */
  86. }
  87.  
  88. /* convert dna errors into errors sendmail can understand.  Then exit. */
  89. exit_with_status() {
  90.   switch(errno) {
  91.   case NET_RESOUR:
  92.   case NODE_DOWN:
  93.   case NODE_UNREACH:
  94.     exit(EX_TEMPFAIL);
  95.   case NODE_NAME:
  96.     exit(EX_NOHOST);
  97.   case OBJ_NAME:
  98.     exit(EX_UNAVAILABLE);
  99.   default:
  100.     exit(EX_PROTOCOL);
  101.   }
  102. }
  103.  
  104. /* close up connection */
  105. drop_connection() {
  106.   SessionData sd;        /* misc session info */
  107.  
  108.   sd.sd_reason = 0;
  109.   sd.sd_data.im_length = 0;
  110.   ioctl(ll, SES_DISCONNECT, &sd);
  111.   close(ll);
  112.   if (debug)
  113.     fprintf(stderr, "Connection terminated by us\n");
  114. }
  115.  
  116. /* Sends headers.  Collects headers from message into a list.  Then
  117.    sends subject line (which terminates VMS header) followed by other
  118.    headers (which are treated as part of the normal message by VMS).
  119.  
  120.    The actual reason this routine exists is to search for a Subject:
  121.    line so it can be sent as the DECnet subject line.  Of course,
  122.    later versions of the software might do more with these headers. */
  123. send_headers() {
  124.   struct clist *headers, *tail;
  125.  
  126.   if (ttyflag || subject) {
  127.         /* if we are a tty or have an explicit subject */
  128.         /* then we only want to send the subject rather than search for it */
  129.       send(subject);
  130.       return;
  131.   }
  132.   sprintf(buff, "Received: by %s; %s", version, mailtime());
  133.     /* initialize list of headers */
  134.   headers = (struct clist*)malloc(sizeof(struct clist));
  135.   headers->next = NULL;
  136.   headers->str = copystr(buff);
  137.   tail = headers;
  138.     /* read in headers */
  139.   while (gets(buff) > 0) {
  140.       /* add onto header list */
  141.     tail->next = (struct clist*)malloc(sizeof(struct clist));
  142.     tail = tail->next;
  143.     tail->next=NULL;
  144.     tail->str = copystr(buff);
  145.     if (strlen(buff) == 0)    /* empty line means no more headers */
  146.       break;
  147.     if (!strncmp(buff, "Subject: ", 9)) {    /* found the Subject: */
  148.       subject = &tail->str[9];
  149.     }
  150.   }
  151.     /* now write out header lines */
  152.   send(subject);        /* this terminates the DECnet mail header */
  153.   tail=headers;
  154.     /* send list of headers - these are treated by DECnet mail as part of
  155.        the normal message */
  156.   while (tail) {
  157.     send(tail->str);
  158.     tail=tail->next;
  159.     cfree(headers->str);
  160.     free(headers);
  161.     headers=tail;
  162.   }
  163. }
  164.  
  165. /* the actual workhorse.  Sends mail using correct DECnet mail protocol.
  166.    Information about the protocol was derived from microfiche VMS
  167.    listings for V4.0.  See dnamaild.c for the other half of the protocol.
  168.  
  169.    Protocol:
  170.      'send' means to write a record over decnet.  A 'marker' is a record
  171.      containing a single NULL (used to terminate list of users and
  172.      message).
  173.      1) send who this mail is from (becomes From: line)
  174.      2) for each user we are sending mail to:
  175.         a) send address
  176.         b) get status back (tells us if address is valid or not)
  177.      3) send a 'marker' specifying the end of step 2
  178.      4) send To: line.  This line contains the original list
  179.         of recipients as entered by the user.  (this line is
  180.         treated as a 'header' by VMS)
  181.      5) send Subj: line (this is handled by send_headers() )
  182.      6) send each line of the message
  183.      7) terminate message with a 'marker'.
  184.      8) For each address that was valid in step 2, read a status value
  185.         back to see if message actually got sent.
  186. */
  187. send_message() {
  188.   int i;
  189.  
  190.   if (debug)
  191.     fprintf(stderr, "Send the From: line\n");
  192.   send(from);
  193.   
  194.     /* for each user, send address and get status */
  195.   baduser_flag = 0;
  196.   for (i=0; i<num_addr; i++) {
  197.     if (debug)
  198.       fprintf(stderr, "Checking user <%s>\n", to[i]);
  199.     send(to[i]);
  200.     if (check_status()) {    /* status tell if deliverable or not */
  201.       if (debug)
  202.         fprintf(stderr, "\tcheck_status returned true\n");
  203.       to[i] = NULL; 
  204.       baduser_flag = 1;  /* remember that we had an invalid address */
  205.     }
  206.   }
  207.   send_marker();    /* specifies end of address check */
  208.  
  209.   if (debug)
  210.     fprintf(stderr, "sending To: line\n");
  211.   send (to_line);
  212.  
  213.     /* send_headers() will send the Subj: line after parsing the headers */
  214.   send_headers();
  215.  
  216.     /* now send actual message */
  217.   if (debug)
  218.     fprintf(stderr, "Sending message body\n");
  219.   if (ttyflag)
  220.     printf("Enter your message below.  Press CTRL/D when complete, or CTRL/C to quit:\n");
  221.   while (gets(buff) > 0)
  222.     send(buff);
  223.   send_marker();  /* specifies end of message */
  224.  
  225.     /* for each address, check status to see if it was actually sent */
  226.   for (i=0; i<num_addr; i++)
  227.     if (to[i] && check_status())
  228.       baduser_flag = 1;  /* so we can exit with an appropriate error */
  229. }
  230.  
  231. /* read status back from remote node.  Since I am assuming the remote
  232.    node is a VAX, I assume a VAX byte order.  If there was an
  233.    error, then read in status message */
  234. int check_status() {
  235. #ifndef DEBUG
  236.   long st;
  237.     /* read longword */
  238.   if (read(ll, &st, sizeof(long)) < 0) {
  239.     dnaerror("CheckStatus");
  240.     exit_with_status();
  241.   }
  242.     /* 0x01000000 is really 0x1 on VAX */
  243.   if (st == 0x01000000)
  244.     return 0;
  245.     /* else we have an error - read error message */
  246.   while (1) {
  247.     if ((st=read(ll, buff, sizeof(buff))) < 0) {
  248.       dnaerror("CheckStatus");
  249.       exit_with_status();
  250.     }
  251.       /* is this end of status message ? */
  252.     if (st == 1 && buff[0] == NULL)
  253.       break;
  254.     buff[st] = NULL;
  255.     fprintf(stderr, "%s\n", buff);    /* write to stderr so sendmail sees it */
  256.   }
  257.   return 1;
  258. #else
  259.   return 0;
  260. #endif
  261. }
  262.  
  263. /* send a string over the decnet connection */
  264. send(s)
  265. char *s;
  266. {
  267. #ifndef DEBUG
  268.   if (write(ll, s, (s?strlen(s):0)) < 0) {
  269.     dnaerror("SEND");
  270.     exit_with_status();
  271.   }
  272. #else
  273. printf("SEND= %s\n", (s?s:""));
  274. #endif
  275. }
  276.  
  277. /* send NULL over decnet link - used as marker */
  278. send_marker() {
  279. #ifndef DEBUG
  280.   if (write(ll, "", 1) < 0) {
  281.     dnaerror("SEND");
  282.     exit_with_status();
  283.   }
  284. #else
  285. printf("SEND_MARKER\n");
  286. #endif
  287. }
  288.  
  289. /* clean up addresses for VMS side - convert to uppercase, anything else
  290.    needed */
  291. char *fix_addr(addr)
  292. char *addr;
  293. {
  294.   char inquote, esc;
  295.   char *p;
  296.     /* make sure usernames are uppercase so that MAIL.EXE doesn't yell
  297.        at us */
  298.   inquote = 0;
  299.   for (p=buff; *addr; addr++) {
  300.     if (esc = (*addr=='\\')) 
  301.       addr++;
  302.     if (*addr=='"' && !esc) {
  303.       addr++;
  304.       inquote ^= 1;
  305.     }
  306.     if (!inquote && islower(*addr))
  307.       *p++ = toupper(*addr);
  308.     else
  309.       *p++ = *addr;
  310.   }
  311.   *p = NULL;
  312.   return copystr(buff);
  313. }
  314.  
  315. /* make one long address out of all specified on command line - append
  316.    "node::" to beginning of each.  Return in 'to_line' */
  317. get_addresses(argv, index, num_addresses)
  318. char *argv[];
  319. int index, num_addresses;
  320. {
  321.   int i;
  322.   int comma_flag;
  323.   comma_flag = 0;
  324.   num_addr = num_addresses;
  325.   to = (char **)malloc(sizeof(char *) * num_addr);
  326.   for (i=0; i<num_addr; i++) {
  327.     if (comma_flag)
  328.       strcat(buff, " ");
  329.     strcat(buff, node);
  330.     strcat(buff, "::");
  331.     strcat(buff, argv[i+index]);
  332.     to[i] = fix_addr(argv[i+index]);
  333.     comma_flag = 1;
  334.   }
  335.   to_line = copystr(buff);
  336. }
  337.  
  338. /* parse arguments, get anything not specified, and call send_message() */
  339. main(argc, argv)
  340.       int argc;
  341.       char *argv[];
  342. {
  343.   int st,            /* return status */
  344.        i;            /* misc. */
  345.  
  346.   extern char *optarg;        /* for use with getopt() */
  347.   extern int optind;
  348.   char opt;
  349.   char *copystr();
  350.   char *t1;
  351.   struct passwd *pw;
  352.  
  353.   subject = node = from = from_o = NULL;
  354.   debug = ttyflag = FALSE;
  355.   
  356.     /* parse arguments */
  357.   while ((opt=getopt(argc, argv, "ds:n:f:")) != EOF) {
  358.     switch(opt) {
  359.       case 'd':        /* debug flag */
  360.         debug = TRUE;
  361.     break;
  362.       case 's':        /* subject */
  363.         subject = optarg;
  364.     break;
  365.       case 'n':        /* remote node */
  366.     node = optarg;
  367.     break;
  368.       case 'f':        /* not included in usage()! */
  369.     from_o = optarg;
  370.     break;
  371.       default:
  372.     usage();
  373.     exit(EX_USAGE);
  374.     }
  375.   }
  376.  
  377.     /* see if we are a tty as opposed to sendmail */
  378.   ttyflag = isatty(0); 
  379.  
  380.     /* prompt for node if not specified */
  381.   if (!node) {
  382.     fputs("Node: ", stdout);
  383.     if (!gets(buff)) {
  384.       puts("Unexpected EOF");
  385.       exit(1);
  386.     }
  387.     node = copystr(buff);
  388.   }
  389.     /* uppercase node name */
  390.   for (t1 = node; *t1; t1++)
  391.     if (islower(*t1))
  392.       *t1 = toupper(*t1);
  393.  
  394.     /* get recipients if not specified */
  395.   if (!argv[optind]) {
  396.     fputs("To: ", stdout);
  397.     if (!gets(buff)) {
  398.       puts("Unexpected EOF");
  399.       exit(1);
  400.     }
  401.       /* create to[] array */
  402.     to = (char **)malloc(sizeof(char*));    /* room for one address */
  403.     to_line = copystr(buff);
  404.     num_addr = 1;
  405.     to[0] = fix_addr(buff);
  406.   } else {
  407.       /* build to_line and to[] from arguments */
  408.     get_addresses(argv, optind, (argc-optind));
  409.   }
  410.   
  411.     /* get subject if not specified */
  412.   if (!subject && ttyflag) {
  413.     fputs("Subject: ", stdout);
  414.     if (!gets(buff)) {
  415.       puts("Unexpected EOF");
  416.       exit(1);
  417.     }
  418.     subject = copystr(buff);
  419.   }
  420.  
  421.     /* figure out who this is from if not specified or this is a tty */
  422.   if (!from_o) {
  423.     t1 = (char *)getlogin();
  424.     if (!t1) {
  425.       pw = getpwuid(getuid());
  426.       t1 = pw->pw_name;
  427.     }
  428.     from_o = t1;
  429.   }
  430.   sprintf(buff, "\"%s\"", from_o);
  431.   from = copystr(buff);
  432.   
  433.   if (debug) {
  434.     fprintf(stderr, "From: '%s'\n", from);
  435.     fprintf(stderr, "To: <%s>", to_line);
  436.   }
  437.  
  438.    /* get connection, send mail, close connection */
  439. #ifndef DEBUG
  440.   get_connection();
  441. #endif
  442.   send_message();
  443. #ifndef DEBUG
  444.   drop_connection();
  445. #endif
  446.  
  447.     /* if mail wasn't sent to a user, then exit with appropriate
  448.        error code, so sendmail can take the appropriate action */
  449.   if (baduser_flag)
  450.     exit(EX_NOUSER);
  451.   else
  452.     exit(EX_OK);
  453. }
  454.