/* $Header: /afs/sipb.mit.edu/project/sipb-athena/repository/src/moira/gen/aliases.dc,v 1.6 1996/06/02 07:42:32 ghudson Exp $
 *
 * This generates the /usr/lib/aliases mail aliases file for the mailhub.
 * The aliases file will contain:
 *	user pobox entries
 *	maillist expansions
 *	sublists of maillists
 *
 *  (c) Copyright 1988, 1990 by the Massachusetts Institute of Technology.
 *  For copying and distribution information, please see the file
 *  <mit-copyright.h>.
 */


#include <mit-copyright.h>
#include <stdio.h>
#include <string.h>
#include <moira.h>
#include <moira_site.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
EXEC SQL INCLUDE sqlca;


#define ML_WID	72
#define AL_MAX_WID 592

char *divide = "########################################################################";
extern int errno;
char *whoami = "aliases.gen";
FILE *out;

main(argc, argv)
int argc;
char **argv;
{
    long tm = time(NULL);
    char filename[64], *targetfile;
    struct stat sb;
    int flag1, flag2, flag3;

    out = stdout;
    initialize_sms_error_table();
#ifsql INGRES
    EXEC SQL CONNECT moira;
    EXEC SQL SET LOCKMODE SESSION WHERE LEVEL=TABLE, READLOCK=SHARED;
#endsql
#ifsql INFORMIX
    EXEC SQL DATABASE moira;
#endsql


    if (argc == 2) {
	if (stat(argv[1], &sb) == 0) {

	  if (ModDiff (&flag1, "list", sb.st_mtime) ||
	      ModDiff (&flag2, "imembers", sb.st_mtime) ||
	      ModDiff (&flag3, "users", sb.st_mtime)) exit (MR_DATE);

	  if (flag1 < 0 && flag2 < 0 && flag3 < 0) {
	    fprintf(stderr, "File %s does not need to be rebuilt.\n",
		    argv[1]);
	    exit(MR_NO_CHANGE);
	  }
	}
	targetfile = argv[1];
	sprintf(filename, "%s~", targetfile);
	if ((out = fopen(filename, "w")) == NULL) {
	    fprintf(stderr, "unable to open %s for output\n", filename);
	    exit(MR_OCONFIG);
	}
    } else if (argc != 1) {
	fprintf(stderr, "usage: %s [outfile]\n", argv[0]);
	exit(MR_ARGS);
    }

    fprintf(out, "%s\n# Aliases File Extract of %s", divide, ctime(&tm));
    fprintf(out, "# This file is automatically generated, do not edit it directly.\n%s\n\n", divide);

    get_info();
#ifsql INFORMIX
    EXEC SQL COMMIT WORK;
#endif
#ifsql INGRES
    EXEC SQL DISCONNECT;
#endsql

    fprintf(stderr, "Dumping information\n");
    do_mlists(out);
    do_poboxes(out);

    fprintf(out, "\n%s\n# End of aliases file\n%s\n", divide, divide);


    if (fclose(out)) {
	perror("close failed");
	exit(MR_CCONFIG);
    }

    if (argc == 2)
      fix_file(targetfile);
    exit(MR_SUCCESS);
}


struct hash *users, *machines, *strings, *lists;
struct user {
    char login[9];
    char *pobox;
};
struct member {
    struct member *next;
    char *name;
    int list_id;
};
struct list {
    char *name;
    char maillist;
    char acl_t;
    char *description;
    int acl_id;
    struct member *m;
};


