static char rcsid[] = "@(#)$Id: shared_mbox.c,v 1.7 2006/06/29 17:26:41 hurtta Exp $";

/******************************************************************************
 *  The Elm (ME+) Mail System  -  $Revision: 1.7 $   $State: Exp $
 *
 *  Author: Kari Hurtta <hurtta+elm@posti.FMI.FI>
 *      or  Kari Hurtta <elm@elmme-mailer.org>
 *****************************************************************************/

/*
 * This file is compiled only if dlopen() is available, so
 * that file does not need to be guarded with #ifdef 
 */

#include "def_mbox.h"

DEBUG_VAR(Debug,__FILE__,"dl");

#include "shared_imp.h"
#include "rc_imp.h"
#include "save_opts.h"
#include "cs_imp.h"
#include "ss_imp.h"

#if ANSI_C
#define S_(x) static x;
#else
#define S_(x)
#endif

static struct shared_POP_CAPA {
    char                * capa;
    int                   priority;
    struct ImpInfo *      imp_idx;
    pop_capa_handler     *capa_handler;
} * shared_pop_capas                             = NULL;
static int shared_pop_capa_count                 = 0;

static struct shared_IMAP_CAPA {
    char                * capa;
    int                   priority;
    struct ImpInfo *      imp_idx;
    imap_capa_handler     *capa_handler;
} * shared_imap_capas                             = NULL;
static int shared_imap_capa_count                 = 0;



#define SHARED_MBOX_magic	0xF905

struct sl_mbox_data {
  uint16            magic;      

};


S_(sl_reg_functions sl_reg_mbox)
static int sl_reg_mbox P_((struct ImpInfo *i,
			      int reg_idx));
