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

/******************************************************************************
 *  The Elm (ME+) Mail System  -  $Revision: 1.31 $   $State: Exp $
 *
 *  Author: Kari Hurtta <hurtta+elm@posti.FMI.FI> (was hurtta+elm@ozone.FMI.FI)
 *****************************************************************************/

#include "headers.h"
#include "ss_imp.h"
#include "connection_imp.h"
#ifdef USE_DLOPEN
#include "shared_imp.h"
#endif
#include "s_me.h"
#include "s_elm.h"

DEBUG_VAR(Debug,__FILE__,"net");

#ifdef REMOTE_MBX

/* Seems that h_errno is macro on AIX */
#ifndef h_errno
extern int h_errno;
#endif

#include <errno.h>
#ifndef ANSI_C
extern int errno;
#endif


struct service_entry * service_list   = NULL;
int                    service_count  = 0;


/* Hard coded */
static struct service_type {
    char * name;
    int    flags;
    PORTS  defport;
} SERVICE_TYPES[] = {
    { "*",       
      STFLAG_browser|STFLAG_mbox|
      STFLAG_is_imap|STFLAG_is_pop |
      STFLAG_smtp_like|
      STFLAG_is_submission|STFLAG_is_smtp,
      PORT_end },
    { "imap",    STFLAG_browser|STFLAG_mbox|STFLAG_is_imap,    PORT_imap4 },
    { "pop",     STFLAG_mbox|STFLAG_is_pop,                    PORT_pop3 },
    { "submission", STFLAG_smtp_like|STFLAG_is_submission,   PORT_submission },
    { "smtp",    STFLAG_smtp_like|STFLAG_is_smtp,              PORT_smtp },
    { NULL,      0,                                            PORT_end },
};


CONST struct service_type *       IMAP_SERVICE     =  & SERVICE_TYPES[1];
CONST struct service_type *        POP_SERVICE     =  & SERVICE_TYPES[2];
CONST struct service_type * SUBMISSION_SERVICE     =  & SERVICE_TYPES[3];
CONST struct service_type *       SMTP_SERVICE     =  & SERVICE_TYPES[4];


static void zero_service_entry P_((struct service_entry *entry,
				   const char *name,
				   const struct service_type *st,
				   int flags));
static void zero_service_entry(entry,name,st,flags)
     struct service_entry *entry;
     CONST char *name;
     CONST struct service_type *st;
     int flags;
{
    bzero((void *)entry,sizeof (*entry));

    entry->flags          = flags;           
    entry->official_name  = safe_strdup(name);
    entry->aliases_list   = 0;
    entry->aliases_count  = 0;
    entry->addr_list      = NULL;
    entry->addr_count     = 0;

    entry->addr_name_list  = NULL;
    entry->addr_name_count = 0;

    entry->service        = st;
    /* ??? */
    entry->port_list      = NULL;
    entry->port_count     = 0;

    entry->option_list    = NULL;
    entry->option_count   = 0;    
}

static void add_addr_name_to_entry P_((struct service_entry *entry,
				       char *name,
				       int malloc_it));
static void add_addr_name_to_entry(entry,name,malloc_it)
     struct service_entry *entry;
     char *name;
     int malloc_it;
{
    entry->addr_name_list = 
	safe_realloc(entry->addr_name_list,
		     (entry->addr_name_count+1) * 
		     sizeof (entry->addr_name_list[0]));
    entry->addr_name_list[entry->addr_name_count++]
	= malloc_it ? safe_strdup(name) : name;
}


static void add_alias_to_entry P_((struct service_entry *entry,
				   const char *name));
static void add_alias_to_entry(entry,name)
     struct service_entry *entry;
     CONST char *name;    
{
    entry->aliases_list = 
	safe_realloc(entry->aliases_list,
		     (entry->aliases_count+1) * 
		     sizeof (entry->aliases_list[0]));
    entry->aliases_list[entry->aliases_count++]
	= safe_strdup(name);
}

static void add_addr_to_entry P_((struct service_entry *entry,
				  SOCKADDR *result));
static void add_addr_to_entry(entry,result)
     struct service_entry *entry;
     SOCKADDR *result;
{
    entry->addr_list = 
	safe_realloc(entry->addr_list,
		     (entry->addr_count+1) * 
		     sizeof (entry->addr_list[0]));
    bzero((void *)&(entry->addr_list[entry->addr_count]),
	  sizeof (entry->addr_list[entry->addr_count]));

    entry->addr_list[entry->addr_count++] = *result;
}

static void add_port_to_entry P_((struct service_entry *entry,
				  int port));
