home *** CD-ROM | disk | FTP | other *** search
- /*************************************************************
- * DNAMAIL - Deliver mail to remote DECnet nodes.
- * Use interactively or with sendmail interface
- *
- * Copyright
- * Darin Johnson, Lockheed Missiles and Space
- *
- * Permission to copy and/or modify as long as reference is made
- * to the authors. This program may not be sold.
- *************************************************************/
-
- #include <stdio.h>
- #include <fcntl.h>
- #include <pwd.h>
- #include <ctype.h>
- #include <sysexits.h> /* the 'proper' exits to return to sendmail */
- #include <sys/ioctl.h>
- #include <netdna/dna.h>
- #include <sys/time.h>
- #include "dnamail.h"
-
- /* list of strings */
- struct clist {
- char *str;
- struct clist *next;
- };
-
- extern int errno;
-
- char *use_str = "USAGE: %s [-d] [-s subject] [-n node] [address-list]\n";
- #define usage() printf(use_str, argv[0])
-
- char buff[MAXLINE];
- char *subject;
- char *node;
- char debug;
- char ttyflag;
- char *from;
- char *from_o;
- int num_addr;
- char *to_line;
- int baduser_flag;
-
- char **to; /* char *to[] */
-
- int ll; /* DECnet file descriptor */
-
- /* open DECnet connection to mail protocol. Descriptor is in 'll' */
- get_connection() {
- OpenBlock ob; /* info for opening DECnet link */
- int ret; /* return status code */
-
- if (debug)
- fprintf(stderr, "Trying to open /dev/dna\n");
-
- if ((ll = open("/dev/dna", O_RDWR)) < 0) {
- dnaerror("Open failed");
- exit(EX_SOFTWARE);
- }
-
- if (debug)
- fprintf(stderr, "Trying to get logical link\n");
-
- if (ioctl(ll, SES_GET_LINK, 0)) {
- dnaerror("Error getting logical link");
- exit_with_status();
- }
-
- if (debug)
- fprintf(stderr, "Trying to get link to mail server on remote node\n");
-
- /* set up open block with access information */
- strcpy(ob.op_node_name, node); /* node we want to send mail to */
- ob.op_object_nbr = MAIL_OBJECT;
- ob.op_userid[0] = ob.op_account[0] = ob.op_password[0] = NULL;
- ob.op_opt_data.im_length = 0;
- if (ioctl(ll, SES_LINK_ACCESS, &ob) < 0) {
- dnaerror("link access");
- exit_with_status();
- }
-
- if (debug)
- fprintf(stderr, "Link established...\n");
- /* ll now contains descriptor to open DECnet connection to MAIL.EXE on
- node 'node' */
- }
-
- /* convert dna errors into errors sendmail can understand. Then exit. */
- exit_with_status() {
- switch(errno) {
- case NET_RESOUR:
- case NODE_DOWN:
- case NODE_UNREACH:
- exit(EX_TEMPFAIL);
- case NODE_NAME:
- exit(EX_NOHOST);
- case OBJ_NAME:
- exit(EX_UNAVAILABLE);
- default:
- exit(EX_PROTOCOL);
- }
- }
-
- /* close up connection */
- drop_connection() {
- SessionData sd; /* misc session info */
-
- sd.sd_reason = 0;
- sd.sd_data.im_length = 0;
- ioctl(ll, SES_DISCONNECT, &sd);
- close(ll);
- if (debug)
- fprintf(stderr, "Connection terminated by us\n");
- }
-
- /* Sends headers. Collects headers from message into a list. Then
- sends subject line (which terminates VMS header) followed by other
- headers (which are treated as part of the normal message by VMS).
-
- The actual reason this routine exists is to search for a Subject:
- line so it can be sent as the DECnet subject line. Of course,
- later versions of the software might do more with these headers. */
- send_headers() {
- struct clist *headers, *tail;
-
- if (ttyflag || subject) {
- /* if we are a tty or have an explicit subject */
- /* then we only want to send the subject rather than search for it */
- send(subject);
- return;
- }
- sprintf(buff, "Received: by %s; %s", version, mailtime());
- /* initialize list of headers */
- headers = (struct clist*)malloc(sizeof(struct clist));
- headers->next = NULL;
- headers->str = copystr(buff);
- tail = headers;
- /* read in headers */
- while (gets(buff) > 0) {
- /* add onto header list */
- tail->next = (struct clist*)malloc(sizeof(struct clist));
- tail = tail->next;
- tail->next=NULL;
- tail->str = copystr(buff);
- if (strlen(buff) == 0) /* empty line means no more headers */
- break;
- if (!strncmp(buff, "Subject: ", 9)) { /* found the Subject: */
- subject = &tail->str[9];
- }
- }
- /* now write out header lines */
- send(subject); /* this terminates the DECnet mail header */
- tail=headers;
- /* send list of headers - these are treated by DECnet mail as part of
- the normal message */
- while (tail) {
- send(tail->str);
- tail=tail->next;
- cfree(headers->str);
- free(headers);
- headers=tail;
- }
- }
-
- /* the actual workhorse. Sends mail using correct DECnet mail protocol.
- Information about the protocol was derived from microfiche VMS
- listings for V4.0. See dnamaild.c for the other half of the protocol.
-
- Protocol:
- 'send' means to write a record over decnet. A 'marker' is a record
- containing a single NULL (used to terminate list of users and
- message).
- 1) send who this mail is from (becomes From: line)
- 2) for each user we are sending mail to:
- a) send address
- b) get status back (tells us if address is valid or not)
- 3) send a 'marker' specifying the end of step 2
- 4) send To: line. This line contains the original list
- of recipients as entered by the user. (this line is
- treated as a 'header' by VMS)
- 5) send Subj: line (this is handled by send_headers() )
- 6) send each line of the message
- 7) terminate message with a 'marker'.
- 8) For each address that was valid in step 2, read a status value
- back to see if message actually got sent.
- */
- send_message() {
- int i;
-
- if (debug)
- fprintf(stderr, "Send the From: line\n");
- send(from);
-
- /* for each user, send address and get status */
- baduser_flag = 0;
- for (i=0; i<num_addr; i++) {
- if (debug)
- fprintf(stderr, "Checking user <%s>\n", to[i]);
- send(to[i]);
- if (check_status()) { /* status tell if deliverable or not */
- if (debug)
- fprintf(stderr, "\tcheck_status returned true\n");
- to[i] = NULL;
- baduser_flag = 1; /* remember that we had an invalid address */
- }
- }
- send_marker(); /* specifies end of address check */
-
- if (debug)
- fprintf(stderr, "sending To: line\n");
- send (to_line);
-
- /* send_headers() will send the Subj: line after parsing the headers */
- send_headers();
-
- /* now send actual message */
- if (debug)
- fprintf(stderr, "Sending message body\n");
- if (ttyflag)
- printf("Enter your message below. Press CTRL/D when complete, or CTRL/C to quit:\n");
- while (gets(buff) > 0)
- send(buff);
- send_marker(); /* specifies end of message */
-
- /* for each address, check status to see if it was actually sent */
- for (i=0; i<num_addr; i++)
- if (to[i] && check_status())
- baduser_flag = 1; /* so we can exit with an appropriate error */
- }
-
- /* read status back from remote node. Since I am assuming the remote
- node is a VAX, I assume a VAX byte order. If there was an
- error, then read in status message */
- int check_status() {
- #ifndef DEBUG
- long st;
- /* read longword */
- if (read(ll, &st, sizeof(long)) < 0) {
- dnaerror("CheckStatus");
- exit_with_status();
- }
- /* 0x01000000 is really 0x1 on VAX */
- if (st == 0x01000000)
- return 0;
- /* else we have an error - read error message */
- while (1) {
- if ((st=read(ll, buff, sizeof(buff))) < 0) {
- dnaerror("CheckStatus");
- exit_with_status();
- }
- /* is this end of status message ? */
- if (st == 1 && buff[0] == NULL)
- break;
- buff[st] = NULL;
- fprintf(stderr, "%s\n", buff); /* write to stderr so sendmail sees it */
- }
- return 1;
- #else
- return 0;
- #endif
- }
-
- /* send a string over the decnet connection */
- send(s)
- char *s;
- {
- #ifndef DEBUG
- if (write(ll, s, (s?strlen(s):0)) < 0) {
- dnaerror("SEND");
- exit_with_status();
- }
- #else
- printf("SEND= %s\n", (s?s:""));
- #endif
- }
-
- /* send NULL over decnet link - used as marker */
- send_marker() {
- #ifndef DEBUG
- if (write(ll, "", 1) < 0) {
- dnaerror("SEND");
- exit_with_status();
- }
- #else
- printf("SEND_MARKER\n");
- #endif
- }
-
- /* clean up addresses for VMS side - convert to uppercase, anything else
- needed */
- char *fix_addr(addr)
- char *addr;
- {
- char inquote, esc;
- char *p;
- /* make sure usernames are uppercase so that MAIL.EXE doesn't yell
- at us */
- inquote = 0;
- for (p=buff; *addr; addr++) {
- if (esc = (*addr=='\\'))
- addr++;
- if (*addr=='"' && !esc) {
- addr++;
- inquote ^= 1;
- }
- if (!inquote && islower(*addr))
- *p++ = toupper(*addr);
- else
- *p++ = *addr;
- }
- *p = NULL;
- return copystr(buff);
- }
-
- /* make one long address out of all specified on command line - append
- "node::" to beginning of each. Return in 'to_line' */
- get_addresses(argv, index, num_addresses)
- char *argv[];
- int index, num_addresses;
- {
- int i;
- int comma_flag;
- comma_flag = 0;
- num_addr = num_addresses;
- to = (char **)malloc(sizeof(char *) * num_addr);
- for (i=0; i<num_addr; i++) {
- if (comma_flag)
- strcat(buff, " ");
- strcat(buff, node);
- strcat(buff, "::");
- strcat(buff, argv[i+index]);
- to[i] = fix_addr(argv[i+index]);
- comma_flag = 1;
- }
- to_line = copystr(buff);
- }
-
- /* parse arguments, get anything not specified, and call send_message() */
- main(argc, argv)
- int argc;
- char *argv[];
- {
- int st, /* return status */
- i; /* misc. */
-
- extern char *optarg; /* for use with getopt() */
- extern int optind;
- char opt;
- char *copystr();
- char *t1;
- struct passwd *pw;
-
- subject = node = from = from_o = NULL;
- debug = ttyflag = FALSE;
-
- /* parse arguments */
- while ((opt=getopt(argc, argv, "ds:n:f:")) != EOF) {
- switch(opt) {
- case 'd': /* debug flag */
- debug = TRUE;
- break;
- case 's': /* subject */
- subject = optarg;
- break;
- case 'n': /* remote node */
- node = optarg;
- break;
- case 'f': /* not included in usage()! */
- from_o = optarg;
- break;
- default:
- usage();
- exit(EX_USAGE);
- }
- }
-
- /* see if we are a tty as opposed to sendmail */
- ttyflag = isatty(0);
-
- /* prompt for node if not specified */
- if (!node) {
- fputs("Node: ", stdout);
- if (!gets(buff)) {
- puts("Unexpected EOF");
- exit(1);
- }
- node = copystr(buff);
- }
- /* uppercase node name */
- for (t1 = node; *t1; t1++)
- if (islower(*t1))
- *t1 = toupper(*t1);
-
- /* get recipients if not specified */
- if (!argv[optind]) {
- fputs("To: ", stdout);
- if (!gets(buff)) {
- puts("Unexpected EOF");
- exit(1);
- }
- /* create to[] array */
- to = (char **)malloc(sizeof(char*)); /* room for one address */
- to_line = copystr(buff);
- num_addr = 1;
- to[0] = fix_addr(buff);
- } else {
- /* build to_line and to[] from arguments */
- get_addresses(argv, optind, (argc-optind));
- }
-
- /* get subject if not specified */
- if (!subject && ttyflag) {
- fputs("Subject: ", stdout);
- if (!gets(buff)) {
- puts("Unexpected EOF");
- exit(1);
- }
- subject = copystr(buff);
- }
-
- /* figure out who this is from if not specified or this is a tty */
- if (!from_o) {
- t1 = (char *)getlogin();
- if (!t1) {
- pw = getpwuid(getuid());
- t1 = pw->pw_name;
- }
- from_o = t1;
- }
- sprintf(buff, "\"%s\"", from_o);
- from = copystr(buff);
-
- if (debug) {
- fprintf(stderr, "From: '%s'\n", from);
- fprintf(stderr, "To: <%s>", to_line);
- }
-
- /* get connection, send mail, close connection */
- #ifndef DEBUG
- get_connection();
- #endif
- send_message();
- #ifndef DEBUG
- drop_connection();
- #endif
-
- /* if mail wasn't sent to a user, then exit with appropriate
- error code, so sendmail can take the appropriate action */
- if (baduser_flag)
- exit(EX_NOUSER);
- else
- exit(EX_OK);
- }
-