static char rcsid[] = "@(#)$Id: aliaslib.c,v 1.31 2006/04/09 07:37:17 hurtta Exp $";

/******************************************************************************
 *  The Elm (ME+) Mail System  -  $Revision: 1.31 $   $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
 *****************************************************************************/

/** Library of functions dealing with the alias system...

 **/

#include "def_elm.h"
#include "s_elm.h"

DEBUG_VAR(Debug,__FILE__,"alias");

extern int current_mail_message;

struct expand {
    struct addr_item *addrs;
    int addrs_len;
};

static int add_ P_((struct expand *x, CONST char *ADR, 
		    CONST struct string *FN,
		    CONST struct string *COMMENT));
static int add_(x,ADR,FN,COMMENT)
     struct expand *x;
     CONST char *ADR;
     CONST struct string *FN;
     CONST struct string *COMMENT;
{
    x->addrs = safe_realloc(x->addrs,(x->addrs_len + 2) * 
			    sizeof (struct addr_item));
    x->addrs[x->addrs_len].addr       = safe_strdup(ADR);
    x->addrs[x->addrs_len].fullname   = dup_string(FN);
    x->addrs[x->addrs_len].comment    = dup_string(COMMENT);
    x->addrs[x->addrs_len+1].addr     = NULL;
    x->addrs[x->addrs_len+1].fullname = NULL;
    x->addrs[x->addrs_len+1].comment  = NULL;
    return x->addrs_len++;
}
#define ADD(x,ADR,FN,COMMENT) add_(&x,ADR,FN,COMMENT)

static int do_get_alias_l P_((
			      char *name,
			      struct expand *buffer,
			      int mailing,
			      int sysalias,
			      int depth,
			      int *too_longp,
			      struct mailer_info  *mailer_info,
			      struct AliasView *aview
			      ));
/*
 * Expand the comma-delimited group of names in "group", storing the result
 * in "buffer".  Returns TRUE if expansion occurs OK, else FALSE in the
 * event of errors.
 */
static int do_expand_group_l P_((
				 char *group,
				 struct expand *buffer,
				 int sysalias,
				 int depth,
				 int *too_longp,
				 struct mailer_info  *mailer_info,
				 struct AliasView *aview
				 ));

static int do_expand_group_l(group, buffer, sysalias, depth, too_longp,
			     mailer_info, aview)
     char *group;	     /* group list to expand			*/
     struct expand *buffer;  /* place to store result of expansion	*/
     int sysalias;	/* TRUE to suppress checks of the user's aliases*/
     int depth;	/* nesting depth					*/
     int *too_longp;	/* error code if expansion overflows buffer	*/
     struct mailer_info  *mailer_info;
     struct AliasView *aview;
{
    char ** tokenized = rfc822_tokenize(group);
    char **ptr, **next = NULL;
    int result = 0;

    remove_space_tokenized(tokenized);  /* removes spaces and comments */
    
