static char rcsid[] = "@(#)$Id: pop.c,v 1.5 2006/06/25 10:41:04 hurtta Exp $";

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

#include "def_mbox.h"
#include "ss_imp.h"
#include "s_me.h"
#include "s_elm.h"

DEBUG_VAR(Debug,__FILE__,"pop");

#ifdef REMOTE_MBX

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

#define POP_TIMEOUT  25


struct uidl_entry {
    char * uidl;
    int    status;
    struct uidl_entry   * smaller;     /* LEFT  */
    struct uidl_entry   * bigger;      /* RIGTH */
    unsigned int changed : 1;          /* == status not on disk */
};

static void destroy_uidl_tree P_((struct uidl_entry **root));
static void destroy_uidl_tree(root)
     struct uidl_entry **root;
{
    struct uidl_entry *ptr = *root;

    if (ptr) {
	destroy_uidl_tree(&(ptr->smaller));
	destroy_uidl_tree(&(ptr->bigger));

	DPRINT(Debug,27,(&Debug,
			 "destroy_uidl_tree: FREE: ptr=%p (%s)\n", 
			 ptr, 
			 ptr->uidl ? ptr->uidl : ""));    

	if (ptr->uidl) {
	    free(ptr->uidl);
	    ptr->uidl = NULL;
	}
	ptr->status  = 0;
	ptr->changed = 0;
	
	free(ptr);
	ptr = NULL;
    }
    *root = ptr;
}

/* Returns existing entry or creates new */

static struct uidl_entry * give_uidl P_((struct uidl_entry **root, 
					 CONST char * uidl));
static struct uidl_entry * give_uidl(root,uidl)
     struct uidl_entry **root;
     CONST char * uidl;
{
    struct uidl_entry *ptr = *root;
    int r;

    if (!ptr) {
	ptr = safe_malloc(sizeof (struct uidl_entry));
	bzero((void *)ptr,sizeof (struct uidl_entry));
	ptr->uidl    = safe_strdup(uidl);
	ptr->status  = UNREAD | NEW;
	ptr->smaller = NULL;
        ptr->bigger  = NULL;
	ptr->changed = 0;
	*root = ptr;

	DPRINT(Debug,27,(&Debug,
			 "give_uidl: MALLOC: ptr=%p (%s)\n",
			 ptr, ptr->uidl));    
	return ptr;
    }
    
    r = strcmp(uidl,ptr->uidl);
    if (0 == r) {
	DPRINT(Debug,28,(&Debug,
			 "give_uidl: RETURN: ptr=%p (%s)\n",
			 ptr, ptr->uidl));   
	return ptr;
    } else if (0 < r) {
	return give_uidl( &(ptr->bigger), uidl);
    } else {
	return give_uidl( &(ptr->smaller), uidl);
    }
}

static void print_uidls P_((FILE *F, struct uidl_entry *root));
static void print_uidls (F, root)
     FILE * F;
     struct uidl_entry *root;
{
    /* If wee print root first, and then other nodes, then
     * data will not go to tree on order when it is read to back.
     * That avoides degerated case of binary tree (ie. linear list)
     * if original tree was not degenerated.
     */

    if (!root)
	return;

    if (!ison(root->status, UNREAD)) {
	putc('R',F);
    }
    if (ison(root->status, NEW)) {
	putc('N',F);
    } else {
	putc('O',F);
    }
    if (ison(root->status, REPLIED)) {
	putc('r',F);
    }
    fprintf(F," %s\n",root->uidl);

    root->changed = 0;

    print_uidls(F,root->smaller);
    print_uidls(F,root->bigger);
}

static void update_uidls P_((CONST char *name, struct uidl_entry **root));
static void update_uidls(name, root)
     CONST char *name;
     struct uidl_entry **root;
{
    FILE * F;
    int ret;
    char buffer[100];
    int err = can_open(name, "a");

    if (0 != err) {
	lib_error(CATGETS(elm_msg_cat, ElmSet,ElmCantOpenFile,
			  "Can't open %s!"),
		  name);
	return;
    }

    F = open_or_create(name);
    if (!F) {
	lib_error(CATGETS(elm_msg_cat, ElmSet,ElmCantOpenFile,
			  "Can't open %s!"),
		  name);
	return;
    }

    ret = Grab_the_file(fileno(F));
    if (FLOCKING_RETRY == ret) {
	lib_transient(CATGETS(elm_msg_cat, MeSet, MeLockingFile,
			      "Locking %s..."),
		      name);
	while (FLOCKING_RETRY == ret) {
	    wait_for_timeout(1);
	    ret = Grab_the_file(fileno(F));
	}

	if (FLOCKING_OK == ret) {
	    lib_transient(CATGETS(elm_msg_cat, MeSet, MeLockingFileOK,
				  "Locking %s... OK."),
			  name);

	}
    }

    if (FLOCKING_FAIL == ret) {
	lib_error(CATGETS(elm_msg_cat, MeSet, MeLockingFileFAIL,
			      "Locking %s... Failed."),
		      name);
	fclose(F);
	return;
    }

    if ((ret = mail_gets(buffer, sizeof buffer,F)) > 0) {
	if (ret != 12 || 0 != memcmp(buffer,"ELMME+ UIDL\n",12)) {
	    lib_error(CATGETS(elm_msg_cat, MeSet, MeFileCorrupted,
			      "File %s is corrupted."),
		      name);
	    fclose(F);
	    return;
	}
    }

    while (0 < (ret = mail_gets(buffer, sizeof buffer,F))) {
	struct uidl_entry * A;
	int status = UNREAD;
	char * p;

	/* So that truncate of file is not necessary */
	if (0 == strcmp("--END\n",buffer))
	    break;

	if ('\n' == buffer[ret-1]) {
	    buffer[ret-1] = '\0';
	    ret--;
	}

	for (p = buffer; p - buffer < ret && ' ' != *p; p++) {
	    switch(*p) {
	    case 'R': 
		status &= ~UNREAD;
		break;
	    case 'O':
		status &= ~NEW;
		break;
	    case 'N':
		status |= NEW;
		break;
	    case 'r':
		status |= REPLIED;
		break;
	    }
	}

	if (p-buffer >= ret) {
	    DPRINT(Debug,28,(&Debug,
			     "update_uidls: %s: Bad line: %s\n",
			     name,buffer)); 
	    continue;
	}
	p++;

	A = give_uidl(root,p);
	if (! A->changed) {
	    A -> status = status;
	}
    }
    rewind(F);
#ifdef FTRUNCATE
    ftruncate(fileno(F),0);
#endif
    fprintf(F,"ELMME+ UIDL\n");
    print_uidls(F,*root);
    fprintf(F,"--END\n");   /* So that truncate of file is not necessary */
    fflush(F);
    Release_the_file(fileno(F));
    fclose(F);

}

static int pop_read_stream P_((struct streamsched *ss,void * data));
static int pop_read_stream (ss,data)
     struct streamsched *ss;
     void * data;
{
    struct folder_info *folder = data;
    int n, i;
    int close_it = 0;

    char * ERROR = NULL;
    
    if (!data || 
	folder->folder_type != &pop_mbx ||
	folder -> p->a.pop_mbx.C.stream != ss) {
	
	panic("MBX PANIC",__FILE__,__LINE__,"pop_read_stream",
	      "Bad data argument !!!",0);
    }

    DPRINT(Debug,13,(&Debug,
		     "pop_read_stream: folder=%p (%s)\n",
		     folder,folder->cur_folder_sys));

    if (POP_error == folder->p->a.pop_mbx.pop_state) {
	DPRINT(Debug,13,(&Debug,
			 "pop_read_stream=0 (stream need closing)\n"));
	return 0;
    }
	    
    n = ReadFromStream(ss, &(folder -> p->a.pop_mbx.read_buffer),-1);

    if (n < 0) {      
	ERROR = RemoveStreamError(ss);
	DPRINT(Debug,13,(&Debug,
			 "pop_read_stream: read error %s\n",
			 ERROR ? ERROR : "(none)"));
    }
    
    DPRINT(Debug,50,(&Debug,
		     "Buffer content="));
    for (i = 0; i < folder -> p->a.pop_mbx.read_buffer.read_len; i++) {
	DPRINT(Debug,50,(&Debug,
			 " %d",
			 folder -> p->a.pop_mbx.read_buffer.read_buffer[i]));
    }
    DPRINT(Debug,50,(&Debug,
		     "\n (message?)="));
    for (i = 0; i < folder -> p->a.pop_mbx.read_buffer.read_len; i++) {
	DPRINT(Debug,50,(&Debug,
			 " %c",
			 isascii(folder -> p->a.pop_mbx.read_buffer.
				 read_buffer[i]) &&
			 isprint(folder -> p->a.pop_mbx.read_buffer.
				 read_buffer[i]) ? 
			 folder -> p->a.pop_mbx.read_buffer.read_buffer[i] :
			 '.'));
    }
    DPRINT(Debug,50,(&Debug,
		     "\n"));
    
    /* Parse response ... */
    
    if (POP_simple_response == folder->p->a.pop_mbx.pop_state ||
	POP_multiline_response == folder->p->a.pop_mbx.pop_state) {
	int linelen;
	
	DPRINT(Debug,13,(&Debug,
			 "pop_read_stream: Wanting response\n"));
	
	linelen = find_crlf(&(folder -> p->a.pop_mbx.read_buffer),1);
	if (!linelen)
	    goto check_read;
	
	folder -> p->a.pop_mbx.command_status =
	    safe_realloc(folder -> p->a.pop_mbx.command_status,
			 linelen);
	memcpy(folder -> p->a.pop_mbx.command_status,
	       folder -> p->a.pop_mbx.read_buffer.read_buffer,linelen);
	cut_line(&(folder -> p->a.pop_mbx.read_buffer),linelen);
	
	DPRINT(Debug,13,(&Debug,
			 "pop_read_stream: response=%s\n",
			 folder -> p->a.pop_mbx.command_status));
	
	if (folder -> p->a.pop_mbx.command_data) {
	    free(folder -> p->a.pop_mbx.command_data);
	    folder -> p->a.pop_mbx.command_data = NULL;
	    folder -> p->a.pop_mbx.data_len  = 0;
	}
	
	if (0 == memcmp("+OK",folder -> p->a.pop_mbx.command_status,3) &&
	    POP_multiline_response == folder->p->a.pop_mbx.pop_state)
	    folder->p->a.pop_mbx.pop_state = POP_multiline_data;
	else {
	    folder->p->a.pop_mbx.pop_state = POP_command_ready;
	    DPRINT(Debug,13,(&Debug,
			     "pop_read_stream: Command ready\n"));
	}
    }
    
    if (POP_multiline_data == folder->p->a.pop_mbx.pop_state) {
	int linelen;
	
	DPRINT(Debug,13,(&Debug,
			 "pop_read_stream: Wanting multiline data\n"));
	
	
	while (0 < (linelen = find_crlf(&(folder -> p->a.pop_mbx.read_buffer),
					0))) {
	    char * p = folder -> p->a.pop_mbx.read_buffer.read_buffer;
	    int plen = linelen;
	    if (p[0] == '.') {
		p++;
		plen--;
		if (plen <= 2) {   /* If CRLF only left then EOM */
		    cut_line(&(folder -> p->a.pop_mbx.read_buffer),
			     linelen);		   
		    folder->p->a.pop_mbx.pop_state = POP_command_ready;
		    DPRINT(Debug,13,(&Debug,
				     "pop_read_stream: Command ready (multiline data len = %d)\n",
				     folder -> p->a.pop_mbx.data_len));
		    break;
		}
	    }
	    folder -> p->a.pop_mbx.command_data = 
		safe_realloc(folder -> p->a.pop_mbx.command_data,
			     folder -> p->a.pop_mbx.data_len + plen);
	    memcpy(folder -> p->a.pop_mbx.command_data + 
		   folder -> p->a.pop_mbx.data_len,
		   p, plen);
	    folder -> p->a.pop_mbx.data_len += plen;
	    cut_line(&(folder -> p->a.pop_mbx.read_buffer),
		     linelen);		   
	}
    }
    
    if (folder -> p->a.pop_mbx.read_buffer.read_len) {
	switch(folder->p->a.pop_mbx.pop_state) {
	    int i;
	case POP_command_ready:	case POP_idle: case POP_simple_command: 
	case POP_multiline_command:
	    DPRINT(Debug,13,(&Debug,
			     "pop_read_stream: %d bytes data left! (bad state):\n",
			     folder -> p->a.pop_mbx.read_buffer.read_len));
	    DPRINT(Debug,13,(&Debug,
			     "pop_read_stream: extra data="));
	    for (i =0; i < folder -> p->a.pop_mbx.read_buffer.read_len; i++) {
		DPRINT(Debug,13,(&Debug,
				 " %d",
				 folder -> p->a.pop_mbx.read_buffer.
				 read_buffer[i]));
	    }
	    DPRINT(Debug,13,(&Debug,
			     "\npop_read_stream: (message?)="));
	    for (i =0; i < folder -> p->a.pop_mbx.read_buffer.read_len; i++) {
		DPRINT(Debug,13,(&Debug,
				 "%c",
				 isascii(folder -> p->a.pop_mbx.read_buffer.
					 read_buffer[i]) &&
				 isprint(folder -> p->a.pop_mbx.read_buffer.
					 read_buffer[i]) ?
				 folder -> p->a.pop_mbx.read_buffer.read_buffer[i] :
				 '.'));
	    }
	    DPRINT(Debug,13,(&Debug,
			     "\n"));

	    lib_error(CATGETS(elm_msg_cat, MeSet,MeUnexpextedResponse,
			      "Unexpected response from POP server"));
	    close_it = 1;
	}
    }

 check_read:
    if (n == 0) {
	lib_error(CATGETS(elm_msg_cat, MeSet,MeConnectionClosed,
			  "Connection closed to POP server"));
	close_it = 1;
    }
    if (n < 0 && ERROR) {
	lib_error(CATGETS(elm_msg_cat, MeSet,MeErrorReading,
			  "Error reading from POP server: %s"),
		  ERROR);
	close_it = 1;
    }

    if (ERROR) {
	free(ERROR);
	ERROR = NULL;
    }

    if (close_it) {
	folder->p->a.pop_mbx.pop_state = POP_error;
	DPRINT(Debug,13,(&Debug,
			 "pop_read_stream=0 (stream need closing)\n"));
	return 0;
    }
    DPRINT(Debug,13,(&Debug,
		     "pop_read_stream=1\n"));
    return 1;
}