static int sl_reg_mbox(i,reg_idx)
     struct ImpInfo *i;
     int reg_idx;
{
    /* union hack to avoid warnings about casting
     * between pointer-to-object and pointer-to-function
     */
    
    int res = 0;

    union F2 {
	void *                 ptr;
	provides_shared_FT_f * f2;
    } f2;           /*  not in use */

    union F4 {
	void *                       ptr;
	provides_shared_POP_CAPA_f * f4;
    } f4;

    union F5 {
	void *                       ptr;
	provides_shared_IMAP_CAPA_f * f5;
    } f5;

    /* not in use */
    f2.ptr = dlsym(i->handle,  "provides_shared_FT");

    f4.ptr = dlsym(i->handle,  "provides_shared_POP_CAPA");
    f5.ptr = dlsym(i->handle,  "provides_shared_IMAP_CAPA");

    if (!f2.f2) {
	DPRINT(Debug,7,(&Debug, " ... NO provides_shared_FT\n"));
    }

    if (!f4.f4) {
	DPRINT(Debug,7,(&Debug, " ... NO provides_shared_POP_CAPA\n"));
    }

    if (!f5.f5) {
	DPRINT(Debug,7,(&Debug, " ... NO provides_shared_IMAP_CAPA\n"));
    }

    if (f2.f2) {
	int count,x;
	size_t   s_res2;
	struct folder_type ** res2 = f2.f2(&count, &s_res2);
	
	if (s_res2 != sizeof (**res2)) {
	    DPRINT(Debug,1,(&Debug,"... (folder_type_p) mismatch: %d should be %d\n",
			    s_res2, sizeof (**res2)));
	} else {
	    shared_folder_types = 
		safe_realloc(shared_folder_types,
			     sizeof (shared_folder_types[0]) *
			     (shared_folder_type_count + count));
	    
	    res = 1;
	    
	    DPRINT(Debug,7,(&Debug," ... provides_shared_FT:  (shared_folder_types) count %d\n",
			    count));
	    
	    for (x = 0; x < count; x++) {
		shared_folder_types[shared_folder_type_count
				    +x].T = res2[x];
#if 0
		shared_folder_types[shared_folder_type_count
				    +x].portnum = res2[x].portnum;
#endif
		shared_folder_types[shared_folder_type_count
				    +x].imp_idx = i;
	    }
	    shared_folder_type_count += count;		
	}
    }
    
#ifdef REMOTE_MBX

    if (f4.f4) {
	int count,x;
	size_t   s_res4;
	struct provides_shared_POP_CAPA * res4 = f4.f4(&count, &s_res4);
	
	if (s_res4 != sizeof (*res4)) {
	    DPRINT(Debug,1,(&Debug,
			    "... struct provides_shared_POP_CAPA mismatch: %d should be %d\n",
			s_res4,sizeof (*res4)));
	} else {
	    shared_pop_capas = 
		safe_realloc(shared_pop_capas,
			     sizeof (shared_pop_capas[0]) *
			     (shared_pop_capa_count + count));
	    
	    DPRINT(Debug,7,(&Debug," ... provides_shared_IMAP_CAPA:  (shared_pop_capas) count %d\n",
			    count));
	    res = 1;
	    
	    for (x = 0; x < count; x++) {
		shared_pop_capas[shared_pop_capa_count
				 +x].capa = res4[x].capa;
		shared_pop_capas[shared_pop_capa_count
				 +x].priority = res4[x].priority;
		shared_pop_capas[shared_pop_capa_count
				 +x].capa_handler = res4[x].capa_handler;
		shared_pop_capas[shared_pop_capa_count
				 +x].imp_idx = i;
		DPRINT(Debug,8,(&Debug,
				"library %s provides POP %s\n", 
				i->shname,
				res4[x].capa));
	    }
	    shared_pop_capa_count += count;
	}
    }

#endif

    if (f5.f5) {
	int count,x;
	size_t   s_res5;
	struct provides_shared_IMAP_CAPA * res5 = f5.f5(&count, &s_res5);
	
	if (s_res5 != sizeof (*res5)) {
	    DPRINT(Debug,1,(&Debug,
			    "... struct provides_shared_IMAP_CAPA mismatch: %d should be %d\n",
			    s_res5,sizeof (*res5)));
	} else {
	    shared_imap_capas = 
		safe_realloc(shared_imap_capas,
			     sizeof (shared_imap_capas[0]) *
			     (shared_imap_capa_count + count));
	    
	    DPRINT(Debug,7,(&Debug," ... provides_shared_IMAP_CAPA:  (shared_imap_capas) count %d\n",
			    count));
	    res = 1;
	    
	    for (x = 0; x < count; x++) {
		shared_imap_capas[shared_imap_capa_count
				  +x].capa = res5[x].capa;
		shared_imap_capas[shared_imap_capa_count
				  +x].priority = res5[x].priority;
		shared_imap_capas[shared_imap_capa_count
				  +x].capa_handler = 
		    res5[x].capa_handler;
		shared_imap_capas[shared_imap_capa_count
				  +x].imp_idx = i;
		DPRINT(Debug,8,(&Debug,
				"library %s provides IMAP %s\n", 
				i->shname,
				res5[x].capa));
	    }
	    shared_imap_capa_count += count;
	}
    }	    
    
    
    i->regs[reg_idx].valid   = res;
    
    DPRINT(Debug,7,(&Debug, 
		    "sl_reg_mbox:  [%p]->regs[%d].valid = %d\n",
		    i,reg_idx,res));


    return res;
}

S_(sl_zero_reg_list sl_zero_mbox)
static void sl_zero_mbox P_((struct dt_shared_info *var,
				struct reg_list *r));
static void sl_zero_mbox(var,r)
     struct dt_shared_info *var;
     struct reg_list *r;
{
    r->var     = var;
    r->valid   = 0;
    r->r.mbox  = safe_malloc(sizeof (*r->r.mbox));   /* never freed ... */
    r->r.mbox->magic = SHARED_MBOX_magic;
}

S_(sl_unreg_functions sl_unreg_mbox)
static void sl_unreg_mbox P_((struct ImpInfo *i, int reg_idx));
static void sl_unreg_mbox(i,reg_idx)
     struct ImpInfo *i; 
     int reg_idx;
{
    struct reg_list *r = & (i->regs[reg_idx] );
    r->valid   = 0;

}

static struct shared_loader mbox_loader = {
    SHARED_LOADER_magic,
    sl_reg_mbox,
    sl_zero_mbox,
    sl_unreg_mbox
};


SHAREDLIB use_shared_mbox = {
    &mbox_loader            /* loader */,
    0,                       
    NULL, 0,

    NULL
};


