static char rcsid[] = "@(#)$Id: duplicate.c,v 1.10 2006/04/20 05:42:15 hurtta Exp $";

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

#include "def_messages.h"
#include "s_me.h"

DEBUG_VAR(Debug,__FILE__,"messages");

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

#define DUPL_MSGID_magic        0xF506

static struct msg_id_data  {
    unsigned short            magic;      /* DUPL_MSGID_magic */
    char                  * message_id;

    struct msg_id_data    * smaller;    /* LEFT  */
    struct msg_id_data    * bigger;     /* RIGHT */


    struct  instance_chain  * chain;
} * give_message_id_data P_((struct msg_id_data **root, 
			   const char * message_id));

/* This is essentially same than give_uidl() on lib/mbox/pop.c  */

static struct msg_id_data *give_message_id_data(root,message_id)
     struct msg_id_data **root; 
     CONST char * message_id;
{
    struct msg_id_data *ptr = *root;
    int r;

    if (!ptr) {
	ptr = safe_malloc(sizeof(*ptr));

	bzero((void *)ptr, sizeof(*ptr));

	ptr->magic  = DUPL_MSGID_magic;

	ptr->message_id = safe_strdup(message_id);
	ptr->smaller    = NULL;
	ptr->bigger     = NULL;
	
	ptr->chain      = NULL;

	*root = ptr;
	return ptr;
    }

    if (ptr->magic  != DUPL_MSGID_magic)
	panic("MBX VIEW PANIC",__FILE__,__LINE__,"give_message_id_data",
	      "Bad magic number",0);
    
    r = strcmp(message_id,ptr->message_id);

    if (0 == r) {

	return ptr;
    } else if (0 < r) {

	return give_message_id_data(& (ptr->bigger), message_id);
    } else {

	return give_message_id_data(& (ptr->smaller), message_id);
    }
}

static void free_message_ids P_((struct msg_id_data **root));
static void free_message_ids(root)
     struct msg_id_data **root;
{
    struct msg_id_data *ptr = *root;

    if (ptr->magic  != DUPL_MSGID_magic)
	panic("MBX VIEW PANIC",__FILE__,__LINE__,"free_message_ids",
	      "Bad magic number",0);

    if (ptr->smaller)
	free_message_ids(& (ptr->smaller));

    if (ptr->bigger)
	free_message_ids(& (ptr->bigger));

    if (ptr->message_id) {
	free(ptr->message_id);
	ptr->message_id = NULL;
    }

    ptr->chain = NULL;

    ptr->magic = 0;  /* Invalidate */

    free(ptr);
    *root = NULL;
}

#define MV_INSTANCE_magic       0xF507

struct  instance_chain {
    unsigned short            magic;      /* MV_INSTANCE_magic */
    struct  instance_chain * next;

    struct header_rec      *mss;

    struct folder_view  *locs;
    int                  loc_len;

};



#define MV_DUPLICATE_magic      0xF505

struct mv_duplicate {
    unsigned short            magic;      /* MV_DUPLICATE_magic */
        
    struct MailboxView       * parent_mailbox;

    struct msg_id_data       * learn_ids;

    struct  instance_chain   ** collected;
    int                         collected_len;
};


static struct  instance_chain * add_to_chain P_((struct mv_duplicate *mv,
						 struct msg_id_data *id));

static struct  instance_chain * add_to_chain(mv,id)
     struct mv_duplicate *mv;
     struct msg_id_data *id;
{
    struct  instance_chain * ret;

    if (MV_DUPLICATE_magic != mv->magic)
	panic("MBX VIEW PANIC",__FILE__,__LINE__,"add_to_chain",
	      "Bad magic number",0);

    if (DUPL_MSGID_magic  != id->magic)
	panic("MBX VIEW PANIC",__FILE__,__LINE__,"add_to_chain",
	      "Bad magic number",0);
	
    ret = safe_malloc(sizeof (*ret));

    bzero((void *)ret, sizeof(*ret));
    ret->magic = MV_INSTANCE_magic;

    ret->mss     = NULL;
    ret->locs    = NULL;

    ret->loc_len    = 0;
    
    ret->next = id->chain;
    id->chain = ret;

    mv->collected = safe_realloc(mv->collected,
				 (mv->collected_len +1) *
				 sizeof (mv->collected+1));
    mv->collected[mv->collected_len] = ret;
    mv->collected_len++;

    return ret;
}

static void free_chain_item P_((struct  instance_chain ** item));
static void free_chain_item(item)
     struct  instance_chain ** item;
{
    if ((*item)->magic != MV_INSTANCE_magic)
	panic("MBX VIEW PANIC",__FILE__,__LINE__,"add_to_chain",
	      "Bad magic number",0);
    
    if ((*item)->mss)
	header_free(& ((*item)->mss) );


    if ((*item)->locs) {
	free((*item)->locs);
	(*item)->locs = NULL;
    }
    (*item)->loc_len = 0;


    (*item)->magic = 0; /* Invalidate */
    free((*item));
    *item = NULL;
    
}

S_(mt_init_mailbox mt_init_duplicate)
static void mt_init_duplicate P_((struct MailboxView *mbx));

static void mt_init_duplicate(mbx)
     struct MailboxView *mbx;
{

    mbx->u.duplicate = safe_malloc(sizeof (* (mbx->u.duplicate)));
		