static int pop_push_command P_((struct folder_info *folder,
				char *command, int simple));
static int pop_command_ok P_((struct folder_info *folder));
static void pop_clear_command P_((struct folder_info *folder));

static int pop_idle_timer P_((struct streamsched *ss,void * data));
static int pop_idle_timer (ss,data)
     struct streamsched *ss;
     void * data;
{
    struct folder_info *folder = data;
    int status;
    
    if (!data || 
	folder->folder_type != &pop_mbx ||
	folder -> p->a.pop_mbx.C.stream != ss) {

	panic("MBX PANIC",__FILE__,__LINE__,"pop_idle_timer",
	      "Bad data argument !!!",0);
    }

    DPRINT(Debug,13,(&Debug,
		     "pop_idle_timer: folder=%p (%s)\n",
		     folder,folder->cur_folder_sys));

    if (POP_error == folder->p->a.pop_mbx.pop_state) {
	DPRINT(Debug,13,(&Debug,
			 "pop_idle_timer=0 (stream need closing)\n"));
	return 0;
    }
    
    if (POP_command_ready == folder->p->a.pop_mbx.pop_state &&
	0 == strncmp(folder->p->a.pop_mbx.command,"NOOP",
		     sizeof folder->p->a.pop_mbx.command)) {
	DPRINT(Debug,13,(&Debug,
			 "pop_idle_timer: Previous idle command was finished.\n"));
	pop_clear_command(folder);
    } else if (POP_idle != folder->p->a.pop_mbx.pop_state) {
	DPRINT(Debug,13,(&Debug,
			 "pop_idle_timer=1 (stream not idle: command %.*s)\n",
			 sizeof folder->p->a.pop_mbx.command,
			 folder->p->a.pop_mbx.command));
	return 1;
    }

    status = pop_push_command(folder,"NOOP",1);
    DPRINT(Debug,13,(&Debug,
		     "pop_idle_timer=%d\n",
		     status));
    return status;
}

static int pop_write_stream P_((struct streamsched * ss,void * data));
static int pop_write_stream (ss,data)
     struct streamsched *ss;
     void * data;
{
    struct folder_info *folder = data;
    int n;
    char * ERROR = NULL;

    if (!data || 
	folder->folder_type != &pop_mbx ||
	folder -> p->a.pop_mbx.C.stream != ss) {

	panic("MBX PANIC",__FILE__,__LINE__,"pop_write_stream",
	      "Bad data argument !!!",0);
    }
    
    DPRINT(Debug,13,(&Debug,
		     "pop_write_stream: folder=%p (%s)\n",
		     folder,folder->cur_folder_sys));

    if (!folder -> p->a.pop_mbx.write_buffer.write_len) {
	DPRINT(Debug,13,(&Debug,
			 "pop_write_stream: NO DATA TO WRITE!\n"));
    }

    if (POP_error == folder->p->a.pop_mbx.pop_state) {
	DPRINT(Debug,13,(&Debug,
			 "pop_write_stream=0 (stream need closing)\n"));
	return 0;
    }
    
    if (POP_simple_command != folder->p->a.pop_mbx.pop_state &&
	POP_multiline_command != folder->p->a.pop_mbx.pop_state) {

	panic("MBX PANIC",__FILE__,__LINE__,"pop_write_stream",
	      "Bad state !!!",0);
    }

    n = WriteToStream(ss, & (folder -> p->a.pop_mbx.write_buffer));

    if (n > 0) {
	int i;

	DPRINT(Debug,70,(&Debug,
		    "Write content="));
	for (i = 0; i < n; i++) {
	    DPRINT(Debug,70,(&Debug," %d",
			     folder->p->a.pop_mbx.write_buffer.write_buffer[i]));
	}
	DPRINT(Debug,70,(&Debug,"\n    (text?)="));
	for (i = 0; i < n; i++) {
	    DPRINT(Debug,70,(&Debug," %c",
			     isascii(folder->p->a.pop_mbx.write_buffer.write_buffer[i]) 
			     &&
			     isprint(folder->p->a.pop_mbx.write_buffer.write_buffer[i]) 
			     ? 
			     folder->p->a.pop_mbx.write_buffer.write_buffer[i] :
			     '.'));
	}
	DPRINT(Debug,70,(&Debug,"\n"));

	cut_Write_Buffer(& (folder -> p->a.pop_mbx.write_buffer),n);
	
    } else if (n < 0) {
	ERROR = RemoveStreamError(ss);

	DPRINT(Debug,13,(&Debug,
			 "pop_write_stream: write error %s\n",
			 ERROR ? ERROR : "(none)"));
    }
    
    if (!folder -> p->a.pop_mbx.write_buffer.write_len) {
	if (POP_multiline_command == folder->p->a.pop_mbx.pop_state)
	    folder->p->a.pop_mbx.pop_state = POP_multiline_response;
	else
	    folder->p->a.pop_mbx.pop_state = POP_simple_response;

	if (ERROR) {
	    free(ERROR);
	    ERROR = NULL;
	}
	DPRINT(Debug,13,(&Debug,
			 "pop_write_stream=0 -- data written\n"));

	return 0;
    }

    if (ERROR) {
	lib_error(CATGETS(elm_msg_cat, MeSet,MeErrorWriting,
			  "Error writing to POP server: %s"),
		  ERROR);
	folder->p->a.pop_mbx.pop_state = POP_error;
	DPRINT(Debug,13,(&Debug,
			 "pop_write_stream=0 (stream need closing)\n"));

	free(ERROR);
	ERROR = NULL;
        return 0;	
    }

    DPRINT(Debug,13,(&Debug,
		     "pop_write_stream=1\n"));
    return 1;
}

