/*
 * (POP3Lite) MailBox - 3lite POP3 Daemon (mailbox driver)
 * Copyright (C) 2000, 2001 Gergely Nagy <8@free.bsd.hu>
 *
 * This file is part of POP3Lite.
 *
 * POP3LIte is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * POP3Lite is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#ifdef HAVE_CONFIG_H
#	include <config.h>
#endif

#include <pop3lite.h>
#include <glib.h>

#include <errno.h>
#include <fcntl.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <stdio.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>

#include "mailbox-upd.h"

#ifdef HAVE_LOCKFILE
#	include <lockfile.h>
#	include "mailbox-p.h"
#endif

static const char rcsid[]="$Id: mailbox-upd.c,v 1.10.2.2 2001/05/30 12:37:30 algernon Exp $";

int errno;

static char *UPD_read_next_message ( int fd, size_t size );
static gboolean UPD_need_update ( P3LControl *control );

CommandResponse *
mailbox_update ( P3LControl *control )
{
	int mailboxfd;
	struct stat stbuf;
	GList *messages=NULL;
	GList *sizes=NULL;
	unsigned long i;
	size_t size=0;
	MailInfo *minfo;
	char *buffer;
	char *mailbox = P3L_GET_DATA ( "MAILBOX" );

#ifdef DEBUG
	control->system->log ( control, LOG_DEBUG, "%s:%d: UPDATE state (%s)",
			       __FILE__, __LINE__, P3L_GET_DATA ( "USER" ) );
#endif

	mailboxfd = GPOINTER_TO_INT ( g_hash_table_lookup ( control->data, "MAILBOXFD" ) );

	/*
	 * If there's nothing to update, we don't check the mailbox
	 */

	if ( ! UPD_need_update ( control ) )
	{
#ifdef HAVE_LOCKFILE
		p3l_unregister_alarm ( mailbox_alarm_handler );
#endif
		p3l_unlock_fd ( mailboxfd, mailbox );
		close ( mailboxfd );
		control->system->log ( control, LOG_NOTICE, "User %s logged out",
				       (char *) g_hash_table_lookup ( control->data, "USER" ) );
		g_hash_table_remove ( control->data, "USER" );
		return p3l_respond ( POP3_OK, "Good bye" );
	}

	if ( fstat ( mailboxfd, &stbuf ) < 0 )
	{
#ifdef HAVE_LOCKFILE
		p3l_unregister_alarm ( mailbox_alarm_handler );
#endif
		p3l_unlock_fd ( mailboxfd, mailbox );
		close ( mailboxfd );
		control->system->log ( control, LOG_ERR,
				       "fstat failed on mailbox '%s': %s", mailbox,
				       g_strerror ( errno ) );
		g_hash_table_remove ( control->data, "USER" );
		return p3l_respond ( POP3_FATAL, "fstat failed" );
	}

	if ( !S_ISREG ( stbuf.st_mode ) )
	{
#ifdef HAVE_LOCKFILE
		p3l_unregister_alarm ( mailbox_alarm_handler );
#endif
		p3l_unlock_fd ( mailboxfd, mailbox );
		close ( mailboxfd );
		control->system->log ( control, LOG_ALERT,
				       "Mailbox is not a regular file: %s", mailbox );
		g_hash_table_remove ( control->data, "USER" );
		return p3l_respond ( POP3_FATAL, "mailbox is not a regular file" );
	}

	if ( stbuf.st_size != (off_t) GPOINTER_TO_INT ( g_hash_table_lookup ( control->data, "MBSIZE" ) ) ||
	     stbuf.st_mtime != (time_t) g_hash_table_lookup ( control->data, "MBTIME" ) )
	{
#ifdef HAVE_LOCKFILE
		p3l_unregister_alarm ( mailbox_alarm_handler );
#endif
		p3l_unlock_fd ( mailboxfd, mailbox );
		close ( mailboxfd );
		control->system->log ( control, LOG_ERR,
				       "Mailbox content changed: %s", mailbox );
		g_hash_table_remove ( control->data, "USER" );
		return p3l_respond ( POP3_FATAL, "mailbox content changed" );
	}

	lseek ( mailboxfd, 0, SEEK_SET );

	for ( i = 0; i < g_list_length ( control->msg_info ); i++ )
	{
		minfo = g_list_nth_data ( control->msg_info, i );
		size = (size_t) g_hash_table_lookup ( minfo->driver_data, "real_size" );
		buffer = UPD_read_next_message ( mailboxfd, (ssize_t) size );
		if ( minfo->deleted == FALSE )
		{
			messages = g_list_append ( messages, buffer );
			sizes = g_list_append ( sizes, (gpointer) size );
		}
	}

