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