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 (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 #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 : "", X->host ? X->host : "")); ret = 1; } clean: DPRINT(Debug,12,(&Debug, "split_remote_name=%d: *rest=%s\n", ret, *rest ? *rest : "")); 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 : "", con->C.host ? con->C.host : "", 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 : "", con->C.host ? con->C.host : "", 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 : "", con->C.host ? con->C.host : "", 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 : "", (*c)->C.host ? (*c)->C.host : "", (*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 : "", X->host ? X->host : "", 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 : "", c->C.host ? c->C.host : "", 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 : "", c->C.host ? c->C.host : "", 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 : "", c->C.host ? c->C.host : "", 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 : "", con->C.host ? con->C.host : "", 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 : "", c->C.host ? c->C.host : "", 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: */