    /* bzero is defined hdrs/defs.h */
    bzero((void *)mbx->u.duplicate, sizeof (* (mbx->u.duplicate)));

    mbx->u.duplicate->parent_mailbox     = NULL;

    mbx->u.duplicate->magic     = MV_DUPLICATE_magic;

    mbx->u.duplicate->learn_ids = NULL;

    mbx->u.duplicate->collected     = NULL;
    mbx->u.duplicate->collected_len = 0;
}

S_(mt_free_mailbox mt_free_duplicate)
static void mt_free_duplicate P_((struct MailboxView *mbx));
static void mt_free_duplicate(mbx)
     struct MailboxView *mbx;
{
    if (mbx->u.duplicate->magic     != MV_DUPLICATE_magic)
	panic("MBX VIEW PANIC",__FILE__,__LINE__,"mt_free_duplicate",
	      "Bad magic number",0);

    if (mbx->u.duplicate->learn_ids)
	free_message_ids( &(mbx->u.duplicate->learn_ids));

    if (mbx->u.duplicate->collected) {
	int i;

	for (i = 0; i < mbx->u.duplicate->collected_len; i++) {

	    if (mbx->u.duplicate->collected[i])
		free_chain_item(& (mbx->u.duplicate->collected[i]));


	}

	free(mbx->u.duplicate->collected);
	mbx->u.duplicate->collected = NULL;
    }
    mbx->u.duplicate->collected_len = 0;

    mbx->u.duplicate->magic  = 0;  /* Invalidate */

    free(mbx->u.duplicate);
    mbx->u.duplicate = NULL;
}




S_(mt_add_mailbox_storage mt_add_duplicate_storage)
static void mt_add_duplicate_storage P_((struct MailboxView *bx,
				       struct current_storage *storage));

static void mt_add_duplicate_storage(mbx,storage)
     struct MailboxView *mbx;
     struct current_storage *storage;
{    
    if (mbx->u.duplicate->magic     != MV_DUPLICATE_magic)
	panic("MBX VIEW PANIC",__FILE__,__LINE__,"mt_add_duplicate_storage",
	      "Bad magic number",0);

    add_storage(mbx->u.duplicate->parent_mailbox,storage);
}



static void mt_make_duplicate_view P_((struct MailboxView *mailbox));

static struct addr_item * dup_addr_items P_((struct addr_item *src));
static struct addr_item * dup_addr_items(src)
     struct addr_item *src;
{
    struct addr_item *ret = NULL;

    append_addr_items(&ret,src);

    return ret;
}

static int addr_is_same P_((struct addr_item *a1, struct addr_item *a2));
static int addr_is_same(a1,a2)
     struct addr_item *a1; 
     struct addr_item *a2;
{

    while(1) {
	if (! a1->addr && ! a1->fullname &&
	    ! a2->addr && ! a2->fullname)
	    return 1;

	if (! a1->addr || ! a1->fullname ||
	    ! a2->addr || ! a2->fullname)
	    return 0;
	
	if (0 != strcmp(a1->addr,a2->addr))
	    return 0;
	if (0 != string_cmp(a1->fullname,
			    a2->fullname, 99)) 
	    return 0;

	if (a1->comment && a2->comment) {
	    if (0 != string_cmp(a1->comment,
				a2->comment, 99)) 
		return 0;
	} else if (a1->comment || a2->comment) 
	    return 0;

	a1++;
	a2++;
    }

    /* NOT REACHED */
}

static struct header_rec *copy_header P_((struct header_rec *X, int idx));
static struct header_rec *copy_header(X,idx)
     struct header_rec *X;
     int idx;
{
    struct header_rec * ret = safe_malloc(sizeof (*ret));

    /* Only save headers which are used on compare and
       status headers   ... others are not used 
    */

    header_zero(ret);

    ret->index_number_X  = idx;   /* NOT USED */

    /* May be used on sorting */
    ret->lines           = X->lines;

    ret->status          = X->status;
    ret->status1         = X->status1;

    strfcpy(ret->env_from, X->env_from, sizeof (ret->env_from));

    if (X->from)
	ret->from    = dup_addr_items(X->from);
    if (X->to)
	ret->to      = dup_addr_items(X->to);
    if (X->cc)
	ret->cc      = dup_addr_items(X->cc);
    if (X->subject)
	ret->subject = dup_string(X->subject);


    strfcpy(ret->messageid, X->messageid, sizeof (ret->messageid));
    strfcpy(ret->time_zone, X->time_zone, sizeof (ret->time_zone));
    ret->time_sent    = X->time_sent;
    ret->tz_offset    = X->tz_offset;

    /* -> list_info  is not supported */

    return ret;
}

static int cmp_headers_ok P_((struct header_rec * h1, struct header_rec *h2));
static int cmp_headers_ok(h1,h2)
     struct header_rec * h1; 
     struct header_rec * h2;
{

    /* Do NOT compare lines -- 
     *
     *  it includes headers and mail with different number of 
     * Received: -headers are OK
     */

    /* Do NOT compare content_length ---
     *
     * Unix mailboxes may use LF as end of line, but
     * on IMAP and POP we use CR LF as end of line
     *  So these give different length ....
     *
     * Also some routes may re-encode 8bit mail with
     * quoted-printable or base64
     *
     */

    if (0 != strcmp(h1->env_from,
		    h2->env_from))
	return 0;

