CTM email tools.
Reviewed by: phk Submitted by: Stephen McKay <syssgm@devetir.qld.gov.au>
This commit is contained in:
parent
e322e6a55a
commit
e86e7d0e07
@ -1,4 +1,4 @@
|
||||
|
||||
SUBDIR= ctm ctm_scan
|
||||
SUBDIR= ctm ctm_scan ctm_rmail ctm_smail
|
||||
|
||||
.include <bsd.subdir.mk>
|
||||
|
5
usr.sbin/ctm/Makefile.inc
Normal file
5
usr.sbin/ctm/Makefile.inc
Normal file
@ -0,0 +1,5 @@
|
||||
# $Id$
|
||||
|
||||
.if exists(${.CURDIR}/../../Makefile.inc)
|
||||
.include "${.CURDIR}/../../Makefile.inc"
|
||||
.endif
|
6
usr.sbin/ctm/ctm_rmail/Makefile
Normal file
6
usr.sbin/ctm/ctm_rmail/Makefile
Normal file
@ -0,0 +1,6 @@
|
||||
PROG= ctm_rmail
|
||||
SRCS= ctm_rmail.c error.c
|
||||
CFLAGS+= -Wall -g
|
||||
MLINKS+= ctm_rmail.1 ctm_smail.1
|
||||
|
||||
.include <bsd.prog.mk>
|
310
usr.sbin/ctm/ctm_rmail/ctm_rmail.1
Normal file
310
usr.sbin/ctm/ctm_rmail/ctm_rmail.1
Normal file
@ -0,0 +1,310 @@
|
||||
.\" NOTICE: This is free documentation. I hope you get some use from these
|
||||
.\" words. In return you should think about all the nice people who sweat
|
||||
.\" blood to document their free software. Maybe you should write some
|
||||
.\" documentation and give it away. Maybe with a free program attached!
|
||||
.\"
|
||||
.\" Author: Stephen McKay
|
||||
.\"
|
||||
.Dd January 15, 1995
|
||||
.Os
|
||||
.Dt CTM_MAIL 1
|
||||
.Sh NAME
|
||||
.Nm ctm_smail, ctm_rmail
|
||||
.Nd send and receive
|
||||
.Nm ctm
|
||||
deltas via mail
|
||||
.Sh SYNOPSIS
|
||||
.Nm ctm_smail
|
||||
.Op Fl l Ar log
|
||||
.Op Fl m Ar maxmsgsize
|
||||
.Op Fl c Ar maxctmsize
|
||||
.Ar ctm-delta
|
||||
.Ar mail-alias
|
||||
.Nm ctm_rmail
|
||||
.Op Fl D
|
||||
.Op Fl l Ar log
|
||||
.Op Fl p Ar piecedir
|
||||
.Op Fl d Ar deltadir
|
||||
.Op Fl b Ar basedir
|
||||
.Op Ar
|
||||
.Sh DESCRIPTION
|
||||
In conjuction with the
|
||||
.Xr ctm 1
|
||||
command,
|
||||
.Nm ctm_smail
|
||||
and
|
||||
.Nm ctm_rmail
|
||||
are used to distribute changes to a source tree via email.
|
||||
.Nm ctm_smail
|
||||
is given a compressed
|
||||
.Nm ctm
|
||||
delta, and a mailing list to send it to. It splits the delta into manageable
|
||||
pieces, encodes them as mail messages and sends them to the mailing list.
|
||||
Each recipient uses
|
||||
.Nm ctm_rmail
|
||||
(either manually or automatically) to decode and reassemble the delta, and
|
||||
optionally call
|
||||
.Xr ctm 1
|
||||
to apply it to the source tree.
|
||||
At the moment,
|
||||
only two source trees are distributed, and both by the same site. These are
|
||||
the FreeBSD-current source and CVS trees, distributed by
|
||||
.Li ref.tfs.com .
|
||||
.Pp
|
||||
Command line arguments for
|
||||
.Nm ctm_smail :
|
||||
.Bl -tag -width indent
|
||||
.It Fl l Ar log
|
||||
Instead of appearing on
|
||||
.Em stderr ,
|
||||
error diagnostics and informational messages (other than command line errors)
|
||||
are time stamped and written to the file
|
||||
.Em log .
|
||||
.It Fl m Ar maxmsgsize
|
||||
Limit the maximum size mail message that
|
||||
.Nm ctm_smail
|
||||
is allowed to send. It is approximate since mail headers and other niceties
|
||||
are not counted in this limit. If not specified, it will default to 64000
|
||||
bytes, leaving room for 1535 bytes of headers before the rumoured 64k mail
|
||||
limit.
|
||||
.It Fl c Ar maxctmsize
|
||||
Limit the maximum size delta that will be sent. Deltas bigger that this
|
||||
limit will cause an apology mail message to be sent to the mailing list.
|
||||
This is to prevent massive changes overwhelming users' mail boxes. Note that
|
||||
this is the size before encoding. Encoding causes a 4/3 size increase before
|
||||
mail headers are added. If not specified, there is no limit.
|
||||
.El
|
||||
.Pp
|
||||
.Ar ctm-delta
|
||||
is the delta to be sent, and
|
||||
.Ar mail-alias
|
||||
is the mailing list to send the delta to.
|
||||
The mail messages are sent using
|
||||
.Xr sendmail 8 .
|
||||
.Pp
|
||||
Command line arguments for
|
||||
.Nm ctm_rmail :
|
||||
.Bl -tag -width indent
|
||||
.It Fl l Ar log
|
||||
Instead of appearing on
|
||||
.Em stderr ,
|
||||
error diagnostics and informational messages (other than command line errors)
|
||||
are time stamped and written to the file
|
||||
.Em log .
|
||||
.It Fl p Ar piecedir
|
||||
Collect pieces of deltas in this directory. Each piece corresponds to a
|
||||
single mail message. Pieces are removed when complete deltas are built.
|
||||
If this flag is not given, no input files will be read, but completed
|
||||
deltas may still be applied with
|
||||
.Xr ctm 1
|
||||
if the
|
||||
.Fl b
|
||||
flag is given.
|
||||
.It Fl d Ar deltadir
|
||||
Collect completed deltas in this directory. Deltas are built from one or
|
||||
more pieces when all pieces are present.
|
||||
.It Fl b Ar basedir
|
||||
Apply any completed deltas to this source tree. If this flag is not given,
|
||||
deltas will be stored, but not applied. The user may then apply the deltas
|
||||
manually, or by using
|
||||
.Nm ctm_rmail
|
||||
without the
|
||||
.Fl p
|
||||
flag.
|
||||
Deltas will not be applied if they do not match the
|
||||
.Li .ctm_status
|
||||
file in
|
||||
.Ar basedir
|
||||
(or if
|
||||
.Li .ctm_status
|
||||
does not exist).
|
||||
.It Fl D
|
||||
Delete deltas after successful application by
|
||||
.Xr ctm 1 .
|
||||
It is probably a good idea to avoid this flag (and keep all the deltas)
|
||||
as one of the possible future enhancements to
|
||||
.Xr ctm 1
|
||||
is the ability to recover small groups of files from a full set of deltas.
|
||||
.El
|
||||
.Pp
|
||||
The file arguments (or
|
||||
.Em stdin ,
|
||||
if there are none) are scanned for delta pieces. Multiple delta pieces
|
||||
can be read from a single file, so an entire maildrop can be scanned
|
||||
and processed with a single command.
|
||||
.Sh FILE FORMAT
|
||||
Following are the important parts of an actual (very small) delta piece:
|
||||
.Bd -literal
|
||||
From: src-cur-owner
|
||||
To: src-cur
|
||||
Subject: ctm-mail src-cur.0003.gz 1/4
|
||||
|
||||
CTM_MAIL BEGIN src-cur.0003.gz 1 4
|
||||
H4sIAAAAAAACA3VU72/bNhD9bP0VByQoEiyRSZEUSQP9kKTeYCR2gDTdsGFAwB/HRogtG5K8NCj6
|
||||
v4+UZSdtUQh6Rz0eee/xaF/dzx8up3/MFlDkBNrGnbttAwyo1pxoRgoiBNX/QJ5d3c9/X8DcPGGo
|
||||
lggkPiXngE4W1gUjKPJCYyk5MZRbIqmNW/ASglIFcdwIzTUxaAqhnCPcBqloKEkJVNDMF0Azk+Bo
|
||||
dDzzk0Ods/+A5gXv9YyJHjMCtJwQNeESNma7hOmXDRxn
|
||||
CTM_MAIL END 61065
|
||||
.Ed
|
||||
.Pp
|
||||
The subject of the message always begins with
|
||||
.Dq ctm-mail
|
||||
followed by the name of the delta, which piece this is, and how many total
|
||||
pieces there are. The data is bracketed by
|
||||
.Dq CTM_MAIL BEGIN
|
||||
and
|
||||
.Dq CTM_MAIL END
|
||||
lines, duplicating the information in the subject line, plus a simple checksum.
|
||||
.Pp
|
||||
If the delta exceeds
|
||||
.Ar maxctmsize ,
|
||||
then a message like this will be received instead:
|
||||
.Bd -literal
|
||||
From: src-cur-owner
|
||||
To: src-cur
|
||||
Subject: ctm-notice src-cur.0999.gz
|
||||
|
||||
src-cur.0999.gz is 792843 bytes. The limit is 300000 bytes.
|
||||
|
||||
You can retrieve this delta via ftpmail, or your good mate at the university.
|
||||
.Ed
|
||||
.Pp
|
||||
You are then on your own!
|
||||
.Sh EXAMPLES
|
||||
To send delta 32 of
|
||||
.Em src-cur
|
||||
to a group of wonderful code hackers known to
|
||||
.Xr sendmail 8
|
||||
as
|
||||
.Em src-guys ,
|
||||
limiting the mail size to roughly 60000 bytes, you could use:
|
||||
.Bd -literal -offset indent
|
||||
ctm_smail -m 60000 /wherever/it/is/src-cur.0032.gz src-guys
|
||||
.Ed
|
||||
.Pp
|
||||
To decode every
|
||||
.Nm ctm-mail
|
||||
message in your mailbox, assemble them into complete deltas, then apply
|
||||
any deltas built or lying around, you could use:
|
||||
.Bd -literal -offset indent
|
||||
ctm_rmail -p ~/pieces -d ~/deltas -b /usr/ctm-src-cur $MAIL
|
||||
.Ed
|
||||
.Pp
|
||||
(Note that no messages are deleted by
|
||||
.Nm ctm_rmail .
|
||||
Any mail reader could be used for that purpose.)
|
||||
.Pp
|
||||
To create a mail alias called
|
||||
.Em receiver-dude
|
||||
that will automatically decode and assemble deltas, but not apply them,
|
||||
you could put the following lines in your
|
||||
.Pa /etc/aliases
|
||||
file (assuming the
|
||||
.Pa /ctm/tmp
|
||||
and
|
||||
.Pa /ctm/deltas
|
||||
directories and
|
||||
.Pa /ctm/log
|
||||
file are writable by user
|
||||
.Em daemon
|
||||
or group
|
||||
.Em wheel ) :
|
||||
.Bd -literal -offset indent
|
||||
receiver-dude: "|ctm_rmail -p /ctm/tmp -d /ctm/deltas -l /ctm/log"
|
||||
owner-receiver-dude: real_dude@wherever.you.like
|
||||
.Ed
|
||||
.Pp
|
||||
The second line will catch failures and drop them into your regular mailbox,
|
||||
or wherever else you like.
|
||||
.Pp
|
||||
To apply all the deltas collected, and delete those applied, you could use:
|
||||
.Bd -literal -offset indent
|
||||
ctm_rmail -d /ctm/deltas -b /ctm/src-cur -l /ctm/apply.log
|
||||
.Ed
|
||||
.Sh SECURITY
|
||||
If you automatically take your mail and pass it to a file tree patcher, you
|
||||
might think you are handing the keys to your system to the hackers! Happily,
|
||||
the window for mischief is quite small.
|
||||
.Nm ctm_rmail
|
||||
is careful to write only to the directories given to it (by not believing any
|
||||
.Dq /
|
||||
characters in the delta name), and the latest
|
||||
.Nm ctm
|
||||
disallows absolute pathnames in files it manipulates, so the worst you
|
||||
could lose are a few source tree files (recoverable from your deltas).
|
||||
Since
|
||||
.Nm ctm
|
||||
requires that a
|
||||
.Nm md5
|
||||
checksum match before it touches a file, only fellow
|
||||
source recipients would be able to generate a fake delta, and they're such
|
||||
nice folk that they wouldn't even think of it! :-)
|
||||
.Pp
|
||||
Even this possibility could be removed by using cryptographic signatures.
|
||||
A possible future enhancement would be to use
|
||||
.Nm PGP
|
||||
to provide a secure wrapper.
|
||||
.\" This next request is for sections 1, 6, 7 & 8 only
|
||||
.Sh ENVIRONMENT
|
||||
If deltas are to be applied then
|
||||
.Xr ctm 1
|
||||
and
|
||||
.Xr gunzip 1
|
||||
must be in your
|
||||
.Ev PATH .
|
||||
.Sh FILES
|
||||
.Bl -tag -width indent
|
||||
.It Pa PIECEDIR/*
|
||||
Pieces of deltas waiting for the rest.
|
||||
.It Pa DELTADIR/*
|
||||
Completed deltas.
|
||||
.It Pa BASEDIR/.ctm_status
|
||||
File containing name and number of the next delta to be applied to this
|
||||
source tree.
|
||||
.\" This next request is for sections 1, 6, 7 & 8 only
|
||||
.\" (command return values (to shell) and fprintf/stderr type diagnostics)
|
||||
.Sh DIAGNOSTICS
|
||||
.Nm ctm_smail
|
||||
and
|
||||
.Nm ctm_rmail
|
||||
return exit status 0 for success, and 1 for various failures.
|
||||
.Nm ctm_rmail
|
||||
is expected to be called from a mail transfer program, and thus signals
|
||||
failure only when the input mail message should be bounced (preferably into
|
||||
your regular maildrop, not back to the sender). In short, failure to
|
||||
apply a completed delta with
|
||||
.Nm ctm
|
||||
is not considered an error important enough to bounce the mail, and
|
||||
.Nm ctm_rmail
|
||||
returns an exit status of 0.
|
||||
.Pp
|
||||
In normal operation,
|
||||
.Nm ctm_smail
|
||||
will report messages like:
|
||||
.Bd -literal -offset indent
|
||||
ctm_smail: src-cur.0250.gz 1/2 sent to src-guys
|
||||
.Ed
|
||||
.Pp
|
||||
.Nm ctm_rmail
|
||||
will report messages like:
|
||||
.Bd -literal -offset indent
|
||||
ctm_rmail: src-cur.0250.gz 1/2 stored
|
||||
ctm_rmail: src-cur.0250.gz 2/2 stored
|
||||
ctm_rmail: src-cur.0250.gz complete
|
||||
.Ed
|
||||
.Pp
|
||||
These messages go to
|
||||
.Em stderr
|
||||
or to the log file. Messages from
|
||||
.Nm ctm
|
||||
turn up here too. Error messages should be self explanatory.
|
||||
.\" The next request is for sections 2 and 3 error and signal handling only.
|
||||
.\" .Sh ERRORS
|
||||
.Sh SEE ALSO
|
||||
.Xr ctm 1
|
||||
(coming soon)
|
||||
.\" .Sh STANDARDS
|
||||
.\" .Sh HISTORY
|
||||
.Sh AUTHOR
|
||||
Stephen McKay <syssgm@devetir.qld.gov.au>
|
||||
.\" .Sh BUGS
|
486
usr.sbin/ctm/ctm_rmail/ctm_rmail.c
Normal file
486
usr.sbin/ctm/ctm_rmail/ctm_rmail.c
Normal file
@ -0,0 +1,486 @@
|
||||
/*
|
||||
* Accept one (or more) ASCII encoded chunks that together make a compressed
|
||||
* CTM delta. Decode them and reconstruct the deltas. Any completed
|
||||
* deltas may be passed to ctm for unpacking.
|
||||
*
|
||||
* Author: Stephen McKay
|
||||
*
|
||||
* NOTICE: This is free software. I hope you get some use from this program.
|
||||
* In return you should think about all the nice people who give away software.
|
||||
* Maybe you should write some free software too.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include "error.h"
|
||||
#include "options.h"
|
||||
|
||||
#define CTM_STATUS ".ctm_status"
|
||||
|
||||
char *piece_dir = NULL; /* Where to store pieces of deltas. */
|
||||
char *delta_dir = NULL; /* Where to store completed deltas. */
|
||||
char *base_dir = NULL; /* The tree to apply deltas to. */
|
||||
int delete_after = 0; /* Delete deltas after ctm applies them. */
|
||||
|
||||
void apply_complete(void);
|
||||
int read_piece(char *input_file);
|
||||
int combine_if_complete(char *delta, int pce, int npieces);
|
||||
int decode_line(char *line, char *out_buf);
|
||||
|
||||
/*
|
||||
* If given a '-p' flag, read encoded delta pieces from stdin or file
|
||||
* arguments, decode them and assemble any completed deltas. If given
|
||||
* a '-b' flag, pass any completed deltas to 'ctm' for application to
|
||||
* the source tree. The '-d' flag is mandatory, but either of '-p' or
|
||||
* '-b' can be omitted. If given the '-l' flag, notes and errors will
|
||||
* be timestamped and written to the given file.
|
||||
*
|
||||
* Exit status is 0 for success or 1 for indigestible input. That is,
|
||||
* 0 means the encode input pieces were decoded and stored, and 1 means
|
||||
* some input was discarded. If a delta fails to apply, this won't be
|
||||
* reflected in the exit status. In this case, the delta is left in
|
||||
* 'deltadir'.
|
||||
*/
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
char *log_file = NULL;
|
||||
int status = 0;
|
||||
|
||||
err_prog_name(argv[0]);
|
||||
|
||||
OPTIONS("[-D] [-p piecedir] [-d deltadir] [-b basedir] [-l log] [file ...]")
|
||||
FLAG('D', delete_after)
|
||||
STRING('p', piece_dir)
|
||||
STRING('d', delta_dir)
|
||||
STRING('b', base_dir)
|
||||
STRING('l', log_file)
|
||||
ENDOPTS
|
||||
|
||||
if (delta_dir == NULL || piece_dir == NULL && (base_dir == NULL || argc>1))
|
||||
usage();
|
||||
|
||||
if (log_file != NULL)
|
||||
err_set_log(log_file);
|
||||
|
||||
if (argc <= 1)
|
||||
{
|
||||
if (piece_dir != NULL)
|
||||
status = read_piece(NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
while (*++argv != NULL)
|
||||
status |= read_piece(*argv);
|
||||
}
|
||||
|
||||
if (base_dir != NULL)
|
||||
apply_complete();
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Construct the file name of a piece of a delta.
|
||||
*/
|
||||
#define mk_piece_name(fn,d,p,n) \
|
||||
sprintf((fn), "%s/%s+%d-%d", piece_dir, (d), (p), (n))
|
||||
|
||||
/*
|
||||
* Construct the file name of an assembled delta.
|
||||
*/
|
||||
#define mk_delta_name(fn,d) \
|
||||
sprintf((fn), "%s/%s", delta_dir, (d))
|
||||
|
||||
/*
|
||||
* If the next required delta is now present, let ctm lunch on it and any
|
||||
* contiguous deltas.
|
||||
*/
|
||||
void
|
||||
apply_complete()
|
||||
{
|
||||
int i, dn;
|
||||
FILE *fp, *ctm;
|
||||
struct stat sb;
|
||||
char class[20];
|
||||
char delta[30];
|
||||
char fname[1000];
|
||||
char buf[2000];
|
||||
char junk[2];
|
||||
char here[1000];
|
||||
|
||||
sprintf(fname, "%s/%s", base_dir, CTM_STATUS);
|
||||
if ((fp = fopen(fname, "r")) == NULL)
|
||||
return;
|
||||
|
||||
i = fscanf(fp, "%s %d %c", class, &dn, junk);
|
||||
fclose(fp);
|
||||
if (i != 2)
|
||||
return;
|
||||
|
||||
/*
|
||||
* We might need to convert the delta filename to an absolute pathname.
|
||||
*/
|
||||
here[0] = '\0';
|
||||
if (delta_dir[0] != '/')
|
||||
{
|
||||
getcwd(here, sizeof(here)-1);
|
||||
i = strlen(here) - 1;
|
||||
if (i >= 0 && here[i] != '/')
|
||||
{
|
||||
here[++i] = '/';
|
||||
here[++i] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Keep applying deltas until we run out or something bad happens.
|
||||
*/
|
||||
for (;;)
|
||||
{
|
||||
sprintf(delta, "%s.%04d.gz", class, ++dn);
|
||||
mk_delta_name(fname, delta);
|
||||
|
||||
if (stat(fname, &sb) < 0)
|
||||
return;
|
||||
|
||||
sprintf(buf, "(cd %s && ctm %s%s) 2>&1", base_dir, here, fname);
|
||||
if ((ctm = popen(buf, "r")) == NULL)
|
||||
{
|
||||
err("ctm failed to apply %s", delta);
|
||||
return;
|
||||
}
|
||||
|
||||
while (fgets(buf, sizeof(buf), ctm) != NULL)
|
||||
{
|
||||
i = strlen(buf) - 1;
|
||||
if (i >= 0 && buf[i] == '\n')
|
||||
buf[i] = '\0';
|
||||
err("ctm: %s", buf);
|
||||
}
|
||||
|
||||
if (pclose(ctm) != 0)
|
||||
{
|
||||
err("ctm failed to apply %s", delta);
|
||||
return;
|
||||
}
|
||||
|
||||
if (delete_after)
|
||||
unlink(fname);
|
||||
|
||||
err("%s applied%s", delta, delete_after ? " and deleted" : "");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This cheap plastic checksum effectively rotates our checksum-so-far
|
||||
* left one, then adds the character. We only want 16 bits of it, and
|
||||
* don't care what happens to the rest. It ain't much, but it's small.
|
||||
*/
|
||||
#define add_ck(sum,x) \
|
||||
((sum) += ((x)&0xff) + (sum) + (((sum)&0x8000) ? 1 : 0))
|
||||
|
||||
|
||||
/*
|
||||
* Decode the data between BEGIN and END, and stash it in the staging area.
|
||||
* Multiple pieces can be present in a single file, bracketed by BEGIN/END.
|
||||
* If we have all pieces of a delta, combine them. Returns 0 on success,
|
||||
* and 1 for any sort of failure.
|
||||
*/
|
||||
int
|
||||
read_piece(char *input_file)
|
||||
{
|
||||
int status = 0;
|
||||
FILE *ifp, *ofp;
|
||||
int decoding = 0;
|
||||
int line_no = 0;
|
||||
int i, n;
|
||||
int pce, npieces;
|
||||
unsigned claimed_cksum;
|
||||
unsigned short cksum;
|
||||
char out_buf[200];
|
||||
char line[200];
|
||||
char delta[30];
|
||||
char pname[1000];
|
||||
char junk[2];
|
||||
|
||||
ifp = stdin;
|
||||
if (input_file != NULL && (ifp = fopen(input_file, "r")) == NULL)
|
||||
{
|
||||
err("cannot open '%s' for reading", input_file);
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (fgets(line, sizeof(line), ifp) != NULL)
|
||||
{
|
||||
line_no++;
|
||||
|
||||
/*
|
||||
* Look for the beginning of an encoded piece.
|
||||
*/
|
||||
if (!decoding)
|
||||
{
|
||||
if (sscanf(line, "CTM_MAIL BEGIN %s %d %d %c", delta, &pce, &npieces, junk) == 3)
|
||||
{
|
||||
char *s;
|
||||
|
||||
while ((s = strchr(delta, '/')) != NULL)
|
||||
*s = '_';
|
||||
|
||||
mk_piece_name(pname, delta, pce, npieces);
|
||||
if ((ofp = fopen(pname, "w")) == NULL)
|
||||
{
|
||||
err("cannot open '%s' for writing", pname);
|
||||
status++;
|
||||
continue;
|
||||
}
|
||||
|
||||
cksum = 0xffff;
|
||||
decoding++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* We are decoding. Stop if we see the end flag.
|
||||
*/
|
||||
if (sscanf(line, "CTM_MAIL END %d %c", &claimed_cksum, junk) == 1)
|
||||
{
|
||||
int e;
|
||||
|
||||
decoding = 0;
|
||||
|
||||
fflush(ofp);
|
||||
e = ferror(ofp);
|
||||
fclose(ofp);
|
||||
|
||||
if (e)
|
||||
err("error writing %s", pname);
|
||||
|
||||
if (cksum != claimed_cksum)
|
||||
err("checksum: read %d, calculated %d", claimed_cksum, cksum);
|
||||
|
||||
if (e || cksum != claimed_cksum)
|
||||
{
|
||||
err("%s %d/%d discarded", delta, pce, npieces);
|
||||
unlink(pname);
|
||||
status++;
|
||||
continue;
|
||||
}
|
||||
|
||||
err("%s %d/%d stored", delta, pce, npieces);
|
||||
|
||||
if (!combine_if_complete(delta, pce, npieces))
|
||||
status++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Must be a line of encoded data. Decode it, sum it, and save it.
|
||||
*/
|
||||
n = decode_line(line, out_buf);
|
||||
if (n < 0)
|
||||
{
|
||||
err("line %d: illegal character: '%c'", line_no, line[-n]);
|
||||
err("%s %d/%d discarded", delta, pce, npieces);
|
||||
|
||||
fclose(ofp);
|
||||
unlink(pname);
|
||||
|
||||
status++;
|
||||
decoding = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
add_ck(cksum, out_buf[i]);
|
||||
|
||||
fwrite(out_buf, sizeof(char), n, ofp);
|
||||
}
|
||||
|
||||
if (decoding)
|
||||
{
|
||||
err("truncated file");
|
||||
err("%s %d/%d discarded", delta, pce, npieces);
|
||||
|
||||
fclose(ofp);
|
||||
unlink(pname);
|
||||
|
||||
status++;
|
||||
}
|
||||
|
||||
if (ferror(ifp))
|
||||
{
|
||||
err("error reading %s", input_file == NULL ? "stdin" : input_file);
|
||||
status++;
|
||||
}
|
||||
|
||||
if (input_file != NULL)
|
||||
fclose(ifp);
|
||||
|
||||
return (status != 0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Put the pieces together to form a delta, if they are all present.
|
||||
* Returns 1 on success (even if we didn't do anything), and 0 on failure.
|
||||
*/
|
||||
int
|
||||
combine_if_complete(char *delta, int pce, int npieces)
|
||||
{
|
||||
int i;
|
||||
FILE *dfp, *pfp;
|
||||
int c;
|
||||
struct stat sb;
|
||||
char pname[1000];
|
||||
char dname[1000];
|
||||
|
||||
/*
|
||||
* All here?
|
||||
*/
|
||||
for (i = 1; i <= npieces; i++)
|
||||
{
|
||||
if (i == pce)
|
||||
continue;
|
||||
mk_piece_name(pname, delta, i, npieces);
|
||||
if (stat(pname, &sb) < 0)
|
||||
return 1;
|
||||
}
|
||||
|
||||
mk_delta_name(dname, delta);
|
||||
|
||||
/*
|
||||
* We can probably just rename() it in to place if it is a small delta.
|
||||
*/
|
||||
if (npieces == 1)
|
||||
{
|
||||
mk_piece_name(pname, delta, 1, 1);
|
||||
if (rename(pname, dname) == 0)
|
||||
{
|
||||
err("%s complete", delta);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ((dfp = fopen(dname, "w")) == NULL)
|
||||
{
|
||||
err("cannot open '%s' for writing", dname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ok, the hard way. Reconstruct the delta by reading each piece in order.
|
||||
*/
|
||||
for (i = 1; i <= npieces; i++)
|
||||
{
|
||||
mk_piece_name(pname, delta, i, npieces);
|
||||
if ((pfp = fopen(pname, "r")) == NULL)
|
||||
{
|
||||
err("cannot open '%s' for reading", pname);
|
||||
fclose(dfp);
|
||||
unlink(dname);
|
||||
return 0;
|
||||
}
|
||||
while ((c = getc(pfp)) != EOF)
|
||||
putc(c, dfp);
|
||||
fclose(pfp);
|
||||
}
|
||||
fflush(dfp);
|
||||
if (ferror(dfp))
|
||||
{
|
||||
err("error writing '%s'", dname);
|
||||
fclose(dfp);
|
||||
unlink(dname);
|
||||
return 0;
|
||||
}
|
||||
fclose(dfp);
|
||||
|
||||
/*
|
||||
* Throw the pieces away.
|
||||
*/
|
||||
for (i = 1; i <= npieces; i++)
|
||||
{
|
||||
mk_piece_name(pname, delta, i, npieces);
|
||||
unlink(pname);
|
||||
}
|
||||
|
||||
err("%s complete", delta);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* MIME BASE64 decode table.
|
||||
*/
|
||||
static unsigned char from_b64[0x80] =
|
||||
{
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
|
||||
0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
|
||||
0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
|
||||
0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
|
||||
0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
|
||||
0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
|
||||
0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
|
||||
0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
|
||||
0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Decode a line of ASCII into binary. Returns the number of bytes in
|
||||
* the output buffer, or < 0 on indigestable input. Error output is
|
||||
* the negative of the index of the inedible character.
|
||||
*/
|
||||
int
|
||||
decode_line(char *line, char *out_buf)
|
||||
{
|
||||
unsigned char *ip = (unsigned char *)line;
|
||||
unsigned char *op = (unsigned char *)out_buf;
|
||||
unsigned long bits;
|
||||
unsigned x;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (*ip >= 0x80 || (x = from_b64[*ip]) >= 0x40)
|
||||
break;
|
||||
bits = x << 18;
|
||||
ip++;
|
||||
if (*ip < 0x80 && (x = from_b64[*ip]) < 0x40)
|
||||
{
|
||||
bits |= x << 12;
|
||||
*op++ = bits >> 16;
|
||||
ip++;
|
||||
if (*ip < 0x80 && (x = from_b64[*ip]) < 0x40)
|
||||
{
|
||||
bits |= x << 6;
|
||||
*op++ = bits >> 8;
|
||||
ip++;
|
||||
if (*ip < 0x80 && (x = from_b64[*ip]) < 0x40)
|
||||
{
|
||||
bits |= x;
|
||||
*op++ = bits;
|
||||
ip++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (*ip == '\0' || *ip == '\n')
|
||||
return op - (unsigned char *)out_buf;
|
||||
else
|
||||
return -(ip - (unsigned char *)line);
|
||||
}
|
70
usr.sbin/ctm/ctm_rmail/error.c
Normal file
70
usr.sbin/ctm/ctm_rmail/error.c
Normal file
@ -0,0 +1,70 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <time.h>
|
||||
#include "error.h"
|
||||
|
||||
static FILE *error_fp = NULL;
|
||||
static char *prog = NULL;
|
||||
|
||||
|
||||
/*
|
||||
* Log errors to the given file.
|
||||
*/
|
||||
void
|
||||
err_set_log(char *log_file)
|
||||
{
|
||||
FILE *fp;
|
||||
|
||||
if ((fp = fopen(log_file, "a")) == NULL)
|
||||
err("cannot log to '%s'", log_file);
|
||||
else
|
||||
error_fp = fp;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Set the error prefix if not logging to a file.
|
||||
*/
|
||||
void
|
||||
err_prog_name(char *name)
|
||||
{
|
||||
if ((prog = strrchr(name, '/')) == NULL)
|
||||
prog = name;
|
||||
else
|
||||
prog++;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Log an error.
|
||||
*/
|
||||
void
|
||||
err(char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
time_t now;
|
||||
struct tm *tm;
|
||||
FILE *fp;
|
||||
|
||||
if ((fp = error_fp) == NULL)
|
||||
{
|
||||
fp = stderr;
|
||||
if (prog != NULL)
|
||||
fprintf(fp, "%s: ", prog);
|
||||
}
|
||||
else
|
||||
{
|
||||
time(&now);
|
||||
tm = localtime(&now);
|
||||
fprintf(fp, "%04d-%02d-%02d %02d:%02d ", tm->tm_year+1900,
|
||||
tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min);
|
||||
}
|
||||
|
||||
va_start(ap, fmt);
|
||||
vfprintf(fp, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
fprintf(fp, "\n");
|
||||
fflush(fp);
|
||||
}
|
3
usr.sbin/ctm/ctm_rmail/error.h
Normal file
3
usr.sbin/ctm/ctm_rmail/error.h
Normal file
@ -0,0 +1,3 @@
|
||||
extern void err_set_log(char *log_file);
|
||||
extern void err_prog_name(char *name);
|
||||
extern void err(char *fmt, ...);
|
139
usr.sbin/ctm/ctm_rmail/options.h
Normal file
139
usr.sbin/ctm/ctm_rmail/options.h
Normal file
@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Macros for processing command arguments.
|
||||
*
|
||||
* Conforms closely to the command option requirements of intro(1) in System V
|
||||
* and intro(C) in Xenix.
|
||||
*
|
||||
* A command consists of: cmdname [ options ] [ cmdarguments ]
|
||||
*
|
||||
* Options consist of a leading dash '-' and a flag letter. An argument may
|
||||
* follow optionally preceded by white space.
|
||||
* Options without arguments may be grouped behind a single dash.
|
||||
* A dash on its own is interpreted as the end of the options and is retained
|
||||
* as a command argument.
|
||||
* A double dash '--' is interpreted as the end of the options and is discarded.
|
||||
*
|
||||
* For example:
|
||||
* zap -xz -f flame -q34 -- -x
|
||||
*
|
||||
* where zap.c contains the following in main():
|
||||
*
|
||||
* OPTIONS("[-xz] [-q queue-id] [-f dump-file] user")
|
||||
* FLAG('x', xecute)
|
||||
* FLAG('z', zot)
|
||||
* STRING('f', file)
|
||||
* fp = fopen(file, "w");
|
||||
* NUMBER('q', queue)
|
||||
* ENDOPTS
|
||||
*
|
||||
* Results in:
|
||||
* xecute = 1
|
||||
* zot = 1
|
||||
* file = "flame"
|
||||
* fp = fopen("flame", "w")
|
||||
* queue = 34
|
||||
* argc = 2
|
||||
* argv[0] = "zap"
|
||||
* argv[1] = "-x"
|
||||
*
|
||||
* Should the user enter unknown flags or leave out required arguments,
|
||||
* the message:
|
||||
*
|
||||
* Usage: zap [-xz] [-q queue-id] [-f dump-file] user
|
||||
*
|
||||
* will be printed. This message can be printed by calling pusage(), or
|
||||
* usage(). usage() will also cause program termination with exit code 1.
|
||||
*
|
||||
* Author: Stephen McKay, February 1991
|
||||
*
|
||||
* Based on recollection of the original options.h produced at the University
|
||||
* of Queensland by Ross Patterson (and possibly others).
|
||||
*/
|
||||
|
||||
static char *O_usage;
|
||||
static char *O_name;
|
||||
extern long atol();
|
||||
|
||||
void
|
||||
pusage()
|
||||
{
|
||||
/*
|
||||
* Avoid gratuitously loading stdio.
|
||||
*/
|
||||
write(2, "Usage: ", 7);
|
||||
write(2, O_name, strlen(O_name));
|
||||
write(2, " ", 1);
|
||||
write(2, O_usage, strlen(O_usage));
|
||||
write(2, "\n", 1);
|
||||
}
|
||||
|
||||
#define usage() (pusage(), exit(1))
|
||||
|
||||
#define OPTIONS(usage_msg) \
|
||||
{ \
|
||||
char O_cont; \
|
||||
O_usage = (usage_msg); \
|
||||
O_name = argv[0]; \
|
||||
while (*++argv && **argv == '-') \
|
||||
{ \
|
||||
if ((*argv)[1] == '\0') \
|
||||
break; \
|
||||
argc--; \
|
||||
if ((*argv)[1] == '-' && (*argv)[2] == '\0') \
|
||||
{ \
|
||||
argv++; \
|
||||
break; \
|
||||
} \
|
||||
O_cont = 1; \
|
||||
while (O_cont) \
|
||||
switch (*++*argv) \
|
||||
{ \
|
||||
case '-': \
|
||||
usage(); \
|
||||
case '\0': \
|
||||
O_cont = 0;
|
||||
|
||||
#define FLAG(x,flag) \
|
||||
break; \
|
||||
case (x): \
|
||||
(flag) = 1;
|
||||
|
||||
#define CHAR(x,ch) \
|
||||
break; \
|
||||
case (x): \
|
||||
O_cont = 0; \
|
||||
if (*++*argv == '\0' && (--argc, *++argv == 0)) \
|
||||
usage(); \
|
||||
(ch) = **argv;
|
||||
|
||||
#define NUMBER(x,n) \
|
||||
break; \
|
||||
case (x): \
|
||||
O_cont = 0; \
|
||||
if (*++*argv == '\0' && (--argc, *++argv == 0)) \
|
||||
usage(); \
|
||||
(n) = atol(*argv);
|
||||
|
||||
#define STRING(x,str) \
|
||||
break; \
|
||||
case (x): \
|
||||
O_cont = 0; \
|
||||
if (*++*argv == '\0' && (--argc, *++argv == 0)) \
|
||||
usage(); \
|
||||
(str) = *argv;
|
||||
|
||||
#define SUFFIX(x,str) \
|
||||
break; \
|
||||
case (x): \
|
||||
(str) = ++*argv; \
|
||||
O_cont = 0;
|
||||
|
||||
#define ENDOPTS \
|
||||
break; \
|
||||
default: \
|
||||
usage(); \
|
||||
} \
|
||||
} \
|
||||
O_end: \
|
||||
*--argv = O_name; \
|
||||
}
|
7
usr.sbin/ctm/ctm_smail/Makefile
Normal file
7
usr.sbin/ctm/ctm_smail/Makefile
Normal file
@ -0,0 +1,7 @@
|
||||
PROG= ctm_smail
|
||||
SRCS= ctm_smail.c error.c
|
||||
NOMAN= 1
|
||||
CFLAGS+= -Wall -g -I${.CURDIR}/../ctm_rmail
|
||||
|
||||
.include <bsd.prog.mk>
|
||||
.PATH: ${.CURDIR}/../ctm_rmail
|
324
usr.sbin/ctm/ctm_smail/ctm_smail.c
Normal file
324
usr.sbin/ctm/ctm_smail/ctm_smail.c
Normal file
@ -0,0 +1,324 @@
|
||||
/*
|
||||
* Send a compressed CTM delta to a recipient mailing list by encoding it
|
||||
* in safe ASCII characters, in mailer-friendly chunks, and passing it
|
||||
* to sendmail. The encoding is almost the same as MIME BASE64, and is
|
||||
* protected by a simple checksum.
|
||||
*
|
||||
* Author: Stephen McKay
|
||||
*
|
||||
* NOTICE: This is free software. I hope you get some use from this program.
|
||||
* In return you should think about all the nice people who give away software.
|
||||
* Maybe you should write some free software too.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#include <paths.h>
|
||||
#include "error.h"
|
||||
#include "options.h"
|
||||
|
||||
#define DEF_MAX_MSG 64000 /* Default maximum mail msg minus headers. */
|
||||
|
||||
#define LINE_LENGTH 76 /* Chars per encode line. Divisible by 4. */
|
||||
|
||||
void chop_and_send(char *delta, off_t ctm_size, long max_msg_size,
|
||||
char *mail_alias);
|
||||
unsigned encode_body(FILE *sm_fp, FILE *delta_fp, long msg_size);
|
||||
void write_header(FILE *sfp, char *mail_alias, char *delta, int pce,
|
||||
int npieces);
|
||||
void write_trailer(FILE *sfp, unsigned sum);
|
||||
void apologise(char *delta, off_t ctm_size, long max_ctm_size,
|
||||
char *mail_alias);
|
||||
FILE *open_sendmail(void);
|
||||
int close_sendmail(FILE *fp);
|
||||
|
||||
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
char *delta_file;
|
||||
char *mail_alias;
|
||||
long max_msg_size = DEF_MAX_MSG;
|
||||
long max_ctm_size = 0;
|
||||
char *log_file = NULL;
|
||||
struct stat sb;
|
||||
|
||||
err_prog_name(argv[0]);
|
||||
|
||||
OPTIONS("[-l log] [-m maxmsgsize] [-c maxctmsize] ctm-delta mail-alias")
|
||||
NUMBER('m', max_msg_size)
|
||||
NUMBER('c', max_ctm_size)
|
||||
STRING('l', log_file)
|
||||
ENDOPTS
|
||||
|
||||
if (argc != 3)
|
||||
usage();
|
||||
|
||||
if (log_file != NULL)
|
||||
err_set_log(log_file);
|
||||
|
||||
delta_file = argv[1];
|
||||
mail_alias = argv[2];
|
||||
|
||||
if (stat(delta_file, &sb) < 0)
|
||||
{
|
||||
err("%s: %s", delta_file, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (max_ctm_size != 0 && sb.st_size > max_ctm_size)
|
||||
apologise(delta_file, sb.st_size, max_ctm_size, mail_alias);
|
||||
else
|
||||
chop_and_send(delta_file, sb.st_size, max_msg_size, mail_alias);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Carve our CTM delta into pieces, encode them, and send them.
|
||||
*/
|
||||
void
|
||||
chop_and_send(char *delta, off_t ctm_size, long max_msg_size, char *mail_alias)
|
||||
{
|
||||
int npieces;
|
||||
long msg_size;
|
||||
long exp_size;
|
||||
int pce;
|
||||
FILE *sfp;
|
||||
FILE *dfp;
|
||||
unsigned sum;
|
||||
|
||||
#define howmany(x,y) (((x)+((y)-1))/(y))
|
||||
|
||||
/*
|
||||
* Work out how many pieces we need, bearing in mind that each piece
|
||||
* grows by 4/3 when encoded. We count the newlines too, but ignore
|
||||
* all mail headers and piece headers. They are a "small" (almost
|
||||
* constant) per message overhead that we make the user worry about. :-)
|
||||
*/
|
||||
exp_size = ctm_size * 4 / 3;
|
||||
exp_size += howmany(exp_size, LINE_LENGTH);
|
||||
npieces = howmany(exp_size, max_msg_size);
|
||||
msg_size = howmany(ctm_size, npieces);
|
||||
|
||||
#undef howmany
|
||||
|
||||
if ((dfp = fopen(delta, "r")) == NULL)
|
||||
{
|
||||
err("cannot open '%s' for reading.", delta);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (pce = 1; pce <= npieces; pce++)
|
||||
{
|
||||
sfp = open_sendmail();
|
||||
if (sfp == NULL)
|
||||
exit(1);
|
||||
write_header(sfp, mail_alias, delta, pce, npieces);
|
||||
sum = encode_body(sfp, dfp, msg_size);
|
||||
write_trailer(sfp, sum);
|
||||
if (!close_sendmail(sfp))
|
||||
exit(1);
|
||||
err("%s %d/%d sent to %s", delta, pce, npieces, mail_alias);
|
||||
}
|
||||
|
||||
fclose(dfp);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* MIME BASE64 encode table.
|
||||
*/
|
||||
static char to_b64[0x40] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
/*
|
||||
* This cheap plastic checksum effectively rotates our checksum-so-far
|
||||
* left one, then adds the character. We only want 16 bits of it, and
|
||||
* don't care what happens to the rest. It ain't much, but it's small.
|
||||
*/
|
||||
#define add_ck(sum,x) \
|
||||
((sum) += ((x)&0xff) + (sum) + (((sum)&0x8000) ? 1 : 0))
|
||||
|
||||
/*
|
||||
* Encode the body. Use an encoding almost the same as MIME BASE64.
|
||||
*
|
||||
* Characters are read from delta_fp and encoded characters are written
|
||||
* to sm_fp. At most 'msg_size' characters should be read from delta_fp.
|
||||
*
|
||||
* The body consists of lines of up to LINE_LENGTH characters. Each group
|
||||
* of 4 characters encodes 3 input characters. Each output character encodes
|
||||
* 6 bits. Thus 64 different characters are needed in this representation.
|
||||
*/
|
||||
unsigned
|
||||
encode_body(FILE *sm_fp, FILE *delta_fp, long msg_size)
|
||||
{
|
||||
unsigned short cksum = 0xffff;
|
||||
unsigned char *ip;
|
||||
char *op;
|
||||
int want, n, i;
|
||||
unsigned char inbuf[LINE_LENGTH*3/4];
|
||||
char outbuf[LINE_LENGTH+1];
|
||||
|
||||
/*
|
||||
* Round up to the nearest line boundary, for the tiniest of gains,
|
||||
* and lots of neatness. :-)
|
||||
*/
|
||||
msg_size += (LINE_LENGTH*3/4) - 1;
|
||||
msg_size -= msg_size % (LINE_LENGTH*3/4);
|
||||
|
||||
while (msg_size > 0)
|
||||
{
|
||||
want = (msg_size < sizeof(inbuf)) ? msg_size : sizeof(inbuf);
|
||||
if ((n = fread(inbuf, sizeof(char), want, delta_fp)) == 0)
|
||||
break;
|
||||
msg_size -= n;
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
add_ck(cksum, inbuf[i]);
|
||||
|
||||
/*
|
||||
* Produce a line of encoded data. Every line length will be a
|
||||
* multiple of 4, except for, perhaps, the last line.
|
||||
*/
|
||||
ip = inbuf;
|
||||
op = outbuf;
|
||||
while (n >= 3)
|
||||
{
|
||||
*op++ = to_b64[ip[0] >> 2];
|
||||
*op++ = to_b64[(ip[0] << 4 & 0x3f) | ip[1] >> 4];
|
||||
*op++ = to_b64[(ip[1] << 2 & 0x3f) | ip[2] >> 6];
|
||||
*op++ = to_b64[ip[2] & 0x3f];
|
||||
ip += 3;
|
||||
n -= 3;
|
||||
}
|
||||
if (n > 0)
|
||||
{
|
||||
*op++ = to_b64[ip[0] >> 2];
|
||||
*op++ = to_b64[(ip[0] << 4 & 0x3f) | ip[1] >> 4];
|
||||
if (n >= 2)
|
||||
*op++ = to_b64[ip[1] << 2 & 0x3f];
|
||||
}
|
||||
*op++ = '\n';
|
||||
fwrite(outbuf, sizeof(char), op - outbuf, sm_fp);
|
||||
}
|
||||
|
||||
if (ferror(delta_fp))
|
||||
{
|
||||
err("error reading input file.");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (ferror(sm_fp))
|
||||
{
|
||||
err("error writing to sendmail");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return cksum;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Write the mail header and data header.
|
||||
*/
|
||||
void
|
||||
write_header(FILE *sfp, char *mail_alias, char *delta, int pce, int npieces)
|
||||
{
|
||||
char *sn;
|
||||
|
||||
if ((sn = strrchr(delta, '/')) == NULL)
|
||||
sn = delta;
|
||||
else
|
||||
sn++;
|
||||
|
||||
fprintf(sfp, "From: %s-owner\n", mail_alias);
|
||||
fprintf(sfp, "To: %s\n", mail_alias);
|
||||
fprintf(sfp, "Subject: ctm-mail %s %d/%d\n\n", sn, pce, npieces);
|
||||
|
||||
fprintf(sfp, "CTM_MAIL BEGIN %s %d %d\n", sn, pce, npieces);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Write the data trailer.
|
||||
*/
|
||||
void
|
||||
write_trailer(FILE *sfp, unsigned sum)
|
||||
{
|
||||
fprintf(sfp, "CTM_MAIL END %ld\n", (long)sum);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* We're terribly sorry, but the delta is too big to send.
|
||||
*/
|
||||
void
|
||||
apologise(char *delta, off_t ctm_size, long max_ctm_size, char *mail_alias)
|
||||
{
|
||||
FILE *sfp;
|
||||
char *sn;
|
||||
|
||||
sfp = open_sendmail();
|
||||
if (sfp == NULL)
|
||||
exit(1);
|
||||
|
||||
if ((sn = strrchr(delta, '/')) == NULL)
|
||||
sn = delta;
|
||||
else
|
||||
sn++;
|
||||
|
||||
fprintf(sfp, "From: %s-owner\n", mail_alias);
|
||||
fprintf(sfp, "To: %s\n", mail_alias);
|
||||
fprintf(sfp, "Subject: ctm-notice %s\n\n", sn);
|
||||
|
||||
fprintf(sfp, "%s is %ld bytes. The limit is %ld bytes.\n\n", sn,
|
||||
(long)ctm_size, max_ctm_size);
|
||||
fprintf(sfp, "You can retrieve this delta via ftpmail, or your good mate at the university.\n");
|
||||
|
||||
if (!close_sendmail(sfp))
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Start a pipe to sendmail. Sendmail will decode the destination
|
||||
* from the message contents.
|
||||
*/
|
||||
FILE *
|
||||
open_sendmail()
|
||||
{
|
||||
FILE *fp;
|
||||
char buf[100];
|
||||
|
||||
sprintf(buf, "%s -t", _PATH_SENDMAIL);
|
||||
if ((fp = popen(buf, "w")) == NULL)
|
||||
err("cannot start sendmail");
|
||||
return fp;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Close a pipe to sendmail. Sendmail will then do its bit.
|
||||
* Return 1 on success, 0 on failure.
|
||||
*/
|
||||
int
|
||||
close_sendmail(FILE *fp)
|
||||
{
|
||||
int status;
|
||||
|
||||
fflush(fp);
|
||||
if (ferror(fp))
|
||||
{
|
||||
err("error writing to sendmail");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((status = pclose(fp)) != 0)
|
||||
err("sendmail failed with status %d", status);
|
||||
|
||||
return (status == 0);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user