static char rcsid[] = "@(#)$Id: folder.c,v 1.4 2006/05/30 16:33:21 hurtta Exp $";

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

#include "def_readmsg.h"

DEBUG_VAR(Debug,__FILE__,"readmsg");

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


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

struct folder_data {
    struct folder_info  *folder_handle;
    struct header_rec  **headers;
    int                  headers_count;
};


static int parse_header_routine P_((struct folder_info *folder,
				    READ_STATE read_state_ptr,
				    struct header_rec *entry,
				    header_list_ptr parsed_headers ));
static int parse_header_routine(folder,read_state_ptr,entry,parsed_headers)
     struct folder_info *folder;
     READ_STATE read_state_ptr;
     struct header_rec *entry;
     header_list_ptr parsed_headers;
{
    header_list_ptr tmphdr;

    /* copy_envelope_folder() may have set entry->content_length */
    /* copy_envelope_folder() have set entry->header_charset */

    mime_parse_helper(entry,parsed_headers);

    return 1;
}

static int parse_body_routine P_((struct folder_info *folder,
			   READ_STATE read_state_ptr,
			   struct header_rec *entry,
			   header_list_ptr parsed_headers,
			   struct counter_data *counter
			   ));
static int parse_body_routine(folder,read_state_ptr,entry,parsed_headers,
			      counter)
     struct folder_info *folder;
     READ_STATE read_state_ptr;
     struct header_rec *entry;
     header_list_ptr parsed_headers;
     struct counter_data *counter;
{
    char * buffer;
    int len;
    long content_remaining                = -1L;
    long A1, A2;

    content_remaining = entry->content_length;

    
    A1 = copy_fbytes_folder(folder,read_state_ptr);

    start_body_helper(entry,A1,parsed_headers);


 reset_body:


    while (copy_body_folder(folder,read_state_ptr,
			    &buffer,&len,&content_remaining)) {
	if (buffer)
	    free(buffer);
    }

    A2 = copy_fbytes_folder(folder,read_state_ptr);

    if (!copy_envelope_end_folder(folder,read_state_ptr)) {	
	if (entry->content_length >= 0L) {
	    entry->content_length = -1L;
	    
	    if (copy_envelope_reset_body(folder,read_state_ptr,
					 &content_remaining))
		goto reset_body;
	}
	DPRINT(Debug,10,(&Debug,			    
			 "-- Message parsing FAILED.\n"));
	return 0;
    }
    DPRINT(Debug,10,(&Debug,			    
		     "-- Message parsed.\n"));

    if (A2-A1 != entry->content_length) {
	entry->content_length = A2-A1;

	DPRINT(Debug,10,(&Debug,"-- Content-length fixed: %ld\n",
			 entry->content_length));
    }

    entry->body_parsed = 1;

    return 1;
}

struct header_rec * malloc_header_helper() 
{
    struct header_rec *entry = safe_malloc(sizeof (* entry));

    /* bzero is defined hdrs/defs.h */
    bzero((void *)entry,sizeof (* entry));

    mime_t_zero (&(entry->mime_rec));

    return entry;
}

/* Return 0 on failure, 1 on succeed */
static int read_headers P_((struct folder_handler *H, 
			    struct read_folder_state * read_state_ptr));
static int read_headers(H,read_state_ptr)
     struct folder_handler *H;
     struct read_folder_state * read_state_ptr;
{
    struct folder_data * fol = H->d.normal;
    int count                = H->num_messages;
    int r = 0;

    if (count > fol->headers_count)
	panic("READMSG PANIC",__FILE__,__LINE__,"read_headers",
              "Bad count",0);

    while (1) {
	if (count >= fol->headers_count) {
	    int nv = count + 100;
	    int i;
	    fol->headers = safe_realloc(fol->headers,
					nv * sizeof(fol->headers[0]));

	    for (i = fol->headers_count; i < nv; i++) 
		fol->headers[i] = NULL;
	    fol->headers_count = nv;

	}

	if (! fol->headers[count]) 
	    fol->headers[count] = malloc_header_helper();
	


	r = copy_envelope_folder(fol->folder_handle,read_state_ptr,
				 fol->headers[count],
				 parse_header_routine,parse_body_routine,
				 NULL);
	if (r <= 0) {

	    DPRINT(Debug,10,(&Debug,"[%d] copy_envelope_folder returned %d \n",
			     count,r));
	    break;
	}
	count++;
    }

    H->num_messages = count;

    if (r < 0) {
	DPRINT(Debug,10,(&Debug,"read_headers failed.\n"));
	return 0;
    }
    return 1;
}


S_(init_folder_handler_f init_normal_handler)
static void init_normal_handler P_((struct folder_handler *H));
static void init_normal_handler(H)
     struct folder_handler *H;
{
    H->d.normal = safe_malloc(sizeof (* H->d.normal));

    /* bzero is defined hdrs/defs.h */
    bzero((void *)H->d.normal,sizeof (* H->d.normal));

    H->d.normal->folder_handle = NULL;
    H->d.normal->headers       = NULL;
    H->d.normal->headers_count = 0;
}

void free_header_helper(entry)
     struct header_rec **entry;
{

