/* RE_SID: @(%)/tmp_mnt/vol/sha/qsilver_SCCS/unix/pop3/solaris2/SCCS/s.pop_updt.c 1.3 93/03/11 18:57:30 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_updt.c	2.3  2.3 3/20/91";
#endif not lint

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

extern int      errno;

static char standard_error[] =
    "Error error updating primary drop. Mailbox unchanged";

/* 
 *  updt:   Apply changes to a user's POP maildrop
 */

int pop_updt_actions (p, msgflg)
POP     *   p;
int	    msgflg;		/* TRUE if we should call pop_msg */
{
    FILE                *   md;                     /*  Stream pointer for 
                                                        the user's maildrop */
    int                     mfd;                    /*  File descriptor for
                                                        above */
    char                    buffer[MAXMSGLINELEN+1];  /*  Read buffer */

    MsgInfoList         *   mp;                     /*  Pointer to message 
                                                        info list */
    register int            msg_num;                /*  Current message 
                                                        counter */
    register int            status_written;         /*  Status header field 
                                                        written */
    int                     nchar;                  /* Bytes read/written */

    long                    offset;                 /* New mail offset */
    int			    i;			    /* number of fgets() */
#ifdef NOTDEF
    struct	flock		lck;
#endif

#ifdef DEBUG
    if (p->debug) {
        pop_log(p,POP_DEBUG,"Performing maildrop update...");
        pop_log(p,POP_DEBUG,"Checking to see if all messages were deleted");
    }
#endif DEBUG

    if (p->msgs_deleted == p->msg_count) {
	/* All messages deleted -- we already removed them from the
	 * original mail drop, so we are done.
	 */
        /* Truncate before close, to avoid race condition,  DO NOT UNLINK!
         * Another process may have opened, and not yet tried to lock.
	 */
        (void)ftruncate ((int)fileno(p->drop),0);
        (void)fclose(p->drop) ;
	p->drop = NULL;
        return (POP_SUCCESS);
    }

#ifdef DEBUG
    if (p->debug) 
        pop_log(p,POP_DEBUG,"Opening mail drop \"%s\"",p->drop_name);
#endif DEBUG

    /*  Open the user's real maildrop */
    if ((mfd = open(p->drop_name,O_RDWR|O_CREAT,S_IRUSR|S_IWUSR)) == -1 ||
        (md = fdopen(mfd,"r+")) == NULL) {
        return (msgflg ? pop_msg(p,POP_FAILURE,standard_error) : POP_FAILURE);
    }

#ifdef NOTDEF
    lck.l_type = F_WRLCK;
    lck.l_whence = 0;
    lck.l_start = 0L;
    lck.l_len = 0L;

    /*  Lock the user's real mail drop */
    if ( fcntl(mfd,F_SETLK,&lck) == -1 ) {
        (void)fclose(md) ;
        return (msgflg ? pop_msg(p,POP_FAILURE, "fcntl: '%s': %s", p->temp_drop,
            (errno < sys_nerr) ? sys_errlist[errno] : "") : POP_FAILURE );
    }
#endif
    /*  Lock the user's real mail drop */
    if ( flock(mfd,LOCK_EX) == -1 ) {
        (void)fclose(md) ;
        return (msgflg ? pop_msg(p,POP_FAILURE, "flock3: '%s': %s", p->temp_drop,
            (errno < sys_nerr) ? sys_errlist[errno] : "") : POP_FAILURE );
    }
    /* Go to the right places */
    offset = lseek((int)fileno(p->drop),0,SEEK_END) ; 

    /*  Append any messages that may have arrived during the session 
        to the temporary maildrop */
    while ((nchar=read(mfd,buffer,sizeof(buffer))) > 0)
        if ( nchar != write((int)fileno(p->drop),buffer,nchar) ) {
            nchar = -1;
            break ;
        }
    if ( nchar != 0 ) {
        (void)fclose(md) ;
        (void)ftruncate((int)fileno(p->drop),(int)offset) ;
        (void)fclose(p->drop) ;
	p->drop = NULL;
        return (msgflg ? pop_msg(p,POP_FAILURE,standard_error) : POP_FAILURE );
    }

    rewind(md);
    (void)ftruncate(mfd,0) ;

    /* Synch stdio and the kernel for the POP drop */
    rewind(p->drop);
    (void)lseek((int)fileno(p->drop),0,L_SET);

    /*  Transfer messages not flagged for deletion from the temporary 
        maildrop to the new maildrop */
#ifdef DEBUG
    if (p->debug) 
        pop_log(p,POP_DEBUG,"Creating new maildrop \"%s\" from \"%s\"",
                p->drop_name,p->temp_drop);
#endif DEBUG

    for (msg_num = 0; msg_num < p->msg_count; ++msg_num) {

        int doing_body;

        /*  Get a pointer to the message information list */
        mp = &p->mlp[msg_num];

        if (mp->del_flag) {
#ifdef DEBUG
            if(p->debug)
                pop_log(p,POP_DEBUG,
                    "Message %d flagged for deletion.",mp->number);
#endif DEBUG
            continue;
        }

        (void)fseek(p->drop,mp->offset,0);

#ifdef DEBUG
        if(p->debug)
            pop_log(p,POP_DEBUG,"Copying message %d.",mp->number);
#endif DEBUG
	status_written = 0;
	doing_body = 0;

	for ( i = 0;
	         (i < mp->num_fgets)
	      && fgets(buffer,MAXMSGLINELEN,p->drop);
	      i++ ) {

            if ( doing_body == 0 ) {
		/* Header */

                /*  Update the message status */
                if ( strncasecmp(buffer,"Status:",7) == 0 ) {
                    if (mp->retr_flag)
                        (void)fputs("Status: RO\n",md);
                    else
                        (void)fputs(buffer, md);
                    status_written++;

                } else if (*buffer == '\n') {
		    /* A blank line signals the end of the header. */
                    doing_body = 1;
                    if (status_written == 0) {
                        if (mp->retr_flag) {
                            (void)fputs("Status: RO\n\n",md);
                        } else {
			    /* leave the status alone -- it should still
			     * look like a new message.
			     */
                            /* (void)fputs("Status: U\n\n",md); */
			    (void)fputs ("\n", md);
			}
                    } else {
			(void)fputs ("\n", md);
		    }
                } else {
		    /*  Save another header line */
		    (void)fputs (buffer, md);
		}
            }  else {
		/* Body */ 
                (void)fputs (buffer, md);
            }
        }
    }

    /* flush and check for errors now!  The new mail will writen
       without stdio,  since we need not separate messages */

    (void)fflush(md) ;
    if (ferror(md)) {
        (void)ftruncate(mfd,0) ;
        (void)fclose(md) ;
        (void)fclose(p->drop) ;
	p->drop = NULL;
        return (msgflg ? pop_msg(p,POP_FAILURE,standard_error) : POP_FAILURE);
    }

    /* Go to start of new mail if any */
    (void)lseek((int)fileno(p->drop),offset,L_SET);

    while((nchar=read((int)fileno(p->drop),buffer,sizeof(buffer))) > 0)
        if ( nchar != write(mfd,buffer,nchar) ) {
            nchar = -1;
            break ;
        }
    if ( nchar != 0 ) {
        (void)ftruncate(mfd,0) ;
        (void)fclose(md) ;
        (void)fclose(p->drop) ;
	p->drop = NULL;
        return (msgflg ? pop_msg(p,POP_FAILURE,standard_error) : POP_FAILURE);
    }

    /*  Close the maildrop and empty temporary maildrop */
    (void)fclose(md);
    (void)ftruncate((int)fileno(p->drop),0);
    (void)fclose(p->drop);
    p->drop = NULL;

    return(POP_SUCCESS);
}

int pop_updt (p)
POP     *   p;
{
    int ret;

    ret = pop_updt_actions( p, 1 );
    return ( ret == POP_SUCCESS ) ? pop_quit(p) : ret;
}