static void add_port_to_entry(entry,port)
     struct service_entry *entry;
     int port;    
{
    entry->port_list = 
	safe_realloc(entry->port_list,
		     (entry->port_count+2) * 
		     sizeof (entry->port_list[0]));

    entry->port_list[entry->port_count++] = port;
    entry->port_list[entry->port_count]   = PORT_end;
}

static int add_option_type_to_entry P_((struct service_entry *entry,
					struct SE_option_type * Y,
					const char * prefix));
static int add_option_type_to_entry(entry,Y,prefix)
     struct service_entry *entry;
     struct SE_option_type * Y;
     CONST char * prefix;
{
    entry->option_list =
	safe_realloc(entry->option_list,
		     (entry->option_count+1) *
		     sizeof (entry->option_list[0]));
    bzero((void *) &(entry->option_list[entry->option_count]),
	   sizeof (entry->option_list[0]));

    entry->option_list[entry->option_count].type   = Y;
    entry->option_list[entry->option_count].prefix = safe_strdup(prefix);
    entry->option_list[entry->option_count].value = NULL;

    Y->zero_options(& (entry->option_list[entry->option_count]));
    return entry->option_count++;
}

static struct service_entry *malloc_service_entry P_((const char *name,
						      const struct 
						      service_type *st));
static struct service_entry *malloc_service_entry(name,st)
     CONST char * name;
     CONST struct service_type *st;
{
    struct service_entry *ret = safe_malloc (sizeof (struct service_entry));

    /* Free this */
    zero_service_entry(ret,name,st,SE_temporary);
   
    return ret;
}

static void merge1_entries P_((struct service_entry *target,
			       struct service_entry *value));
static void merge1_entries(target,value)
     struct service_entry *target;
     struct service_entry *value;
{

    if (target->service == &(SERVICE_TYPES[0])) {
	target->service = value->service;
	if (target->service != &(SERVICE_TYPES[0])) {
	    DPRINT(Debug,10,(&Debug,  "... setting temporary entry for %s to be type %s\n",
			     target->official_name,
			     target->service->name));
	}
    } else if (target->service != value->service &&
	       value->service != &(SERVICE_TYPES[0])) {
	DPRINT(Debug,1,(&Debug,  
			 "... conflicting service entry types. Type %s used, %s ignored\n",
			target->service->name,
			value->service->name
			));
	return;
    }

    
    if (0 != istrcmp(target->official_name,value->official_name)) {
	int j;

	for (j = 0; j < target->aliases_count; j++) {
	    if (0 == istrcmp(value->official_name,target->aliases_list[j]))
		break;
	}
	if (j >= target->aliases_count) 
	    add_alias_to_entry(target,value->official_name);
	
    }

    /* Add missing aliases */
    if (value->aliases_count > 0) {
	int i;

	for (i = 0; i < value->aliases_count; i++) {
	    int j;
	    if (0 == istrcmp(target->official_name,value->aliases_list[i]))
		continue;

	    for (j = 0; j < target->aliases_count; j++) {
		if (0 == istrcmp(value->aliases_list[i],
				 target->aliases_list[j]))
		    break;
	    }
	    if (j >= target->aliases_count) 
		add_alias_to_entry(target,value->aliases_list[i]);
	}
    }
	
    if (value->addr_name_count > 0) {
	int i;
	target->flags |= SE_given_name_addr;

	for (i = 0; i < value->addr_name_count; i++) {
	    int j;

	    for (j = 0; j < target->addr_name_count; j++) {
		if (0 == istrcmp(value->addr_name_list[i],
				 target->addr_name_list[j]))
		    break;		
	    }
	    if (j >= target->addr_name_count)
		add_addr_name_to_entry(target,
				       value->addr_name_list[i],
				       1 /* strdup it */);
	}
    }

    if (value->addr_count > 0 && (value->flags & SE_given_addr)) {
	int i;
	
	if (! (target->flags & SE_given_addr))
	    target->addr_count = 0;   /* Forget cached addresses */

	target->flags |= SE_given_addr;

	for (i = 0; i < value->addr_count; i++) {
	    int j;

	    for (j = 0; j < target->addr_count; j++) {	    

		if (0 == memcmp(&(value->addr_list[i]),
				&(target->addr_list[j]),
				sizeof (value->addr_list[i])))
		    break;
	    }
	    if (j >= target->addr_count)
		add_addr_to_entry(target,& (value->addr_list[i]));
	}
    }
    