static int pop_push_command(folder,command,simple)
     struct folder_info *folder;
     char *command;
     int simple;
{
    int len = strlen(command);
    int status = 0;

    DPRINT(Debug,12,(&Debug,
		"pop_push_command: folder=%p (%s)\n",
		folder,folder->cur_folder_sys));

    if (POP_error == folder->p->a.pop_mbx.pop_state) {
    failure:
	if (folder -> p->a.pop_mbx.C.stream ) {
	    DPRINT(Debug,12,(&Debug,
			     "pop_push_command: Closing stream\n"));
	    
	    FreeStreamStack( &(folder -> p->a.pop_mbx.C.stream));
	}	
	status = 0;
	goto clean;
    }
    
    if (POP_idle != folder->p->a.pop_mbx.pop_state) {
	DPRINT(Debug,7,(&Debug,
			 "pop_push_command: wrong state -- perhaps idle timer (command %.*s) -- waiting...\n",
			 sizeof folder->p->a.pop_mbx.command,
			 folder->p->a.pop_mbx.command));
	
	while (POP_command_ready != folder->p->a.pop_mbx.pop_state &&
	       POP_error  != folder->p->a.pop_mbx.pop_state &&
	       NULL         != folder -> p->a.pop_mbx.C.stream) {
	    WaitStreamFor(folder -> p->a.pop_mbx.C.stream,SS_read_act);
	    /* TODO:    Should user to have some way to interrupt progress ? */
	}
	DPRINT(Debug,7,(&Debug,
			 "pop_push_command: ... wait ended\n"));
	if (POP_error == folder->p->a.pop_mbx.pop_state) 
	    goto failure;
	
    }
    if (0 != folder->p->a.pop_mbx.write_buffer.write_len) {
	DPRINT(Debug,12,(&Debug,
			 "pop_push_command: write buffer not empty (%d bytes left)\n",
			 folder->p->a.pop_mbx.write_buffer.write_len));
	status = 0;
	goto clean;
    }

    if (!folder -> p->a.pop_mbx.C.stream) {
	DPRINT(Debug,12,(&Debug,
			 "pop_push_command: stream not open\n"));
	status = 0;
	goto clean;
    }

    if (simple)
	folder->p->a.pop_mbx.pop_state = POP_simple_command;
    else
	folder->p->a.pop_mbx.pop_state = POP_multiline_command;

    strncpy(folder->p->a.pop_mbx.command,command,
	    sizeof folder->p->a.pop_mbx.command);

    DPRINT(Debug,15,(&Debug,
		     "pop_push_command: Command %.4s...\n",command));

    DPRINT(Debug,50,(&Debug,
		     "pop_push_command: command=%s\n",command));

    folder->p->a.pop_mbx.write_buffer.write_buffer = 
	safe_realloc(folder->p->a.pop_mbx.write_buffer.write_buffer,len+2);
    memcpy(folder->p->a.pop_mbx.write_buffer.write_buffer,command,len);
    memcpy(folder->p->a.pop_mbx.write_buffer.write_buffer+len,"\r\n",2);
    folder->p->a.pop_mbx.write_buffer.write_len = len + 2;

    ConfigStream(folder -> p->a.pop_mbx.C.stream,
		 pop_read_stream,pop_write_stream,
		 pop_idle_timer,POP_TIMEOUT,folder);
    status = 1;

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

static int pop_command_ok(folder)
     struct folder_info *folder;
{
    int status = 0;

    DPRINT(Debug,12,(&Debug,
		     "pop_command_ok: folder=%p (%s)\n",
		     folder,folder->cur_folder_sys));
    
    if (POP_idle == folder->p->a.pop_mbx.pop_state) {
	DPRINT(Debug,12,(&Debug,
			 "pop_command_ok: WRONG STATE\n"));
	status = 0;
	goto clean;
    }

    while (POP_command_ready != folder->p->a.pop_mbx.pop_state &&
	   POP_error  != folder->p->a.pop_mbx.pop_state &&
	   NULL         != folder -> p->a.pop_mbx.C.stream) {
	WaitStreamFor(folder -> p->a.pop_mbx.C.stream,SS_read_act);
	/* TODO:    Should user to have some way to interrupt progress ? */
    }

    if (POP_error == folder->p->a.pop_mbx.pop_state) {
	if (folder -> p->a.pop_mbx.C.stream ) {
	    DPRINT(Debug,12,(&Debug,
			     "pop_command_ok: Closing stream\n"));
	    
	    FreeStreamStack(& (folder -> p->a.pop_mbx.C.stream));
	}	
	status = 0;
	goto clean;
    }

    if (0 == memcmp("+OK",folder -> p->a.pop_mbx.command_status,3))
	status = 1;
    else if (0 == memcmp("-ERR",folder -> p->a.pop_mbx.command_status,4)) {
	lib_error(CATGETS(elm_msg_cat, MeSet,MePopError,
			  "POP server: %s"),
		  folder -> p->a.pop_mbx.command_status+4);
	status = 0;
    } else {
	DPRINT(Debug,12,(&Debug,
			 "pop_command_ok: UNEXPECTED RESPONSE: %s\n",
			 folder -> p->a.pop_mbx.command_status));
	folder->p->a.pop_mbx.pop_state = POP_error;	
	status = 0;
    }

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

static void pop_clear_command(folder)
     struct folder_info *folder;
{
    DPRINT(Debug,12,(&Debug,
		     "pop_clear_command: folder=%p (%s)\n",
		     folder,folder->cur_folder_sys));

    if (POP_idle == folder->p->a.pop_mbx.pop_state) {
	DPRINT(Debug,12,(&Debug,
			 "pop_clear_command: Already clear!\n"));
    } else if (POP_command_ready != folder->p->a.pop_mbx.pop_state) {
	DPRINT(Debug,12,(&Debug,
			 "pop_clear_command: WRONG STATE!\n"));
	folder->p->a.pop_mbx.pop_state = POP_error;
    } else
	folder->p->a.pop_mbx.pop_state = POP_idle;

    strncpy(folder->p->a.pop_mbx.command,"",
	    sizeof folder->p->a.pop_mbx.command);

    if (folder -> p->a.pop_mbx.command_status)
	free(folder -> p->a.pop_mbx.command_status);
    folder -> p->a.pop_mbx.command_status = NULL;

    if (folder -> p->a.pop_mbx.command_data)
	free(folder -> p->a.pop_mbx.command_data);
    folder -> p->a.pop_mbx.command_data = NULL;
    folder -> p->a.pop_mbx.data_len = 0;
}
			      
static void mbx_close_pop P_((struct folder_info *folder,
			      enum close_mode mode));
static void mbx_close_pop(folder,mode) 
     struct folder_info *folder;
     enum close_mode mode;
{
    DPRINT(Debug,11,(&Debug,
		     "mbx_close_pop: folder=%p (%s)\n",
		     folder,folder->cur_folder_sys));

    if(pop_push_command(folder,"QUIT",1)) {
	if (pop_command_ok(folder)) {
	    lib_error(CATGETS(elm_msg_cat, MeSet,MePopError,
			      "POP server: %s"),
		      folder -> p->a.pop_mbx.command_status+4);
	}
    }
    pop_clear_command(folder);

    if (folder -> p->a.pop_mbx.C.stream ) {
	DPRINT(Debug,12,(&Debug,
			 "mbx_close_pop: Closing stream\n"));
	
	FreeStreamStack( & (folder -> p->a.pop_mbx.C.stream));
    }	
    
    switch (mode) {
    case CLOSE_NORMAL:
	
	/* falltru */
    case CLOSE_LEAVE_LOCKED:
	if (!folder -> p->fh_temp) {
	    DPRINT(Debug,1,(&Debug,
		       "mbx_close_pop: tempfile (%s) not open -- will not unlink\n",
		       folder -> cur_tempfolder));
	} else if (unlink(folder -> cur_tempfolder) != 0) {
	    if (errno != ENOENT) {
		lib_error(CATGETS(elm_msg_cat, ElmSet, ElmSorryCantUnlinkTemp,
				  "Sorry, can't unlink the temp file %s [%s]!\n\r"),
			  folder -> cur_tempfolder, error_description(errno));
	    }
	} else {
	    DPRINT(Debug,1,(&Debug, 
			    "close_folder: Unlinking tempfile (%s).\n",
			    folder -> cur_tempfolder));
	}      
    }

    return;
}

static int  mbx_lock_pop  P_((int direction,struct folder_info *folder));
static int  mbx_lock_pop(direction,folder)
     int direction;
     struct folder_info *folder;
{
    int status=1;

    DPRINT(Debug,11,(&Debug,
		     "mbx_lock_pop=%d (dummy): folder=%p (%s)\n",
		     folder,folder->cur_folder_sys));
  
    return status;
}

static int mbx_unlock_pop P_((int interrupt, struct folder_info *folder));
static int mbx_unlock_pop(interrupt,folder) 
     int interrupt;
     struct folder_info *folder;
{
    int status = 1;

    DPRINT(Debug,11,(&Debug,
		     "mbx_unlock_pop=%d (dummy): folder=%p (%s)\n",
		     status,folder,folder->cur_folder_sys));

    return status;
}

static void mbx_init_pop P_((struct folder_info *folder));
static void mbx_init_pop(folder) 
     struct folder_info *folder;
{
    DPRINT(Debug,11,(&Debug,
		     "mbx_init_pop: folder=%p (%s)\n",
		     folder,folder->cur_folder_sys));

    folder -> cur_tempfolder[0] = '\0';
    /* remote_mbox generates tempfolder name */
    
    folder -> p = safe_malloc(sizeof (struct private_data));
    /* defined in hdrs/defs.h */
    bzero((void *)folder -> p,sizeof( struct private_data));   
    
    folder ->p->fh_temp     = NULL;
    folder ->p->fh_folder   = NULL;
    folder ->p->flags1      = 0;

    zero_remote_account(&(folder -> p->a.pop_mbx.C));
    
    folder -> p->a.pop_mbx.pop_state    = POP_not_logged;
    folder -> p->a.pop_mbx.pop_flags    = 0;
    zero_Read_Buffer(&(folder -> p->a.pop_mbx.read_buffer));
    
    zero_Write_Buffer (& (folder -> p->a.pop_mbx.write_buffer));

    folder -> p->a.pop_mbx.command_status = NULL;
    folder -> p->a.pop_mbx.command_data   = NULL;
    folder -> p->a.pop_mbx.data_len       = 0;
    folder -> p->a.pop_mbx.stat_size      = 0;
    folder -> p->a.pop_mbx.stat_count      = 0;
    strncpy(folder->p->a.pop_mbx.command,"",
	    sizeof folder->p->a.pop_mbx.command);
    folder -> p->a.pop_mbx.uidl_root       = NULL;

}

static void pop_clear_buffers P_((struct folder_info *folder));
static void pop_clear_buffers(folder)
     struct folder_info *folder;
{
    DPRINT(Debug,12,(&Debug,
		     "pop_clear_buffers: folder=%p (%s)\n",
		     folder,folder->cur_folder_sys));    
    
    free_Read_Buffer(&(folder -> p->a.pop_mbx.read_buffer));

    free_Write_Buffer(&(folder -> p->a.pop_mbx.write_buffer));

    if (folder -> p->a.pop_mbx.command_status)
	free(folder -> p->a.pop_mbx.command_status);
    folder -> p->a.pop_mbx.command_status = NULL;
    if (folder -> p->a.pop_mbx.command_data)
	free(folder -> p->a.pop_mbx.command_data);
    folder -> p->a.pop_mbx.command_data = NULL;
    folder -> p->a.pop_mbx.data_len       = 0;
    strncpy(folder->p->a.pop_mbx.command,"",
	    sizeof folder->p->a.pop_mbx.command);

}

static void mbx_free_pop P_((struct folder_info *folder));
static void mbx_free_pop(folder)
     struct folder_info *folder;
{
    DPRINT(Debug,11,(&Debug,
		     "mbx_free_pop: folder=%p (%s)\n",
		     folder,folder->cur_folder_sys));    

    if (folder -> p->fh_temp) {
	fclose(folder -> p->fh_temp);
	folder -> p -> fh_temp = NULL;
    }
    if (folder -> p -> fh_folder) {
	fclose(folder -> p -> fh_folder);	
	folder -> p -> fh_folder = NULL;
    }

    free_remote_account(&(folder -> p->a.pop_mbx.C));
    pop_clear_buffers(folder);
	    
    destroy_uidl_tree(&(folder -> p->a.pop_mbx.uidl_root));

    folder -> p->a.pop_mbx.pop_flags    = 0;

    free(folder -> p);
    folder -> p = NULL;
}

#ifdef USE_DLOPEN
static struct pop_callbacks POP_FUNCTIONS = {
    pop_push_command,
    pop_command_ok,
    pop_clear_command 
};
#endif

static int pop_open_connection P_((struct folder_info *folder));
static int pop_open_connection(folder)
     struct folder_info *folder;
{
    int status = 0;
    int bits;
    char *data, *data1;

    DPRINT(Debug,12,(&Debug,
		     "pop_open_connection: folder=%p (%s)\n",
		     folder,folder->cur_folder_sys));
	
    /* Clear buffer from old data ... */
    pop_clear_buffers(folder);

    if (NULL == folder->p->a.pop_mbx.C.stream) {
	struct service_entry *se = 
	    give_service_entry(folder->p->a.pop_mbx.C.host,
			       STFLAG_is_pop);
	PORTS POP_ports[] = { PORT_pop3, PORT_end };
	int got;

	if (!se) {
	    status = 0;
	    goto clean;
	}
	
	/* canonify folder name .... */
	free(folder->cur_folder_sys);
	folder->cur_folder_sys = elm_message(FRM("%s@%s"),
					     folder->p->a.pop_mbx.C.username,
					     se->official_name);
	free_string (&(folder->cur_folder_disp));
	folder->cur_folder_disp = format_string(FRM("%s@%s"),
						folder->p->
						a.pop_mbx.C.username,
						se->official_name);

	free(folder->p->a.pop_mbx.C.host);
	folder->p->a.pop_mbx.C.host = safe_strdup(se->official_name);

	if (!connect_remote_account(&(folder->p->a.pop_mbx.C),
				    &got,se,POP_ports,
				    PORT_end)) {
	    status = 0;
	    free_temporary_service_entry(&se);
	    goto clean;
	}
	free_temporary_service_entry(&se);
    }

    /* We want server's initial response to connection */
    folder -> p->a.pop_mbx.pop_state = POP_simple_response;
    ConfigStream(folder -> p->a.pop_mbx.C.stream,
		 pop_read_stream,ss_noaction_routine,pop_idle_timer,
		 POP_TIMEOUT,folder);
    if (!pop_command_ok(folder)) {
	/* Server was given non-OK response to connection ... */	    
	status = 0;
	goto clean;	    
    }
    if (POP_show_greeting)
	lib_error(CATGETS(elm_msg_cat, MeSet,MePopError,
                          "POP server: %s"),
                  folder -> p->a.pop_mbx.command_status+3);
    pop_clear_command(folder);
    

#ifdef USE_DLOPEN    
 retry_prelogin_capa:
#endif

    if (!pop_push_command(folder,"CAPA",0)) {
	status = 0;
	goto clean;
    }
    /* If command unimplemented, that is OK ... */
    if (pop_command_ok(folder)) {
#ifdef USE_DLOPEN
	struct POP_capa_libs * pop_capa_libs     = NULL;
	int                    pop_capa_libcount = 0;

	enum CAPA_phase  phase = capa_prelogin;

#endif
	int p1,p2;

	int TOP_found = 0;

	for (p1 = 0; p1 < folder->p->a.pop_mbx.data_len; p1 = p2) {
	    for (p2 = p1; p2 < folder->p->a.pop_mbx.data_len-1; p2++) {
		if ('\r' == folder->p->a.pop_mbx.command_data[p2] &&
		    '\n' == folder->p->a.pop_mbx.command_data[p2+1]) {
		    char * capa = & (folder->p->a.pop_mbx.command_data[p1]);
		    char * arg = NULL;
		    
		    folder->p->a.pop_mbx.command_data[p2] = '\0';

		    if (NULL != (arg = strchr(capa,' '))) {
			*arg = '\0';
			arg++;
		    }

		    DPRINT(Debug,2,(&Debug, 
				    "POP server capacity: '%s'%s%s\n",
				    capa,
				    arg ? " args: " : "",
				    arg ? arg : ""));

		    if (0 == strcmp(capa,"TOP"))
			TOP_found ++;

#ifdef USE_DLOPEN
		    probe_pop_capa_lib(&pop_capa_libs,
				       &pop_capa_libcount,
				       capa,
				       arg);
#endif

		    

		    p2 += 2;
		    break;
		}	       
	    }	    
	}
	pop_clear_command(folder);

	if (!TOP_found) {
	    DPRINT(Debug,8,(&Debug, "Disabling (possible) skipping mode\n"));
	    folder -> p->a.pop_mbx.pop_flags    |= POP_disable_skip;
	} else {
	    folder -> p->a.pop_mbx.pop_flags    &= ~POP_disable_skip;
	    if (pop_max_dl_size >= 0) {
		DPRINT(Debug,8,(&Debug, 
				"Skipping mode available (limit %d)\n",
				pop_max_dl_size
				));
	    }
	}

#ifdef USE_DLOPEN
	DPRINT(Debug,13,(&Debug,
			 "%d libraries available\n",pop_capa_libcount));
	if (!handle_pop_capa_libs(folder,&pop_capa_libs,
				  &pop_capa_libcount,
				  &phase,
				  &POP_FUNCTIONS)) {
	    /* Something gone wrong ... */

	    status = 0;
	    goto clean;	    	    
	}

	switch(phase) {
	case capa_prelogin_again:
	    goto retry_prelogin_capa;
	case capa_logged:
	    goto is_already_logged;
	}

#endif
    } else {
	pop_clear_command(folder);
    }

    StreamInfo(folder -> p->a.pop_mbx.C.stream,SS_ssf,&bits,NULL);
    

    data = elm_message(FRM("USER %s"),
		       folder -> p->a.pop_mbx.C.username);
    pop_push_command(folder,data,1);
    free(data);
    
    if (bits < 2)
	lib_transient(CATGETS(elm_msg_cat, MeSet,MePOPLoginClear,
			      "POP login to %s as %s ... (clear text)"),
		      folder -> p->a.pop_mbx.C.host,
		      folder -> p->a.pop_mbx.C.username);
    else
	lib_transient(CATGETS(elm_msg_cat, MeSet,MePOPLogin,
			      "POP login to %s as %s ..."),
		      folder -> p->a.pop_mbx.C.host,
		      folder -> p->a.pop_mbx.C.username);

    if (!pop_command_ok(folder)) {
	/* Server was given non-OK response to USER command ... */	    
	status = 0;
	goto clean;	    
    }
    pop_clear_command(folder);
    
    data1 = lib_prompt(1,CATGETS(elm_msg_cat, MeSet,MeLoginPasswd,
				 "Password for %s@%s:"),
		       folder -> p->a.pop_mbx.C.username,
		       folder -> p->a.pop_mbx.C.host);
    if (!data1) {
	status = 0;
	goto clean;	    
    }
    data = elm_message(FRM("PASS %s"),data1);
    pop_push_command(folder,data,1);
    free(data);
    free(data1);
    if (!pop_command_ok(folder)) {
	/* Server was given non-OK response to PASS command ... */	    
	status = 0;
	goto clean;	    
    }

#ifdef USE_DLOPEN
 is_already_logged:
#endif

    status = 1;

    lib_transient(CATGETS(elm_msg_cat, MeSet,MePOPLoginOK,
			  "POP login to %s as %s ... OK"),
		  folder -> p->a.pop_mbx.C.host,
		  folder -> p->a.pop_mbx.C.username);
    
 clean:
    pop_clear_command(folder);

    if (!status) {
	if (folder->p->a.pop_mbx.C.stream) {
	    DPRINT(Debug,12,(&Debug,
			     "pop_open_connection: Closing stream\n"));
	    FreeStreamStack( &(folder->p->a.pop_mbx.C.stream));
	}
    }
    DPRINT(Debug,12,(&Debug,
		     "pop_open_connection=%d\n",
		     status));
    return status;
}

static int mbx_sessionlock_pop P_((struct folder_info *folder,
				   enum sessionlock_mode mode));

static int mbx_sessionlock_pop(folder,mode)
     struct folder_info *folder;
     enum sessionlock_mode mode;
{
    int status = 0;

    DPRINT(Debug,11,(&Debug,
		     "mbx_sessionlock_pop: folder=%p (%s)\n",
		     folder,folder->cur_folder_sys));

    if (mode == SESSIONLOCK_REOPEN) {
	if (NULL != folder -> p->a.pop_mbx.C.stream ) {
	    if(pop_push_command(folder,"QUIT",1)) {
		pop_command_ok(folder);
		pop_clear_command(folder);
	    }
	    
	    DPRINT(Debug,12,(&Debug,
			     "mbx_sessionlock_pop: Closing stream\n"));
	    
	    FreeStreamStack( &(folder -> p->a.pop_mbx.C.stream));
	}		
    }

    if (NULL == folder->p->a.pop_mbx.C.stream ||
	folder -> p->a.pop_mbx.pop_state  == POP_not_logged) {
	if (!pop_open_connection(folder)) {
	    status = 0;
	    goto clean;
	}
    }
    
    switch(mode) {
	int need_reopen;
    case SESSIONLOCK_NONE:
	status = 1;
	goto clean;
    case SESSIONLOCK_TRUNCATE:
	need_reopen = 1;
#ifdef FTRUNCATE  
	if (folder->p->fh_temp) {
	    rewind(folder->p->fh_temp);
	    if (0 == ftruncate(fileno(folder->p->fh_temp),0)) {
		need_reopen = 0;
		DPRINT(Debug,10,(&Debug,
				 "truncate_tempfolder: tempfile %s truncated (not recreated)\n",
				 folder->cur_tempfolder));
	    }
	    else 
		lib_error(CATGETS(elm_msg_cat, ElmSet, 
				  ElmSorryCantTruncateTemp,
				  "Sorry, can't truncate the temp file %s [%s]!"),
			  folder -> cur_tempfolder, error_description(errno));
	    /* IF FAIL REOPEN IT INSTEAD */
	}
#endif
	if (!need_reopen)
	    break;  /* DONE */
	if (folder->p->fh_temp) {
	    fclose (folder->p->fh_temp);
	    folder->p->fh_temp = NULL;
	}

	if (0 != unlink(folder -> cur_tempfolder)) {
	    int err = errno;
	    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmSorryCantTruncateTemp,
			      "Sorry, can't truncate the temp file %s [%s]!"),
		      folder -> cur_tempfolder, error_description(err));    
	    status = 0;
	    goto clean;
	}
	goto create_it;
    case   SESSIONLOCK_REOPEN:
	if (folder -> p->fh_temp) {
	    int temp_handle;
	    fclose(folder -> p->fh_temp);
	    folder -> p->fh_temp = NULL;

	    if ( (temp_handle = open(folder-> cur_tempfolder, 
				     O_RDWR,
				     0600)) == -1 ||
		 NULL == (folder -> p->fh_temp = fdopen(temp_handle,"w+"))) {
		int err = errno;
		
		lib_error(CATGETS(elm_msg_cat, ElmSet, ElmSorryCantReopenTemp,
				  "Sorry, can't reopen the temp file %s [%s]!"),
			  folder -> cur_tempfolder, error_description(err));  
		
		if (-1 != temp_handle)
		    close(temp_handle);
  
		status = 0;
		goto clean;
	    }

	    break;
	}
	/* FALLTHRU */
    case SESSIONLOCK_NORMAL:
    case SESSIONLOCK_CHECK:
    create_it:
	if (!folder -> p->fh_temp) {
	    int temp_handle;

	    /* If we are changing files and we are changing to a spool file,
	     * make sure there isn't a temp file for it, because if
	     * there is, someone else is using ELM to read the new file,
	     * and we don't want to be reading it at the same time.
	     */
    
	    if ( (temp_handle = open(folder-> cur_tempfolder, 
				     O_RDWR|O_CREAT|O_EXCL,
				     0600)) == -1) {
		int err = errno;
		
		if (err == EEXIST) {
		    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmAlreadyRunningX,
				      "You seem to have ELM already reading this mail! You may not have two copies of\n\
ELM running simultaneously. If this is in error, then you'll need to remove \n\
the following file: %s"),
			      folder -> cur_tempfolder);
		    wait_for_timeout(5 + 6 * sleepmsg);
		    status = 0;
		    goto clean;	    
		}
	    }
	    if (-1 == temp_handle || 
		NULL == (folder -> p->fh_temp = fdopen(temp_handle,"w+"))) {
		int err = errno;
		lib_error(CATGETS(elm_msg_cat, ElmSet, ElmFailedCreateTmpFolder,
				  "Failed to create %.50s: %.60s"),
			  folder -> cur_tempfolder,
			  error_description(err));
		lib_error(CATGETS(elm_msg_cat, ElmSet, ElmIGiveUp,
				  "Ahhhh... I give up."));
		
		if (-1 != temp_handle)
		    close(temp_handle);
		
		status = 0;
		goto clean;	    
	    }
	    
	    DPRINT(Debug,1,(&Debug,
			    "Creating tempfile (%s).\n",
			    folder -> cur_tempfolder));
	    
	    elm_chown(folder->cur_tempfolder, userid, groupid);
	    chmod(folder -> cur_tempfolder, 0700);	
	    /* shut off file for other people! */	    
	}
	break;
    }
    status = 1;
    
 clean:
    DPRINT(Debug,11,(&Debug,
		"mbx_sessionlock_pop=%d\n",
		status));
    return status;
}