    if (h1->from && h2->from) {
	if ( ! addr_is_same(h1->from,h2->from) )
	    return 0;
    } else if (h1->from || h2->from) 
	return 0;

    if (h1->to && h2->to) {
	if ( ! addr_is_same(h1->to,h2->to) )
	    return 0;
    } else if (h1->to || h2->to) 
	return 0;

    if (h1->cc && h2->cc) {
	if ( ! addr_is_same(h1->cc,h2->cc) )
	    return 0;
    } else if (h1->cc || h2->cc) 
	return 0;

    if (0 != strcmp(h1->messageid,
		    h2->messageid))
	return 0;

    if (0 != string_cmp(h1->subject,
			h2->subject, 99)) 
	return 0;

    /* FIXME: Mailing list headers are not currently supported */
    if (h1->list_info ||
	h2->list_info)
	return 0;

    if (0 != strcmp(h1->time_zone,
		    h2->time_zone))
	return 0;

    if (h1->time_sent != h2->time_sent)
	return 0;
    if (h1->tz_offset != h2->tz_offset)
	return 0;

    return 1;

}

static void update_header P_((struct header_rec * trg, struct header_rec *src));
static void update_header(trg,src)
     struct header_rec * trg; 
     struct header_rec * src;
{
    trg->status   |= src->status;    /* Not necessary correct */
    trg->status1  |= src->status1;   /* Not necessary correct */

    /* May be used on sorting */
    if (src->lines >= 0 && src->lines < trg->lines)
	trg->lines           = src->lines;


    if (isoff(src->status,UNREAD))
	clearit(trg->status,UNREAD);
}


/* Return 1 if redraw required */
S_(mt_update_view_mailbox mt_update_view_duplicate)
/* Return 1 if redraw required */
static int mt_update_view_duplicate P_((struct MailboxView *mbx));
static int mt_update_view_duplicate(mbx)
     struct MailboxView *mbx;
{
    struct MailboxView * Z;
    int hidden = 0;
    int r,i;
    int count = 0;

    if (mbx->u.duplicate->magic     != MV_DUPLICATE_magic)
	panic("MBX VIEW PANIC",__FILE__,__LINE__,"mt_update_view_duplicate",
	      "Bad magic number",0);


    Z = mbx->u.duplicate->parent_mailbox;

    r = update_view(Z);

    if (!r) {
	/* OPTIMIZE: Do not check is no changes on parent !! */

	int c = get_message_count(Z);

	if (c && Z->view_len) {

	    DPRINT(Debug,7,(&Debug,
			    "mt_update_view_duplicate=0: parent len = %d, view len = %d\n", 
			    c,Z->view_len));
	    return 0;

	}
    }

    for (i = 0; i < Z->view_len; i++) {
	int z;
	struct header_rec *X = give_header(Z,i);

	if (X) {
	    /* FIXME: Mailing list headers are not currently supported */

	    if (X->list_info)
		count++;

	    else if (X->messageid[0]) {
		struct msg_id_data * I = 
		    give_message_id_data( & (mbx->u.duplicate->learn_ids),
					  X->messageid );
		
		struct instance_chain * C;

		for (C = I->chain; C; C = C->next)
		    if ( cmp_headers_ok(C->mss,X))
			break;

		if (!C) {
		    r = 1;

		    C = add_to_chain(mbx->u.duplicate,I);
		    
		    C->mss = copy_header(X,mbx->u.duplicate->collected_len);
		}

		update_header(C->mss,X);

	    } else
		count++;
	} else
	    count++;		
    }


    count += mbx->u.duplicate->collected_len;


    if (r  || count != mbx->view_len ) {
	  
	mt_make_duplicate_view(mbx);

	DPRINT(Debug,7,(&Debug,
			"mt_update_view_duplicate=1 (view len=%d)\n",
			mbx->view_len));
	return 1;
    }
    
    DPRINT(Debug,7,(&Debug,
		    "mt_update_view_duplicate=0\n"));
    return 0;
}


S_(mt_get_main_mailbox_folder mt_get_main_duplicate_folder)
static struct folder_info * mt_get_main_duplicate_folder P_((struct MailboxView
							     *mbx));
static struct folder_info * mt_get_main_duplicate_folder(mbx)
     struct MailboxView *mbx;
{
    if (mbx->u.duplicate->magic     != MV_DUPLICATE_magic)
	panic("MBX VIEW PANIC",__FILE__,__LINE__,
	      "mt_get_main_duplicate_folder",
	      "Bad magic number",0);

    return get_main_folder(mbx->u.duplicate->parent_mailbox);
}



/* Can be called from signal handler */
S_(mt_get_mailbox_storage mt_get_duplicate_storage)
/* Can be called from signal handler */
static struct current_storage * mt_get_duplicate_storage P_((struct MailboxView
							     *mbx,
							     int i));
/* Can be called from signal handler */
static struct current_storage * mt_get_duplicate_storage(mbx,i) 
     struct MailboxView *mbx;
     int i;
{
    if (mbx->u.duplicate->magic     != MV_DUPLICATE_magic)
	panic("MBX VIEW PANIC",__FILE__,__LINE__,
	      "mt_get_duplicate_storage",
	      "Bad magic number",0);
 
    if (i == 0) 
	return NULL;           /* Not available */

    /* Shift by one */
    return get_storage(mbx->u.duplicate->parent_mailbox,i-1);
}

