static char rcsid[] = "@(#)$Id: remote_mbx.c,v 1.58 2006/07/01 07:37:48 hurtta Exp $";

/******************************************************************************
 *  The Elm (ME+) Mail System  -  $Revision: 1.58 $   $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 POLL_METHOD
void zero_Read_Buffer(buffer)
     struct Read_Buffer *buffer;
{
    buffer->read_buffer = NULL;
    buffer->read_len    = 0;
}

void free_Read_Buffer(buffer)
     struct Read_Buffer *buffer;
{
    if (buffer->read_buffer) {
	free(buffer->read_buffer);
	buffer->read_buffer = NULL;
    }
    buffer->read_len = 0;
}

/* For non-lieral we read with quite short block because
   many answers are quite small
*/

#define READ_BLOCK   1024

int ReadFromSocket(fd,buffer,wanted)
     int fd; 
     struct Read_Buffer *buffer;
     int wanted;
{
    int n;

    if (wanted > 0) {
	buffer -> read_buffer = safe_realloc(buffer -> read_buffer,
					     buffer -> read_len + wanted);
	n = read(fd, buffer -> read_buffer + buffer -> read_len, wanted);
    } else {
	buffer -> read_buffer = safe_realloc(buffer -> read_buffer,
					     buffer -> read_len + READ_BLOCK);
	n = read(fd, buffer -> read_buffer + buffer -> read_len, READ_BLOCK);
    }

    return n;
}

int find_crlf(buffer, add_null)
     struct Read_Buffer *buffer;
     int add_null;
{
    char * p = buffer->read_buffer;
    int i;

    for (i = 0; i < buffer->read_len -1; i++)
	if ('\r' == p[i] && '\n' == p[i+1]) {
	    if (add_null) {
		p[i] = '\0';
		p[i+1] = '\0';
	    }
	    DPRINT(Debug,49,(&Debug,
			     "find_crlf=%d (%d total)\n",
			     i+2,buffer->read_len));
	    return i+2;
	}
    DPRINT(Debug,49,(&Debug,
		     "find_crlf=0 (%d total)\n",
		     buffer->read_len));
    return 0;
}

void cut_line(buffer, len)
     struct Read_Buffer *buffer;
     int len;
{
    char * p = buffer->read_buffer;
    
    buffer->read_len -= len;
    DPRINT(Debug,49,(&Debug,
		     "cut_line: %d chars consumed, %d left\n",
		     len,buffer->read_len));
    if (buffer->read_len)
	memmove(p, p+len, buffer->read_len);
}     

void zero_Write_Buffer(buffer)
     struct Write_Buffer *buffer;
{
    buffer->write_buffer = NULL;
    buffer->write_len    = 0;
}

void free_Write_Buffer(buffer)
     struct Write_Buffer *buffer;
{
    if (buffer->write_buffer) {
	free(buffer->write_buffer);
	buffer->write_buffer = NULL;
    }
    buffer->write_len = 0;
}

int WriteToSocket(fd,buffer)
     int fd; 
     struct Write_Buffer *buffer;
{
    int n = write(fd,buffer->write_buffer, buffer->write_len);
    
    return n;
}

void cut_Write_Buffer(buffer,n)
     struct Write_Buffer *buffer; 
     int n;
{
    if (n > 0) {
	char * p = buffer->write_buffer;
	
	buffer->write_len -= n;
	if (buffer->write_len)
	    memmove(p, p+n, buffer->write_len);

	DPRINT(Debug,13,(&Debug,
			 "cut_Write_Buffer: Written %d bytes (%d left)\n",
			 n,buffer->write_len));
    }
}