static void mbx_flush_pop P_((struct folder_info *folder));
static void mbx_flush_pop(folder) 
     struct folder_info *folder;
{
    DPRINT(Debug,11,(&Debug,
		     "mbx_flush_pop: folder=%p (%s)\n",
		     folder,folder->cur_folder_sys));
    mbx_flush_temp(folder);
}

static int mbx_ferror_pop P_((struct folder_info *folder, int clean));
static int mbx_ferror_pop(folder,clean) 
     struct folder_info *folder; 
     int clean; 
{
    int status = 0;

    DPRINT(Debug,11,(&Debug,
		     "mbx_ferror_pop: folder=%p (%s)\n",
		     folder,folder->cur_folder_sys));
    
    if (POP_error == folder->p->a.pop_mbx.pop_state)
	status = 1;

    if (folder->p->fh_temp) {
	if (ferror(folder->p->fh_temp))
	    status = 1;
	if (clean)
	    clearerr(folder->p->fh_temp);
    }

    DPRINT(Debug,11,(&Debug,
		     "mbx_ferror_pop=%d\n",status));
    return status;
}

static int pop_got_line P_((struct folder_info *folder,
			    READ_STATE read_state_ptr,
			    char **buffer, int *len));

static int mbx_prepare_read_pop P_((struct folder_info *folder,
				    enum prepare_mode mode,
				    READ_STATE read_state_ptr));