    for (ptr = tokenized; *ptr; ptr = next) {
	char * name = *ptr;
	int q_seen = 0;
	int q = 0;
	
	for (next = ptr; *next; next++) {
	    if (',' == (*next)[0] && !q)
		break;
	    else if ('<' == (*next)[0]) {
		q++;
		q_seen++;
	    } else if ('>' == (*next)[0])
		q--;
	}
	
	if (ptr+1 == next) {
	    struct addr_item verify_result;
	    enum mailer_errcode errcode = MAILER_NOT_AVAIL;

	    /* see if this name is really an alias */
	    if (do_get_alias_l(name,buffer,TRUE,sysalias,depth,too_longp,
			       mailer_info, aview))
		continue;
	    
	    if ( *too_longp ) {
		result = 0;
		goto fail;
	    }

	    /* verify it is a valid address */
	    if (mailer_info &&
		verify_mailer_addr(mailer_info,name,
			       &verify_result,&errcode)) {

		DPRINT(Debug,7,
		       (&Debug, 
			"Alias %s verified as address -- really %s\n",
			name,verify_result.addr));

		if (!verify_result.fullname)
		    verify_result.fullname = new_string(display_charset);
		if (!verify_result.comment)
		    verify_result.comment = new_string(display_charset);


		/* Max alias expansion limit? */
		add_(buffer,
		     verify_result.addr,
		     verify_result.fullname,
		     verify_result.comment );      
		
		free_string(& verify_result.fullname);
		free_string(& verify_result.comment);
		free(verify_result.addr); verify_result.addr = NULL;

	    } else {
		struct string * fn = NULL;
		struct string * cm = NULL;

		cm = new_string(display_charset);

		fn = new_string(display_charset);

		/* Max alias expansion limit? */
		add_(buffer,name,fn,cm);      

		free_string(&fn);
		free_string(&cm);
	    }

	} else {  /* must have address .... */
	    char * buffer1 = NULL;
	    char ** scanner = NULL;
	    
	    for (scanner = ptr; scanner < next; scanner++) {
		if (buffer1 && q_seen && !q)
		    buffer1 = strmcat(buffer1," ");  /* Regenerate spaces for phrase */
		if ('<' == (*scanner)[0]) 
		    q++;
		else if ('>' == (*scanner)[0])
		    q--;
		buffer1 = strmcat(buffer1,*scanner);
	    }
	    
	    if (buffer1) {
		struct addr_item * address = 
		    break_down_address(buffer1, 
				       /* If user pastes encoded words from 
					* somewhere decode them also.
					*/
				       is_rfc1522(buffer1),
				       display_charset);
		struct addr_item *ptr1;
		
		for (ptr1 = address; 
		     ptr1 && ptr1 -> addr && ptr1->fullname && ptr1->comment; 
		     ptr1++) {
		    /* Max alias expansion limit? */
		    add_(buffer,ptr1->addr,ptr1->fullname, ptr1->comment);
		}
		if (address)
		    free_addr_items(address);
		
		free(buffer1);
	    }      
	}
	
	if (*next &&
	    ',' == (*next)[0])
	    next++;
    }

    result = 1;
 fail:

    if (tokenized)
	free_rfc822tokenized(tokenized);
    
    return result;
}


static int do_get_alias_l(name, buffer, mailing, sysalias, depth, too_longp,
			  mailer_info, aview)
     char *name;	    /* name to expand as an alias		     */
     struct expand *buffer; /* place to store result of expansion	     */
     int mailing;	   /* TRUE to fully expand group names & recursive aliases	*/
     int sysalias;	   /* TRUE to suppress checks of the user's aliases  */
     int depth;	           /* recursion depth - initially call at depth=0    */
     int *too_longp;	   /* error code if expansion overflows buffer	     */
     struct mailer_info  *mailer_info;
     struct AliasView *aview;

{
    struct alias_rec *match = NULL;
    char * alias_address = NULL;
    struct string * alias_fullname = NULL;
    struct string * alias_comment = NULL;
    struct addr_item * address;
    int loc;
    
    /* update the recursion depth counter */
    ++depth;
    
    DPRINT(Debug,5,
	   (&Debug, "%*s->attempting alias expansion on \"%s\"\n",
	    (depth*2), "", name));
    
    /* The next two blocks could be merged somewhat */
    /* check for a user alias, unless in the midst of sys alias expansion */
    if ( !sysalias &&
	 (loc = find_alias(name, USER, aview)) >= 0)
	match = give_alias(aview,loc);

    /* check for a system alias */
    else if ( (loc = find_alias(name, SYSTEM, aview)) >= 0 ) {
	match = give_alias(aview,loc);
	sysalias = TRUE;
    }
    
    /* nope...this name wasn't an alias */
    else
	return FALSE;
    
    if (!match)
	return FALSE;

    alias_address = match->address;
    if ( 0 != (match->type & PERSON )) {
	/* If user pastes encoded words from 
	 *	somewhere decode them also.
	 */	
	alias_fullname = hdr_to_string(HDR_TEXT,match->name,
				       display_charset,
				       is_rfc1522(match->name));
    }

    DPRINT(Debug,5,
	   (&Debug, "%*s  ->expanded alias to \"%s\"\n",
	    (depth*2), "", alias_address));

    if (!alias_fullname)
	alias_fullname = new_string(display_charset);
    if (!alias_comment)
	alias_comment = new_string(display_charset);


    
    /* check for an exact match */
    if (0 == strcmp(name,alias_address)) {
	add_(buffer,alias_address,alias_fullname,alias_comment);
	
	free_string(&alias_fullname);
	free_string(&alias_comment);

	/* Max alias expansion limit? */
	return TRUE;
    }
  