void add_to_Write_Buffer(buffer,str,l)
     struct Write_Buffer *buffer;
     char **str;
     int l;
{
    if (!buffer->write_len) {
	if (buffer->write_buffer) 
	    free(buffer->write_buffer);
	buffer->write_buffer = *str;
	buffer->write_len    = l;
    } else if (l > 0) {
	buffer->write_buffer = safe_realloc(buffer->write_buffer,
					    buffer->write_len+l);
	memmove(buffer->write_buffer + buffer->write_len,*str,l);
	buffer->write_len += l;
	free(*str);
    }
    *str = NULL;
}

#endif

/* 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

/* Closed only implicity on exit */
FILE * transaction_file = NULL;

int set_transaction_file(filename)
     CONST char *filename;
{
    FILE * f;
    int err;
    int fd;


    if (userid == -1 || groupid == -1) {
	/* save original user and group ids */
	userid  = getuid();
	groupid = getgid();	

	DPRINT(Debug,1,(&Debug,"set_transaction_file: %s: Saving userid/groupid=%d/%d\n",
			filename,userid,groupid));

    }

    err = can_open(filename,"a");
    if (0 != err) {
	lib_error(CATGETS(elm_msg_cat, MeSet, MeFileNotWriteable,
			  "File %.50s is not writeable: %s"),
		  filename, error_description(err));
	return 0;
    }

    fd = open(filename,O_CREAT|O_WRONLY|O_APPEND,00600);
    if (-1 == fd) {
	int err = errno;
	lib_error(CATGETS(elm_msg_cat, MeSet, MeFileNotWriteable,
			  "File %.50s is not writeable: %s"),
		  filename, error_description(err));
	return 0;
    }
    elm_chown(filename, userid, groupid); /* file owned by user */


    f = fdopen(fd,"a");
    if (!f) {
	close(fd);
	return 0;
    }

    if (transaction_file) {
	fprintf(transaction_file,
		"\n===== Changing logging to file %s\n",
		filename);
	fclose(transaction_file);
    }
    transaction_file = f;
    
#ifdef SETLINEBUF
    setlinebuf(transaction_file);
#endif

    DPRINT(Debug,1,(&Debug,
		    "set_transaction_file: %s: transaction file opened\n",
		    filename));

    return 1;
}

#ifdef REMOTE_MBX


void zero_remote_account(ra)
     struct remote_account *ra;
{
    /* bzero is defined hdrs/defs.h */
    bzero ((void *)ra, sizeof (struct remote_account));

    ra->magic                 = REMOTE_ACCOUNT_magic;

    ra->hostaddr.sa.sa_family = AF_UNSPEC;
    ra->service_idx           = 0;
    ra->stream                = NULL;
    ra->username              = NULL;
    ra->host                  = NULL;
}


void free_remote_account(ra)
     struct remote_account *ra;
{
    if (ra->magic  != REMOTE_ACCOUNT_magic)
	panic("CONNECTION PANIC",__FILE__,__LINE__,
	      "free_remote_account",
	      "Bad magic number",0);

    ra->hostaddr.sa.sa_family = AF_UNSPEC;
    ra->service_idx           = -1;

    if (ra->stream) 
	FreeStreamStack(& (ra->stream));
    if (ra->username) {
	free(ra->username);
	ra->username = NULL;
    }
    if (ra->host) {
	free(ra->host);
	ra->host = NULL;
    }
}

#ifdef I_NETINET_IN


static int connect_one_IN P_((struct remote_account *ra,
			      int *cur_socket, int *last_error,
			      struct chancel_data *can));
static int connect_one_IN(ra,cur_socket,last_error,can)
     struct remote_account *ra;
     int *cur_socket;
     int *last_error;
     struct chancel_data *can;
{
    int r = 0;

    /* r == 0: not succeed
       r <  0: fatal error
       r >  0: succeed
    */

