/* RE_SID: @(%)/tmp_mnt/vol/sha/qsilver_SCCS/unix/pop3/solaris2/SCCS/s.pop_dropinfo.c 1.6 93/03/11 18:57:10 SMI */
/*
 * Copyright (c) 1989 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#ifndef lint
static char copyright[] = "Copyright (c) 1990 Regents of the University of California.\nAll rights reserved.\n";
static char SccsId[] = "@(#)@(#)pop_dropinfo.c	2.1  2.1 3/18/91";
#endif not lint

#include <errno.h>
#include <stdio.h>
#include <sys/types.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/file.h>
#include "popper.h"

extern int      errno;
extern int      sys_nerr;
extern char    *sys_errlist[];

/* 
 *  dropinfo:   Extract information about the POP maildrop and store 
 *  it for use by the other POP routines.
 */

pop_dropinfo(p)
POP     *   p;
{
    char                    buffer[MAXMSGLINELEN];  /*  Read buffer */
    MsgInfoList         *   mp;                     /*  Pointer to message 
                                                        info list */
    register int            msg_num;                /*  Current message 
                                                        counter */
    int                     nchar;                  /*  Bytes written/read */
    int			    in_header;
    int			    start_of_line;
    int			    after_skipped_line;
    long		    content_length;
    long		    body_length;
    int			    after_content_length;

    /*  Initialize maildrop status variables in the POP parameter block */
    p->msg_count = 0;
    p->msgs_deleted = 0;
    p->last_msg = 0;
    p->bytes_deleted = 0;
    p->drop_size = 0;

    /*  Allocate memory for message information structures */
    p->msg_count = ALLOC_MSGS;
    p->mlp = (MsgInfoList *)calloc((unsigned)p->msg_count,sizeof(MsgInfoList));
    if (p->mlp == NULL){
        (void)fclose (p->drop);
	p->drop = NULL;
        p->msg_count = 0;
        return pop_msg (p,POP_FAILURE,
            "Can't build message list for '%s': Out of memory", p->user);
    }

    rewind (p->drop);

    /*  Scan the file, loading the message information list with 
        information about each message */

    msg_num = 0;
    p->drop_size = 0;
    mp = p->mlp - 1;
    in_header = 0;
    start_of_line = 1;
    after_skipped_line = 0;
    after_content_length = 1;
    content_length = -1;	/* so we search for "From " */
    body_length = 0;
    while ( !ferror(p->drop) && fgets( buffer, MAXMSGLINELEN, p->drop ) ) {
	/* Here with another line from the mail drop file */
        nchar = strlen(buffer);
	if ( nchar == 0 ) continue;	/* unusual! */

	if ( in_header ) {
	    /* Do our header processing:
	     *   1) if we are after_skipped_line, and this line
	     *      starts with white space, skip this line also
	     *   2) If this line is "\n" and we are at start_of_line,
	     *      then this is the blank line after the header.
	     *   3) If at start_of_line, and the line starts:
	     *      "Content-Length:" then we have found the
	     *      content length header field -- assume the length
	     *      value is on the same line.
	     *   4) If not at start_of_line, then just pass this
	     *      portion through -- nothing to decide with it.
	     */
	    if ( start_of_line ) {

		if ( (nchar == 1) && (buffer[0] == '\n') ) {
		    /* We found the end of the header */
		    in_header = 0;
		    after_skipped_line = 0;

		} else if ( strncasecmp( "Content-Length:", buffer, 15 ) == NULL ) {
		    /* This is the Content-Length field -- convert
		     * the body length.
		     */
		    content_length = strtol( buffer+15, (char**)NULL, 10 );
		    after_skipped_line = 1;

#if 0
		/* This is how to handle other header lines to be skipped. */
		} else if ( strncasecmp( "Status:", buffer, 7 ) == NULL ) {
		    after_skipped_line = 1;
		    if ( strpbrk( buffer+7, "Rr") ) {
			mp->status = STATUS_READ;
		    } else if ( strpbrk( buffer+7, "Oo" ) ) {
			mp->status = STATUS_OLD;
		    } else {
			mp->status = STATUS_NEW;
		    }
#endif /* 0 */

		} else if ( !isascii(buffer[0]) || !isspace(buffer[0]) ) {
		    /* This is not a "folded" line -- so it is
		     * some other kind of header line
		     */
		    after_skipped_line = 0;
		}
		/* else it was a folded header line -- handle it like the
		 * preceeding header line.
		 */
	    }
	    /* else not at start -- just copy it */

	} else {
	    /* in the body -- accumulate body info, and check for
	     * "From " line
	     */
	    if (   start_of_line
		&& (content_length <= body_length)
		&& (strncmp("From ", buffer, 5) == 0) ) {

		/* This is the start of the next message */

		if (++msg_num > p->msg_count) {
		    /* Increase the message list length */
		    p->mlp = (MsgInfoList *) realloc(p->mlp,
                	(p->msg_count+=ALLOC_MSGS)*sizeof(MsgInfoList));
		    if ( p->mlp == NULL ) {
                	(void)fclose (p->drop);
			p->drop = NULL;
                	p->msg_count = 0;
                	return pop_msg( p, POP_FAILURE,
			     "Can't build message list for '%s': Out of memory",
					p->user );
		    }
		    mp = p->mlp + msg_num - 2;
		}
#ifdef DEBUG
		if ( p->debug && (msg_num > 1) ) {
		    pop_log( p, POP_DEBUG,
                    "Msg %d at offset %ld: %ld octets, %u lines, %u fgets() %u msg fgets().",
				mp->number, mp->offset, mp->octets, mp->lines,
				mp->num_fgets, mp->msg_fgets );
		}
#endif /* DEBUG */
		++mp;
		/* initialize the new message information */
		mp->number = msg_num;
		mp->offset = ftell(p->drop) - nchar;
		mp->del_flag = FALSE;
		mp->retr_flag = FALSE;
		mp->length = 0;
		mp->lines = 0;
		mp->octets = 0;		/* don't count this line */
		mp->num_fgets = 0;
		mp->msg_fgets = 0;
#if 0
		mp->status = STATUS_NONE;
#endif
		in_header = 1;
		after_skipped_line = 1;
		body_length = 0;
		after_content_length = 0;
		content_length = -1;
#ifdef DEBUG
		if ( p->debug ) {
		    pop_log( p, POP_DEBUG, "Msg %d being added to list",
			     mp->number);
		}
#endif /* DEBUG */
	 
	    } else {
		/* This is another body line for the current message,
		 * and it is not the "From " line starting another message
		 */
		body_length += nchar;
	    }
	}

	/* Add the length of this line to the message length over the
	 * wire (if not after_skipped_line)
	 * and set start_of_line properly
	 */
	mp->length += nchar;
	if ( !after_skipped_line && !after_content_length ) {
            p->drop_size += nchar;
	    mp->octets   += nchar;
	}
	if ( buffer[nchar-1] == '\n' ) {
	    if ( !after_skipped_line && !after_content_length ) {
		p->drop_size++;
		mp->octets++;		/* add in the CR */
	    }
	    start_of_line = 1;
	    mp->lines++;
	} else {
	    start_of_line = 0;
	}

	mp->num_fgets++;
	if ( !after_content_length ) {
	    mp->msg_fgets++;
	    /* Check for a "full" body, based on content_length */
	    if (   start_of_line
		&& !in_header
		&& (content_length >= 0)
		&& (content_length <= body_length) ) {
		after_content_length = 1;
	    }
	}
	
    }
    p->msg_count = msg_num;
#ifdef DEBUG
    if ( p->debug && (msg_num > 1) ) {
	pop_log( p, POP_DEBUG,
                    "Msg %d at offset %ld: %ld octets, %u lines, %u fgets(), %u msg fgets().",
		mp->number, mp->offset, mp->octets, mp->lines,
		mp->num_fgets, mp->msg_fgets );
    }
#endif DEBUG

#ifdef DEBUG
    if(p->debug && msg_num > 0) {
        register    i;
        for (i = 0, mp = p->mlp; i < p->msg_count; i++, mp++)
            pop_log(p,POP_DEBUG,
                "Msg %d at offset %d is %d octets long and has %u lines.",
                    mp->number,mp->offset,mp->octets,mp->lines);
    }
#endif DEBUG
 
    return(POP_SUCCESS);
}