static int mbx_prepare_read_pop(folder,mode,read_state_ptr)
     struct folder_info *folder;
     enum prepare_mode mode;
     READ_STATE read_state_ptr;
{
    int status = 0, r, tmp, i;
    int add_new_only = PREPARE_NEW_ONLY == mode ||
                       PREPARE_NEW_ONLY_NOLOCK == mode ||
	               PREPARE_ACCESS  == mode;

    DPRINT(Debug,11,(&Debug,
		     "mbx_prepare_read_pop: folder=%p (%s)\n",
		     folder,folder->cur_folder_sys));
    DPRINT(Debug,11,(&Debug,
		     "                    : read_state_ptr=%p\n",
		     read_state_ptr));

    if (add_new_only) {
	/* start from unread messages */
	read_state_ptr->a.pop_mbx.msg_num = folder->p->a.pop_mbx.stat_count;
	if (!folder->p->fh_temp) {
	    DPRINT(Debug,11,(&Debug,
			     "mbx_prepare_read_pop -- NO temp file %s\n",
			     folder->cur_tempfolder));
	} else if (fseek(folder -> p->fh_temp, 
			 folder -> mailfile_size, SEEK_SET) == -1) {
	    int err = errno;
	    
	    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmCouldntSeekTempEnd,
			      "\nCouldn't fseek to end of temp mbox.\n"));
	    lib_error(FRM("** %s. **\n"), error_description(err));
	    DPRINT(Debug,1,(&Debug,
			    "Error: Couldn't fseek to end of reopened temp mbox.  errno %s (%s)\n",
			    error_description(err), "mbx_prepare_read_spool"));
	    status = 0;
	    goto clean;	    
	}
	read_state_ptr->fbytes = folder-> mailfile_size;  /* start correctly */
    } else { 
	/* start from beginning */
	read_state_ptr->a.pop_mbx.msg_num = 1;
    }

    if (!pop_push_command(folder,"STAT",1)) {
	status = 0;
	goto clean;
    }
    if (!pop_command_ok(folder)) {
	status = 0;
	goto clean;
    }
    tmp = folder -> p->a.pop_mbx.stat_size;
    if (2 != (r = sscanf(folder -> p->a.pop_mbx.command_status,"%*s %i %i",
		     & folder -> p->a.pop_mbx.stat_count,
		     & folder -> p->a.pop_mbx.stat_size))) {

	DPRINT(Debug,11,(&Debug,
			 "mbx_prepare_read_pop: Failed to parse STAT output (r = %d  != 2)\n",
			 r));
	lib_error(CATGETS(elm_msg_cat, MeSet,MeCantParseSTAT,
			  "Can't parse STAT output: %s"),
		  folder -> p->a.pop_mbx.command_status);
	status = 0;
	goto clean;
    }
    /* Estimate new size for percent display */
    folder -> mailfile_size += folder -> p->a.pop_mbx.stat_size - tmp;
    status = 1;

    /* Try UIDL command */
    pop_clear_command(folder);
    DPRINT(Debug,11,(&Debug,
		     "   -- previous uidl count=%d, vector=%p\n",
		     read_state_ptr->a.pop_mbx.uidl_count,
		     read_state_ptr->a.pop_mbx.uidl_vector));
    for (i = 0; i < read_state_ptr->a.pop_mbx.uidl_count; i++)
	if (read_state_ptr->a.pop_mbx.uidl_vector[i]) {
	    DPRINT(Debug,11,(&Debug,
			     "   ... was vector[%d]=%p (%s)\n",
			     i,
			     read_state_ptr->a.pop_mbx.uidl_vector[i],
			     read_state_ptr->a.pop_mbx.uidl_vector[i]));
	    free(read_state_ptr->a.pop_mbx.uidl_vector[i]);
	    read_state_ptr->a.pop_mbx.uidl_vector[i] = NULL;
	}

    if (!pop_push_command(folder,"UIDL",0)) {
	status = 0;
	goto clean;
    }
    if (pop_command_ok(folder)) {      
	char * buffer = NULL;
	int buffer_len = 0;
	char * str;

	/* If command is unimplemeted that is OK ... */

	read_state_ptr->a.pop_mbx.uidl_count = 
	    folder -> p->a.pop_mbx.stat_count;
	read_state_ptr->a.pop_mbx.uidl_vector = 
	    safe_realloc(read_state_ptr->a.pop_mbx.uidl_vector,
			 read_state_ptr->a.pop_mbx.uidl_count * 
			 sizeof (char *));
	for (i = 0; i < read_state_ptr->a.pop_mbx.uidl_count; i++)
	    read_state_ptr->a.pop_mbx.uidl_vector[i] = NULL;

	read_state_ptr->a.pop_mbx.data_idx = 0;
	while (pop_got_line(folder,read_state_ptr,&buffer,&buffer_len)) {
	    int l;
	    char *ptr;
	    
	    read_state_ptr->a.pop_mbx.data_idx += buffer_len;
	    
	    if (buffer_len > 0 && '\n' == buffer[buffer_len-1]) {
		buffer[buffer_len-1] = '\0';
		buffer_len--;
		if (buffer_len > 0 && '\r' == buffer[buffer_len-1]) {
		    buffer[buffer_len-1] = '\0';
		    buffer_len--;
		}
	    }
	    /* pop_got_line malloces also space for \0 */
	    buffer[buffer_len] = '\0';

	    l = strtol(buffer,&ptr,10);
	    if (ptr == buffer || ' ' != *ptr ||
		l < 1 || l > read_state_ptr->a.pop_mbx.uidl_count) {
		DPRINT(Debug,1,(&Debug,
				"Bad UIDL line: %s\n",buffer));
	    } else {
		read_state_ptr->a.pop_mbx.uidl_vector[l-1]
		    = safe_strdup(ptr+1);
		DPRINT(Debug,18,(&Debug,
				 "UIDL(%ld) = %s\n",
				 l,
				 read_state_ptr->a.pop_mbx.uidl_vector[l-1]));
		/* Prefill */
		give_uidl(& (folder -> p->a.pop_mbx.uidl_root),
			  read_state_ptr->a.pop_mbx.uidl_vector[l-1]);
	    }

	    if (buffer) {
		free(buffer);
		buffer = NULL;
	    }
	}
	if (buffer) {
	    free(buffer);
	    buffer = NULL;
	}


	/* give_dt_estr_as_str adds / to end */
	str = give_dt_estr_as_str(&folders_e,"maildir");

	if (str) {
	    char * fname = elm_message(FRM("%s.%s"),str,folder->cur_folder_sys);
	    update_uidls(fname,&(folder -> p->a.pop_mbx.uidl_root));
	    free(fname);
	}
    }

    
    /* Try LIST command */
    pop_clear_command(folder);
    DPRINT(Debug,11,(&Debug,
		     "   -- previous rfc822_size count=%d, vector=%p\n",
		     read_state_ptr->a.pop_mbx.rfc822_size_count,
		     read_state_ptr->a.pop_mbx.rfc822_size_vector));
    for (i = 0; i < read_state_ptr->a.pop_mbx.rfc822_size_count; i++)
	if (read_state_ptr->a.pop_mbx.rfc822_size_vector[i]) {
	    DPRINT(Debug,11,(&Debug,
			     "   ... was vector[%d]=%d\n",
			     i,
			     read_state_ptr->a.pop_mbx.rfc822_size_vector[i]));
	    read_state_ptr->a.pop_mbx.rfc822_size_vector[i] = -1;
	}

    if (pop_max_dl_size >= 0 &&
	! (folder -> p->a.pop_mbx.pop_flags & POP_disable_skip)
	) {
	if (!pop_push_command(folder,"LIST",0)) {
	    status = 0;
	    goto clean;
	}
	if (pop_command_ok(folder)) {
	    char * buffer = NULL;
	    int buffer_len = 0;

	    read_state_ptr->a.pop_mbx.rfc822_size_count = 
		folder -> p->a.pop_mbx.stat_count;
	    read_state_ptr->a.pop_mbx.rfc822_size_vector = 
		safe_realloc(read_state_ptr->a.pop_mbx.rfc822_size_vector,
			     read_state_ptr->a.pop_mbx.rfc822_size_count * 
			     sizeof (int));
	    for (i = 0; i < read_state_ptr->a.pop_mbx.rfc822_size_count; i++)
		read_state_ptr->a.pop_mbx.rfc822_size_vector[i] = -1;
	    
	    read_state_ptr->a.pop_mbx.data_idx = 0;
	    while (pop_got_line(folder,read_state_ptr,&buffer,&buffer_len)) {
		int l;
		char *ptr;
		
		read_state_ptr->a.pop_mbx.data_idx += buffer_len;
		
		if (buffer_len > 0 && '\n' == buffer[buffer_len-1]) {
		    buffer[buffer_len-1] = '\0';
		    buffer_len--;
		    if (buffer_len > 0 && '\r' == buffer[buffer_len-1]) {
			buffer[buffer_len-1] = '\0';
			buffer_len--;
		    }
		}
		/* pop_got_line malloces also space for \0 */
		buffer[buffer_len] = '\0';
		
		l = strtol(buffer,&ptr,10);
		if (ptr == buffer || ' ' != *ptr ||
		    l < 1 || l > read_state_ptr->a.pop_mbx.rfc822_size_count) {
		    DPRINT(Debug,1,(&Debug,
				    "Bad LIST line: %s\n",buffer));
		} else {
		    read_state_ptr->a.pop_mbx.rfc822_size_vector[l-1]
			= atoi(ptr+1);
		    DPRINT(Debug,18,(&Debug,
				     "LIST(%ld) = %d\n",
				     l,
				     read_state_ptr->a.pop_mbx.rfc822_size_vector[l-1]));
		}
		
		if (buffer) {
		    free(buffer);
		    buffer = NULL;
		}
	    }
	    if (buffer) {
		free(buffer);
		buffer = NULL;
	    }
	}
    }
 
 clean:
    pop_clear_command(folder);
    DPRINT(Debug,11,(&Debug,
		     "mbx_prepare_read_pop=%d\n",status));
    return status;
}