    free_rec_mbx_info(*entry);
    mime_t_clear(& ((*entry)->mime_rec));
    
    
    if ((*entry)->header_error)
	free_header_errors(& ((*entry)->header_error));
    
    free(*entry);    /* LEAKS !!! */
    *entry = NULL;
}


S_(free_folder_handler_f free_normal_handler)
static void free_normal_handler P_((struct folder_handler *H));
static void free_normal_handler(H)
     struct folder_handler *H;
{

    /* leave_old_folder calls close_folder() 
       so it need not be called separately
    */

    if (H->d.normal->folder_handle) 
	leave_old_folder(& (H->d.normal->folder_handle), CLOSE_NORMAL);

    if (H->d.normal->headers) {
	int i;

	for (i = 0; i < H->d.normal->headers_count; i++) {
	    /* FIXME -- there is no way to free that -- this leaks ... */

	    if (H->d.normal->headers[i]) {

		free_header_helper(& H->d.normal->headers[i]);
	
	    }

	}
	free(H->d.normal->headers);
	H->d.normal->headers = NULL;
    }
    H->d.normal->headers_count = 0;
}

S_(give_message_from_folder_f give_message_from_normal)
static FILE * give_message_from_normal P_((struct folder_handler *folder,
					   int idx,
					   long *content_length,
					   enum message_error *errret,
					   int print_separator,
					   char **env_buffer,
					   struct header_rec ** entryret
					   ));

static FILE * give_message_from_normal(folder,idx,content_length,errret,
				       print_separator,env_buffer,entryret) 
     struct folder_handler *folder;
     int idx;
     long *content_length;
     enum message_error *errret;
     int print_separator;
     char **env_buffer;
     struct header_rec ** entryret;
{
    FILE * F;
    long ZZ;

    struct folder_data * fol = folder->d.normal;

     if (idx < 0  || idx >= folder->num_messages ||
	 idx >= fol->headers_count ||
	 ! fol->headers[idx])
	 panic("READMSG PANIC",__FILE__,__LINE__,"give_message_from_normal",
	       "Bad index",0);

     if (! prepare_message_access(fol->folder_handle,
				  fol->headers[idx],
				  parse_header_routine,
				  parse_body_routine,
				  NULL,NO_mime_parse)) {

	 *errret = error_none;  /* Error message already printed ? */
	 return NULL;
     }

     if (fol->headers[idx]->header_error)
	 print_header_errors(fol->headers[idx]->header_error);

     *content_length = fol->headers[idx]->content_length;
     
     F =  folder_to_fd(fol->folder_handle,
		       fol->headers[idx]->offset);

     if (!F) {
	 *errret = error_seek;
	 return NULL;
     }

     
    ZZ = separator_helper(NULL,errret,
			  print_separator,env_buffer,F);
    if (-1 == ZZ)
	return NULL;

    DPRINT(Debug,9,(&Debug,
		    "give_message_from_folder: beginning of headers=%ld\n",ZZ));

    if (fseek(F, ZZ, SEEK_SET) == -1) {
	int err = errno;
	DPRINT(Debug,1,(&Debug, 
			
			"give_message_from_folder: Couldn't seek folder to offset %ld Errno %s (%s)\n",
			ZZ, 
			error_description(err)));

	*errret = error_seek;
	return NULL;
    }

    if (entryret)
	*entryret = fol->headers[idx];

     return F;
}

S_(parse_folder_handler_f parse_folder_normal)
static int parse_folder_normal P_((struct folder_handler *H));
static int parse_folder_normal(H)
     struct folder_handler *H;
{
    int ret = 0;
    struct read_folder_state * read_state_ptr = NULL;

    struct folder_data * fol = H->d.normal;
    enum prepare_mode mode =  PREPARE_NOLOCK;

    /* Already called */
    if (H->num_messages > 0)
	mode = PREPARE_NEW_ONLY_NOLOCK;

    if (!prepare_read_folder(fol->folder_handle,mode,&read_state_ptr)) {
	DPRINT(Debug,10,(&Debug,"prepare_read_folder failed.\n"));
	goto fail;
    }

    ret = read_headers(H,read_state_ptr);

    if (!end_read_folder(fol->folder_handle,&read_state_ptr,0)) {
	DPRINT(Debug,10,(&Debug,"end_read_folder failed.\n"));
	ret = 0;
    }

 fail:
    return ret;
}

static struct folder_routines NORMAL = {
    FOLDER_ROUTINES_magic,

    init_normal_handler,
    free_normal_handler,
    give_message_from_normal,
    parse_folder_normal
};


struct folder_handler * open_normal_folder(folder_name)
     const char * folder_name;
{
    struct folder_handler *ret = NULL;
    
    
    struct folder_info * f = enter_new_folder(folder_name);

    if (!f) 
	return NULL;

    /* Actually open folder 

       folder_to_fd do not work if SESSIONLOCK_NONE is used       
    */

    if (!sessionlock_folder(f,SESSIONLOCK_NORMAL)) {
	leave_old_folder(& f, CLOSE_NORMAL);

	return NULL;
    }

    ret = malloc_folder_handler(&NORMAL);
    ret->d.normal->folder_handle = f;

    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