/* Can be called from signal handler */
S_(mt_get_mailbox_storage_count mt_get_duplicate_storage_count)
/* Can be called from signal handler */
static int mt_get_duplicate_storage_count P_((struct MailboxView *mbx));

/* Can be called from signal handler */
static int mt_get_duplicate_storage_count(mbx) 
     struct MailboxView *mbx;
{
    if (mbx->u.duplicate->magic     != MV_DUPLICATE_magic)
	panic("MBX VIEW PANIC",__FILE__,__LINE__,
	      "mt_get_duplicate_storage_count",
	      "Bad magic number",0);


    /* Storage number 0 is internal so add +1 */
    
    return 1 + get_storage_count(mbx->u.duplicate->parent_mailbox);
}




S_(mt_give_header_mailbox mt_give_header_duplicate)
static struct header_rec * mt_give_header_duplicate(mbx,index,v)
     struct MailboxView *mbx;
     int index;
     struct folder_view *v;
{
    if (mbx->u.duplicate->magic     != MV_DUPLICATE_magic)
	panic("MBX VIEW PANIC",__FILE__,__LINE__,
	      "mt_give_header_duplicate",
	      "Bad magic number",0);


    if (0 == v->mailbox_number) {   /* Number 0 is internal */

	int j;
	struct  instance_chain *E;
	struct MailboxView *Z = mbx->u.duplicate->parent_mailbox;

	if (Z->mailbox_type->magic != MAILBOXTYPE_magic)
	    panic("MBX VIEW PANIC",__FILE__,__LINE__,
		  "mt_give_header_duplicate",
		  "Bad type magic number",0);


	if (v->index < 0 || v->index >= mbx->u.duplicate->collected_len)
	    return NULL;
	
	E =  mbx->u.duplicate->collected[v->index];
	if (MV_INSTANCE_magic != E->magic)
	    panic("MBX VIEW PANIC",__FILE__,__LINE__,
		  "mt_give_header_duplicate",
		  "Bad magic number",0);
	

	/* FIXME -- is it better to return header struct
	   just from instance chain  ?
	*/

	for (j = 0; j < E->loc_len; j++) {
	    struct header_rec * H = Z->mailbox_type->mt_give_header_it(Z,index, & (E->locs[j]));

	    if (H) {

		/* Hack     -- hide ! from status   */
		if (E->mss) 
		    E->mss->body_parsed = H->body_parsed;

		return H;
	    }
	}

	return E->mss;

    } else {
	struct folder_view V;
	struct MailboxView *Z = mbx->u.duplicate->parent_mailbox;

	/* Shift by one */
	V.mailbox_number = v->mailbox_number-1;
	V.index          = v->index;

	if (Z->mailbox_type->magic != MAILBOXTYPE_magic)
	    panic("MBX VIEW PANIC",__FILE__,__LINE__,
		  "mt_give_header_duplicate",
		  "Bad type magic number",0);

	return Z->mailbox_type->mt_give_header_it(Z,index, &V);
    }
}


struct duplicate_sort {
    struct MailboxView         *parent_mailbox;
    struct  instance_chain     * duplicate_record;
};

S_(sdt_give_header_s sdt_give_header_dup)
static struct header_rec * sdt_give_header_dup P_((struct sort_data *s,
						   struct folder_view *v));
static struct header_rec * sdt_give_header_dup(s,v)
     struct sort_data *s;
     struct folder_view *v;
{
    if (0 == v->mailbox_number) {
	/* FIXME:   not necessary correct .... */
	return s->u.dup->duplicate_record->mss;
    } else {
	struct folder_view V;
	struct MailboxView *Z = s->u.dup->parent_mailbox;

	/* Shift by one */
	V.mailbox_number = v->mailbox_number-1;
	V.index          = v->index;
	
	if (Z->mailbox_type->magic != MAILBOXTYPE_magic)
	    panic("MBX VIEW PANIC",__FILE__,__LINE__,"sdt_give_header_dup",
		  "Bad type magic number",0);

	return Z->mailbox_type->mt_give_header_it(Z,-1, &V);
    }
}

static struct sort_data_type dup_sort = {
    SORTDATATYPE_magic,
    sdt_give_header_dup
};


S_(mt_sort_mailbox_view mt_sort_duplicate_view)
static void mt_sort_duplicate_view P_((struct MailboxView *mbx,
				       hdr_compare_func   *func));
static void mt_sort_duplicate_view(mbx,func)
     struct MailboxView *mbx;
     hdr_compare_func   *func;
{
    int i;
    struct sort_data * array;

    /* Little dirty ... */
    typedef int (*compar) P_((const void *, const void *));
    compar X = (compar) func;

    if (mbx->u.duplicate->magic     != MV_DUPLICATE_magic)
	panic("MBX VIEW PANIC",__FILE__,__LINE__,
	      "mt_sort_duplicate_view",
	      "Bad magic number",0);

    
    array = safe_malloc(mbx->view_len * sizeof (array[0]));

    for (i = 0; i < mbx->view_len; i++) {
	int mbxnum = mbx->view[i].mailbox_number;

	array[i].w              = mbx->view[i];
	array[i].t              = mbx->thread_view;   /* For thread sorting */
	array[i].sort_data_type = &dup_sort;
	array[i].u.dup         = safe_malloc(sizeof (* array[i].u.dup));
	array[i].u.dup->parent_mailbox   = mbx->u.duplicate->parent_mailbox;
	array[i].u.dup->duplicate_record = NULL;

	if (0 == mbxnum) {
	    int v = mbx->view[i].index;

	    array[i].u.dup->duplicate_record = 
		mbx->u.duplicate->collected[v];
	}

    }

    qsort(array,mbx->view_len,sizeof (array[0]), X);
   
    for (i = 0; i < mbx->view_len; i++) {
	mbx->view[i] = array[i].w;

	free(array[i].u.dup);
	array[i].u.dup = NULL;
    }

    free(array);
}