get_info()
{
    EXEC SQL BEGIN DECLARE SECTION;
    int id, maillistp, acl, pid, bid, mid;
    char name[129], type[9], buf[257];
    EXEC SQL END DECLARE SECTION;
    char *s;
    register struct user *u;
    register struct list *l;
    register struct member *m;
    register struct list *memberlist;

    EXEC SQL WHENEVER SQLERROR GOTO sqlerr;

    /* get locks */
    EXEC SQL SELECT modtime INTO :buf FROM list WHERE LIST_ID = 0;
    EXEC SQL SELECT modtime INTO :buf FROM users WHERE USERS_ID = 0;

    fprintf(stderr, "Loading machines\n");
    machines = create_hash(1000);

    EXEC SQL DECLARE m_cursor CURSOR FOR 
      SELECT mach_id, name 
      FROM machine
      ORDER BY mach_id;
    EXEC SQL OPEN m_cursor;
    while (1) {
      EXEC SQL FETCH m_cursor INTO :id, :name;
      if (sqlca.sqlcode != 0) break;
      if (s = index(name, '.'))
	*s = 0;
#ifdef ATHENA
      strcat(name, ".LOCAL");
#endif
      hash_store(machines, id, strsave(name));
    }
    EXEC SQL CLOSE m_cursor;


    fprintf(stderr, "Loading strings\n");
    strings = create_hash(11001);

    EXEC SQL DECLARE s_cursor CURSOR FOR 
      SELECT string_id, trim(string) 
      FROM strings
      ORDER BY string_id;
    EXEC SQL OPEN s_cursor;
    while (1) {
      EXEC SQL FETCH s_cursor INTO :id, :name;
      if (sqlca.sqlcode != 0) break;
      hash_store(strings, id, strsave(name));
    }
    EXEC SQL CLOSE s_cursor;

    fprintf(stderr, "Loading users\n");
    users = create_hash(12001);

    EXEC SQL DECLARE u_cursor CURSOR FOR
      SELECT users_id, login, potype, pop_id, box_id
      FROM users WHERE status = 1 OR status = 5 OR status = 6
      ORDER BY users_id;
    EXEC SQL OPEN u_cursor;
    while (1) {
        EXEC SQL FETCH u_cursor INTO :id, :name, :type, :pid, :bid;
	if (sqlca.sqlcode != 0) break;
	u = (struct user *) malloc(sizeof(struct user));
	strcpy(u->login, strtrim(name));
	if (type[0] == 'P') {
	    if (s = hash_lookup(machines, pid)) {
		sprintf(buf, "%s@%s", name, s);
		u->pobox = strsave(buf);
	    } else {
		u->pobox = (char *) NULL;
		fprintf(stderr, "User %s's pobox is on a missing machine!\n",
			u->login);
	    }
	} else if (type[0] ==  'S') {
	    if ((u->pobox = hash_lookup(strings, bid)) == NULL) {
		u->pobox = (char *) NULL;
		fprintf(stderr, "User %s's pobox string is missing!\n",
			u->login);
	    }
	} else
	  u->pobox = (char *) NULL;
	hash_store(users, id, u);
     }
    EXEC SQL CLOSE u_cursor;
    EXEC SQL COMMIT;

    fprintf(stderr, "Loading lists\n");
    lists = create_hash(15001);

    EXEC SQL DECLARE l_cursor CURSOR FOR
      SELECT list_id, name, maillist, description, acl_type, acl_id
      FROM list WHERE active != 0
      ORDER BY list_id;
    EXEC SQL OPEN l_cursor;
    while (1) {
        EXEC SQL FETCH l_cursor INTO :id, :name, :maillistp,
	                             :buf, :type, :acl;
	if (sqlca.sqlcode != 0) break;
	l = (struct list *) malloc(sizeof(struct list));
	l->name = strsave(strtrim(name));
	l->maillist = maillistp;
	l->description = strsave(strtrim(buf));
	l->acl_t = type[0];
	l->acl_id = acl;
	l->m = (struct member *) NULL;
	hash_store(lists, id, l);
      }
    EXEC SQL CLOSE l_cursor;
    EXEC SQL COMMIT;

    fprintf(stderr, "Loading members\n");

    EXEC SQL DECLARE mem_cursor CURSOR FOR
      SELECT list_id, member_type, member_id
      FROM imembers WHERE direct = 1
      ORDER BY list_id;
    EXEC SQL OPEN mem_cursor;
    while (1) {
        EXEC SQL FETCH mem_cursor INTO :id, :type, :mid;
	if (sqlca.sqlcode != 0) break;
	if (l = (struct list *) hash_lookup(lists, id)) {
	    m = (struct member *) malloc(sizeof(struct member));
	    if (type[0] == 'U' &&
		(u = (struct user *) hash_lookup(users, mid))) {
		m->list_id = 0;
		m->name = u->login;
		m->next = l->m;
		l->m = m;
	    } else if (type[0] == 'L' &&
		       (memberlist = (struct list *) hash_lookup(lists, mid))) {
		m->list_id = mid;
		m->name = memberlist->name;
		m->next = l->m;
		l->m = m;
	    } else if (type[0] == 'S' &&
		       (s = hash_lookup(strings, mid))) {
		m->list_id = 0;
		m->name = s;
		m->next = l->m;
		l->m = m;
	    }
	}
    }
    EXEC SQL CLOSE mem_cursor;
    return;
 sqlerr:
    com_err(whoami, MR_INGRES_ERR, " code %d\n", sqlca.sqlcode);
    critical_alert("DCM", "Alias build encountered INGRES ERROR %d",
		   sqlca.sqlcode);
    exit(MR_INGRES_ERR);
}