    DPRINT(Debug,12,(&Debug,
		     "connect_one_IN: addr=%s, port=%d\n",
		     inet_ntoa(ra->hostaddr.sin.sin_addr),
		     ntohs(ra->hostaddr.sin.sin_port)));

#ifdef NEED_REOPEN_AFTER_FAILED_CONNECT
    if (-1 != (*cur_socket)) {
	DPRINT(Debug,12,(&Debug,
			 "connect_one_IN: NEED_REOPEN_AFTER_FAILED_CONNECT -- closing socket (%d) for reopening\n",
			 (*cur_socket)));;
	close(*cur_socket);
	(*cur_socket) = -1;
    }
#endif
    
    if (-1 == (*cur_socket)) {
	(*cur_socket) = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
	if (-1 == (*cur_socket)) {
	    int err =errno;
	    lib_error(CATGETS(elm_msg_cat, MeSet,MeFailedToCreate,
			      "Failed to create socket: %s"),
		      error_description(err));
	    r = -1;
	    goto clean;
	}
	DPRINT(Debug,12,(&Debug,
			 "connect_one_IN: socket=%d\n",(*cur_socket)));
    }
    	
 retry:

    if (-1 == connect((*cur_socket),& (ra->hostaddr.sa),
			  sizeof (ra->hostaddr.sin))) {
	(*last_error) = errno;
	DPRINT(Debug,12,(&Debug,
		    "connect_one_IN: connect failed: %s\n",
		    error_description(*last_error)));
	
	if (errno == EINVAL) {
	    /* Seems that it is not not allowed several connect 
	     * attempts with same socket ...
	     */
	    (*last_error) = errno;
	    DPRINT(Debug,12,(&Debug,
			     "connect_one_IN: -- reopening socket and retrying...\n"));
	    
	    close(*cur_socket);
	    (*cur_socket) = socket(PF_INET,SOCK_STREAM,
				IPPROTO_TCP);
	    if (-1 != (*cur_socket)) {
		DPRINT(Debug,12,(&Debug,
				 "connect_one_IN: socket=%d\n",(*cur_socket)));
	    }
	    if (-1 == (*cur_socket) ||
		-1 == connect(*cur_socket,&(ra->hostaddr.sa),
			      sizeof (ra->hostaddr.sin))) {
		(*last_error) = errno;
		DPRINT(Debug,12,(&Debug,
				 "connect_one_IN: connect failed: %s\n",
				 error_description(*last_error)));
	    } else
		r = 1;
	}

	if (0 == r && EINTR == errno) {
	    
	    if (is_canceled(can)) {
		DPRINT(Debug,12,(&Debug,"connect canceled...\n"));
		r = -1;
	    } else {
		DPRINT(Debug,12,(&Debug,"Retrying connect...\n"));
		goto retry;
	    }
	    
	}
    } else
	r = 1;
		
    /* If got connection to host and and it refused
       connection, try another ports
    */
    if (r < 1 && *last_error != ECONNREFUSED) 
	r = -2;			   

 clean:
    DPRINT(Debug,12,(&Debug,
		     "connect_one_IN=%d%s\n",r,r > 0 ? " (succeed)" : ""));
    return r;
}
#endif