static int mbx_end_read_pop P_((struct folder_info *folder,
				READ_STATE read_state_ptr,
				int silent));
static int mbx_end_read_pop(folder,read_state_ptr,silent)
     struct folder_info *folder;
     READ_STATE read_state_ptr;
     int silent;
{
    int status = 0;

    DPRINT(Debug,11,(&Debug,
		     "mbx_end_read_pop: folder=%p (%s)\n",
		     folder,folder->cur_folder_sys));
    DPRINT(Debug,11,(&Debug,
		     "                : read_state_ptr=%p\n",
		     read_state_ptr));

    
    if (PREPARE_ACCESS != read_state_ptr->mode) {
	if (folder -> p->a.pop_mbx.stat_count > 
	    read_state_ptr->a.pop_mbx.msg_num) {
	    if (!silent) {
		lib_error(CATGETS(elm_msg_cat, MeSet,MeMessagesNotRead,
				  "Messages not read; current=%d, count=%d"),
			  read_state_ptr->a.pop_mbx.msg_num,
			  folder -> p->a.pop_mbx.stat_count);
	    }
	    status = 0;
	    goto clean;
	}
    }

    if (!folder->p->fh_temp) {
	DPRINT(Debug,11,(&Debug,
			 "mbx_end_read_pop=1 -- NO temp file %s\n",
			 folder->cur_tempfolder));
	return 1;
    }

    if ((ferror(folder->p->fh_temp)) || 
	(fflush(folder->p->fh_temp) == EOF)) {
	if (!silent) {
	    int err = errno;
	    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmFlushOnTempFailed,
			      "Flush on temp file %s failed: %s"), 
		      folder -> cur_tempfolder,
		      error_description(err));
	}
	DPRINT(Debug,1,(&Debug,
			"Can't flush on temp file %s!!\n",
			folder -> cur_tempfolder));
	status = 0;
	goto clean;
    }

    /* Use tempfile size as folder size */
    folder-> mailfile_size = file_bytes(folder-> cur_tempfolder);

    rewind(folder->p->fh_temp);
    status = 1;

 clean:
    DPRINT(Debug,11,(&Debug,
		     "mbx_end_read_pop=%d\n",status));
    return status;
}

static void info_zero_pop_mbx P_((struct mbx_hdr_info *info));
static void info_zero_pop_mbx(info)
     struct mbx_hdr_info *info;
{
    info->a.pop_mbx.msg_num = 0;
    info->a.pop_mbx.uidl    = NULL;
}

static void info_free_pop_mbx P_((struct mbx_hdr_info *info));
static void info_free_pop_mbx(info)
     struct mbx_hdr_info *info;
{
    info->a.pop_mbx.msg_num = 0;
    if (info->a.pop_mbx.uidl) {
	free (info->a.pop_mbx.uidl);
	info->a.pop_mbx.uidl = NULL;
    }
}

static void info_pop_mbx_status P_((struct mbx_hdr_info *info,
				    char status_buffer[3]));
static void info_pop_mbx_status(info,status_buffer)
     struct mbx_hdr_info *info;
     char status_buffer[3];
{

}


static struct info_type pop_mbx_info = { info_zero_pop_mbx, 
					 info_free_pop_mbx,
					 info_pop_mbx_status
};

static int mbx_copy_envelope_pop P_((struct folder_info *folder,
				     READ_STATE read_state_ptr,
				     struct header_rec *entry,
				     int force));
static int mbx_copy_envelope_pop(folder,read_state_ptr,entry,force)
     struct folder_info *folder;
     READ_STATE read_state_ptr;
     struct header_rec *entry;
     int force;
{
    int status = 0,i;
    char * data = NULL;
    char * tmp;
    int l;

    DPRINT(Debug,11,(&Debug,
		     "mbx_copy_envelope_pop: folder=%p (%s)\n",
		     folder,folder->cur_folder_sys));
    DPRINT(Debug,11,(&Debug,
		     "                     : read_state_ptr=%p\n",
		     read_state_ptr));

    if (PREPARE_ACCESS == read_state_ptr->mode) {
	if(! entry->mbx_info ||
	   &pop_mbx_info != entry->mbx_info->type_code) {
	    DPRINT(Debug,11,(&Debug, 
			     "                     : Unable to access message\n"));
	    status = 0;
	    goto clean;	    
	}

	/* Hack It */
	read_state_ptr->a.pop_mbx.msg_num = entry->mbx_info->a.pop_mbx.msg_num;
	
    } else {
	if (folder -> p->a.pop_mbx.stat_count < 
	    read_state_ptr->a.pop_mbx.msg_num) {
	    DPRINT(Debug,11,(&Debug,
			     "                     : End of messages\n"));
	    status = 0;
	    goto clean;
	}

	/* Do not reset status if PREPARE_ACCESS */
	entry->status = VISIBLE | UNREAD | NEW;
	/* If UIDLs are supported we save them and use that
	   to determine if message is read before or new
	*/

    }

    read_state_ptr -> linecounter   = 0;      /* Linecounter of current
					         message */
    read_state_ptr -> skipping      = 0;      /* not skipping */
    entry->offset = read_state_ptr -> fbytes; /* Offset of current message */
   
    DPRINT(Debug,12,(&Debug,
		     "                     : temp file offset %ld\n",
		     entry->offset));


    i = read_state_ptr->a.pop_mbx.msg_num ;
    if (pop_max_dl_size >= 0 &&
	i > 0 && i <= read_state_ptr->a.pop_mbx.rfc822_size_count &&
	read_state_ptr->a.pop_mbx.rfc822_size_vector[i-1] > pop_max_dl_size &&
	!force &&
	! (folder -> p->a.pop_mbx.pop_flags & POP_disable_skip)
	) {
	/* Need skip this */


	DPRINT(Debug,8,(&Debug,
                        "mbx_copy_envelope_pop: Size %d -- skipping this message (only read headers)\n",
			read_state_ptr->a.pop_mbx.rfc822_size_vector[i-1]));


	data = elm_message(FRM("TOP %d 0"),read_state_ptr->a.pop_mbx.msg_num);
	if (!pop_push_command(folder,data,0)) {
	    status = 0;
	    goto clean;
	}
	if (!pop_command_ok(folder)) {
	    free(data);
	    data = NULL;

	    DPRINT(Debug,8,(&Debug,
			    "mbx_copy_envelope_pop: TOP command failed -- can't skip message\n"));

	    goto TOP_failed;
	}
	
	free(data);
	data = NULL;


	read_state_ptr-> skipping = 1;
	entry->offset             = -1;

	read_state_ptr->skip_count++;
	read_state_ptr->skip_bytes += 
	    read_state_ptr->a.pop_mbx.rfc822_size_vector[i-1];

	/* Hack -- no line numbers available */
	entry->lines = -1;

    } else {
    TOP_failed:

	data = elm_message(FRM("RETR %d"),read_state_ptr->a.pop_mbx.msg_num);
	if (!pop_push_command(folder,data,0)) {
	    status = 0;
	    goto clean;
	}
	if (!pop_command_ok(folder)) {
	    status = 0;
	    goto clean;
	}

	free(data);
	data = NULL;
        
	/* Very lazy method to get something to put "From " -separator */
	if (0 == memcmp("Return-Path: ",
			folder->p->a.pop_mbx.command_data,13)) {
	    int i;
	    for (i=13; i < folder->p->a.pop_mbx.data_len; i++) {
		if ('\r' == folder->p->a.pop_mbx.command_data[i] ||
		    '\n' == folder->p->a.pop_mbx.command_data[i])
		    break;
	    }
	    data = safe_malloc(i-12);
	    memcpy(data,folder->p->a.pop_mbx.command_data+13,i-13);
	    data[i-13]='\0';
	} else
	    data = safe_strdup("nobody@localhost");
	
	/* Make some kind mailbox separator line ... */
	time(&entry->received_time);
	entry->env_from[0] = '\0'; /* Let parse it later */
	tmp = elm_message(FRM("From %s %s"),data,ctime(&entry->received_time));
	free(data);
	data = tmp;    
	l = strlen(data);
	/* ctime() will include terminating \n
	 * -- notice that on mailbox separator line we don't use \r\n
	 * that even when on 'binary' mail message lines are terminated with 
	 * \r\n
	 */
	if (!mbx_copy_line_to_temp(folder,data,l)) {
	    status = 0;
	    goto clean;
	}
	read_state_ptr -> fbytes += l;
	read_state_ptr -> linecounter++;
    }

    read_state_ptr->a.pop_mbx.data_idx = 0;
    change_rec_mbx_info(entry,&pop_mbx_info);
    entry->mbx_info->a.pop_mbx.msg_num = read_state_ptr->a.pop_mbx.msg_num;

    i = read_state_ptr->a.pop_mbx.msg_num ;
    if (i > 0 && i <= read_state_ptr->a.pop_mbx.uidl_count &&
	read_state_ptr->a.pop_mbx.uidl_vector[i-1]) {
	struct uidl_entry * A;
	
	entry->mbx_info->a.pop_mbx.uidl =
	    strmcpy(entry->mbx_info->a.pop_mbx.uidl,
		    read_state_ptr->a.pop_mbx.uidl_vector[i-1]);
	
	DPRINT(Debug,11,(&Debug,
			     "                     : (uidl)=%s\n",
			 entry->mbx_info->a.pop_mbx.uidl));
	A = give_uidl(& (folder -> p->a.pop_mbx.uidl_root),
		      entry->mbx_info->a.pop_mbx.uidl);
	if (PREPARE_ACCESS == read_state_ptr->mode) {
	    A->status = entry->status;
	} else {
	    entry->status = VISIBLE | A->status;
	}
    }

    status = 1;

 clean:
    if (data) 
	free(data);
    data = NULL;
    DPRINT(Debug,11,(&Debug,
		     "mbx_copy_envelope_pop=%d\n",
		     status));
    return status;
}

static CONST char * mbx_is_forwarded_pop P_((struct folder_info *folder,
					      READ_STATE read_state_ptr));