#ifdef REMOTE_MBX

static enum CAPA_phase pri_to_phase P_((int pri));
static enum CAPA_phase pri_to_phase(pri)
     int pri;
{
    
    if (pri < 10)
	return capa_prelogin;
    if (pri < 20)
	return capa_do_login;
    return capa_logged;
}



struct POP_capa_libs {
    char                 * capa;
    char                 * capa_args;
    struct ImpInfo       * imp_idx;
    int                    priority;
    pop_capa_handler     * capa_handler;
};

void probe_pop_capa_lib(pop_capa_libs,pop_capa_libcount,capa,capa_args)
     struct POP_capa_libs * *pop_capa_libs;
     int                  * pop_capa_libcount;
     CONST char *capa;
     CONST char *capa_args;
{
    int i;

    load_shared_libs1(&use_shared_mbox);  

    for (i = 0; i < shared_pop_capa_count; i++) {
	/* rfc2449: CAPA response tags are case-insensitive. */


	/* Also check that librrary is currently loaded ... */
	if (!verify_shared_index(shared_pop_capas[i].imp_idx)) {
	    DPRINT(Debug,8,(&Debug,"shared_pop_capas[%d] unloaded?\n",i));
	    continue;
	}

	if ( 0 == istrcmp(shared_pop_capas[i].capa, capa))
	    break;	
    }
    if (i >= shared_pop_capa_count)
	return;          /* Not found */

    DPRINT(Debug,8,(&Debug,"shared_pop_capas[%d] is %s\n",i,capa));

    *pop_capa_libs = safe_realloc(*pop_capa_libs,
				  (*pop_capa_libcount +1) *
				  sizeof ((*pop_capa_libs)[0]));

    (*pop_capa_libs)[*pop_capa_libcount].capa      = safe_strdup(capa);
    
    (*pop_capa_libs)[*pop_capa_libcount].capa_args = 
	capa_args ? safe_strdup(capa_args) : NULL;
    (*pop_capa_libs)[*pop_capa_libcount].imp_idx   = 
	shared_pop_capas[i].imp_idx;
    (*pop_capa_libs)[*pop_capa_libcount].priority  = 
	shared_pop_capas[i].priority;
    (*pop_capa_libs)[*pop_capa_libcount].capa_handler =
	shared_pop_capas[i].capa_handler;

    DPRINT(Debug,7,(&Debug,
		    "** Using %s library for POP capacity %s\n",
		    shared_pop_capas[*pop_capa_libcount].
				    imp_idx->tag,
		    (*pop_capa_libs)[*pop_capa_libcount].capa));

    (*pop_capa_libcount)++;		
}


static int pop_capa_compare P_((const void *A, const void *B));
static int pop_capa_compare(A,B)
     CONST void *A; 
     CONST void *B;
{
    CONST struct POP_capa_libs *A1 = A;
    CONST struct POP_capa_libs *B1 = B;

    if (A1->priority < B1->priority)
	return -1;
    if (A1->priority > B1->priority)
	return 1;
    return 0;
}


int handle_pop_capa_libs(folder,pop_capa_libs,pop_capa_libcount,phase,commands)
     struct folder_info *folder;
     struct POP_capa_libs * *pop_capa_libs;
     int                  * pop_capa_libcount;
     enum CAPA_phase      *  phase;
     struct pop_callbacks *commands;
{
    int ret = 1;
    struct remote_account * A;
    enum CAPA_phase  old_phase = *phase;

    if (!folder->p ||
	folder->folder_type != &pop_mbx) 
	panic("CONNECTION PANIC",__FILE__,__LINE__,"handle_pop_capa_libs",
	      "Not a POP connection",0);
        
    A = &(folder->p->a.pop_mbx.C);
    