int connect_remote_account(ra,got,se,default_portlist,force_port)
     struct remote_account *ra;     
     int *got;
     struct service_entry *se;    
     PORTS default_portlist[];
     PORTS force_port;
{
    int ok = 0;
    int last_error   = 0;
    int cur_socket   = -1;
    int prev_family  = AF_UNSPEC;
    int was_canceled = 0;

    int idx;

    if (ra->magic  != REMOTE_ACCOUNT_magic)
	panic("CONNECTION PANIC",__FILE__,__LINE__,
	      "connect_remote_account",
	      "Bad magic number",0);

    if(ra->stream) {
	DPRINT(Debug,12,(&Debug,
			 "connect_remote_account: Closing for reopening\n"));
	FreeStreamStack(& (ra->stream));
    }

    for (idx = 0; idx < se->addr_count; idx++) {

	/* Copy all information */
	ra->hostaddr = se->addr_list[idx];;

	if (prev_family != ra->hostaddr.sa.sa_family &&
	    cur_socket != -1) {

	    DPRINT(Debug,12,(&Debug,
			     "connect_remote_account: family change from %d to %d -- closing socket (%d)\n",
			     prev_family,ra->hostaddr.sa.sa_family,
			     cur_socket
			     ));

	    close(cur_socket);
	    cur_socket = -1;	    
	}

	switch (ra->hostaddr.sa.sa_family) {
	    int r;
#ifdef I_NETINET_IN
	case AF_INET: {
	    
	    struct chancel_data * can;

	    if (force_port != PORT_end)
		can = new_cancel(CATGETS(elm_msg_cat, MeSet,MeConnectingPort,
					 "Connecting to %s [%s], port %d ... (%d)"),
				 se->official_name,
				 inet_ntoa(ra->hostaddr.sin.sin_addr),
				 force_port,
				 idx);
	    else
		can = new_cancel(CATGETS(elm_msg_cat, MeSet,MeConnecting,
					 "Connecting to %s [%s]... (%d)"),
				 se->official_name,
				 inet_ntoa(ra->hostaddr.sin.sin_addr),
				 idx);

	    /* r == 0: not succeed
	       r <  0: fatal error
	       r >  0: succeed
	    */

	    r = 0;

	    if (force_port != PORT_end) {

		DPRINT(Debug,11,(&Debug,
				 "-- force_port = %d\n", force_port));
		    
		ra->hostaddr.sin.sin_port = htons(force_port);
		r = connect_one_IN(ra,&cur_socket,&last_error,can);
		if (r > 0)
		    *got = force_port;

	    } else if (ra->hostaddr.sin.sin_port != htons(PORT_end)) {
		DPRINT(Debug,11,(&Debug,"-- address record gives port %d\n",
				 ntohs(ra->hostaddr.sin.sin_port)));
		r = connect_one_IN(ra,&cur_socket,&last_error,can);
		if (r > 0)
		    *got = ntohs(ra->hostaddr.sin.sin_port);
	    } else if (se->port_count > 0) {
		int idx2;
		DPRINT(Debug,11,(&Debug,
				 "-- service list gives portlist (%d ports)\n",
				 se->port_count));

		for (idx2 = 0; idx2 < se->port_count && 0 == r; idx2++) {
		    ra->hostaddr.sin.sin_port = 
			htons(se->port_list[idx2]);
		    r = connect_one_IN(ra,&cur_socket,&last_error,can);
		    if (r > 0)
			*got = se->port_list[idx2];
		}
	    } else {
		int idx2;

		DPRINT(Debug,11,(&Debug,
				 "-- service list does not give portlist -- trying default portlist\n"));

		for (idx2 = 0; default_portlist[idx2] != PORT_end && 0 == r; 
		     idx2++) {
		    
		    ra->hostaddr.sin.sin_port = 
			htons(default_portlist[idx2]);
		    r = connect_one_IN(ra,&cur_socket,&last_error,can);
		    if (r > 0)
			*got = default_portlist[idx2];
		}
	    }

	    if (r > 0) {
		    
		DPRINT(Debug,12,(&Debug,
				 "connect_remote_account: Connection succeed %s [%s], port %d\n",
				 se->official_name,
				 inet_ntoa(ra->hostaddr.sin.sin_addr),
				 *got));
		ok = 1;

		if (transaction_file) {
		    time_t tmp = time(NULL);
		    struct tm * zz = localtime(&tmp);
		    fprintf(transaction_file,
			    "%d [%d] %02d:%02d:%02d === CONNECT %s [%s], port %d\n",
			    getpid(),cur_socket,
			    zz ? zz->tm_hour : 00,
			    zz ? zz->tm_min  : 00,
			    zz ? zz->tm_sec  : 00,
			    se->official_name,
			    inet_ntoa(ra->hostaddr.sin.sin_addr),
			    *got);
		}
	    }

	    was_canceled = is_canceled(can);
	    free_cancel(&can);

	}	       
	    break;
#endif
	default:
	    lib_error(CATGETS(elm_msg_cat, MeSet,MeUnsupportedAddrType,
			      "Name %s have odd type address"),
		      se->official_name);
	    break;
	}

	if (ok) {
	    DPRINT(Debug,20,(&Debug,
			     "connect_remote_account: Connection succeed -- quiting loop\n"));
	    break;
	}
    }

    if (!ok) {

	if (cur_socket != -1) {
	    DPRINT(Debug,12,(&Debug,
			     "connect_remote_account: Closing socket (%d) after failure\n",
			     cur_socket));
	    close(cur_socket);
	    cur_socket = -1;	    
	}

	if (was_canceled)
	    lib_error(CATGETS(elm_msg_cat, MeSet,MeConnectCanceled,
			      "Connect %s canceled."),
		      se->official_name);
	else
	    lib_error(CATGETS(elm_msg_cat, MeSet,MeConnectFailed,
			      "Failed to connect %s: %s"),
		      se->official_name,error_description(last_error));
	ok = 0;
	goto clean;
    }

    /* We set folder to non-blocking after connect, because
     * non-blocking connect is little complicate to use
     */
    
    if (-1 == fcntl(cur_socket,F_SETFL,O_NONBLOCK)) {
	int err = errno;
	DPRINT(Debug,12,(&Debug,
			 "connect_remote_account: fcntl [O_NONBLOCK] failed: %s\n",
			 error_description(err)));
    }

    ra->stream = returnSimpleStream(cur_socket);
    ra->service_idx = -1;

    if (!(se->flags  & SE_temporary)) {
	ra->service_idx = se - service_list;
	DPRINT(Debug,12,(&Debug,
			 "connect_remote_account: service_idx = %d\n",
			 ra->service_idx));
	if (ra->service_idx < 0 || ra->service_idx >= service_count) {
	    panic("CONNECTION PANIC",__FILE__,__LINE__,
		  "connect_remote_account",
		  "Non-temporary service entry not in list",0);
	}
    }

 clean:
    DPRINT(Debug,12,(&Debug,
		     "connect_remote_account=%d\n",ok)); 
    return ok;
}