S_(mt_give_message_data_mailbox mt_give_message_data_duplicate)
static int mt_give_message_data_duplicate P_((struct MailboxView *mbx,
					      int index,
					      struct header_rec **ret_header,
					      FILE              **ret_F,
					      struct counter_data *counter,
					      parse_mime_callback *parse_mime,
					      struct folder_view *v));
static int mt_give_message_data_duplicate(mbx,index,ret_header,ret_F,
					  counter,parse_mime,v)
     struct MailboxView *mbx;
     int index;
     struct header_rec **ret_header;
     FILE              **ret_F;
     struct counter_data *counter;
     parse_mime_callback *parse_mime;
     struct folder_view *v;
{

    if (mbx->u.duplicate->magic     != MV_DUPLICATE_magic)
	panic("MBX VIEW PANIC",__FILE__,__LINE__,
	      "mt_give_message_data_duplicate",
	      "Bad magic number",0);

        
    if (0 == v->mailbox_number) {   /* Number 0 is internal */
	int j;
	struct  instance_chain *E;
	struct MailboxView *Z = mbx->u.duplicate->parent_mailbox;

	if (Z->mailbox_type->magic != MAILBOXTYPE_magic)
	    panic("MBX VIEW PANIC",__FILE__,__LINE__,
		  "mt_give_header_duplicate",
		  "Bad type magic number",0);


	if (v->index < 0 || v->index >= mbx->u.duplicate->collected_len)
	    return 0;
	
	E =  mbx->u.duplicate->collected[v->index];
	if (MV_INSTANCE_magic != E->magic)
	    panic("MBX VIEW PANIC",__FILE__,__LINE__,
		  "mt_give_header_duplicate",
		  "Bad magic number",0);

	/* We have better to return same header that is data */
	for (j = 0; j < E->loc_len; j++) {
	    if (Z->mailbox_type->mt_give_message_data_it(Z,index,
							 ret_header,ret_F,
							 counter,parse_mime,
							 & (E->locs[j]))) {

		/* Hack     -- hide ! from status   */
		if (E->mss && ret_header && *ret_header) 
		    E->mss->body_parsed = (*ret_header)->body_parsed;

		return 1;
	    }

	}

	return 0;

    } else {
	struct folder_view V;
	struct MailboxView *Z = mbx->u.duplicate->parent_mailbox;

	/* Shift by one */
	V.mailbox_number = v->mailbox_number-1;
	V.index          = v->index;

	if (Z->mailbox_type->magic != MAILBOXTYPE_magic)
	    panic("MBX VIEW PANIC",__FILE__,__LINE__,
		  "mt_give_message_data_duplicate",
		  "Bad type magic number",0);

	return Z->mailbox_type->mt_give_message_data_it(Z,index,
							ret_header,ret_F,
							counter,parse_mime,
							& V);
    }


}


S_(mt_write_mailbox_info mt_write_duplicate_info)
static void mt_write_duplicate_info P_((FILE *fp, struct MailboxView *mbx,
					int s, int cur_idx));
static void mt_write_duplicate_info(fp,mbx,s,cur_idx)
     FILE *fp; 
     struct MailboxView *mbx;
     int s;
     int cur_idx;
{

    if (mbx->u.duplicate->magic     != MV_DUPLICATE_magic)
	panic("MBX VIEW PANIC",__FILE__,__LINE__,
	      "mt_write_duplicate_info",
	      "Bad magic number",0);

    if (0 == s) {   /* 0 is internal */

	/* FIXME: -- is not supported */

    } else {
	struct MailboxView *Z = mbx->u.duplicate->parent_mailbox;


	if (Z->mailbox_type->magic != MAILBOXTYPE_magic)
	    panic("MBX VIEW PANIC",__FILE__,__LINE__,
		  "mt_write_duplicate_info",
		  "Bad type magic number",0);


	/* Shift by one */
	Z->mailbox_type->mt_write_it_info(fp,Z,s-1,cur_idx);
    }
}


S_(mt_mailbox_title mt_duplicate_title)
static struct string * mt_duplicate_title P_((struct MailboxView *mbx));
static struct string * mt_duplicate_title(mbx)
     struct MailboxView *mbx;
{
    if (mbx->u.duplicate->magic     != MV_DUPLICATE_magic)
	panic("MBX VIEW PANIC",__FILE__,__LINE__,
	      "mt_duplicate_title",
	      "Bad magic number",0);

    return mailbox_title(mbx->u.duplicate->parent_mailbox);
}



S_(mt_make_mailbox_view mt_make_duplicate_view)
static void mt_make_duplicate_view (mbx)
     struct MailboxView *mbx;
{
    struct MailboxView * Z;    
    int count = 0;
    int i,x = 0;

    if (mbx->u.duplicate->magic     != MV_DUPLICATE_magic)
	panic("MBX VIEW PANIC",__FILE__,__LINE__,
	      "mt_make_duplicate_view",
	      "Bad magic number",0);