    if (*pop_capa_libs) {
	int i;
	qsort(*pop_capa_libs,*pop_capa_libcount,
	      sizeof ((*pop_capa_libs)[0]),
	      pop_capa_compare);

	
	for (i = 0; i < *pop_capa_libcount; i++) {
	    struct SE_option       *config = NULL;
	    struct SE_option_type  *T      = NULL;
	    int j;

	    if (*phase != pri_to_phase((*pop_capa_libs)[i].priority))
		continue;

	    DPRINT(Debug,8,(&Debug,
			    "[%d] handling %s with %s library\n",
			    i,(*pop_capa_libs)[i].capa,
			    shared_pop_capas[i].imp_idx->tag));

	    for (j = 0; j < shared_SE_option_type_count; j++) {
		if (shared_SE_option_types[j].imp_idx ==
		    shared_pop_capas[j].imp_idx) {
		    T = shared_SE_option_types[j].T;
		    DPRINT(Debug,8,(&Debug,
				    "  ... have service option type %p\n",
				    T));
		}
	    }

	    if (A->service_idx >= 0 && T) {
		if (A->service_idx >= service_count) 
		    panic("CONNECTION PANIC",__FILE__,__LINE__,
			  "handle_pop_capa_libs",
			  "Bad service index",0);
		
		for (j = 0; 
		     j < service_list[A->service_idx].option_count; 
		     j++) {
		    if (service_list[A->service_idx].
			option_list[j].type == T) {
			config = & (service_list[A->service_idx].
				    option_list[j]);
			DPRINT(Debug,8,(&Debug,
					"  ... have service config %p (%s)\n",
					config,
					config->prefix ? config->prefix : "NONE"));
		    }
		}		
	    }


	    if (!(*pop_capa_libs)[i].
		capa_handler(folder,
			     (*pop_capa_libs)[i].capa,
			     (*pop_capa_libs)[i].capa_args,
			     sizeof (*folder),
			     commands,
			     sizeof (*commands),
			     config,
			     sizeof (*config),
			     phase)) {
		ret = 0;
		goto fail;
	    }

	    if (*phase != old_phase) {
		DPRINT(Debug,8,(&Debug," ... done (phase changed)\n"));
		break;
	    }
	}

    fail:
	free(*pop_capa_libs);
	*pop_capa_libs     = NULL;
	*pop_capa_libcount = 0;
    }
    return ret;
}






struct IMAP_capa_libs {
    char                 *  capa;
    struct ImpInfo       *  imp_idx;
    int                     priority;
    imap_capa_handler     * capa_handler;
};

void probe_imap_capa_lib(imap_capa_libs,imap_capa_libcount,capa)
     struct IMAP_capa_libs * *imap_capa_libs;
     int                  * imap_capa_libcount;
     CONST char *capa;
{
    int i;

    load_shared_libs1(&use_shared_mbox);  
    
    for (i = 0; i < shared_imap_capa_count; i++) {
	/* rfc2460:  (Syntax) Except as noted otherwise, 
	   all alphabetic characters are case-insensitive.
	*/
	
	/* Also check that library is currently loaded ... */
	if (!verify_shared_index(shared_imap_capas[i].imp_idx)) {
	    DPRINT(Debug,8,(&Debug,"shared_imap_capas[%d] unloaded?\n",i));
	    continue;
	}

	if ( 0 == istrcmp(shared_imap_capas[i].capa, capa))
	    break;	
    }
    if (i >= shared_imap_capa_count)
	return;          /* Not found */
    
    DPRINT(Debug,8,(&Debug,"shared_imap_capas[%d] unloaded is %s\n",i,capa));

    *imap_capa_libs = safe_realloc(*imap_capa_libs,
				   (*imap_capa_libcount +1) *
				   sizeof ((*imap_capa_libs)[0]));

    (*imap_capa_libs)[*imap_capa_libcount].capa      = safe_strdup(capa);
    
    (*imap_capa_libs)[*imap_capa_libcount].imp_idx   = 
	shared_imap_capas[i].imp_idx;
    (*imap_capa_libs)[*imap_capa_libcount].priority  = 
	shared_imap_capas[i].priority;
    (*imap_capa_libs)[*imap_capa_libcount].capa_handler =
	shared_imap_capas[i].capa_handler;

    DPRINT(Debug,7,(&Debug,
		    "** Using %s library for IMAP capacity %s\n",
		    shared_imap_capas[*imap_capa_libcount].imp_idx->tag,
		    (*imap_capa_libs)[*imap_capa_libcount].capa));

    (*imap_capa_libcount)++;		

}