static CONST char * mbx_is_forwarded_pop(folder,read_state_ptr)
     struct folder_info *folder;
     READ_STATE read_state_ptr;
{
    DPRINT(Debug,11,(&Debug,
		     "mbx_is_forwarded_pop: folder=%p (%s)\n",
		     folder,folder->cur_folder_sys));
    DPRINT(Debug,11,(&Debug,
		     "                     : read_state_ptr=%p\n",
		     read_state_ptr));

    DPRINT(Debug,11,(&Debug,
		     "mbx_is_forwarded_pop=NULL\n"));
    return NULL;
}

static int pop_got_line(folder,read_state_ptr,buffer,len)
     struct folder_info *folder;
     READ_STATE read_state_ptr;
     char **buffer; 
     int *len;
{
    int i;

    if (read_state_ptr->a.pop_mbx.data_idx >= 
	folder->p->a.pop_mbx.data_len) {
	DPRINT(Debug,65,(&Debug,
			 "pop_got_line=0\n"));
	return 0;
    }

    for (i = read_state_ptr->a.pop_mbx.data_idx;
	 i < folder->p->a.pop_mbx.data_len-1; i++) {
	if ('\r' == folder->p->a.pop_mbx.command_data[i] &&
	    '\n' == folder->p->a.pop_mbx.command_data[i+1]) {
	    i += 2;
	    goto found;
	}
    }
    i = folder->p->a.pop_mbx.data_len;
    DPRINT(Debug,10,(&Debug,"pop_got_line: EOLN NOT FOUND\n"));


 found:
    *len = i - read_state_ptr->a.pop_mbx.data_idx;
    *buffer = safe_malloc(*len+1);
    memcpy(*buffer,
	   folder->p->a.pop_mbx.command_data +
	   read_state_ptr->a.pop_mbx.data_idx,
	   *len);

    DPRINT(Debug,65,(&Debug,
		"pop_got_line=1: len=%d, buffer=%.*s",
		*len,*len,(*buffer) ? (*buffer) : ""));
    if (*len < 1 || !(*buffer) || (*buffer)[*len -1] != '\n') {
	DPRINT(Debug,65,(&Debug,
			 "\npop_got_line: NO NEWLINE\n"));
    } 
    return 1;
    
}

static void pop_accept_line P_((struct folder_info *folder,
					 READ_STATE read_state_ptr,
					 char **buffer, int *len));
static void pop_accept_line(folder,read_state_ptr,buffer,len)
     struct folder_info *folder;
     READ_STATE read_state_ptr;
     char **buffer; 
     int *len;
{
    if (!*buffer)
	return;

    if (*len < 1 || (*buffer)[*len -1] != '\n') {
    } else {
	read_state_ptr -> linecounter++;
    }
    if (! read_state_ptr -> skipping)
	read_state_ptr -> fbytes             += *len;
    read_state_ptr->a.pop_mbx.data_idx   += *len;

    free(*buffer);
    *buffer = NULL;
    *len    = 0;
}

static int mbx_copy_header_pop P_((struct folder_info *folder,
				   READ_STATE read_state_ptr,
				   char **buffer, int *len));
static int mbx_copy_header_pop(folder,read_state_ptr,buffer,len)
     struct folder_info *folder;
     READ_STATE read_state_ptr;
     char **buffer; 
     int *len;
{
    int status = 0;
    char *buffer1 = NULL;
    int len1,i;

    DPRINT(Debug,11,(&Debug,
		     "mbx_copy_header_pop: folder=%p (%s)\n",
		     folder,folder->cur_folder_sys));
    DPRINT(Debug,11,(&Debug,
		     "                   : read_state_ptr=%p\n",
		     read_state_ptr));
	
 hide:
    if (!pop_got_line(folder,read_state_ptr,&buffer1,&len1)) {
	status = 0;
	goto clean;
    }

    if ((len1 == 1 && 0 == memcmp(buffer1,"\n",len1)) ||
	(len1 == 2 && 0 == memcmp(buffer1,"\r\n",len1))) {

	if (! read_state_ptr -> skipping) {
	    /* End of headers */
	    if (!mbx_copy_line_to_temp(folder,buffer1,len1)) {
		status = 0;
		goto clean;
	    }
	}
	pop_accept_line(folder,read_state_ptr,
			&buffer1,&len1);
	status = 0;
	goto clean;
    }
    if (NULL == memchr(buffer1,':',len1)) {
	/* End of headers -- bad header */
	status = 0;
	goto clean;
    }
   
    do {
	append_buffer(buffer,len,buffer1,len1);
	if (! read_state_ptr -> skipping) {
	    if (!mbx_copy_line_to_temp(folder,buffer1,len1)) {
		status = 0;
		goto clean;
	    }
	}
	pop_accept_line(folder,read_state_ptr,
			&buffer1,&len1);
	if (!pop_got_line(folder,read_state_ptr,
			  &buffer1,&len1)) {
	    break;
	}
    } while (len1 > 0 && whitespace(buffer1[0]));

    i = read_state_ptr->a.pop_mbx.msg_num ;
    if (*buffer && *len > 6 &&
	header_cmp(*buffer, "Status", NULL) &&
	i > 0 && i <= read_state_ptr->a.pop_mbx.uidl_count &&
	read_state_ptr->a.pop_mbx.uidl_vector[i-1]) {
	DPRINT(Debug,11,(&Debug,
		    "                   : Hiding header %s",*buffer));
	DPRINT(Debug,11,(&Debug,
			 "                   : (uidl)=%s\n",
			 read_state_ptr->a.pop_mbx.uidl_vector[i-1]));
	free(*buffer);
	*buffer = NULL;
	*len = 0;
	goto hide;
    }
	
    status = 1;
   
 clean:
    if (buffer1) {
	free(buffer1);
	buffer1 = NULL;
    }

    if (!status) {
	if (*buffer) {
	    free(*buffer);
	    *buffer = NULL;
	}
    }
    DPRINT(Debug,50,(&Debug,
		     "mbx_copy_header_pop=%d | len=%d, buffer=%s",
		     status,*len,
		     *buffer ? *buffer : "NULL"));
    if (*len < 1 || !*buffer || (*buffer)[*len -1] != '\n') {
	DPRINT(Debug,50,(&Debug,
			 "\nmbx_copy_header_pop <- NO NEWLINE\n"));
    }
    return status;
}

static int mbx_copy_body_pop P_((struct folder_info *folder,
				     READ_STATE read_state_ptr,
				     char **buffer, int *len,
				     long *content_remaining));

static int mbx_copy_body_pop(folder,read_state_ptr,buffer,len,
				 content_remaining)
     struct folder_info *folder;
     READ_STATE read_state_ptr;
     char **buffer; 
     int *len;
     long *content_remaining;
{ 
    char *buffer1 = NULL;
    int len1;
    int status = 0;

    DPRINT(Debug,11,(&Debug,
		     "mbx_copy_body_pop: folder=%p (%s)\n",
		     folder,folder->cur_folder_sys));
    DPRINT(Debug,11,(&Debug,
		     "                 : read_state_ptr=%p\n",
		     read_state_ptr));

    if (read_state_ptr -> skipping) {
	return 0;
    }

    if (!pop_got_line(folder,read_state_ptr,
		      &buffer1,&len1)) {
	status = 0;
	goto clean;
    }

    append_buffer(buffer,len,buffer1,len1);
    if (!mbx_copy_line_to_temp(folder,buffer1,len1)) {
	status = 0;
	goto clean;
    }
    pop_accept_line(folder,read_state_ptr,
		     &buffer1,&len1);
    status = 1;

 clean:
    if (buffer1) {
	free(buffer1);
	buffer1 = NULL;
    }

    if (!status) {
	if (*buffer) {
	    free(*buffer);
	    *buffer = NULL;
	}
    }

    DPRINT(Debug,50,(&Debug,
		     "mbx_copy_body_pop=%d | len=%d, buffer=%s",
		     status,*len,
		     *buffer ? *buffer : "NULL"));
    if (*len < 1 || !*buffer || (*buffer)[*len -1] != '\n') {
	DPRINT(Debug,50,(&Debug,
			 "\nmbx_copy_body_pop <- NO NEWLINE\n"));
    }
    return status;
}

static int mbx_copy_envelope_end_pop P_((struct folder_info *folder,
					     READ_STATE read_state_ptr));
static int mbx_copy_envelope_end_pop(folder,read_state_ptr)
     struct folder_info *folder;
     READ_STATE read_state_ptr;
{
    int status = 0;
    
    DPRINT(Debug,11,(&Debug,
		     "mbx_copy_envelope_end_pop: folder=%p (%s)\n",
		     folder,folder->cur_folder_sys));
    DPRINT(Debug,11,(&Debug,
		     "                         : read_state_ptr=%p\n",
		     read_state_ptr));

    pop_clear_command(folder);
    read_state_ptr->a.pop_mbx.msg_num++;

    if (! read_state_ptr -> skipping) {
	/* On mailbox format there is empty line (\n\n)
	 * on end of mail...
	 */

	if (!mbx_copy_line_to_temp(folder,"\n",1)) {
	    status = 0;
	    goto clean;
	}
	read_state_ptr -> fbytes ++;    /* Increment position */
    }
    status = 1;

 clean:
    read_state_ptr -> skipping = 0;
    
    DPRINT(Debug,11,(&Debug,
		     "mbx_copy_envelope_end_pop=%d\n",
		     status));
    return status; /* Always end of message */
}

static int mbx_copy_envelope_reset_body_pop P_((struct folder_info *folder,
						READ_STATE read_state_ptr));
static int mbx_copy_envelope_reset_body_pop(folder,read_state_ptr)
     struct folder_info *folder;
     READ_STATE read_state_ptr;
{
    DPRINT(Debug,11,(&Debug,
		     "mbx_copy_envelope_reset_body_pop: folder=%p (%s)\n",
		     folder,folder->cur_folder_sys));
    DPRINT(Debug,11,(&Debug,
		     "                                : read_state_ptr=%p\n",
		     read_state_ptr));
    DPRINT(Debug,11,(&Debug,
		     "mbx_copy_envelope_reset_body_pop=0\n"));
    return 0;
}

static FILE * mbx_pop_to_fd P_((struct folder_info *folder,long offset));
static FILE * mbx_pop_to_fd(folder,offset)
     struct folder_info *folder;
     long offset;
{
    FILE * status = NULL;
    DPRINT(Debug,11,(&Debug,
		     "mbx_pop_to_fd: folder=%p (%s)\n",
		     folder,folder->cur_folder_sys));

    if (offset != -1L &&
	fseek(folder->p->fh_temp, offset , 
	      SEEK_SET) == -1) {
	int err = errno;
	lib_error(CATGETS(elm_msg_cat, ElmSet, 
			  ElmCouldntSeekBytesIntoFolder,
			  "\nCouldn't seek %ld bytes into folder.\n"),
		  offset);		
	lib_error(FRM("** %s. **\n"), error_description(err));
	DPRINT(Debug,1,(&Debug,
			"Error: Couldn't seek folder %s: (offset %ld) Errno %s (%s)\n",
			folder->cur_tempfolder, offset, 
			error_description(err), "mbx_pop_to_fd"));
	status = NULL;
	goto clean;
    }
    status = folder->p->fh_temp;

 clean:
    DPRINT(Debug,11,(&Debug,
		     "mbx_pop_to_fd=%s (%p)\n",
		     status ? "NON-NULL" : "NULL",
		     status));
    return status;
}