    if (value->port_count > 0 && (value->flags & SE_given_port)) {
	int i;
	
	if (! (target->flags & SE_given_port))
	    target->port_count = 0;   /* Forget default port */

	target->flags |= SE_given_port;

	for (i = 0; i < value->port_count; i++) {
	    int j;

	    for (j = 0; j < target->port_count; j++) {	    
		if (value->port_list[i] ==
		    target->port_list[j])
		    break;
	    }

	    if (j >= target->port_count)
		add_port_to_entry(target,value->port_list[i]);	    
	}
    }

    if (value->option_count > 0) {
	int i;

	for (i = 0; i < value->option_count; i++) {
	    int j;
	    int idx = -1;
	    
	    for (j = 0; j < target->option_count; j++) {
		if (0 == strcmp(value->option_list[i].prefix,
				target->option_list[j].prefix))
		    idx = j;
	    }
	    if (-1 == idx) {
		struct SE_option_type * Y = 
#ifdef USE_DLOPEN
		    get_option_type(value->option_list[i].prefix)
#else
		    NULL
#endif
		    ;

		if (!Y) {
		    panic("CONNECTION PANIC",__FILE__,__LINE__,
			  "merge1_entries",
			  "option type disappeared",0);
		    continue;
		}
		
		idx = add_option_type_to_entry(target,Y,
					       value->option_list[i].prefix);
					       
	    }
	    if (SE_option_t_magic != target->option_list[idx].type->magic)
		panic("CONNECTION PANIC",__FILE__,__LINE__,
		      "merge1_entries",
		      "Bad option type magic on service list",0);

	    if (target->option_list[idx].type != value->option_list[i].type)
		panic("CONNECTION PANIC",__FILE__,__LINE__,
                      "merge1_entries",
                      "option type mismatch",0);

	    target->option_list[idx].type->
		merge_options(& (target->option_list[idx]),
			      & (value->option_list[i]) );
	}
    }
}

static void merge_entries P_((struct service_entry **ret,
			      struct service_entry *value));
static void merge_entries(ret,value)
     struct service_entry **ret;
     struct service_entry *value;
{
    if (value == *ret)
	return;

    if (!*ret)
	*ret = value;
    else { 
	if ((*ret)->flags & SE_temporary) {
	    merge1_entries(*ret,value);
	} else {
	    struct service_entry * A = 
		malloc_service_entry((*ret)->official_name,
				     (*ret)->service);
	    merge1_entries(A,*ret);
	    merge1_entries(A,value);
	    *ret = A;
	    DPRINT(Debug,10,(&Debug,  "... making temporary entry for %s from permanent\n",
			    (*ret)->official_name));
	}
    }
}

static int name_ok P_((CONST char *name));
static int name_ok(name)
     CONST char *name;
{
    if ('\0' == *name)
	return 0;

    return strlen(name) == strspn(name,"abcdefghijklmnopqrstuvxyz.-");
}

static void dump_service_entry P_((FILE *f, struct service_entry *entry));
static void dump_service_entry(f,entry)
     FILE *f; 
     struct service_entry *entry;
{
    char * sep = "\t";
    int idx;

    fputs(entry->official_name,f);
    putc('\t',f);
    fputs(entry->service->name,f);
	
    if (entry->flags & SE_given_alias) {
	int y;
	for (y = 0; y < entry->aliases_count; y++) {

	    if (name_ok(entry->aliases_list[y]))
		elm_fprintf(f,FRM("%salias=%s"),
			    sep,entry->aliases_list[y]);
	    else
		elm_fprintf(f,FRM("%salias=%Q"),
			    sep,entry->aliases_list[y]);
		sep = "; ";
	    }
	}

    if (entry->flags & SE_given_name_addr) {
	int y;
	for (y = 0; y < entry->addr_name_count; y++) {
	    if (name_ok(entry->addr_name_list[y]))
		elm_fprintf(f,FRM("%saddr=%s"),
			    sep,entry->addr_name_list[y]);
	    else
		elm_fprintf(f,FRM("%salias=%Q"),
			    sep,entry->addr_name_list[y]);
	    sep = "; ";
	}
    }
	
    if (entry->flags & SE_given_addr) {
	int y;
	for (y = 0; y < entry->addr_count; y++) {
	    SOCKADDR X = entry->addr_list[y];
	    switch (X.sa.sa_family) {
		char * z;
#ifdef I_NETINET_IN
	    case AF_INET:
		z = inet_ntoa(X.sin.sin_addr);
		elm_fprintf(f,FRM("%salias=ip:%Q"),
			    sep,z);
		if (ntohs(X.sin.sin_port) != PORT_end)
		    elm_fprintf(f,FRM(":%d"),
				ntohs(X.sin.sin_port));
		
		sep = "; ";
		break;
#endif
	    default:
		/* none */
		break;
	    }		
	}
    }