/* -1 == name not found or bad syntax
   0 == not a remote address
   1 == name found 
*/
int split_remote_name(name,X,se,rest,lookup_flags)
     char *name;
     struct remote_account *X;
     struct service_entry **se;
     char **rest;
     int lookup_flags;
{
    char *sep;
    int ret = 0;
    
    DPRINT(Debug,12,(&Debug,
		     "split_remote_name: name=\"%s\", lookup_flags=%d\n",
		     name,lookup_flags));
    
    *rest = NULL;
    zero_remote_account(X);
    
    *se = NULL;

    *rest = strpbrk(name,"/:");
    sep  = strchr(name,'@');

    if (sep && (!*rest || *rest > sep)) {
	char * sep2;
       
	if (sep == name || sep[1] == '\0' ||
	    *rest == sep+1) {
	    lib_error(CATGETS(elm_msg_cat, MeSet,MeBadRemoteMailbox,
			      "Bad remote mailbox: %s"),
		      name);
	    ret = -1;
	    goto clean;
	}

	/* Test for '@' on hostname -- if there is, then
	   assume that first '@' was on username
	*/
	sep2 = strchr(sep+1,'@');
	if (sep2 && (!*rest || *rest > sep2+1)) {
	    DPRINT(Debug,12,(&Debug,
			     "split_remote_name: Found second '@' from hostname, Assuming that first '@' was on username\n"));
	    sep = sep2;
	}
	
	if (*rest) {
	    CONST char * START = sep+1;
	    X->host = safe_malloc(*rest - START+1);
	    memcpy(X->host,START,*rest - START);
	    X->host[*rest - START] = '\0';
	} else
	    X->host = safe_strdup(sep+1);	   

	X->username = safe_malloc(sep - name+1);
	memcpy(X->username,name,sep - name);
	X->username[sep - name] = '\0';

	if (0 == lookup_flags) {
	    lookup_flags = *rest ? STFLAG_browser : STFLAG_mbox;

	      DPRINT(Debug,12,(&Debug,
			       "split_remote_name: (%s) Using lookup_flags=%d\n",
			       name,lookup_flags));
	}

	*se = give_service_entry(X->host, lookup_flags);
	if (!*se) {
	    ret = -1;
	    goto clean;
	}

	/* Canonify host name ... */
	free(X->host);
	X->host = safe_strdup((*se)->official_name);

	DPRINT(Debug,12,(&Debug,
			 "split_remote_name: username=%s, host=%s\n",
			 X->username ? X->username : "<NULL>",
			 X->host ? X->host : "<NULL>"));

	ret = 1;
    } 
    
 clean:
    DPRINT(Debug,12,(&Debug,
		     "split_remote_name=%d: *rest=%s\n",
		     ret,
		     *rest ? *rest : "<NULL>"));

    return ret;
}		