    Z = mbx->u.duplicate->parent_mailbox;

    /* Remove old locations -- they will be recalculated on 
       rediscovered 
    */

    for (i = 0; i < mbx->u.duplicate->collected_len; i++) {
	
	if (mbx->u.duplicate->collected[i]) {
	    if (mbx->u.duplicate->collected[i]->locs) {
		free(mbx->u.duplicate->collected[i]->locs);
		mbx->u.duplicate->collected[i]->locs = 0;
	    }
	    mbx->u.duplicate->collected[i]->loc_len = 0;

	}
    }


    for (i = 0; i < Z->view_len; i++) {
	int z;
	struct header_rec *X = give_header(Z,i);

	if (X) {
	    /* FIXME: Mailing list headers are not currently supported */

	    if (X->list_info)
		count++;

	    else if (X->messageid[0]) {
		struct msg_id_data * I = 
		    give_message_id_data( & (mbx->u.duplicate->learn_ids),
					  X->messageid );
		
		struct instance_chain * C;

		for (C = I->chain; C; C = C->next)
		    if ( cmp_headers_ok(C->mss,X))
			break;

		if (!C) {
		    C = add_to_chain(mbx->u.duplicate,I);
		    
		    C->mss = copy_header(X,mbx->u.duplicate->collected_len);
		}

		update_header(C->mss,X);

		C->locs = safe_realloc(C->locs,
				       (C->loc_len +1 ) * 
				       sizeof (C->locs[0]));

		C->locs[C->loc_len] = Z->view[i];
		C->loc_len++;

	    } else
		count++;
	} else
	    count++;		
    }

    count += mbx->u.duplicate->collected_len;


    DPRINT(Debug,9,(&Debug,
		    "mt_make_duplicate_view: count = %d, collected len = %d, parent view len = %d\n",
		    count, mbx->u.duplicate->collected_len, Z->view_len));
    
    if (count < 1) {
	if (mbx->view)
	    free(mbx->view);
	mbx->view     = NULL;
	mbx->view_len = 0;
	
	return;
    }
    
    
    mbx->view = 
	safe_realloc(mbx->view,
		     count * (sizeof ( mbx->view[0])));
    
    for (i = 0, x = 0; i < Z->view_len; i++) {

	struct header_rec *X = give_header(Z,i);

	if (!X ||
	    X->list_info ||
	    ! X->messageid[0]) {

	    struct folder_view Y;
	    
	    give_index_number(Z,i,&Y);
	    
	    Y.mailbox_number ++;    /* Shift by one! */
	    
	    if (x >= count)
		panic("MBX VIEW PANIC",__FILE__,__LINE__,
		      "mt_make_duplicate_view",
		      "overflow",0);
	    
	    mbx->view[x] = Y;
	    x++;
	}
    }
    
    for (i = 0; i < mbx->u.duplicate->collected_len; i++) {

	struct  instance_chain *E = mbx->u.duplicate->collected[i];


	if (x >= count)
	    panic("MBX VIEW PANIC",__FILE__,__LINE__,
		  "mt_make_duplicate_view",
		  "overflow",0);
	
	if (MV_INSTANCE_magic != E->magic)
	    panic("MBX VIEW PANIC",__FILE__,__LINE__,
		  "mt_make_duplicate_view",
		  "Bad magic number",0);

	/* If exactly one mail, do not use duplicate record */ 
	if (1 == E->loc_len) {
	    struct folder_view Y = E->locs[0];

	    Y.mailbox_number ++;    /* Shift by one! */

	    mbx->view[x] = Y;
	} else {
	    zero_folder_view(& (mbx->view[x]));

	    mbx->view[x].mailbox_number = 0;
	    mbx->view[x].index          = i;
	}
	x++;	
    }
    
    mbx->view_len = x;
}

S_(mt_add_mailbox_digest mt_add_duplicate_digest)
static void mt_add_duplicate_digest P_((struct MailboxView *mbx,
				     mime_t *list,
				     time_t received_time,
				     char *env_from,
				     FILE *F,
				     charset_t defcharset));
static void mt_add_duplicate_digest(mbx, list, received_time, env_from, F,
				  defcharset)
     struct MailboxView *mbx;
     mime_t *list;
     time_t received_time;
     char *env_from;
     FILE *F;
     charset_t defcharset;
{

    if (mbx->u.duplicate->magic     != MV_DUPLICATE_magic)
	panic("MBX VIEW PANIC",__FILE__,__LINE__,
	      "mt_add_duplicate_digest",
	      "Bad magic number",0);


    add_digest(mbx,list,received_time,env_from,F,defcharset);   
}

static struct mailbox_type mt_duplicate = {
    MAILBOXTYPE_magic,
    mt_init_duplicate,
    mt_free_duplicate,
    mt_add_duplicate_storage,
    mt_update_view_duplicate,
    mt_get_main_duplicate_folder,
    mt_get_duplicate_storage,
    mt_get_duplicate_storage_count,
    mt_give_header_duplicate,
    mt_sort_duplicate_view,
    mt_give_message_data_duplicate,
    mt_write_duplicate_info,
    mt_duplicate_title,
    mt_make_duplicate_view,
    mt_add_duplicate_digest
};