    if (entry->flags & SE_given_port) {
	int y;
	for (y = 0; y < entry->port_count; y++) {
	    elm_fprintf(f,FRM("%sport=%d"),
			sep,entry->port_list[y]);
	    sep = "; ";
	}
    }

    for (idx = 0; idx < entry->option_count; idx++) {
	char * val;
	
	if (SE_option_t_magic != entry->option_list[idx].type->magic)
	    panic("CONNECTION PANIC",__FILE__,__LINE__,
		  "dump_service_entry",
		  "Bad option type magic on service list",0);
	
	val = entry->option_list[idx].type->
	    give_values(& (entry->option_list[idx]),
			entry->option_list[idx].prefix);
	
	if (val) {
	    fputs(sep,f);
	    fputs(val,f);
	    free(val); val = NULL;
	}
    }
    putc('\n',f);
}				
   

static struct service_entry *scan_list P_((const char *hostname,
					   int flag));
static struct service_entry *scan_list(hostname,flag)
     CONST char *hostname;
     int flag;
{
    struct service_entry *ret = NULL;
    int i;
    
    for (i = service_count-1; i >= 0; i--) {
	if (0 != (flag & service_list[i].service->flags)) {
	    int j;
	    if (0 == istrcmp(hostname,service_list[i].official_name)) {

#ifdef DEBUG
		if (Debug.active >= 8) {
		    FILE *F;
		    DPRINT(Debug,8,(&Debug,"\n-- found [%d] ",i));
		    F = debug_to_FILE(&Debug);
		    if (F) {
			dump_service_entry(F,&(service_list[i]));
			fclose(F);
		    }
		}
#endif

		/* ret = &(service_list[i]);	     */
		merge_entries(&ret,&(service_list[i]));
	    }
	    
	    for (j = 0; j < service_list[i].aliases_count; j++) {
		if (0 == istrcmp(hostname,service_list[i].aliases_list[j])) {
#ifdef DEBUG
		if (Debug.active >= 8) {
		    FILE *F;
		    DPRINT(Debug,8,(&Debug,"\n-- found [%d] ",i));
		    F = debug_to_FILE(&Debug);
		    if (F) {
			dump_service_entry(F,&(service_list[i]));
			fclose(F);
		    }
		}
#endif

		    /* ret = &(service_list[i]); */
		    merge_entries(&ret,&(service_list[i]));
		}
	    }
	}	
    }

#ifdef DEBUG
    if (Debug.active >= 8 && ret) {
	FILE *F;
	if (ret->flags & SE_temporary) {
	    DPRINT(Debug,8,(&Debug,"\nMERGED        ",i));
	} else {
	    DPRINT(Debug,8,(&Debug,"\nRESULT        ",i));
	}
	F = debug_to_FILE(&Debug);
	if (F) {
	    dump_service_entry(F,ret);
	    fclose(F);
	}
	DPRINT(Debug,8,(&Debug,"\n"));
    }
#endif

    return ret;
}

static struct hostent * lookup_name P_((const char *hostname,int silent));
static struct hostent * lookup_name(hostname,silent)
     CONST char *hostname;
     int silent;
{
    struct hostent *he = NULL;

    lib_transient(CATGETS(elm_msg_cat, MeSet,MeLookingUp,
			  "Looking up %s ..."),
		  hostname);
	
    he = gethostbyname(hostname);
	
    if (!he) {
	DPRINT(Debug,9,(&Debug, 
			"lookup_name: %s: h_errno=%d\n",
			hostname,h_errno));
	    
	/* We are silent about errors if cached
	   data exists
	*/
	if (!silent) {
	    switch(h_errno) {
	    case HOST_NOT_FOUND:
		lib_error(CATGETS(elm_msg_cat, MeSet,MeNoRemoteHost,
				  "No remote mailbox server host: %s"),
			  hostname);		
		break;
	    case NO_ADDRESS:
		lib_error(CATGETS(elm_msg_cat, MeSet,MeNoAddress,
				  "Name %s have not IP-address"),
			  hostname);
		break;
	    case TRY_AGAIN:
		lib_error(CATGETS(elm_msg_cat, MeSet,MeTemporary,
				  "Address for %s is not yet found..."),
			  hostname);
		break;			  
	    default:
		lib_error(CATGETS(elm_msg_cat, MeSet,MeNameError,
				  "Failed to get address for %s"),
			  hostname);
		break;
	    }
	}
    } else {
	lib_transient(CATGETS(elm_msg_cat, MeSet,MeLookingUpOK,
			      "Looking up %s ... OK"),
		      hostname);

    }

    return he;
}