static struct connection_cache * CACHE_LIST = NULL;

/* May not be 
       uin16 port
   because this is promoted to int
*/
struct connection_cache * locate_from_cache(username,host,con_type,port)
     CONST char * username;
     CONST char * host;
     CONST struct connection_type  *con_type;
     int port;     /* 0 == default port */
{
    struct connection_cache * ptr;
 
    for (ptr = CACHE_LIST; ptr; ptr = ptr -> next) {
	if (!ptr->C.username || !ptr->C.host) {
	    DPRINT(Debug,11,(&Debug,
			     "locate_from_cache: %p: No host or username on cache!\n",
			     ptr));
	    continue;
	}
	
	if (0 == strcmp(username,ptr->C.username) &&
	    0 == istrcmp(host,ptr->C.host) &&
	    (!con_type || con_type == ptr->type) &&
	    port == ptr->port)
	    return ptr;
    }
   
    return NULL;
}

static void remove_from_cache P_((struct connection_cache *con,
				  int maybe));
static void remove_from_cache(con,maybe)
     struct connection_cache *con;
     int maybe;
{
    struct connection_cache * ptr, *prev = NULL;

    for (ptr = CACHE_LIST; ptr; prev=ptr, ptr = ptr -> next) {
	if (ptr == con) {
	    if (NULL == prev)
		CACHE_LIST = ptr->next;
	    else
		prev->next = ptr->next;
	    ptr->next = NULL;

	    DPRINT(Debug,11,(&Debug,
			     "remove_from_cache: con=%p (%s@%s), type=%p (%s): removed from cache\n",
			     con,
			     con->C.username ? con->C.username : "<NULL>",
			     con->C.host ? con->C.host : "<NULL>",
			     con->type,
			     con->type->type_name));

	    return;
	}
    }
    con->next = NULL;
    
    if (maybe) {
	DPRINT(Debug,11,(&Debug,
			 "remove_from_cache: con=%p (%s@%s), type=%p (%s): Not in cache\n",
			 con,
			 con->C.username ? con->C.username : "<NULL>",
			 con->C.host ? con->C.host : "<NULL>",
			 con->type,
			 con->type->type_name));
	
    } else {
	DPRINT(Debug,1,(&Debug,
			"remove_from_cache: con=%p (%s@%s), type=%p: Not in cache\n",
			con,
			con->C.username ? con->C.username : "<NULL>",
			con->C.host ? con->C.host : "<NULL>",
			con->type));
	
	panic("CONNECTION PANIC",__FILE__,__LINE__,"remove_from_cache",
	      "Connection not in connection cache",0);
    }
}



struct connection_cache *  create_connection(T)
     struct connection_type *T;
{
    struct connection_cache *ret;

    if (CONNECTION_TYPE_magic != T->magic)
	panic("REMOTE CONNECTION PANIC",__FILE__,__LINE__,
	      "create_connection",
              "Bad connection type",0);