save_mlist(id, l, force)
int id;
register struct list *l;
int force;
{
    register struct member *m;
    struct list *l1;

    if (l->maillist == 2 ||
	(l->maillist == 0 && !force))
      return;

    if (l->m && l->m->next == NULL &&
	!strcasecmp(l->name, l->m->name)) {
	l->maillist = 0;
	return;
    }
    l->maillist = 2;
    output_mlist(id, l);

    if (l->acl_t == 'L' && (l1 = (struct list *)hash_lookup(lists, l->acl_id)))
      save_mlist(0, l1, 1);
    
    for (m = l->m; m; m = m->next) {
	if (m->list_id && (l1 = (struct list *)hash_lookup(lists, m->list_id)))
	  save_mlist(0, l1, 1);
    }
}


int lwid, bol, awid;

output_mlist(id, l)
int id;
register struct list *l;
{
    struct list *l1;
    register struct member *m;
    struct user *u;

    put_fill(out, l->description);
    if (l->acl_t ==  'L') {
	if (l1 = (struct list *) hash_lookup(lists, l->acl_id))
	  fprintf(out, "owner-%s: %s\n", l->name, l1->name);
    } else if (l->acl_t ==  'U') {
	if (u = (struct user *) hash_lookup(users, l->acl_id))
	  fprintf(out, "owner-%s: %s\n", l->name, u->login);
    }

    fprintf(out, "%s: ", l->name);
    lwid = strlen(l->name) + 2;
    bol = 1;
    for (m = l->m; m; m = m->next)
      do_member(out, m->name);
    if (l->m == (struct member *)NULL)
      fprintf(out, "/dev/null");
    fprintf(out, "\n\n");
}


/* Extract mailing lists.  Make a list of all mailinglists, then
 * process them, adding any sub-lists or acl lists to the list of lists
 * to be processed.  If further sublists are encountered, repeat...
 */

do_mlists(out)
FILE *out;
{
    fprintf(out, "\n%s\n# Mailing lists\n%s\n", divide, divide);
    hash_step(lists, save_mlist, 0);
}


/* print out strings separated by commas, doing line breaks as appropriate */

do_member(out, s)
FILE *out;
register char *s;
{
    register wwid;
    static int cont = 1;

    wwid = strlen(s);

    if (!bol && awid + wwid + 2 > AL_MAX_WID) {
	fprintf(out, ",\n\tcontinuation-%d\ncontinuation-%d: ", cont, cont);
	cont++;
	awid = lwid = 17 + wwid;
	fputs(s, out);
	return;
    }

    if (bol) {
	lwid += wwid;
	awid = lwid;
	fputs(s, out);
	bol = 0;
	return;
    }
    if (lwid + wwid + 2 > ML_WID) {
	fprintf(out, ",\n\t%s", s);
	awid += lwid + wwid + 2;
	lwid = wwid + 8;
	return;
    }
    lwid += wwid + 2;
    fprintf(out, ", %s", s);
}

do_pobox(id, u, out)
int id;
register struct user *u;
FILE *out;
{
    if (u->pobox)
      fprintf(out, "%s: %s\n", u->login, u->pobox);
}


/* Do user poboxes.  Just step through the users table, and print any
 * we extracted earlier.
 */

do_poboxes(out)
FILE *out;
{
    fprintf(out, "\n%s\n# User Poboxes\n%s\n", divide, divide);

    hash_step(users, do_pobox, out);
}


put_fill(aliases, string)
FILE *aliases;
register char *string;
{
    register char *c;
    register int lwid;
    register int wwid;

    if (*string == 0) return;
    fputs("#  ", aliases);
    lwid = 3;

    while (1) {
	while (*string && *string == ' ') string++;
	c = (char *)index(string, ' ');
	if (c == 0) {
	    wwid = strlen(string);
	} else {
	    wwid = c - string;
	    *c = 0;
	}

	if ((lwid + wwid) > ML_WID) {
	    fputs("\n#  ", aliases);
	    lwid = 3;
	    fputs(string, aliases);
	} else {
	    fputs(string, aliases);
	}

	if (c == (char *)0) break;
	/* add a space after the word */
	(void) fputc(' ', aliases);
	wwid++;
	lwid += wwid;
	string += wwid;
	/* add another if after a period */
	if (*--c == '.') {
	    (void) fputc(' ', aliases);
	    lwid++;
	}
    }

    (void) fputc('\n', aliases);
}