static void fill_address P_((struct service_entry *ret,
			     struct hostent *he));
static void fill_address(ret,he)
     struct service_entry *ret;
     struct hostent *he;
{   
    if (he->h_addr_list) {
	int x;
	for (x = 0; he->h_addr_list[x]; x++) {
	    SOCKADDR X;

	    /* Make compaarision easier */
	    bzero((void *)&X, sizeof X);

	    switch(he->h_addrtype) {
#ifdef I_NETINET_IN
	    case AF_INET:
		X.sin.sin_family = he->h_addrtype;
	        X.sin.sin_port = htons(PORT_end);    /* No port given */

		if (sizeof(X.sin.sin_addr)  != he->h_length) {
		    lib_error(FRM("%s: Bad IP-addr length %d (should be %d)"),
			      he->h_name,he->h_length,
			      sizeof(X.sin.sin_addr));
		    return;
		}
		memcpy(&(X.sin.sin_addr), he->h_addr_list[x], he->h_length);
		add_addr_to_entry(ret,&X);
		break;
#endif			    
	    default:
		lib_error(CATGETS(elm_msg_cat, MeSet,MeUnsupportedAddrType,
				  "Name %s have odd type address"),
			  he->h_name);
		/* NOTE: We do not know on what compoents arbitary
		         sockect address is constructed so we can't
			 fill it
		*/
		return;
	    }
	}
    }
}


struct service_entry * give_service_entry(hostname,flag)
     CONST char *hostname;
     int flag;
{
    struct service_entry *ret;       
    int have_port = 0;   /* set if addresses have port set */

    DPRINT(Debug,9,(&Debug, 
		    "give_service_entry(name=\"%s\",flags=%d)\n",
		    hostname,flag));

    ret = scan_list(hostname,flag);
    /* If on service entry do not have ip addresses
       they needed to be retrieved. Notice that we
       retrieve new entries every time (alternatively
       we should keep track of expiry times).
       However all other data is cached (also addresses if
       them is generated from names given on configuration data)
    */
    if (ret)
	hostname = ret->official_name;

    if (ret && (0 != (ret->flags & SE_given_name_addr) &&
		ret->addr_count == 0 ||
		0 != (ret->flags & SE_rescan_name_addr))) {
	int x;

	/* Clear data on case of rescan */
	if (ret->addr_list) {
	    free(ret->addr_list);
	    ret->addr_list = NULL;
	    ret->addr_count = 0;
	}
	ret->flags   &= ~SE_rescan_name_addr;

	for (x = 0; x < ret->addr_name_count; x++) {
	    struct hostent *he  = 
		lookup_name(ret->addr_name_list[x],0);
	    
	    if (he)
		fill_address(ret,he);
	    else
		ret->flags |= SE_rescan_name_addr;
	}
    }

    if (!ret || 
	0 == (ret->flags & SE_given_addr) &&
	0 == (ret->flags & SE_given_name_addr)) {

	struct hostent *he    = lookup_name(hostname,
					    /* Silent: */
					    ret && ret->addr_count > 0);

	if (he) {  	  
	    /* Now try found entry again ... */
	    if (!ret) { 
		ret = scan_list(he->h_name,flag);
		
		if (!ret && he->h_aliases) {
		    int j;
		    for (j = 0; he->h_aliases[j] && !ret; j++)
			ret = scan_list(he->h_aliases[j],flag);
		}
	    
		/* we need to add hostname to aliases because
		   it was not on there (otherwise entry
		   should have found earlier)
		*/
		if (ret) 
		    add_alias_to_entry(ret,hostname);
	    }
	    
	    if (!ret) {
		struct service_type *st;

		for (st = &SERVICE_TYPES[0]; st->name; st++) {
		    if (flag == (flag &st->flags))
			break;
		}

		if (!st->name)
		    panic("CONNECTION PANIC",__FILE__,__LINE__,
			  "give_service_entry",
			  "Service type do not found", 0);

		ret = malloc_service_entry(hostname,st);

	    }
	
	    if (he->h_aliases) {  /* Add missing aliases */
		int x;
		for (x = 0; he->h_aliases[x]; x++) {
		    int j;
		    for (j=0; j < ret->aliases_count; j++)
			if (0 == istrcmp(he->h_aliases[x],
					 ret->aliases_list[j]))
			    break;
		    if (j >= ret->aliases_count) 
			add_alias_to_entry(ret,he->h_aliases[x]);
		}
	    }

	    if (he->h_addr_list) {
		/* Clear old data */
	    	if (ret->addr_list) {
		    free(ret->addr_list);
		    ret->addr_list = NULL;
		    ret->addr_count = 0;
		}

		fill_address(ret,he);
	    }		
	}    
    }

    if (ret && ret->addr_count > 0) {
	int x;

	for (x = 0; x < ret->addr_count; x++) {
	    switch(ret->addr_list[x].sa.sa_family) {
#ifdef I_NETINET_IN
	    case AF_INET:
		if (ret->addr_list[x].sin.sin_port == htons(PORT_end))
		    have_port = 0;
		break;
#endif
	    default:
		have_port = 0;
		break;
	    }
	}
    }


    if (ret && ret->port_count == 0 && !have_port) {
	PORTS r = PORT_end;
	
	/* builtin definations here */
	if ( PORT_end == ret->service->defport) {
	    
	    /* Caller will define portlist */
	    if (ret->port_list)
	 	free(ret->port_list);
	    ret->port_list = NULL;
	    
	} else 
	    r = ret->service->defport;
	
	if (r != PORT_end) {
	    ret->port_list = safe_realloc(ret->port_list,
					  2 * sizeof (ret->port_list[0]));
	    ret->port_list[0] = r;
	    ret->port_list[1] = PORT_end;
	    ret->port_count  = 1;
	}
    }

    if (ret) {
	DPRINT(Debug,9,(&Debug, 
			"give_service_entry=%p: type=%p (%s)%s\n",
			ret,ret->service,ret->service->name,
			ret->flags & SE_temporary ? ", temporary" : ""));
    } else {
	DPRINT(Debug,9,(&Debug, 
			"give_service_entry=NULL\n"));
    }

    return ret;
}