S_(mt_show_status_message mt_show_status_duplicate)
static char * mt_show_status_duplicate P_((struct MailboxView *mailbox,
					   int index, 
					   struct folder_view *v));
static char * mt_show_status_duplicate(mailbox,index,v)
     struct MailboxView *mailbox;
     int index; 
     struct folder_view *v;
{
    if (mailbox->u.duplicate->magic     != MV_DUPLICATE_magic)
	panic("MBX VIEW PANIC",__FILE__,__LINE__,
	      "mt_show_status_duplicate",
	      "Bad magic number",0);

 
    if (0 == v->mailbox_number) {   /* Number 0 is internal */
	struct  instance_chain *E;
	struct header_rec *hdr;
		
	if (v->index < 0 || v->index >= mailbox->u.duplicate->collected_len)
	    return NULL;

	E =  mailbox->u.duplicate->collected[v->index];
	if (MV_INSTANCE_magic != E->magic)
	    panic("MBX VIEW PANIC",__FILE__,__LINE__,
		  "mt_show_status_duplicate",
		  "Bad magic number",0);

	hdr = E->mss;
	if (!hdr)
	    return NULL;	

	return show_message_status(hdr);
    } else {
	struct folder_view V;
	struct MailboxView *Z = mailbox->u.duplicate->parent_mailbox;
	
	/* Shift by one */
	V.mailbox_number = v->mailbox_number-1;
	V.index          = v->index;

	if (Z->status_type->magic != STATUSTYPE_magic)
	    panic("MBX VIEW PANIC",__FILE__,__LINE__,
		  "mt_show_status_duplicate",
		  "Bad type magic number",0);

	return Z->status_type->mt_show_status_it(Z,index,&V);
    }
}

S_(mt_ison_status_message mt_ison_status_duplicate)
static int   mt_ison_status_duplicate  P_((struct MailboxView *mailbox,
					int index, 
					struct folder_view *v,
					enum status_x t, int mask));
static int   mt_ison_status_duplicate(mailbox,index,v,t,mask)
     struct MailboxView *mailbox;     int index;
     struct folder_view *v;
     enum status_x t; 
     int mask;
{
    struct header_rec *hdr;
    int r = 0;

    if (mailbox->u.duplicate->magic     != MV_DUPLICATE_magic)
	panic("MBX VIEW PANIC",__FILE__,__LINE__,
	      "mt_ison_status_duplicate",
	      "Bad magic number",0);

 
    if (0 == v->mailbox_number) {   /* Number 0 is internal */
	struct  instance_chain *E;
	struct header_rec *hdr;
		
	if (v->index < 0 || v->index >= mailbox->u.duplicate->collected_len)
	    return 0;

	E =  mailbox->u.duplicate->collected[v->index];
	if (MV_INSTANCE_magic != E->magic)
	    panic("MBX VIEW PANIC",__FILE__,__LINE__,
		  "mt_show_status_duplicate",
		  "Bad magic number",0);

	hdr = E->mss;
	
	if (hdr) {
	    switch (t) {
	    case status_basic:
		r = ison(hdr->status,mask);
		break;
	    case status_1:
		r = ison(hdr->status1,mask);
		break;
	    default:
		panic("MBX VIEW PANIC",__FILE__,__LINE__,
		      "mt_ison_status_duplicate",
		      "bad status type",0);		
	    }
	}

    } else {
	struct folder_view V;
	struct MailboxView *Z = mailbox->u.duplicate->parent_mailbox;
	
	/* Shift by one */
	V.mailbox_number = v->mailbox_number-1;
	V.index          = v->index;

	if (Z->status_type->magic != STATUSTYPE_magic)
	    panic("MBX VIEW PANIC",__FILE__,__LINE__,
		  "mt_ison_status_duplicate",
		  "Bad type magic number",0);

	r = Z->status_type->mt_ison_status_it(Z,index,&V,t,mask);
    }

    return r;
}

S_(mt_setf_status_message mt_setf_status_duplicate)
static int   mt_setf_status_duplicate  P_((struct MailboxView *mailbox,
					int index, 
					struct folder_view *v,
					enum status_x t, int mask));