    DPRINT(Debug,10,(&Debug,
		     "create_connection: type=%p (%s)\n",
		     T,T->type_name));

    ret = safe_malloc(sizeof (*ret));
    bzero((void *)ret,sizeof (*ret));

    ret->type  = T;
    ret->next  = NULL;
    ret->f     = NULL;
    ret->d     = NULL;
    ret->state = CON_error;
    ret->a.any = NULL;     /* Initilize to NULL */
    ret->port  = 0;        /* Default port */

    zero_remote_account(&(ret->C));

    ret->type->cache_zero_it(ret);

    DPRINT(Debug,10,(&Debug,
		     "create_connection=%p\n",ret));

    return ret;
}

void  free_connection(c)
     struct connection_cache **c;
{
    if (CONNECTION_TYPE_magic != (*c)->type->magic)
	panic("REMOTE CONNECTION PANIC",__FILE__,__LINE__,
	      "free_connection",
              "Bad connection type",0);

    DPRINT(Debug,10,(&Debug,
		     "free_connection: con=%p (%s@%s), type=%p (%s)\n",
		     (*c),
		     (*c)->C.username ? (*c)->C.username : "<NULL>",
		     (*c)->C.host ? (*c)->C.host : "<NULL>",
		     (*c)->type,
		     (*c)->type->type_name));
    
    if (CACHE_LIST)
	remove_from_cache((*c),1);

    if (NULL != (*c)->C.stream &&
	(*c)->state != CON_error)
	(*c)->type->cache_close_it((*c));

    (*c)->type->cache_free_it((*c));

    free_remote_account(&((*c)->C));

    (*c)->state = CON_error;
    (*c)->f    = NULL;
    (*c)->d    = NULL;
    (*c)->next = NULL;
    (*c)->type = NULL;

    free(*c);
    *c = NULL;
}

int  join_connection(c,X,st)
     struct connection_cache *c;
     struct remote_account *X;
     enum connection_state st;
{
    int ret = 0;

    DPRINT(Debug,10,(&Debug,
		     "join_connection: con=%p, type=%p (%s), X=%p (%s@%s), st=%d\n",
		     c,
		     c->type,
		     c->type->type_name,
		     X,
		     X->username ? X->username : "<NULL>",
		     X->host ? X->host : "<NULL>",
		     st));

    if (CONNECTION_TYPE_magic != c->type->magic)
	panic("REMOTE CONNECTION PANIC",__FILE__,__LINE__,
	      "join_connection",
              "Bad connection type",0);

    free_remote_account(&(c->C));
    
    c->C     = *X;
    c->state = st;

    /* Avoid double free() ... */
    X->host      = NULL;
    X->username  = NULL;
    X->stream    = NULL;

    ret = c->type->cache_open_it(c);

    DPRINT(Debug,10,(&Debug,
		     "join_connection=%d  (state=%d)\n",
		     ret,c->state));
    return ret;
}

int  login_connection(c,password)
     struct connection_cache *c;
     CONST struct string *password; /* May be NULL */
{
    int ret = 0;

    if (CONNECTION_TYPE_magic != c->type->magic)
	panic("REMOTE CONNECTION PANIC",__FILE__,__LINE__,
	      "login_connection",
              "Bad connection type",0);

    DPRINT(Debug,10,(&Debug,
		     "login_connection: con=%p (%s@%s), type=%p (%s)\n",
		     c,
		     c->C.username ? c->C.username : "<NULL>",
		     c->C.host ? c->C.host : "<NULL>",
		     c->type,c->type->type_name));
    
    if (c->state != CON_open) {
	DPRINT(Debug,10,(&Debug,
			 "login_connection: Opening it first...\n")); 
	ret = c->type->cache_open_it(c);
	if (!ret)
	    goto fail;
    }

    ret = c->type->cache_login_it(c,password);

 fail:
    DPRINT(Debug,10,(&Debug,
		     "login_connection=%d\n",ret));
    return ret;
}

