static char rcsid[] = "@(#)$Id: mk_aliases.c,v 1.12 2006/04/09 07:37:05 hurtta Exp $";
/******************************************************************************
* The Elm (ME+) Mail System - $Revision: 1.12 $ $State: Exp $
*
* Modified by: Kari Hurtta <hurtta+elm@posti.FMI.FI>
* (was hurtta+elm@ozone.FMI.FI)
******************************************************************************
* The Elm Mail System
*
* Copyright (c) 1988-1992 USENET Community Trust
* Copyright (c) 1986,1987 Dave Taylor
*****************************************************************************/
/** Install a new set of aliases for the 'Elm' mailer.
This code is shared with newalias and elm so that
it is easier to do updates while in elm. The main routine
here is do_newalias(). If the third argument is TRUE then
we were called from elm. That means that we will need to
sleep() between error messages.... The fourth arguement
controls whether or not we should warn about missing aliases.text
files. For the newalias program that's an error, for elm it's
ok. Warning -- this last is a *hack* to get elm2.4b out the
door -- do_newalias should really return an error value and
the caller should generate the message.
**/
#include "headers.h"
#include "s_newalias.h"
#include "ndbz.h"
#ifdef BSD_TYPE
# include <sys/file.h>
#endif
#define group(string) (index(string,',') != NULL)
int buff_loaded; /* for file input overlap... */
int err_flag; /* if errors, don't save! */
int al_count; /* how many aliases so far? */
DBZ *aliases_hash; /* current alias file */
char *buffer; /* alias line buffer */
long buffer_size; /* size of alias buffer */
long file_offset = 0; /* offset into file so far */
int is_system = 0; /* system file updating? */
#ifdef DEBUG
extern int debug;
#endif
int
get_alias(file, fromelm)
FILE *file;
int fromelm;
{
/* load buffer with the next complete alias from the file.
(this can include reading in multiple lines and appending
them all together!) Returns EOF after last entry in file.
Lines that start with '#' are assumed to be comments and are
ignored. White space as the first field of a line is taken
to indicate that this line is a continuation of the previous. */
static char mybuffer[LONG_STRING];
int done = 0, len;
char *s;
/** get the first line of the entry... **/
buffer[0] = '\0'; /* zero out line */
len = 0;
if (get_line(file, mybuffer, TRUE, fromelm) == -1)
return(-1);
strfcpy(buffer, mybuffer, buffer_size);
len = strlen(buffer);
/** now read in the rest (if there is any!) **/
do {
if (get_line(file, mybuffer, FALSE, fromelm) == -1) {
if (err_flag)
return(-1);
buff_loaded = 0; /* force a read next pass! */
return(0); /* okay. let's just hand 'buffer' back! */
}
done = (! whitespace(mybuffer[0]));
if (! done) {
for (s = mybuffer; *s && whitespace(*s); s++) ;
*--s = ' ';
len += strlen(s);
if (len >= buffer_size) {
lib_error(CATGETS(elm_msg_cat,
NewaliasSet, NewaliasLineToLong,
"Line + continuations exceeds maximum length of %ld:"),
buffer_size);
if (fromelm && sleepmsg > 0) {
#if POLL_METHOD
wait_for_timeout(sleepmsg);
#else
sleep(sleepmsg);
#endif
}
lib_error(FRM("%.40s"), buffer);
err_flag++;
} else
strfcat(buffer, s, buffer_size);
}
} while (! done);
return(0); /* no sweat! */
}
int get_line(file, buffer, first_line, fromelm)
FILE *file;
char *buffer;
int first_line, fromelm;
{
/*
* Read line from file.
* If first_line and buff_loaded, then just return!
* All comment and blank lines are just ignored (never passed back).
*/
int len;
if (first_line && buff_loaded) {
buff_loaded = 1;
return(0);
}
buff_loaded = 1; /* we're going to get SOMETHING in the buffer */
do {
/*
* We will just ignore any line that begins with comment (no
* matter how long).
*/
do {
len = mail_gets(buffer, LONG_STRING, file);
} while (buffer[0] == '#');
if (len > 0) {
if (buffer[len - 1] != '\n') {
if (fromelm) {
lib_error(CATGETS(elm_msg_cat, NewaliasSet,
NewaliasSplitShort,
"Line too long, split using continuation line format:"));
if (sleepmsg > 0) {
#if POLL_METHOD
wait_for_timeout(sleepmsg);
#else
sleep(sleepmsg);
#endif
}
}
else {
lib_error(CATGETS(elm_msg_cat,
NewaliasSet, NewaliasSplit,
"Line too long, split using continuation line format (starting line\nwith whitespace):\n%.40s\n"),
buffer);
}
err_flag++;
return(-1);
}
}
else {
return(-1);
}
no_ret(buffer);
/*
* If the buffer is zero length after returns are stripped (and
* len was > 0) we need to go ahead and get another line.
*/
} while (strlen(buffer) == 0);
return(0);
}
void de_escape(the_string)
char *the_string;
{
register char *s, *out;
for (s = the_string, out = the_string; *s; s++) {
if (*s != '\\')
*out++ = *s;
else
*out++ = *++s;
}
*out = '\0';
return;
}
int add_to_hash_table(word, offset)
char *word;
int32 offset;
{
datum key, value, ovalue;
int32 off;
key.dptr = word;
key.dsize = strlen(word);
off = offset;
value.dptr = (char *) &off;
value.dsize = sizeof(off);
ovalue = dbz_fetch(aliases_hash, key);
if (ovalue.dptr != NULL) {
lib_error(CATGETS(elm_msg_cat,
NewaliasSet, NewaliasDupAlias,
"** Duplicate alias '%s' in file. Multiples ignored."),
word);
return(-1);
}
if (dbz_store(aliases_hash, key, value) < 0) {
lib_error(CATGETS(elm_msg_cat,
NewaliasSet, NewaliasErrWrite,
"** Error writing alias '%s'."), word);
err_flag++;
return(-1);
}
/*
* No probs. Increment the number of aliases done.
*/
al_count++;
return(0);
}
void add_to_table(data, aliases, lastn, firstn, comment, addresses)
FILE *data;
char *aliases, *lastn, *firstn, *comment, *addresses;
{
struct alias_disk_rec alias;
char *s;
/*
* crack the information into an alias_rec structure, then add the entry
* each alias at a time to the dbz file.
*/
alias.status = 0;
alias.alias = 0;
/*
* loop over each alias in aliases (split at the ,)
*/
while (aliases != NULL) {
if ((s = index(aliases, ',')) != NULL)
*s++ = '\0';
alias.last_name = alias.alias + strlen(aliases) + 1;
alias.name = alias.last_name + strlen(lastn) + 1;
alias.comment = alias.name + strlen(lastn) + 1;
if (firstn)
alias.comment += strlen(firstn) + 1;
if (comment)
alias.address = alias.comment + strlen(comment) + 1;
else
alias.address = alias.comment + 1;
alias.type = is_system ? SYSTEM : USER;
alias.type |= group(addresses) ? GROUP : PERSON;
alias.length = ((int) alias.address) + strlen(addresses) + 1;
/*
* Convert alias name to lower case for writing to file
*/
aliases = shift_lower(aliases);
/*
* Only add an entry if we could add it to the hash table.
* (no errors or duplicates)
*/
if (add_to_hash_table(aliases, (int32)(file_offset+sizeof(alias)))
== 0) {
file_offset += alias.length + sizeof(alias);
/*
* Write the entry to the data file, followed by its data
*/
fwrite((char *)&alias, sizeof(alias), 1, data);
fwrite(aliases, strlen(aliases) + 1, 1, data);
fwrite(lastn, strlen(lastn) + 1, 1, data);
if (firstn) {
fwrite(firstn, strlen(firstn), 1, data);
fwrite(" ", 1, 1, data);
}
fwrite(lastn, strlen(lastn) + 1, 1, data);
if (comment)
fwrite(comment, strlen(comment) + 1, 1, data);
else
fwrite("\0", 1, 1, data);
fwrite(addresses, strlen(addresses) + 1, 1, data);
fflush(data);
}
aliases = s;
}
}
int check_alias(aliases)
char *aliases;
{
/*
* Check and make sure this is a legal alias.
*/
register char *s, *out;
int badws_flg = 0;
/*
* First, strip out any whitespace (and make sure it was
* legal whitespace). Legal whitespace follows ','.
*/
for (s = aliases, out = aliases; *s; s++) {
if (whitespace(*s)) {
if (*(out-1) != ',') {
badws_flg++; /* Keep going for now */
*out++ = *s;
}
}
else {
*out++ = *s;
}
}
*out = '\0';
/*
* We have to delay reporting the error until the (legal)
* spaces are striped, otherwise aliases is all screwed
* up and doesn't display well at all.
*/
if (badws_flg) {
lib_error(CATGETS(elm_msg_cat,
NewaliasSet, NewaliasAliasWSNotAllowed,
"Error - whitespace in alias '%.30s' is not allowed."),
aliases);
return(-1);
}
/*
* Verify the alias name is valid
*/
for (s = aliases; *s != '\0' && (ok_alias_char(*s)||*s==','); ++s ) ;
if ( *s != '\0' ) {
lib_error(CATGETS(elm_msg_cat,
NewaliasSet, NewaliasCharNotSupported,
"Error - character '%c' in alias '%s' is not supported."),
*s, aliases);
return(-1);
}
return(0);
}
int check_address(addresses)
char *addresses;
{
register char *s, *out;
int in_quote = FALSE;
int badws_flg = 0;
/*
* Now strip out any whitespace (and make sure it was
* legal whitespace). Legal whitespace follows ','.
* Don't mess with white space in quotes.
*/
for (s = addresses, out = addresses; *s; s++) {
if (*s == '"')
in_quote = !in_quote;
if (!in_quote && whitespace(*s)) {
if (*(out-1) == ',') {
continue;
}
else {
badws_flg++; /* Keep going for now */
}
}
*out++ = *s;
}
*out = '\0';
/*
* We have to delay reporting the error until the (legal)
* spaces are striped, otherwise addresses is all screwed
* up and doesn't display well at all.
*/
if (badws_flg) {
lib_error(CATGETS(elm_msg_cat,
NewaliasSet, NewaliasAddressWSNotAllowed,
"Error - whitespace in address '%.30s' is not allowed."),
addresses);
return(-1);
}
return(0);
}
void put_alias(data)
FILE *data;
{
/*
* parse the buffer into aliases, names, comments and addresses
* and then add the alias
*/
char *s, *aliases, *lastn, *firstn, *comment, *addresses = NULL;
int in_quote = FALSE;
/*
* extract the alias name, its the part up to the first =, less the
* white space on the end.
*/
aliases = buffer;
if ((s = index(buffer, '=')) == NULL) {
lib_error(CATGETS(elm_msg_cat,
NewaliasSet, NewaliasNoFieldSep,
"Error - alias \"%.40s\" missing '=' field separator."),
aliases);
err_flag++;
return;
}
lastn = s + 1;
while (--s >= aliases && whitespace(*s) ) ;
*++s = '\0';
if (check_alias(aliases) == -1) {
err_flag++;
return;
}
/*
* get the second field into "lastn" - putting stuff after ','
* into "comment". skip over white space after = and before last =
*/
while (*lastn != '\0' && whitespace(*lastn) )
lastn++;
for (s = lastn; *s; s++) {
if (*s == '\\') {
s++;
continue;
}
if (*s == '"')
in_quote = !in_quote;
if (in_quote)
continue;
if (*s == '=') {
addresses = s + 1;
break;
}
}
if (*s != '=') {
lib_error(CATGETS(elm_msg_cat,
NewaliasSet, NewaliasNoFieldSep,
"Error - alias \"%.40s\" missing '=' field separator."),
aliases);
err_flag++;
return;
}
/*
* Remove trailing whitespace from second field
*/
while (--s >= lastn && whitespace(*s) ) ;
*++s = '\0';
/*
* now get anything after a comma (marks comment within name field)
*/
for (s = lastn, comment = NULL; *s; s++) {
if (*s == '\\') {
s++;
continue;
}
if (*s == '"')
in_quote = !in_quote;
if (in_quote)
continue;
if (*s == ',') {
comment = s + 1;
while (--s >= lastn && whitespace(*s) ) ;
*++s = '\0'; /* Trailing whitespace... */
break;
}
}
/*
* strip leading whitespace from comment
*/
if (comment) {
while (*comment != '\0' && whitespace(*comment) )
comment++;
}
/*
* remainder of line is the addresses, remove leading and
* trailing whitespace
*/
while (*addresses != '\0' && whitespace(*addresses) )
addresses++;
s = addresses + strlen(addresses);
while (--s >= addresses && whitespace(*s) ) ;
*++s = '\0';
if (check_address(addresses) == -1) {
err_flag++;
return;
}
/*
* split the lastn field into firstn and lastn
*/
for (s = lastn, firstn = NULL; *s; s++) {
if (*s == '\\') {
s++;
continue;
}
if (*s == '"')
in_quote = !in_quote;
if (in_quote)
continue;
if (*s == ';') {
firstn = s + 1;
while (--s >= lastn && whitespace(*s) ) ;
*++s = '\0'; /* Trailing whitespace... */
break;
}
}
/*
* strip leading whitespace from firstn
*/
if (firstn) {
while (*firstn && whitespace(*firstn))
firstn++;
}
/*
* now remove 'escapes' from name/comment and aliases fields, in place
*/
de_escape(lastn);
if (firstn) {
de_escape(firstn);
}
if (comment) {
de_escape(comment);
}
de_escape(addresses);
add_to_table(data, aliases, lastn, firstn, comment, addresses);
}
static void delete_alias_files P_((char *, int)); /* Prototype */
int do_newalias(inputname, dataname, fromelm, textwarn)
char *inputname, *dataname;
int fromelm, textwarn;
{
FILE *in, *data;
int err;
/*
* try and allocate a big buffer (larger than a 64k segment...
* if it succeeds, mark dbz to run in-core
* if not, allocate a smaller buffer
*/
if ((buffer = malloc(20 * VERY_LONG_STRING)) == NULL) {
if ((buffer = malloc(2 * VERY_LONG_STRING)) == NULL) {
lib_error(CATGETS(elm_msg_cat, NewaliasSet, NewaliasNoAlloc,
"Unable to allocate space for alias buffer!"));
return(-1);
}
buffer_size = 2 * VERY_LONG_STRING;
dbz_incore(FALSE);
}
else {
buffer_size = 20 * VERY_LONG_STRING;
dbz_incore(TRUE);
}
err = can_open(inputname,"r");
if (err) {
if ( textwarn ) {
lib_error(CATGETS(elm_msg_cat,
NewaliasSet, NewaliasNoOpenIn,
"Couldn't open %s for input!"),
inputname);
}
free(buffer);
return(-1);
}
if ((in = fopen(inputname,"r")) == NULL) {
if ( textwarn ) {
lib_error(CATGETS(elm_msg_cat,
NewaliasSet, NewaliasNoOpenIn,
"Couldn't open %s for input!"),
inputname);
}
free(buffer);
return(-1);
}
if ((aliases_hash = dbz_fresh(dataname, 4999, 0, 0)) == NULL) {
lib_error(CATGETS(elm_msg_cat,
NewaliasSet, NewaliasNoOpendbz,
"Couldn't open %s.pag or %s.dir for output!"),
dataname, dataname);
free(buffer);
return(-1);
}
err = can_open(dataname, "w");
if (err) {
lib_error(CATGETS(elm_msg_cat,
NewaliasSet, NewaliasNoOpenOut,
"Couldn't open %s for output!"), dataname);
free(buffer);
return(-1);
}
if ((data = fopen(dataname, "w")) == NULL) {
lib_error(CATGETS(elm_msg_cat,
NewaliasSet, NewaliasNoOpenOut,
"Couldn't open %s for output!"), dataname);
free(buffer);
return(-1);
}
buff_loaded = 0; /* file buffer empty right now! */
al_count = 0;
err_flag = 0;
file_offset = 0;
while (get_alias(in, fromelm) != -1) {
put_alias(data);
if (err_flag) break;
}
if (err_flag) {
if (fromelm && sleepmsg > 0)
sleep(sleepmsg);
lib_error(CATGETS(elm_msg_cat, NewaliasSet, NewaliasNoSave,
"** Not saving tables! Please fix and re-run!"));
free(buffer);
return(-1);
}
else {
dbz_close(aliases_hash);
fclose(data);
fclose(in);
free(buffer);
if (al_count == 0) {
delete_alias_files(dataname, fromelm);
}
return(al_count);
}
/*NOTREACHED*/
}
static void delete_alias_files(dataname, fromelm)
char *dataname;
int fromelm;
{
/*
* This routine remove all the alias hash and data files.
* This is called from do_newalias() when there are no user
* aliases to be kept.
*/
char fname[SLEN];
if (unlink(dataname)) {
lib_error(
CATGETS(elm_msg_cat, NewaliasSet,
NewaliasCouldntDeleteData,
"Could not delete alias data file %s!"),
fname);
if (fromelm && sleepmsg > 0)
sleep(sleepmsg);
}
elm_sfprintf(fname,sizeof fname,FRM("%s.dir"), dataname);
if (unlink(fname)) {
lib_error(
CATGETS(elm_msg_cat, NewaliasSet,
NewaliasCouldntDeleteHash,
"Could not delete alias hash file %s!"),
fname);
if (fromelm && sleepmsg > 0)
sleep(sleepmsg);
}
elm_sfprintf(fname,sizeof fname,FRM("%s.pag"), dataname);
if (unlink(fname)) {
lib_error(
CATGETS(elm_msg_cat, NewaliasSet,
NewaliasCouldntDeleteHash,
"Could not delete alias hash file %s!"), fname);
if (fromelm && sleepmsg > 0)
sleep(sleepmsg);
}
}
/*
* Local Variables:
* mode:c
* c-basic-offset:4
* buffer-file-coding-system: iso-8859-1
* End:
*/
syntax highlighted by Code2HTML, v. 0.9.1