static int   mt_setf_status_duplicate(mailbox,index,v,t,mask)
     struct MailboxView *mailbox;
     int index;
     struct folder_view *v;
     enum status_x t; 
     int mask;
{
    struct header_rec *hdr;
    int r = 0;

    if (mailbox->u.duplicate->magic     != MV_DUPLICATE_magic)
	panic("MBX VIEW PANIC",__FILE__,__LINE__,
	      "mt_setf_status_duplicate",
	      "Bad magic number",0);

 
    if (0 == v->mailbox_number) {   /* Number 0 is internal */
	struct  instance_chain *E;
	struct header_rec *hdr;
	struct MailboxView *Z = mailbox->u.duplicate->parent_mailbox;
	int j;

	if (v->index < 0 || v->index >= mailbox->u.duplicate->collected_len)
	    return 0;

	E =  mailbox->u.duplicate->collected[v->index];
	if (MV_INSTANCE_magic != E->magic)
	    panic("MBX VIEW PANIC",__FILE__,__LINE__,
		  "mt_setf_status_duplicate",
		  "Bad magic number",0);

	hdr = E->mss;
	
	if (hdr) {

	    switch (t) {
	    
	    case status_basic:
		setit(hdr->status,mask);
		r = hdr->status;
		break;
		
	    case status_1:
		setit(hdr->status1,mask);
		r = hdr->status1;
		break;
		
	    default:
		panic("MBX VIEW PANIC",__FILE__,__LINE__,
		      "mt_setf_status_duplicate",
		      "bad status type",0);	    
	    }

	    /* TODO: What status codes should cause flagging??? */
	    hdr->status_chgd = TRUE;
	    
	}

	if (Z->status_type->magic != STATUSTYPE_magic)
	    panic("MBX VIEW PANIC",__FILE__,__LINE__,
		  "mt_setf_status_duplicate",
		  "Bad type magic number",0);
	
	/* change status of all duplicates ... */
	for (j = 0; j < E->loc_len; j++) {
	    Z->status_type->mt_setf_status_it(Z,index,& (E->locs[j]),
					      t,mask);		
	}    


    } else {
	struct folder_view V;
	struct MailboxView *Z = mailbox->u.duplicate->parent_mailbox;
	
	/* Shift by one */
	V.mailbox_number = v->mailbox_number-1;
	V.index          = v->index;

	if (Z->status_type->magic != STATUSTYPE_magic)
	    panic("MBX VIEW PANIC",__FILE__,__LINE__,
		  "mt_setf_status_duplicate",
		  "Bad type magic number",0);

	r = Z->status_type->mt_setf_status_it(Z,index,&V,t,mask);
    }
	
    return r;
}

S_(mt_clearf_status_message mt_clearf_status_duplicate)
static int   mt_clearf_status_duplicate  P_((struct MailboxView *mailbox,
					   int index, 
					   struct folder_view *v,
					   enum status_x t, int mask));
static int   mt_clearf_status_duplicate(mailbox,index,v,t,mask)
     struct MailboxView *mailbox;
     int index;
     struct folder_view *v;
     enum status_x t; 
     int mask;
{
    struct header_rec *hdr;
    int r = 0;

    if (mailbox->u.duplicate->magic     != MV_DUPLICATE_magic)
	panic("MBX VIEW PANIC",__FILE__,__LINE__,
	      "mt_clearf_status_duplicate",
	      "Bad magic number",0);

 
    if (0 == v->mailbox_number) {   /* Number 0 is internal */
	struct  instance_chain *E;
	struct header_rec *hdr;
	struct MailboxView *Z = mailbox->u.duplicate->parent_mailbox;
	int j;

	if (v->index < 0 || v->index >= mailbox->u.duplicate->collected_len)
	    return 0;

	E =  mailbox->u.duplicate->collected[v->index];
	if (MV_INSTANCE_magic != E->magic)
	    panic("MBX VIEW PANIC",__FILE__,__LINE__,
		  "mt_clearf_status_duplicate",
		  "Bad magic number",0);

	hdr = E->mss;

	if (hdr) {
	    switch (t) {
	    case status_basic:
		clearit(hdr->status,mask);
		r = hdr->status;
		break;
		
	    case status_1:
		clearit(hdr->status1,mask);
		r = hdr->status1;
		break;
		
	    default:
		panic("MBX VIEW PANIC",__FILE__,__LINE__,
		      "mt_clearf_status_duplicate",
		      "bad status type",0);		
	    }
	    
	    /* TODO: What statuses should cause flagging??? */
	    hdr->status_chgd = TRUE;	   
	}
	
	if (Z->status_type->magic != STATUSTYPE_magic)
	    panic("MBX VIEW PANIC",__FILE__,__LINE__,
		  "mt_clearf_status_duplicate",
		  "Bad type magic number",0);
	
	/* change status of all duplicates ... */
	for (j = 0; j < E->loc_len; j++) {
	    Z->status_type->mt_clearf_status_it(Z,index,& (E->locs[j]),
						t,mask);		
	}    

    } else {
	struct folder_view V;
	struct MailboxView *Z = mailbox->u.duplicate->parent_mailbox;
	
	/* Shift by one */
	V.mailbox_number = v->mailbox_number-1;
	V.index          = v->index;

	if (Z->status_type->magic != STATUSTYPE_magic)
	    panic("MBX VIEW PANIC",__FILE__,__LINE__,
		  "mt_clearf_status_duplicate",
		  "Bad type magic number",0);

	r = Z->status_type->mt_clearf_status_it(Z,index,&V,t,mask);
    }

    return r;
}


static struct status_type status_duplicate = {
    STATUSTYPE_magic,
    mt_show_status_duplicate,
    mt_ison_status_duplicate,
    mt_setf_status_duplicate,
    mt_clearf_status_duplicate
};

struct MailboxView * duplicate_to_mailbox_view (parent)
     struct MailboxView * parent;
{
    struct MailboxView *ret = malloc_view(&mt_duplicate);

    if (parent->magic         != MAILBOXVIEW_magic)
	panic("MBX VIEW PANIC",__FILE__,__LINE__,"duplicate_to_mailbox_view",
	      "Bad magic number",0);
    
    if (parent->mailbox_type->magic != MAILBOXTYPE_magic)
	panic("MBX VIEW PANIC",__FILE__,__LINE__,"duplicate_to_mailbox_view",
	      "Bad type magic number",0);

    /* Override default status handling */
    ret->status_type = &status_duplicate;  

    ret->u.duplicate->parent_mailbox = parent;
    ret->mailbox_type->mt_make_it_view(ret);

    return ret;
}




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


syntax highlighted by Code2HTML, v. 0.9.1