void free_temporary_service_entry(Y)
     struct service_entry **Y;
{
    if (!(*Y))
	return;

    if ((*Y)->flags  & SE_temporary) {
	int x;

	DPRINT(Debug,12,(&Debug, 
			 "free_temporary_service_entry: Freeing entry %p\n",
			 *Y));
	if ((*Y)->official_name)
	    free((*Y)->official_name);
	(*Y)->official_name = NULL;

	for (x = 0; x < (*Y)->aliases_count; x++) {
	    if ((*Y)->aliases_list[x])
		free((*Y)->aliases_list[x]);
	    (*Y)->aliases_list[x] = NULL;
	}
	if ((*Y)->aliases_list)
	    free((*Y)->aliases_list);
	(*Y)->aliases_list  = NULL;
	(*Y)->aliases_count = 0;

	if ((*Y)->addr_list)
	    free((*Y)->addr_list);
	(*Y)->addr_list = NULL;
	(*Y)->addr_count = 0;

	for (x = 0; x < (*Y)->addr_name_count; x++) {
	    if ((*Y)->addr_name_list[x])
		free((*Y)->addr_name_list[x]);
	    (*Y)->addr_name_list[x] = NULL;
	}
	if ((*Y)->addr_name_list)
	    free((*Y)->addr_name_list);
	(*Y)->addr_name_list = NULL;
	(*Y)->addr_name_count = 0;

	(*Y)->service = NULL;

	if ((*Y)->port_list)
	    free((*Y)->port_list);
	(*Y)->port_list  = NULL;
	(*Y)->port_count = 0;

	for (x = 0; x < (*Y)->option_count; x++) {
	    if (SE_option_t_magic != (*Y)->option_list[x].type->magic)
		panic("CONNECTION PANIC",__FILE__,__LINE__,
		      "free_temporary_service_entry",
		      "Bad option type magic on service list",0);
	    (*Y)->option_list[x].type->
		free_options(& ((*Y)->option_list[x]));	    

	    if ((*Y)->option_list[x].prefix)
		free((*Y)->option_list[x].prefix);
	    (*Y)->option_list[x].prefix = NULL;
	}
	if ((*Y)->option_list)
	    free((*Y)->option_list);
	(*Y)->option_list = NULL;
	(*Y)->option_count = 0;

	/* Temporary entry is malloced ... */
	free(*Y);
    }
    /* If not temporary, we assume that this entry is on list
       and therefore that does not produce dangling pointer
    */
    (*Y) = NULL;
}