static int mbx_new_mail_on_pop P_((struct folder_info *folder,int *bytes));
static int mbx_new_mail_on_pop(folder,bytes)
     struct folder_info *folder;
     int *bytes;
{
    int status = 0;
    int count,size,r;

    DPRINT(Debug,11,(&Debug,
		     "mbx_new_mail_on_pop: folder=%p (%s)\n",
		     folder,folder->cur_folder_sys));

    if (!pop_push_command(folder,"STAT",1)) {
	status = 0;
	goto clean;
    }
    if (!pop_command_ok(folder)) {
	status = 0;
	goto clean;
    }
    if (2 != (r = sscanf(folder -> p->a.pop_mbx.command_status,"%*s %i %i",
			 & count, & size))) {
	DPRINT(Debug,11,(&Debug,
			 "mbx_new_mail_on_pop: Failed to parse STAT output (r = %d  != 2)\n",
			 r));
	lib_error(CATGETS(elm_msg_cat, MeSet,MeCantParseSTAT,
			  "Can't parse STAT output: %s"),
		  folder -> p->a.pop_mbx.command_status);
	status = 0;
	goto clean;
    }
    if (count > folder -> p->a.pop_mbx.stat_count)
	status = 1;
    *bytes = size - folder -> p->a.pop_mbx.stat_size;

 clean:
    pop_clear_command(folder);

    DPRINT(Debug,11,(&Debug,
		     "mbx_new_mail_on_pop=%d (*bytes=%d)\n",
		     status,*bytes));
    return status;
}

static int mbx_consider_remove_pop P_((struct folder_info *folder));
static int mbx_consider_remove_pop(folder)
     struct folder_info *folder;
{
    DPRINT(Debug,11,(&Debug,
		     "mbx_consider_remove_pop: folder=%p (%s)\n",
		     folder,folder->cur_folder_sys));
    DPRINT(Debug,11,(&Debug,
		     "mbx_consider_remove_pop=0\n"));
    return 0;
}

static int mbx_prepare_keep_pop P_((struct folder_info *folder,
				    KEEP_STATE keep_state_ptr));
static int mbx_prepare_keep_pop(folder,keep_state_ptr)
     struct folder_info *folder;
     KEEP_STATE keep_state_ptr;
{
    int status = 1;
 
    DPRINT(Debug,11,(&Debug,
		     "mbx_prepare_keep_pop=%d (dummy): folder=%p (%s)\n",
		     status,folder,folder->cur_folder_sys));
    
    return status;
}

static int mbx_end_keep_pop P_((struct folder_info *folder,
				KEEP_STATE keep_state_ptr));
static int mbx_end_keep_pop(folder,keep_state_ptr)
     struct folder_info *folder;
     KEEP_STATE keep_state_ptr;
{
    char *str;
    int status = 0;

    DPRINT(Debug,11,(&Debug,
		     "mbx_end_keep_pop: folder=%p (%s)\n",
		     folder,folder->cur_folder_sys));

    /* POP mailbox is needed to be closed so that
     * it will enter UPDATE state. sessionlock_folder()
     * will reopen it when needed
     */
    if (!pop_push_command(folder,"QUIT",1)) {
	status = 0;
	pop_clear_command(folder);
	goto clean;
    }
    status = pop_command_ok(folder);
    pop_clear_command(folder);
    if (folder->p->a.pop_mbx.C.stream) {
	DPRINT(Debug,12,(&Debug,
			 "mbx_end_keep_pop: Closing stream\n"));
	
	FreeStreamStack( &(folder -> p->a.pop_mbx.C.stream));
    }

    /* give_dt_estr_as_str adds / to end */
    str = give_dt_estr_as_str(&folders_e,"maildir");
    if (str) {
	char * fname = elm_message(FRM("%s.%s"),str,folder->cur_folder_sys);
	update_uidls(fname,&(folder -> p->a.pop_mbx.uidl_root));
	free(fname);
    }

 clean:
    DPRINT(Debug,11,(&Debug,
		     "mbx_end_keep_pop=%d\n",
		     status));
    return status;
}

static void mbx_mark_keep_pop P_((struct folder_info *folder,
				      KEEP_STATE keep_state_ptr,
				      struct header_rec *entry,
				      int keep));
static void mbx_mark_keep_pop(folder,keep_state_ptr,entry,keep)
     struct folder_info *folder;
     KEEP_STATE keep_state_ptr;
     struct header_rec *entry;
     int keep;
{
    DPRINT(Debug,11,(&Debug,
		     "mbx_mark_keep_pop: folder=%p (%s)\n",
		     folder,folder->cur_folder_sys));

    /* Store read/unread together
     * with UIDL
     */

    if(entry->mbx_info &&
       &pop_mbx_info == entry->mbx_info->type_code) {
	if (entry->mbx_info->a.pop_mbx.uidl) {
	    struct uidl_entry * A;

	    DPRINT(Debug,11,(&Debug,
			     "                     : (uidl)=%s\n",
			     entry->mbx_info->a.pop_mbx.uidl));

	    A = give_uidl(& (folder -> p->a.pop_mbx.uidl_root),
			  entry->mbx_info->a.pop_mbx.uidl);
	    A->status  = entry->status; 
	    A->changed = 1;
	}
	
	if (!keep) { 
	    char * data = NULL;
	    data = elm_message(FRM("DELE %d"),
			       entry->mbx_info->a.pop_mbx.msg_num);
	    if (pop_push_command(folder,data,1))
		pop_command_ok(folder);
	    pop_clear_command(folder);
	}
    }
}

CONST char * mbx_pop_type P_((struct folder_info *folder));
CONST char * mbx_pop_type(folder)
     struct folder_info *folder;
{
    static char *mailbox = NULL;
    if (!mailbox)
	mailbox = catgets(elm_msg_cat, MeSet, MePopMailbox, "POP mailbox");

    DPRINT(Debug,11,(&Debug,
		"mbx_pop_type: folder=%p (%s)\n",
		folder,folder->cur_folder_sys));
    DPRINT(Debug,11,(&Debug,
		     "mbx_pop_type=%s\n",
		     mailbox));
    return mailbox;
}

static int mbx_start_edit_pop P_((struct folder_info *folder, 
				  CONST char **buffer));
static int mbx_start_edit_pop(folder,buffer)
     struct folder_info *folder;
     CONST char **buffer;
{
    DPRINT(Debug,11,(&Debug,
		"mbx_start_edit_pop=0 (dummy): folder=%p (%s)\n",
		folder,folder->cur_folder_sys));
    return 0;
}

static int mbx_end_edit_pop P_((struct folder_info *folder));
static int mbx_end_edit_pop(folder)
     struct folder_info *folder;
{
    DPRINT(Debug,11,(&Debug,
		"mbx_end_edit_pop=0 (dummy): folder=%p (%s)\n",
		folder,folder->cur_folder_sys));
    return 0;
}

static void mbx_zero_rs_fields_pop P_((struct read_folder_state *rs));
static void mbx_zero_rs_fields_pop(rs)
     struct read_folder_state *rs;
{
    DPRINT(Debug,11,(&Debug,
		     "mbx_zero_rs_fields_pop: rs=%p\n",
		     rs));

    rs->a.pop_mbx.msg_num = 0;
    rs->a.pop_mbx.uidl_vector = NULL;
    rs->a.pop_mbx.uidl_count  = 0;
    rs->a.pop_mbx.rfc822_size_vector = NULL;
    rs->a.pop_mbx.rfc822_size_count  = 0;    
}

static void mbx_free_rs_fields_pop P_((struct read_folder_state *rs));
static void mbx_free_rs_fields_pop(rs)
     struct read_folder_state *rs;
{
    DPRINT(Debug,11,(&Debug,
		     "mbx_free_rs_fields_pop: rs=%p\n",
		     rs));

    rs->a.pop_mbx.msg_num = 0;
    if (rs->a.pop_mbx.uidl_vector) {
	int i;
	for (i = 0; i < rs->a.pop_mbx.uidl_count; i++)
	    if (rs->a.pop_mbx.uidl_vector[i]) {
		free(rs->a.pop_mbx.uidl_vector[i]);
		rs->a.pop_mbx.uidl_vector[i] = NULL;
	    }
	free(rs->a.pop_mbx.uidl_vector);
	rs->a.pop_mbx.uidl_vector = NULL;
    }
    rs->a.pop_mbx.uidl_count  = 0;

    if (rs->a.pop_mbx.rfc822_size_vector) {
	int i;
	for (i = 0; i < rs->a.pop_mbx.rfc822_size_count; i++)	    	    
	    rs->a.pop_mbx.rfc822_size_vector[i] = -1;          
	free(rs->a.pop_mbx.rfc822_size_vector);
	rs->a.pop_mbx.rfc822_size_vector = NULL;
    }
    rs->a.pop_mbx.rfc822_size_count  = 0;
}


static void mbx_zero_ks_fields_pop P_((struct keep_folder_state *rs));
static void mbx_zero_ks_fields_pop(rs)
     struct keep_folder_state *rs;
{
    DPRINT(Debug,11,(&Debug,
		     "mbx_zero_ks_fields_pop: rs=%p\n",
		     rs));
}

static void mbx_free_ks_fields_pop P_((struct keep_folder_state *rs));
static void mbx_free_ks_fields_pop(rs)
     struct keep_folder_state *rs;
{
    DPRINT(Debug,11,(&Debug,
		     "mbx_free_ks_fields_pop: rs=%p\n",
		     rs));
}

static int mbx_get_pop_mode P_((struct folder_info *folder));
static int mbx_get_pop_mode(folder)
     struct folder_info *folder;
{
    DPRINT(Debug,11,(&Debug,
		     "mbx_get_pop_mode=FOLDER_MBOX: folder=%p (%s)\n",
		     folder,folder->cur_folder_sys));
    return FOLDER_MBOX;
}

struct folder_type pop_mbx = { "POP",
                               mbx_close_pop,
			       mbx_lock_pop,
			       mbx_init_pop,
			       mbx_sessionlock_pop,
			       mbx_unlock_pop,
			       mbx_flush_pop,
			       mbx_ferror_pop,
			       mbx_prepare_read_pop,
			       mbx_end_read_pop,
			       mbx_copy_envelope_pop,
			       mbx_is_forwarded_pop,
			       mbx_copy_header_pop,
			       mbx_copy_body_pop,
			       mbx_copy_envelope_end_pop,
			       mbx_copy_envelope_reset_body_pop,
			       mbx_pop_to_fd,
			       mbx_new_mail_on_pop,
			       mbx_consider_remove_pop,
			       mbx_prepare_keep_pop,
			       mbx_end_keep_pop,
			       mbx_mark_keep_pop,
			       mbx_pop_type,
			       mbx_start_edit_pop,
			       mbx_end_edit_pop,
                               mbx_free_pop,
			       mbx_zero_rs_fields_pop,
			       mbx_free_rs_fields_pop,
			       mbx_zero_ks_fields_pop,
			       mbx_free_ks_fields_pop,
			       mbx_get_pop_mode };
#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