void folder_from_connection(c,f)
     struct connection_cache *c;
     struct folder_info *f;
{
    if (CONNECTION_TYPE_magic != c->type->magic)
	panic("REMOTE CONNECTION PANIC",__FILE__,__LINE__,
	      "folder_from_connection",
              "Bad connection type",0);

    DPRINT(Debug,10,(&Debug,
		     "folder_from_connection: con=%p (%s@%s), type=%p (%s)\n",
		     c,
		     c->C.username ? c->C.username : "<NULL>",
		     c->C.host ? c->C.host : "<NULL>",
		     c->type,c->type->type_name));
    DPRINT(Debug,10,(&Debug,
		     "                      : folder=%p\n",f));

    /* Must have be on connection cache */
    remove_from_cache(c,0);

    c->f = f;
    c->type->cache_folder_from_it(c,f);
}

void browser_from_connection(c,d)
     struct connection_cache *c;
     struct folder_browser *d;
{
    if (CONNECTION_TYPE_magic != c->type->magic)
	panic("REMOTE CONNECTION PANIC",__FILE__,__LINE__,
	      "browser_from_connection",
              "Bad connection type",0);

    DPRINT(Debug,10,(&Debug,
		     "browser_from_connection: con=%p (%s@%s), type=%p (%s)\n",
		     c,
		     c->C.username ? c->C.username : "<NULL>",
		     c->C.host ? c->C.host : "<NULL>",
		     c->type,c->type->type_name));
    DPRINT(Debug,10,(&Debug,
		     "                      : browser=%p\n",d));

    /* Must have be on connection cache */
    remove_from_cache(c,0);

    c->d = d;
    c->type->cache_browser_from_it(c,d);
}

void  close_connection(con)
     struct connection_cache *con;
{
    if (CONNECTION_TYPE_magic != con->type->magic)
	panic("REMOTE CONNECTION PANIC",__FILE__,__LINE__,
	      "close_connection",
              "Bad connection type",0);
 
    DPRINT(Debug,10,(&Debug,
		     "close_connection: con=%p (%s@%s), type=%p (%s)\n",
		     con,
		     con->C.username ? con->C.username : "<NULL>",
		     con->C.host ? con->C.host : "<NULL>",
		     con->type,con->type->type_name));
   
    con->type->cache_close_it(con);

    if (NULL != con->C.stream ) {
	DPRINT(Debug,10,(&Debug,
			 "close_connection: Closing socket\n"));
	
	FreeStreamStack( &(con->C.stream));
    }	
    
    con->state = CON_error;
}

int close_cached_connections()
{
    struct connection_cache * ptr, *next;
    int X = 0;

    DPRINT(Debug,4,(&Debug,
		"Closing cached connections...\n"));
    for (ptr = CACHE_LIST; ptr; ptr = next) {
	next = ptr -> next;
	close_connection(ptr);
	X++;
	free_connection(&ptr);
    }
    DPRINT(Debug,4,(&Debug,
		    "Cached connections closed\n"));

    return X;
}

void  cache_connection(c)
     struct connection_cache *c;
{    
    if (CONNECTION_TYPE_magic != c->type->magic)
	panic("REMOTE CONNECTION PANIC",__FILE__,__LINE__,
	      "cache_connection",
              "Bad connection type",0);

    DPRINT(Debug,10,(&Debug,
		     "cache_connection: con=%p (%s@%s), type=%p (%s)\n",
		     c,
		     c->C.username ? c->C.username : "<NULL>",
		     c->C.host ? c->C.host : "<NULL>",
		     c->type,c->type->type_name));

    c->f    = NULL;            /* Detach from folder */
    c->d    = NULL;            /* Detach from browser */
    c->next = CACHE_LIST;
    CACHE_LIST = c;    
}


#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