int parse_service_entries (filename,system, errors)
     CONST char *filename; 
     int system;
     int *errors;
{
    /*   hostname   service        options               */
    int max_count = 0;
    int count = 0;
    FILE * f;
    char buf[LONG_STRING];
    int c, l1;

    if (!system) {
	int err = can_open(filename,"r");
	
	if (err) {
	    DPRINT(Debug,2,(&Debug, 
			    "parse_service_entries=0: %s: %s (can_open)\n",
			    filename,error_description(err)));
	    return 0;
	}
    }

    f = fopen(filename,"r");
    if (!f) {
	int err = errno;
	DPRINT(Debug,2,(&Debug, 
			"parse_service_entries=0: %s: %s\n",
			filename,error_description(err)));
	return 0;
    }
    
    while(EOF != (c = fgetc(f)))
	if ('\n' == c)
	    max_count++;

    DPRINT(Debug,9,(&Debug, 
		    "parse_service_entries: %s, max_count=%d\n",
		    filename,max_count));
    
    if (!max_count) {
	fclose(f);
	return 0;
    }
    rewind(f);

    service_list = safe_realloc(service_list,
				(service_count + max_count) *
				sizeof (service_list[0]));

    while (count < max_count && 
	   (l1 = mail_gets(buf,sizeof buf, f)) > 0) {
	char *c,*c1, *options;
	struct service_type *st;

	if ('\n' == buf[l1 -1]) 
	    buf[l1 - 1] = '\0';
	else {
	    lib_error(CATGETS(elm_msg_cat, MeSet, MeTooLongLine,
			      "%.30s: Too long line: %.30s..."),
		      filename,buf);
	    (*errors) ++;
	    break;
	}
	l1--;

	while (l1-- > 0 && whitespace(buf[l1]))
	    buf[l1] = '\0';

	c = buf;
	while (*c && whitespace (*c)) /* skip leading whitespace */
	    c++;
	if ('#' == *c)
	    continue;
	if (!*c)
	    continue;

	c1 = strpbrk(c," \t");
	if (!c1) {
	    lib_error(CATGETS(elm_msg_cat, MeSet, MeBadLine,
			      "%.30s: Bad line: %.30s..."),
		      filename,buf);
	    (*errors) ++;
	    break;	    
	}
	*c1 = '\0';
	c1++;

	while (*c1 && whitespace (*c1)) /* skip leading whitespace */
	    c1++;
	if (!*c1) {
	    lib_error(CATGETS(elm_msg_cat, MeSet, MeBadLine,
			      "%.30s: Bad line: %.30s..."),
		      filename,buf);
	    (*errors) ++;
	    break;	    
	}
	
	options = strpbrk(c1," \t;");
	if (options) {
	    *options = '\0';
	    options++;
	}
	
	for (st = &SERVICE_TYPES[0]; st->name; st++) 
	    if (0 == istrcmp(c1,st->name))
		break;

	if (!st->name) {
	    lib_error(CATGETS(elm_msg_cat, MeSet, MeServiceType,
			      "%.30s: Bad service type %s"),
		      filename,c1);
	    (*errors) ++;
	    break;	    	    
	}

	zero_service_entry( & (service_list[service_count+count]),
			    c  /* hostname */,
			    st /* service */,
			    0  /* This is NOT temporary entry */);

	if (system)
	    service_list[service_count+count].flags |= SE_system;
		
	if (options) {
	    char *opt;
	    char * WALK = NULL;

	    for (opt = mime_parse_content_opts(options, &WALK); 
		 opt; 
		 opt = mime_parse_content_opts(NULL, &WALK)) {
		char *val = NULL;          /* malloced by dequote_opt */ 
		char * q = strchr(opt,'=');
		char * zopt = NULL;

		DPRINT(Debug,65,(&Debug, 
				 "mime_parse_content_opts gives: %s\n",
				 opt));
		
		if (q) 
		    *q++ = '\0';		    

		if (0 == strcmp(opt,"alias")) {
		    if (!q || !q[0]) {
			lib_error(CATGETS(elm_msg_cat, MeSet, MeOptionReqValue,
					  "%.30s: Option %s requires value"),
				  filename,opt);
			(*errors) ++;
			continue;
		    }

		    val = dequote_opt(q,strlen(q));		
		    add_alias_to_entry(& (service_list[service_count+count]),
				       val);
		    free(val);  /* add_alias_to_entry strdup's value */
		    service_list[service_count+count].flags |= SE_given_alias;

		} else if (0 == strcmp(opt,"addr")) {
		    char *q1 = NULL;
		    if (!q || !q[0]) {
			lib_error(CATGETS(elm_msg_cat, MeSet, MeOptionReqValue,
					  "%.30s: Option %s requires value"),
				  filename,opt);
			(*errors) ++;
			continue;
		    }
		    if ('"' != q[0])
			q1 = strchr(q,':');

		    if (!q1) { /* name given */
			add_addr_name_to_entry(&(service_list[service_count+count]),
					       /* Malloces result */
					       dequote_opt(q,strlen(q)),
					       0);
			service_list[service_count+count].flags |= 
			    SE_given_name_addr;
		    } else {  /* Addr? given */
			SOCKADDR result;
#ifndef USE_INET_ATON
			long tmp;
#endif
			char * port;

			/* Make comparision easier */
			bzero((void *)&result, sizeof result);

			*q1++ = '\0';
			port = qstrpbrk(q1,":/");
			if (port)
			    *port++ = '\0';
			val = dequote_opt(q1,strlen(q1));		

			/* inet_aton() is better but not available on
			   some systems so we need use inet_addr()
			*/

#ifdef I_NETINET_IN			
			if (0 == strcmp(q,"ip") &&
#ifdef USE_INET_ATON
			    inet_aton(q1,&result.sin.sin_addr)
#else
			    -1 != (tmp = inet_addr(q1)) 
#endif
			    ) {
			    int p = PORT_end;   /* No port given */

#ifndef USE_INET_ATON
			    memcpy(&(result.sin.sin_addr.s_addr),
				   &tmp,
				   sizeof (result.sin.sin_addr.s_addr));
#endif

			    result.sin.sin_family = AF_INET;
			    if (port)
				p = atoi(port);
			    result.sin.sin_port = htons(p);
			} else
#endif			
			    {
				lib_error(CATGETS(elm_msg_cat, MeSet,
						  MeBadAddrSpec,
						  "%.30s: Bad address specification %s:%s"),
					  filename,q,q1);	
				(*errors) ++;
				free(val); val= NULL;
				continue;			    
			    }	

			add_addr_to_entry(& (service_list[service_count+
							 count]),
					  &result);
			free(val); val = NULL;
			service_list[service_count+count].flags |= 
			    SE_given_addr;
		    }
		} else if (0 == strcmp(opt,"port")) {
		    int port;
		    if (!q || !q[0]) {
			lib_error(CATGETS(elm_msg_cat, MeSet, MeOptionReqValue,
					  "%.30s: Option %s requires value"),
				  filename,opt);
			(*errors) ++;
			continue;
		    }

		    port = atoi(q);

		    if (port == PORT_end) {
			lib_error(CATGETS(elm_msg_cat, MeSet, MeOptionPortBad,
					  "%.30s: Option port=%d unsupported"),
				  filename,
				  port);
			(*errors) ++;
			continue;
		    }

		    add_port_to_entry(& (service_list[service_count+count]),
				      port);
		    service_list[service_count+count].flags |= 
			SE_given_port;

#ifdef USE_DLOPEN
		} else if (NULL != (zopt = strchr(opt,':'))) {
		    int idx = -1;
		    int x;

		    *zopt++ = '\0';

		    for (x = 0; x < service_list[service_count+count].
			     option_count; x++) {
			if (0 == strcmp(opt,service_list[service_count+count].
			    option_list[x].prefix))
			    idx = x;
		    }
		    if (-1 == idx) {
			struct SE_option_type * Y = get_option_type(opt);
			if (!Y)
			    continue;

			idx = 
			    add_option_type_to_entry(& (service_list
							[service_count+count]),
						     Y,opt);
		    }

		    if (SE_option_t_magic != 
			service_list[service_count+count].
			option_list[idx].type->magic)
			panic("CONNECTION PANIC",__FILE__,__LINE__,
			      "parse_service_entries",
			      "Bad option type on service list",0);

		    if (!service_list[service_count+count].
			option_list[idx].type ->
			parse_on_option(& (service_list[service_count+count].
					   option_list[idx]),
					zopt,q)) {
			(*errors) ++;
			continue;
		    }					   
#endif
		} else {
		    lib_error(CATGETS(elm_msg_cat, MeSet, MeOptionUnspported,
				      "%.30s: Option %s unsupported"),
			      filename,opt);
		    (*errors) ++;
		    continue;
		}
	    }

	}

	count++;	    
    }

    service_count += count;
    DPRINT(Debug,9,(&Debug, 		    
		    "parse_service_entries: %s, %d parsed (%d total)\n",
		    filename,count,service_count));
    fclose(f);
    return 1;
}

void dump_service_entries(f,system)
     FILE *f; 
     int system;
{
    int x;

    for (x = 0; x < service_count; x++) {

	if (system &&
	    (service_list[x].flags & SE_system) == 0)
	    continue;
	if (!system &&
	    (service_list[x].flags & SE_system) != 0)
	    continue;
	
	dump_service_entry(f,&(service_list[x]));
    }
    fflush(f);
}
#endif


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



syntax highlighted by Code2HTML, v. 0.9.1