home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
High Voltage Shareware
/
high1.zip
/
high1
/
DIR3
/
KA9Q212.ZIP
/
SMTPCLI.C
< prev
next >
Wrap
C/C++ Source or Header
|
1993-07-16
|
34KB
|
1,531 lines
/*
* CLIENT routines for Simple Mail Transfer Protocol ala RFC821
* A.D. Barksdale Garbee II, aka Bdale, N3EUA
* Copyright 1986 Bdale Garbee, All Rights Reserved.
* Permission granted for non-commercial copying and use, provided
* this notice is retained.
* Modified 14 June 1987 by P. Karn for symbolic target addresses,
* also rebuilt locking mechanism
* Copyright 1987 1988 David Trulli, All Rights Reserved.
* Permission granted for non-commercial copying and use, provided
* this notice is retained.
*/
/****************************************************************************
* $Id: smtpcli.c 1.17 93/07/16 11:50:25 ROOT_DOS Exp $
* 29 May 92 1.2 GT mailroute (): use gateway if defined. *
* 02 Jun 92 1.3 GT Add "smtp wait" and timeout on connect () call *
* in smtp_send (). *
* 04 Jun 92 1.4 GT Make smtptick () a separate process. *
* 05 Jun 92 1.5 GT Timeout on connect () to Gateway. *
* 26 Jun 92 Paul@wolf.demon.co.uk Fixed routing for internal *
* email (doesn't go via the external gateway) *
* 13 Jul 92 1.7 GT Remove 1.6 changes. *
* 05 Aug 92 amc@beryl.demon.co.uk changed printfs to tprintf *
* 27 Aug 92 1.10 mt@kram.demon.co.uk added smtp separator *
* Dec 92 mt@kram.org don't use gateway for any mail MXed via *
* us. Add delay channel for local mail *
* Add smtp beep on|off *
* 19 Apr 93 1.16 GT If newproc () fails, reset tick flag. *
* 08 May 93 1.17 GT Fix warnings. *
* Fix "smtp list". *
* IAY Fix dot transparency as per RFC 821. *
****************************************************************************/
#include <stdio.h>
#include <fcntl.h>
#include <time.h>
#include <setjmp.h>
#ifdef UNIX
#include <sys/types.h>
#endif
#ifdef AMIGA
#include <stat.h>
#else
#include <sys/stat.h>
#endif
#ifdef __TURBOC__
#include <dir.h>
#include <io.h>
#endif
#include "global.h"
#ifdef ANSIPROTO
#include <stdarg.h>
#endif
#include "config.h"
#include "domain.h"
#include "mbuf.h"
#include "cmdparse.h"
#include "proc.h"
#include "socket.h"
#include "timer.h"
#include "netuser.h"
#include "smtp.h"
#include "dirutil.h"
#include "commands.h"
#include "session.h"
#include "ip.h"
static struct timer Smtpcli_t;
static int32 Gateway;
static int32 Delaytime = 0;
int smtpverbose = 1;
char *smtp_separator = NULL;
int are_we_an_mx;
#ifdef SMTPTRACE
static unsigned short Smtptrace = 0; /* used for trace level */
static int dosmtptrace __ARGS ((int argc, char *argv[], void *p));
#endif
static unsigned short Smtpmaxcli = MAXSESSIONS; /* the max client connections
* allowed */
static int Smtpsessions = 0; /* number of client connections currently open */
static int Smtpbatch;
int Smtpmode = 0;
int Smtpbeep = 0;
int32 connect_wait_val = 60000L; /* default connection timeout */
static struct smtpcli *cli_session[MAXSESSIONS]; /* queue of client
* sessions */
static void del_job __ARGS ((struct smtp_job * jp));
static void del_session __ARGS ((struct smtpcli * cb));
static int dogateway __ARGS ((int argc, char *argv[], void *p));
static int doseparator __ARGS ((int argc, char *argv[], void *p));
static int dosmtpmaxcli __ARGS ((int argc, char *argv[], void *p));
static int dotimer __ARGS ((int argc, char *argv[], void *p));
static int dodelay __ARGS ((int argc, char *argv[], void *p));
static int dowait __ARGS ((int argc, char *argv[], void *p));
static int dosmtpkill __ARGS ((int argc, char *argv[], void *p));
static int dosmtplist __ARGS ((int argc, char *argv[], void *p));
static int dobatch __ARGS ((int argc, char *argv[], void *p));
static int dobeep __ARGS ((int argc, char *argv[], void *p));
static void execjobs __ARGS ((void));
static int getresp __ARGS ((struct smtpcli * ftp, int mincode));
static void logerr __ARGS ((struct smtpcli * cb, char *line));
static struct smtpcli *lookup __ARGS ((int32 destaddr));
static struct smtpcli *newcb __ARGS ((void));
static int next_job __ARGS ((struct smtpcli * cb));
static void retmail __ARGS ((struct smtpcli * cb));
static void sendcmd __ARGS ((struct smtpcli * cb, char *fmt,...));
static int smtpsendfile __ARGS ((struct smtpcli * cb));
static int setsmtpmode __ARGS ((int argc, char *argv[], void *p));
static struct smtp_job *setupjob __ARGS ((struct smtpcli * cb, char *id, char *from));
static void smtp_send __ARGS ((int unused, void *cb1, void *p));
static int smtpkick __ARGS ((int argc, char *argv[], void *p));
static void smtptick_body __ARGS ((int unused, void *t, void *p));
static int doverbose __ARGS ((int argc, char *argv[], void *p));
static int should_enter_delay __ARGS ((struct smtpcli * cb));
static int explock __ARGS ((char *dir, char *pre, char *suf, int32 age));
static struct cmds Smtpcmds[] = {
{"batch", dobatch, 0, 0, NULLCHAR},
{"beep", dobeep, 0, 0, NULLCHAR},
{"delay", dodelay, 0, 0, NULLCHAR},
{"gateway", dogateway, 0, 0, NULLCHAR},
{"kick", smtpkick, 0, 0, NULLCHAR},
{"kill", dosmtpkill, 0, 2, "kill <jobnumber>"},
{"list", dosmtplist, 0, 0, NULLCHAR},
{"maxclients", dosmtpmaxcli, 0, 0, NULLCHAR},
{"mode", setsmtpmode, 0, 0, NULLCHAR},
{"separator", doseparator, 0, 0, NULLCHAR},
{"timer", dotimer, 0, 0, NULLCHAR},
#ifdef SMTPTRACE
{"trace", dosmtptrace, 0, 0, NULLCHAR},
#endif
{"wait", dowait, 0, 0, NULLCHAR},
{"verbose", doverbose, 0, 0, NULLCHAR}, /* Actually for Server */
{NULLCHAR},
};
int
dosmtp (argc, argv, p)
int argc;
char *argv[];
void *p;
{
return subcmd (Smtpcmds, argc, argv, p);
}
static int
dobatch (argc, argv, p)
int argc;
char *argv[];
void *p;
{
return setbool (&Smtpbatch, "SMTP batching", argc, argv);
}
static int
dobeep (argc, argv, p)
int argc;
char *argv[];
void *p;
{
return setbool (&Smtpbeep, "SMTP beep on delivery", argc, argv);
}
static int
dosmtpmaxcli (argc, argv, p)
int argc;
char *argv[];
void *p;
{
return setshort (&Smtpmaxcli, "Max clients", argc, argv);
}
static int
setsmtpmode (argc, argv, p)
int argc;
char *argv[];
void *p;
{
if (argc < 2)
{
tprintf ("smtp mode: %s\n",
(Smtpmode & QUEUE) ? "queue" : "route");
}
else
{
switch (*argv[1])
{
case 'q':
Smtpmode |= QUEUE;
break;
case 'r':
Smtpmode &= ~QUEUE;
break;
default:
tprintf ("Usage: smtp mode [queue | route]\n");
break;
}
}
return 0;
}
static int
dogateway (argc, argv, p)
int argc;
char *argv[];
void *p;
{
int32 n;
if (argc < 2)
{
tprintf ("%s\n", inet_ntoa (Gateway));
}
else
if ((n = resolve (argv[1])) == 0)
{
tprintf (Badhost, argv[1]);
return 1;
}
else
Gateway = n;
return 0;
}
static int
doseparator (argc, argv, p)
int argc;
char *argv[];
void *p;
{
int i;
char *t;
if (argc < 2)
{
if (smtp_separator)
free (smtp_separator);
smtp_separator = NULL;
}
else
{
t = mallocw (81);
if (t == NULL)
return 1;
t[0] = '\0';
for (i = 1; i < argc; i++)
{
strcat (t, argv[i]);
if (i < (argc - 1))
strcat (t, " ");
}
if ((smtp_separator = mallocw (strlen (t) + 1)) == NULL)
{
free (t);
return 1;
}
strcpy (smtp_separator, t);
free (t);
}
return 0;
}
#ifdef SMTPTRACE
static int
dosmtptrace (argc, argv, p)
int argc;
char *argv[];
void *p;
{
return setshort (&Smtptrace, "SMTP tracing", argc, argv);
}
#endif
/* list jobs wating to be sent in the mqueue */
static int dosmtplist (argc, argv, p)
int argc;
char *argv[];
void *p;
{
char tstring[80];
char line[20];
char host[LINELEN];
char to[LINELEN];
char from[LINELEN];
char *cp;
char status;
struct stat stbuf;
struct tm *tminfo, *localtime ();
FILE *fp;
Current->flowmode = 1; /* Enable the more mechanism */
tprintf ("S Job Size Date Time Host From\n");
filedir (Mailqueue, 0, line);
while (line[0] != '\0')
{
(void) sprintf (tstring, "%s/%s", Mailqdir, line);
if ((fp = fopen (tstring, READ_TEXT)) == NULLFILE)
{
tprintf ("Can't open %s: %s\n", tstring, sys_errlist[errno]);
pwait (NULL);
filedir (Mailqueue, 1, line);
continue;
}
if ((cp = strrchr (line, '.')) != NULLCHAR)
*cp = '\0';
(void) sprintf (tstring, "%s/%s.lck", Mailqdir, line);
if (access (tstring, 0))
status = ' ';
else
status = 'L';
/* Now see if it's in delay */
if (status == ' ')
{
sprintf (tstring, "%s/%s.dly", Mailqdir, line);
if (!access (tstring, 0))
status = 'D';
}
(void) sprintf (tstring, "%s/%s.txt", Mailqdir, line);
if (stat (tstring, &stbuf) == -1)
{
tprintf ("Can't stat %s: %s\n", tstring, sys_errlist[errno]);
pwait (NULL);
filedir (Mailqueue, 1, line);
continue;
}
tminfo = localtime (&stbuf.st_ctime);
fgets (host, sizeof (host), fp);
rip (host);
fgets (from, sizeof (from), fp);
rip (from);
tprintf ("%c %7s %7ld %02d/%02d %02d:%02d %-20s %s\n ",
status, line, stbuf.st_size,
tminfo->tm_mon + 1,
tminfo->tm_mday,
tminfo->tm_hour,
tminfo->tm_min,
host, from);
while (fgets (to, sizeof (to), fp) != NULLCHAR)
{
rip (to);
tprintf ("%s ", to);
}
tprintf ("\n");
(void) fclose (fp);
pwait (NULL);
filedir (Mailqueue, 1, line);
}
Current->flowmode = 0;
return 0;
} /* static int dosmtplist (argc, argv, p) */
/* kill a job in the mqueue */
static int
dosmtpkill (argc, argv, p)
int argc;
char *argv[];
void *p;
{
char s[SLINELEN];
char *cp, c;
sprintf (s, "%s/%s.lck", Mailqdir, argv[1]);
cp = strrchr (s, '.');
if (!access (s, 0))
{
Current->ttystate.echo = Current->ttystate.edit = 0;
c = keywait ("Warning, the job is locked by SMTP. Remove (y/n)? ", 0);
Current->ttystate.echo = Current->ttystate.edit = 1;
if (c != 'y')
return 0;
(void) unlink (s);
}
strcpy (cp, ".wrk");
if (unlink (s))
tprintf ("Job id %s not found\n", argv[1]);
strcpy (cp, ".txt");
(void) unlink (s);
strcpy (cp, ".dly");
(void) unlink (s);
return 0;
}
/* Set outbound spool scan interval */
static int
dotimer (argc, argv, p)
int argc;
char *argv[];
void *p;
{
if (argc < 2)
{
tprintf ("%lu/%lu\n",
read_timer (&Smtpcli_t) / 1000L,
dur_timer (&Smtpcli_t) / 1000L);
return 0;
}
Smtpcli_t.func = (void (*) ()) smtptick; /* what to call on timeout */
Smtpcli_t.arg = NULL; /* dummy value */
set_timer (&Smtpcli_t, atol (argv[1]) * 1000L); /* set timer duration */
start_timer (&Smtpcli_t); /* and fire it up */
return 0;
}
/* Set delay channel time */
static int
dodelay (argc, argv, p)
int argc;
char *argv[];
void *p;
{
if (argc < 2)
{
tprintf ("%lu\n", Delaytime);
return 0;
}
Delaytime = atol (argv[1]);
return 0;
}
/****************************************************************************
* dowait *
* Displays or sets the connection wait timeout. *
****************************************************************************/
static int dowait (argc, argv, p)
int argc;
char *argv[];
void *p;
{
if (argc < 2)
{
tprintf ("smtp connection timeout: %lu\n", connect_wait_val / 1000L);
return (0);
}
connect_wait_val = atol (argv[1]) * 1000L; /* set timeout */
return (0);
} /* static int dowait (argc, argv, p) */
static int
smtpkick (argc, argv, p)
int argc;
char *argv[];
void *p;
{
int32 addr = 0;
if (argc > 1 && (addr = resolve (argv[1])) == 0)
{
tprintf (Badhost, argv[1]);
return 1;
}
smtptick ((void *) addr);
return 0;
}
/* This is the routine that gets called every so often to do outgoing
* mail processing. When called with a null argument, it runs the entire
* queue; if called with a specific non-zero IP address from the remote
* kick server, it only starts up sessions to that address.
*/
static char smtptick_running = 0;
int
smtptick (t)
void *t;
{
if (smtptick_running != 0)
return (0);
smtptick_running = 1;
if (newproc ("smtp_tick", 1024, smtptick_body, 0, t, NULL, 0) == NULLPROC)
{
/* Wait a while. */
start_timer (&Smtpcli_t);
smtptick_running = 0;
}
return (0);
}
static void smtptick_body (unused, t, p)
int unused;
void *t;
void *p;
{
register struct smtpcli *cb;
struct smtp_job *jp;
struct list *ap;
char tmpstring[LINELEN], wfilename[13], prefix[9];
char from[LINELEN], to[LINELEN];
char *cp, *cp1;
int32 destaddr, target, num_files;
FILE *wfile;
target = (int32) t;
#ifdef SMTPTRACE
if (Smtptrace > 5)
tprintf ("smtp daemon entered, target = %s\n", inet_ntoa (target));
#endif
if (availmem () < Memthresh)
{
/* Memory is tight, don't do anything */
/* Restart timer */
start_timer (&Smtpcli_t);
smtptick_running = 0;
return;
}
/* First see how many there are now */
num_files = 0;
for (filedir (Mailqueue, 0, wfilename); wfilename[0] != '\0';
filedir (Mailqueue, 1, wfilename))
num_files++;
/* Try to deal with that many messages, no more or we may loop forever */
for (filedir (Mailqueue, 0, wfilename); wfilename[0] != '\0' && num_files--;
filedir (Mailqueue, 1, wfilename))
{
/* save the prefix of the file name which it job id */
cp = wfilename;
cp1 = prefix;
while (*cp && *cp != '.')
*cp1++ = *cp++;
*cp1 = '\0';
/* lock this file from the smtp daemon */
if (mlock (Mailqdir, prefix))
continue;
/* Try to bring the job outof delay (in case it's in delay) */
explock (Mailqdir, prefix, "dly", Delaytime);
/* Check that it's not in delay */
if (mlock_suffix (Mailqdir, prefix, "dly"))
{
rmlock (Mailqdir, prefix);
continue;
}
else
rmlock_suffix (Mailqdir, prefix, "dly");
sprintf (tmpstring, "%s/%s", Mailqdir, wfilename);
if ((wfile = fopen (tmpstring, READ_TEXT)) == NULLFILE)
{
/* probably too many open files */
(void) rmlock (Mailqdir, prefix);
/* continue to next message. The failure may be temporary */
continue;
}
(void) fgets (tmpstring, LINELEN, wfile);/* read target host */
rip (tmpstring);
if ((destaddr = mailroute (tmpstring, Ip_addr)) == 0)
{
fclose (wfile);
tprintf ("** smtp: Unknown address %s\n", tmpstring);
(void) rmlock (Mailqdir, prefix);
continue;
}
if (target != 0 && destaddr != target)
{
fclose (wfile);
(void) rmlock (Mailqdir, prefix);
continue; /* Not the proper target of
* a kick */
}
if ((cb = lookup (destaddr)) == NULLSMTPCLI)
{
/* there are enough processes running already */
if (Smtpsessions >= Smtpmaxcli)
{
#ifdef SMTPTRACE
if (Smtptrace)
{
tprintf ("smtp daemon: too many processes\n");
}
#endif
fclose (wfile);
(void) rmlock (Mailqdir, prefix);
break;
}
if ((cb = newcb ()) == NULLSMTPCLI)
{
fclose (wfile);
(void) rmlock (Mailqdir, prefix);
break;
}
cb->ipdest = destaddr;
cb->destname = strdup (tmpstring);
}
else
{
if (cb->lock)
{
/* This system is already is sending mail lets not
* interfere with its send queue. */
fclose (wfile);
(void) rmlock (Mailqdir, prefix);
continue;
}
}
(void) fgets (from, LINELEN, wfile); /* read from */
rip (from);
if ((jp = setupjob (cb, prefix, from)) == NULLJOB)
{
fclose (wfile);
(void) rmlock (Mailqdir, prefix);
del_session (cb);
break;
}
while (fgets (to, LINELEN, wfile) != NULLCHAR)
{
rip (to);
if (addlist (&jp->to, to, DOMAIN) == NULLLIST)
{
fclose (wfile);
del_session (cb);
}
}
fclose (wfile);
#ifdef SMTPTRACE
if (Smtptrace > 1)
{
tprintf ("queue job %s From: %s To:", prefix, from);
for (ap = jp->to; ap != NULLLIST; ap = ap->next)
tprintf (" %s", ap->val);
tprintf ("\n");
}
#endif
}
/* start sending that mail */
execjobs ();
/* Restart timer */
start_timer (&Smtpcli_t);
smtptick_running = 0;
return;
}
static int
should_enter_delay (cb)
struct smtpcli *cb;
{
resolve_mx (cb->destname);
if (are_we_an_mx)
return 1;
return 0;
}
void
delay_job (msgid)
int32 msgid;
{
char t[20];
if (Delaytime == 0)
/* Not using SMTP delay */
return;
sprintf (t, "%lu", msgid);
#ifdef SMTPTRACE
if (Smtptrace)
tprintf ("Delivery Failed: adding job %s to delay queue\n", t);
#endif
mlock_suffix (Mailqdir, t, "dly");
}
/* This is the master state machine that handles a single SMTP transaction.
* It is called with a queue of jobs for a particular host.
* The logic is complicated by the "Smtpbatch" variable, which controls
* the batching of SMTP commands. If Smtpbatch is true, then many of the
* SMTP commands are sent in one swell foop before waiting for any of
* the responses. Unfortunately, this breaks many brain-damaged SMTP servers
* out there, so provisions have to be made to operate SMTP in lock-step mode.
*/
static void
smtp_send (unused, cb1, p)
int unused;
void *cb1;
void *p;
{
register struct smtpcli *cb;
register struct list *tp;
struct sockaddr_in fsocket;
char *cp;
int rcode;
int rcpts;
int goodrcpt;
int i;
int init = 1;
cb = (struct smtpcli *) cb1;
cb->lock = 1;
fsocket.sin_family = AF_INET;
fsocket.sin_addr.s_addr = cb->ipdest;
fsocket.sin_port = IPPORT_SMTP;
cb->s = socket (AF_INET, SOCK_STREAM, 0);
sockmode (cb->s, SOCK_ASCII);
setflush (cb->s, -1); /* We'll explicitly flush
* before reading */
#ifdef SMTPTRACE
if (Smtptrace)
tprintf ("SMTP client Trying...\n");
#endif
/* Run connect () under a timeout in case the destination is
* unreachable. */
alarm (connect_wait_val);
if (connect (cb->s, (char *) &fsocket, SOCKSIZE) == 0)
{
alarm (0L);
#ifdef SMTPTRACE
if (Smtptrace)
tprintf ("Connected\n");
#endif
;
}
else
{
alarm (0L);
if (Delaytime && (cb->ipdest == Ip_addr || should_enter_delay (cb)))
delay_job (atol (cb->jobq->jobname));
else
if (Gateway != 0L)
{
/* Try sending it via the gateway, as long as it's not for us. */
#ifdef SMTPTRACE
if (Smtptrace)
tprintf ("SMTP client Trying gateway...\n");
#endif
(void) close_s (cb->s);
cb->ipdest = Gateway;
fsocket.sin_addr.s_addr = Gateway;
cb->s = socket (AF_INET, SOCK_STREAM, 0);
sockmode (cb->s, SOCK_ASCII);
setflush (cb->s, -1);
alarm (connect_wait_val);
if (connect (cb->s, (char *) &fsocket, SOCKSIZE) != 0)
{
alarm (0L);
cp = sockerr (cb->s);
#ifdef SMTPTRACE
if (Smtptrace)
tprintf ("Connect failed: %s\n", cp != NULLCHAR ? cp : "");
#endif
log (cb->s, "SMTP %s Connect failed: %s", psocket (&fsocket),
cp != NULLCHAR ? cp : "");
goto quit;
}
else
{
alarm (0L);
#ifdef SMTPTRACE
if (Smtptrace)
tprintf ("Connected to Gateway\n");
#endif
;
}
} /* if (Gateway != 0L) */
else
{
cp = sockerr (cb->s);
#ifdef SMTPTRACE
if (Smtptrace)
tprintf ("Connect failed: %s\n", cp != NULLCHAR ? cp : "");
#endif
log (cb->s, "SMTP %s Connect failed: %s", psocket (&fsocket),
cp != NULLCHAR ? cp : "");
goto quit;
}
}
if (!Smtpbatch)
{
rcode = getresp (cb, 200);
if (rcode == -1 || rcode >= 400)
goto quit;
}
/* Say HELO */
sendcmd (cb, "HELO %s\n", Hostname);
if (!Smtpbatch)
{
rcode = getresp (cb, 200);
if (rcode == -1 || rcode >= 400)
goto quit;
}
do
{ /* For each message... */
/* if this file open fails, skip it */
if ((cb->tfile = fopen (cb->tname, READ_TEXT)) == NULLFILE)
continue;
/* Send MAIL and RCPT commands */
sendcmd (cb, "MAIL FROM:<%s>\n", cb->jobq->from);
if (!Smtpbatch)
{
rcode = getresp (cb, 200);
if (rcode == -1 || rcode >= 400)
goto quit;
}
rcpts = 0;
goodrcpt = 0;
for (tp = cb->jobq->to; tp != NULLLIST; tp = tp->next)
{
sendcmd (cb, "RCPT TO:<%s>\n", tp->val);
if (!Smtpbatch)
{
rcode = getresp (cb, 200);
if (rcode == -1)
goto quit;
if (rcode < 400)
goodrcpt = 1; /* At least one good */
}
rcpts++;
}
/* Send DATA command */
sendcmd (cb, "DATA\n");
if (!Smtpbatch)
{
rcode = getresp (cb, 200);
if (rcode == -1 || rcode >= 400)
goto quit;
}
if (Smtpbatch)
{
/* Now wait for the responses to come back. The first time we
* do this, we wait first for the start banner and HELO
* response. In any case, we wait for the response to the MAIL
* command here. */
for (i = init ? 3 : 1; i > 0; i--)
{
rcode = getresp (cb, 200);
if (rcode == -1 || rcode >= 400)
goto quit;
}
init = 0;
/* Now process the responses to the RCPT commands */
for (i = rcpts; i != 0; i--)
{
rcode = getresp (cb, 200);
if (rcode == -1)
goto quit;
if (rcode < 400)
goodrcpt = 1; /* At least one good */
}
/* And finally get the response to the DATA command. Some
* servers will return failure here if no recipients are valid,
* some won't. */
rcode = getresp (cb, 200);
if (rcode == -1 || rcode >= 400)
goto quit;
/* check for no good rcpt on the list */
if (goodrcpt == 0)
{
sendcmd (cb, ".\n"); /* Get out of data mode */
goto quit;
}
}
/* Send the file. This also closes it */
smtpsendfile (cb);
/* Wait for the OK response */
rcode = getresp (cb, 200);
if (rcode == -1)
goto quit;
if ((rcode >= 200 && rcode < 300) || rcode >= 500)
{
/* if a good transfer or permanent failure remove job */
if (cb->errlog != NULLLIST)
retmail (cb);
/* Unlink the textfile */
(void) unlink (cb->tname);
(void) unlink (cb->wname); /* unlink workfile */
log (cb->s, "SMTP sent job %s To: %s From: %s",
cb->jobq->jobname, cb->jobq->to->val, cb->jobq->from);
}
} while (next_job (cb));
quit:
sendcmd (cb, "QUIT\n");
if (cb->errlog != NULLLIST)
{
retmail (cb);
(void) unlink (cb->wname); /* unlink workfile */
(void) unlink (cb->tname); /* unlink text */
}
(void) close_s (cb->s);
if (cb->tfile != NULLFILE)
fclose (cb->tfile);
cb->lock = 0;
del_session (cb);
}
/* create mail lockfile */
int
mlock (dir, id)
char *dir, *id;
{
return mlock_suffix (dir, id, "lck");
}
int
mlock_suffix (dir, id, suffix)
char *dir, *id, *suffix;
{
char lockname[LINELEN];
int fd;
#ifdef MSDOS
if (strlen (id) > 8)
{ /* truncate long filenames */
id[8] = '\0';
if (id[7] == '/')
id[7] = '\0';
}
#endif
/* Try to create the lock file in an atomic operation */
sprintf (lockname, "%s/%s.%s", dir, id, suffix);
#ifdef AMIGA
/* don't ask, really, just don't ask... I'd do file locking on an Amiga
* much more differently than this. */
if (access (lockname, 0) == 0)
return -1;
#endif
if ((fd = open (lockname, O_WRONLY | O_EXCL | O_CREAT, 0600)) == -1)
return -1;
close (fd);
return 0;
}
/* remove mail lockfile */
int
rmlock (dir, id)
char *dir, *id;
{
return rmlock_suffix (dir, id, "lck");
}
int
rmlock_suffix (dir, id, suffix)
char *dir, *id, *suffix;
{
char lockname[LINELEN];
#ifdef MSDOS
if (strlen (id) > 8)
{ /* truncate long filenames */
id[8] = '\0';
if (id[7] == '/')
id[7] = '\0';
}
#endif
sprintf (lockname, "%s/%s.%s", dir, id, suffix);
return (unlink (lockname));
}
/* free the message struct and data */
static void
del_session (cb)
register struct smtpcli *cb;
{
register struct smtp_job *jp, *tp;
register int i;
if (cb == NULLSMTPCLI)
return;
for (i = 0; i < MAXSESSIONS; i++)
if (cli_session[i] == cb)
{
cli_session[i] = NULLSMTPCLI;
break;
}
free (cb->wname);
free (cb->tname);
free (cb->destname);
for (jp = cb->jobq; jp != NULLJOB; jp = tp)
{
tp = jp->next;
del_job (jp);
}
del_list (cb->errlog);
free ((char *) cb);
Smtpsessions--; /* number of connections
* active */
}
static void
del_job (jp)
register struct smtp_job *jp;
{
if (*jp->jobname != '\0')
(void) rmlock (Mailqdir, jp->jobname);
free (jp->from);
del_list (jp->to);
free ((char *) jp);
}
/* delete a list of list structs */
void
del_list (lp)
struct list *lp;
{
register struct list *tp, *tp1;
for (tp = lp; tp != NULLLIST; tp = tp1)
{
tp1 = tp->next;
free (tp->val);
free ((char *) tp);
}
}
/* stub for calling mdaemon to return message to sender */
static void
retmail (cb)
struct smtpcli *cb;
{
FILE *infile;
#ifdef SMTPTRACE
if (Smtptrace > 5)
{
tprintf ("smtp job %s returned to sender\n", cb->wname);
}
#endif
if ((infile = fopen (cb->tname, READ_TEXT)) == NULLFILE)
return;
mdaemon (infile, cb->jobq->from, cb->errlog, 1);
fclose (infile);
}
/* look to see if a smtp control block exists for this ipdest */
static struct smtpcli *
lookup (destaddr)
int32 destaddr;
{
register int i;
for (i = 0; i < MAXSESSIONS; i++)
{
if (cli_session[i] == NULLSMTPCLI)
continue;
if (cli_session[i]->ipdest == destaddr)
return cli_session[i];
}
return NULLSMTPCLI;
}
/* create a new smtp control block */
static struct smtpcli *
newcb ()
{
register int i;
register struct smtpcli *cb;
for (i = 0; i < MAXSESSIONS; i++)
{
if (cli_session[i] == NULLSMTPCLI)
{
cb = (struct smtpcli *) callocw (1, sizeof (struct smtpcli));
cb->wname = mallocw ((unsigned) strlen (Mailqdir) + JOBNAME);
cb->tname = mallocw ((unsigned) strlen (Mailqdir) + JOBNAME);
cli_session[i] = cb;
Smtpsessions++; /* number of connections
* active */
return (cb);
}
}
return NULLSMTPCLI;
}
static void
execjobs ()
{
register struct smtpcli *cb;
register int i;
for (i = 0; i < MAXSESSIONS; i++)
{
cb = cli_session[i];
if (cb == NULLSMTPCLI)
continue;
if (cb->lock)
continue;
sprintf (cb->tname, "%s/%s.txt", Mailqdir, cb->jobq->jobname);
sprintf (cb->wname, "%s/%s.wrk", Mailqdir, cb->jobq->jobname);
newproc ("smtp_send", 1024, smtp_send, 0, cb, NULL, 0);
#ifdef SMTPTRACE
if (Smtptrace)
tprintf ("Trying Connection to %s\n", inet_ntoa (cb->ipdest));
#endif
}
}
/* add this job to control block queue */
static struct smtp_job *
setupjob (cb, id, from)
struct smtpcli *cb;
char *id, *from;
{
register struct smtp_job *p1, *p2;
p1 = (struct smtp_job *) callocw (1, sizeof (struct smtp_job));
p1->from = strdup (from);
strcpy (p1->jobname, id);
/* now add to end of jobq */
if ((p2 = cb->jobq) == NULLJOB)
cb->jobq = p1;
else
{
while (p2->next != NULLJOB)
p2 = p2->next;
p2->next = p1;
}
return p1;
}
/* called to advance to the next job */
static int
next_job (cb)
register struct smtpcli *cb;
{
register struct smtp_job *jp;
jp = cb->jobq->next;
del_job (cb->jobq);
/* remove the error log of previous message */
del_list (cb->errlog);
cb->errlog = NULLLIST;
cb->jobq = jp;
if (jp == NULLJOB)
return 0;
sprintf (cb->tname, "%s/%s.txt", Mailqdir, jp->jobname);
sprintf (cb->wname, "%s/%s.wrk", Mailqdir, jp->jobname);
#ifdef SMTPTRACE
if (Smtptrace > 5)
{
tprintf ("sending job %s\n", jp->jobname);
}
#endif
return 1;
}
/* Mail routing function. */
int32
mailroute (dest, gateway)
char *dest;
int32 gateway;
{
int32 destaddr;
/* If the destination is this site, then *don't* go through a mail
* server (MX site)
*/
if (stricmp (dest, Hostname) == 0)
if ((destaddr = resolve (dest)) != 0)
return destaddr;
/* look up address or use the gateway */
destaddr = resolve_mx (dest);
if (destaddr == 0 && (destaddr = resolve (dest)) == 0)
{
#ifdef DSERVER
if (zone_filename (dest, NULL))
destaddr = gateway;
else
#endif
if (Gateway != 0)
destaddr = Gateway; /* Use the gateway */
}
return destaddr;
}
/* save line in error list */
static void
logerr (cb, line)
struct smtpcli *cb;
char *line;
{
register struct list *lp, *tp;
tp = (struct list *) callocw (1, sizeof (struct list));
tp->val = strdup (line);
/* find end of list */
if ((lp = cb->errlog) == NULLLIST)
cb->errlog = tp;
else
{
while (lp->next != NULLLIST)
lp = lp->next;
lp->next = tp;
}
}
static int
smtpsendfile (cb)
register struct smtpcli *cb;
{
int error = 0;
strcpy (cb->buf, "\n");
while (fgets (cb->buf, sizeof (cb->buf), cb->tfile) != NULLCHAR)
{
/*
** Dot transparency: add another '.' at the start of
** any line which already starts with a '.'
*/
if (cb->buf[0] == '.')
usputc (cb->s,'.');
usputs (cb->s, cb->buf);
}
fclose (cb->tfile);
cb->tfile = NULLFILE;
/* Send the end-of-message command */
if (cb->buf[strlen (cb->buf) - 1] == '\n')
sendcmd (cb, ".\n");
else
sendcmd (cb, "\n.\n");
return error;
}
/* do a printf() on the socket with optional local tracing */
#ifdef ANSIPROTO
static void
sendcmd (struct smtpcli * cb, char *fmt,...)
{
va_list args;
va_start (args, fmt);
#ifdef SMTPTRACE
if (Smtptrace)
{
vsprintf (cb->buf, fmt, args);
tprintf ("smtp sent: %s", cb->buf);
}
#endif
vsprintf (cb->buf, fmt, args);
usputs (cb->s, cb->buf);
va_end (args);
}
#else
static void
sendcmd (cb, fmt, arg1, arg2, arg3, arg4)
struct smtpcli *cb;
char *fmt;
int arg1, arg2, arg3, arg4;
{
#ifdef SMTPTRACE
if (Smtptrace)
{
tprintf ("smtp sent: ");
tprintf (fmt, arg1, arg2, arg3, arg4);
}
#endif
sprintf (cb->buf, fmt, arg1, arg2, arg3, arg4);
usputs (cb->s, cb->buf);
}
#endif
/* Wait for, read and display response from server. Return the result code. */
static int
getresp (cb, mincode)
struct smtpcli *cb;
int mincode; /* Keep reading until at least this code comes
* back */
{
int rval;
char line[LINELEN];
usflush (cb->s);
for (;;)
{
/* Get line */
if (recvline (cb->s, line, LINELEN) == -1)
{
rval = -1;
break;
}
rip (line); /* Remove cr/lf */
rval = atoi (line);
#ifdef SMTPTRACE
if (Smtptrace)
tprintf ("smtp recv: %s\n", line); /* Display to user */
#endif
if (rval >= 500)
{ /* Save permanent error
* replies */
char tmp[LINELEN];
if (cb->errlog == NULLLIST)
{
sprintf (tmp, "While talking to %s:",
cb->destname);
logerr (cb, tmp);
}
if (cb->buf[0] != '\0')
{ /* Save offending command */
rip (cb->buf);
sprintf (tmp, ">>> %s", cb->buf);
logerr (cb, tmp);
cb->buf[0] = '\0';
}
sprintf (tmp, "<<< %s", line);
logerr (cb, tmp); /* save the error reply */
}
/* Messages with dashes are continued */
if (line[3] != '-' && rval >= mincode)
break;
}
return rval;
}
static int
doverbose (argc, argv, p)
int argc;
char *argv[];
void *p;
{
return setbool (&smtpverbose, "SMTP verbose mode", argc, argv);
}
unsigned long golden_ymdhms (long year, long month, long day, long hour, long minute, long second)
{
long result = 365L * year + day + 31L * (month - 1);
if (month < 3)
result += (year - 1L) / 4L - (75L * ((year - 1) / 100 + 1)) / 100L;
else
result = result - (40L * month + 230L) / 100L + year / 4L - (75L * (year / 100 + 1)) / 100L;
result *= 86400L;
result += 3600 * hour;
result += 60 * minute;
result += second;
return result;
}
static int
explock (dir, pre, suf, age)
char *dir, *pre, *suf;
int32 age;
{
union FILE_TIME
{
struct ftime ft;
unsigned int ft_int[2];
} file_time;
#if 0
struct ftime ft;
#endif
struct ffblk fileinfo;
struct tm *tm_now;
time_t t_now;
char t[80];
unsigned long now, fage, difference;
sprintf (t, "%s/%s.%s", dir, pre, suf);
if (findfirst (t, &fileinfo, 0) == -1)
return 1;
if (Delaytime == 0)
return !unlink (t);
t_now = time (NULL);
tm_now = localtime (&t_now);
now = golden_ymdhms (tm_now->tm_year + 1900, tm_now->tm_mon + 1,
tm_now->tm_mday, tm_now->tm_hour, tm_now->tm_min,
tm_now->tm_sec);
file_time.ft_int[0] = fileinfo.ff_ftime;
file_time.ft_int[1] = fileinfo.ff_fdate;
fage = golden_ymdhms (file_time.ft.ft_year + 1980, file_time.ft.ft_month,
file_time.ft.ft_day, file_time.ft.ft_hour, file_time.ft.ft_min,
file_time.ft.ft_tsec * 2);
difference = now - fage;
if (difference >= Delaytime)
return !unlink (t);
return 0;
}