static int imap_capa_compare P_((const void *A, const void *B));
static int imap_capa_compare(A,B)
     CONST void *A; 
     CONST void *B;
{
    CONST struct IMAP_capa_libs *A1 = A;
    CONST struct IMAP_capa_libs *B1 = B;

    if (A1->priority < B1->priority)
	return -1;
    if (A1->priority > B1->priority)
	return 1;
    return 0;
}
				
int handle_imap_capa_libs(con,imap_capa_libs,imap_capa_libcount,phase,commands)
     struct connection_cache *con;
     struct IMAP_capa_libs * *imap_capa_libs;
     int                    * imap_capa_libcount;
     enum CAPA_phase        * phase;
     struct imap_callbacks *commands;
{
    int ret = 1;

    struct remote_account * A;
    enum CAPA_phase old_phase = *phase;

    if (con->type != &IMAP_connection) 
	panic("CONNECTION PANIC",__FILE__,__LINE__,"handle_imap_capa_libs",
	      "Not a imap connection",0);
        
    A = &(con->C);


    if (*imap_capa_libs) {
	int i;
	qsort(*imap_capa_libs,*imap_capa_libcount,
	      sizeof ((*imap_capa_libs)[0]),
	      imap_capa_compare);
	
	for (i = 0; i < *imap_capa_libcount; i++) {
	    struct SE_option       *config = NULL;
	    struct SE_option_type  *T      = NULL;
	    int j;
	    
	    if (*phase != pri_to_phase((*imap_capa_libs)[i].priority))
		continue;
	    
	    DPRINT(Debug,8,(&Debug,
			    "[%d] handling %s with %s library\n",
			    i,(*imap_capa_libs)[i].capa,
			    shared_imap_capas[i].imp_idx->tag));

	    for (j = 0; j < shared_SE_option_type_count; j++) {
		if (shared_SE_option_types[j].imp_idx ==
		    shared_pop_capas[j].imp_idx) {
		    T = shared_SE_option_types[j].T;
		    DPRINT(Debug,8,(&Debug,
				    "  ... have service option type %p\n",
				    T));
		}
	    }
	    
	    if (A->service_idx >= 0 && T) {
		if (A->service_idx >= service_count) 
		    panic("CONNECTION PANIC",__FILE__,__LINE__,
			  "handle_pop_capa_libs",
			  "Bad service index",0);
		
		for (j = 0; 
		     j < service_list[A->service_idx].option_count; 
		     j++) {
		    if (service_list[A->service_idx].
			option_list[j].type == T) {
			config = & (service_list[A->service_idx].
				    option_list[j]);
			DPRINT(Debug,8,(&Debug,
					"  ... have service config %p (%s)\n",
					config,
					config->prefix ? config->prefix : 
					"NONE"));
		    }
		}		
	    }

	    if (!(*imap_capa_libs)[i].
		capa_handler(con,
			     (*imap_capa_libs)[i].capa,
			     sizeof (*con),
			     commands,
			     sizeof (*commands),
			     config,
			     sizeof (*config),
			     phase)) {
		ret = 0;
		goto fail;
	    }
	    
	    if (old_phase != *phase) {
		DPRINT(Debug,8,(&Debug," ... done (phase changed)\n"));
		break;
	    }
	}

    fail:
	;
    }

    free_only_imap_capa_libs(imap_capa_libs,imap_capa_libcount);

    return ret;

}


void free_only_imap_capa_libs(imap_capa_libs,imap_capa_libcount)
     struct IMAP_capa_libs * *imap_capa_libs;
     int                  * imap_capa_libcount;
{
    if (*imap_capa_libs) {
	int i;

	for (i = 0; i < *imap_capa_libcount; i++) {
	    if ((*imap_capa_libs)[i].capa) {
		free((*imap_capa_libs)[i].capa);
		(*imap_capa_libs)[i].capa = NULL;
	    }
	}
	free(*imap_capa_libs);
	*imap_capa_libs     = NULL;
	*imap_capa_libcount = 0;
    }
}



#endif






/*
 * Local Variables:
 *  mode:c
 *  c-basic-offset:4
 * End:
 */


syntax highlighted by Code2HTML, v. 0.9.1