#ifdef HAVE_LOCKFILE
	p3l_unregister_alarm ( mailbox_alarm_handler );
#endif
	p3l_unlock_fd ( mailboxfd, mailbox );
	close ( mailboxfd );

	if ( ( mailboxfd = open ( mailbox, O_RDWR | O_TRUNC ) ) < 0 )
	{
		control->system->log ( control, LOG_ERR,
				       "Can't open mailbox '%s': %s", mailbox,
				       g_strerror ( errno ) );
		g_hash_table_remove ( control->data, "USER" );
		return p3l_respond ( POP3_FATAL, "can't open mailbox" );
	}

	if ( p3l_lock_fd ( mailboxfd, mailbox ) != 0 )
	{
		control->system->log ( control, LOG_WARNING,
				       "Can't lock mailbox '%s': %s", mailbox,
				       g_strerror ( errno ) );
		g_hash_table_remove ( control->data, "USER" );
		return p3l_respond ( POP3_FATAL, "can't lock mailbox" );
	}

	if ( fstat ( mailboxfd, &stbuf ) < 0 )
	{
		p3l_unlock_fd ( mailboxfd, mailbox );
		close ( mailboxfd );
		control->system->log ( control, LOG_ERR,
				       "fstat failed on mailbox '%s': %s", mailbox,
				       g_strerror ( errno ) );
		g_hash_table_remove ( control->data, "USER" );
		return p3l_respond ( POP3_FATAL, "fstat failed" );
	}

	if ( !S_ISREG ( stbuf.st_mode ) )
	{
		p3l_unlock_fd ( mailboxfd, mailbox );
		close ( mailboxfd );
		control->system->log ( control, LOG_ALERT,
				       "Mailbox is not a regular file: %s", mailbox );
		g_hash_table_remove ( control->data, "USER" );
		return p3l_respond ( POP3_FATAL, "mailbox is not a regular file" );
	}

#ifdef HAVE_LOCKFILE
	p3l_register_alarm ( mailbox_alarm_handler, 60 );
#endif

	for ( i = 0; i < g_list_length ( messages ); i++ )
	{
		char *buffer;

		buffer = g_list_nth_data ( messages, i );
		write ( mailboxfd, buffer, (size_t) g_list_nth_data ( sizes, i ) );
		g_free ( buffer );
	}

#ifdef HAVE_LOCKFILE
	p3l_unregister_alarm ( mailbox_alarm_handler );
#endif
	p3l_unlock_fd ( mailboxfd, mailbox );
	close ( mailboxfd );
	control->system->log ( control, LOG_NOTICE, "User %s logged out",
			       (char *) g_hash_table_lookup ( control->data, "USER" ) );
	g_hash_table_remove ( control->data, "USER" );
	return p3l_respond ( POP3_OK, "Good bye" );
}

/**
 * UPD_read_next_message: read the next message from the mailbox
 * @fd: file descriptor
 * @size: message size
 *
 * This little function is responsible for retrieving the next message
 * from a mailbox.
 *
 * Returns: a newly allocated buffer containing the message
 **/
static char *
UPD_read_next_message ( int fd, size_t size )
{
	char *buffer;

	buffer = (char *) g_malloc ( size + 1 );

	read ( fd, buffer, size );

	return buffer;
}

/**
 * UPD_need_update: determine if the mailbox needs an update
 * @control: the main control struct
 *
 * Determines wheter the current mailbox needs an update, by
 * checking if there are any deleted messages.
 *
 * Returns: TRUE if it needs, FALSE if it doesn't
 **/
static gboolean
UPD_need_update ( P3LControl *control )
{
	unsigned long i;

	for ( i = 0; i < g_list_length ( control->msg_info ); i++ )
		if ( ( (MailInfo *) g_list_nth_data ( control->msg_info, i ) )->deleted == TRUE )
			return TRUE;

	return FALSE;
}


syntax highlighted by Code2HTML, v. 0.9.1