    /* see if we are stuck in a loop */
    if ( depth > 12 ) {
	DPRINT(Debug,2,
	       (&Debug,
		"alias expansion loop detected at \"%s\" - bailing out\n", 
		name));
	lib_error(CATGETS(elm_msg_cat, ElmSet, ElmErrorExpanding,
			  "Error expanding \"%s\" - probable alias definition loop."),
		  name);
	
	free_string(&alias_fullname);
	free_string(&alias_comment);
	
	return FALSE;
    }
    
    /* see if the alias equivalence is a group name */
    if ( mailing && match->type & GROUP ) {
	free_string(&alias_fullname);
	free_string(&alias_comment);

	return do_expand_group_l(alias_address,buffer,
				 sysalias,depth,too_longp,
				 mailer_info, aview
				 );
    }
    
    /* see if the alias equivalence is an email address,
     * but not list of addresses. If address starts with @
     * then it is source-route address and not list of addresses
     */
    if (!(match->type & GROUP) &&
	('@' == alias_address[0] || qstrpbrk(alias_address,",") == NULL) &&
	qstrpbrk(alias_address,"!@:") != NULL ) {
	add_(buffer,alias_address,alias_fullname,alias_comment);
    
	free_string(&alias_fullname);
	free_string(&alias_comment);

	/* Max alias expansion limit? */
	return TRUE;
    }
    
    /* see if the alias equivalence is itself an alias */
    if ( mailing && qstrpbrk(alias_address,",!@:") == NULL &&
	 do_get_alias_l(alias_address,buffer,TRUE,sysalias,depth,
			too_longp,mailer_info, aview))
	return TRUE;
    
    /* the alias equivalence must just be a local address  or 
       list of addresses */
    
    address = 	  break_down_address(alias_address, 
				     /* If user pastes encoded words from 
				      *	somewhere decode them also.
				      */
				     is_rfc1522(alias_address),
				     display_charset);
    
    /* only one address -- take fullname from alias */
    if (address[0].addr && address[0].fullname &&
	!string_len(address[0].fullname) && !address[1].addr)
	add_(buffer,address[0].addr,alias_fullname,alias_comment);
    else {
	struct addr_item *ptr1;
	
	/* List of addresses */
	for (ptr1 = address; 
	     ptr1 && ptr1 -> addr && ptr1->fullname && ptr1->comment; 
	     ptr1++) {
	    /* Max alias expansion limit? */
	    add_(buffer,ptr1->addr,ptr1->fullname,ptr1->comment);
	}
	if (address)
	    free_addr_items(address);
    } 

    free_string(&alias_fullname);
    free_string(&alias_comment);

    return TRUE;
}

struct addr_item *get_alias_address_l (name, mailing, too_longp,mailer_info,
				       aview)
     char *name; /* name to expand as an alias */
     int mailing; /* TRUE to fully expand group names & recursive aliases */
     int *too_longp; /* error code if expansion overflows buffer          */
     struct mailer_info  *mailer_info;
     struct AliasView *aview;
{
    struct expand result;
    struct addr_item *bufptr = NULL;
    int dummy = 0;  /* header_page */

    result.addrs_len   = 0;
    result.addrs = NULL;
    
    /*
     *	Reopens files iff changed since last read
     */
    open_alias_files(aview, NULL);
    /*
     *	If name is an alias then return its expansion
     */
    
    if (do_get_alias_l(name,&result,mailing,FALSE,0,too_longp,
		       mailer_info, aview)) {
	bufptr = result.addrs;
	DPRINT(Debug,2,
	       (&Debug,
		"get_alias_address_l(%s) expands %d addresses.\n",
		name,result.addrs_len));
    }
    else {
	/*
	 *  Nope...not an alias (or it was too long to expand)
	 */
	DPRINT(Debug,2,
	       (&Debug,
		"Could not expand alias in get_alias_address_l(%s)%s\n",
		name, *too_longp ? "\t...alias buffer overflowed." : ""));
	if (result.addrs)
	    free_addr_items(result.addrs);
	bufptr = NULL;
    }
        
    return(bufptr);    
}

/*
 * Local Variables:
 *  mode:c
 *  c-basic-offset:4
 *  buffer-file-coding-system: iso-8859-1
 * End:
 */


syntax highlighted by Code2HTML, v. 0.9.1