/*
* (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 "md5-wrap.h"
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include "mailbox.h"
#include "mailbox-trans.h"
#ifdef HAVE_LOCKFILE
# include <lockfile.h>
# include "mailbox-p.h"
#endif
static const char rcsid[]="$Id: mailbox-trans.c,v 1.16.2.1 2001/08/19 04:18:12 algernon Exp $";
extern int errno;
/*
* FORWARD DECLARATIONS
*/
static char *TRANS_read_next_message ( int fd, size_t size );
static CommandResponse *TRANS_do_retrieve ( P3LControl *control,
const char *args,
gboolean is_top );
CommandResponse *
mailbox_trans_init ( P3LControl *control )
{
int mailboxfd;
MailInfo *minfo;
off_t offset=0;
md5_context context;
struct stat stbuf;
P3LString **mailbox_split;
char *mailbox, *mailbox_fn, *buffer;
size_t size, new_size, i, lines, cline=0;
P3LHook_mailbox_mail_parse parse_hook=NULL;
mode_t old_umask;
#ifdef DEBUG
control->system->log ( control, LOG_DEBUG, "%s:%d: TRANSACTION state started (%s)",
__FILE__, __LINE__, P3L_GET_DATA ( "USER" ) );
#endif
g_hash_table_insert ( control->data, "BANNER", p3l_received_header ( control->data ) );
mailbox_fn = P3L_CALL_HOOK ( P3LHook_get_mailbox, "GET-MAILBOX" ) ( control );
g_hash_table_insert ( control->data, "MAILBOX", g_strdup ( mailbox_fn ) );
parse_hook = (P3LHook_mailbox_mail_parse)
g_hash_table_lookup ( control->hooks, "MAILBOX-PARSE-MAIL" );
/*
* Open, lock & parse mailbox
*/
if ( ( mailboxfd = open ( mailbox_fn, O_RDWR ) ) < 0 )
{
old_umask = umask ( 000 );
if ( ( mailboxfd = open ( mailbox_fn, O_RDWR | O_CREAT, 0660 ) ) < 0 )
{
umask ( old_umask );
control->system->log ( control, LOG_ERR,
"Can't open mailbox '%s': %s", mailbox_fn,
g_strerror ( errno ) );
return p3l_respond ( POP3_FATAL, "can't open mailbox" );
}
umask ( old_umask );
}
if ( p3l_lock_fd ( mailboxfd, mailbox_fn ) != 0 )
{
close ( mailboxfd );
control->system->log ( control, LOG_WARNING,
"Can't lock mailbox: '%s': %s", mailbox_fn,
g_strerror ( errno ) );
return p3l_respond ( POP3_FATAL, "can't lock mailbox" );
}
if ( fstat ( mailboxfd, &stbuf ) < 0 )
{
p3l_unlock_fd ( mailboxfd, mailbox_fn );
close ( mailboxfd );
control->system->log ( control, LOG_ERR,
"fstat failed on mailbox '%s': %s", mailbox_fn,
g_strerror ( errno ) );
return p3l_respond ( POP3_FATAL, "fstat failed" );
}
if ( !S_ISREG ( stbuf.st_mode ) )
{
p3l_unlock_fd ( mailboxfd, mailbox_fn );
close ( mailboxfd );
control->system->log ( control, LOG_ALERT,
"Mailbox is not a regular file: %s", mailbox_fn );
return p3l_respond ( POP3_FATAL, "mailbox is not a regular file" );
}
#ifdef HAVE_LOCKFILE
p3l_register_alarm ( mailbox_alarm_handler, 60 );
#endif
g_hash_table_insert ( control->data, "MBSIZE", (gpointer) (gint) stbuf.st_size );
g_hash_table_insert ( control->data, "MBTIME", (gpointer) stbuf.st_mtime );
mailbox = p3l_read_fd ( mailboxfd, &size );
mailbox_split = p3l_split_lines ( mailbox, size, &lines );
i = 0;
buffer = NULL;
size = 0;
offset = 0;
for ( i = 0 ; i < lines; i++ )
{
if ( ( mailbox_split[i] == NULL ) || ( mailbox_split[i] != NULL &&
i > 0 && ! memcmp ( "From ", mailbox_split[i]->str, 5 ) ) )
{
minfo = (MailInfo *) g_malloc ( sizeof ( MailInfo ) );
minfo->driver_data = g_hash_table_new ( (GHashFunc) g_str_hash,
(GCompareFunc) g_str_equal );
g_hash_table_insert ( minfo->driver_data, "offset", (gpointer) (gint) offset );
g_hash_table_insert ( minfo->driver_data, "real_size", (gpointer) size );
minfo->deleted = FALSE;
if ( parse_hook != NULL )
(*parse_hook) ( control, buffer, minfo );
__md5_init_ctx ( &context );
__md5_process_bytes ( buffer, size, &context);
__md5_finish_ctx ( &context, &(minfo->digest));
buffer = p3l_parse_buffer ( buffer, control->data, size, &new_size );
minfo->virtual_size = new_size + cline + 1;
control->msg_info=g_list_append ( control->msg_info, minfo );
g_free ( buffer );
buffer = NULL;
offset = size;
size = 0;
cline = 0;
}
cline++;
if ( mailbox_split[i] == NULL )
break;
buffer = (char *) g_realloc ( buffer, size + mailbox_split[i]->length );
memcpy ( buffer+size, mailbox_split[i]->str,
mailbox_split[i]->length );
size+=mailbox_split[i]->length;
}
if ( size > 0 )
{
minfo = (MailInfo *) g_malloc ( sizeof ( MailInfo ) );
minfo->driver_data = g_hash_table_new ( (GHashFunc) g_str_hash,
(GCompareFunc) g_str_equal );
g_hash_table_insert ( minfo->driver_data, "offset", (gpointer) (gint) offset );
g_hash_table_insert ( minfo->driver_data, "real_size", (gpointer) size );
minfo->deleted = FALSE;
if ( parse_hook != NULL )
(*parse_hook) ( control, buffer, minfo );
__md5_init_ctx ( &context );
__md5_process_bytes ( buffer, size, &context );
__md5_finish_ctx ( &context, &(minfo->digest) );
control->msg_info=g_list_append ( control->msg_info, minfo );
buffer = p3l_parse_buffer ( buffer, control->data, size, &new_size );
minfo->virtual_size = new_size + cline + 1;
}
g_free ( buffer );
g_free ( mailbox );
g_hash_table_insert ( control->data, "MAILBOXFD", GINT_TO_POINTER ( mailboxfd ) );
return p3l_respond ( POP3_OK, "Congratulations!" );
}
CommandResponse *
mailbox_cmd_trans_dele ( P3LControl *control, const char *args )
{
unsigned long msg;
MailInfo *minfo;
#ifdef DEBUG
control->system->log ( control, LOG_DEBUG, "%s:%d: handling DELE (%s)",
__FILE__, __LINE__, P3L_GET_DATA ( "USER" ) );
#endif
if ( args == NULL )
return p3l_respond ( POP3_ERR, "No such message" );
if ( ! p3l_is_numeric ( args ) )
return p3l_respond ( POP3_ERR, "Invalid argument" );
msg = strtoul ( args, (char **) NULL, 10 );
minfo = g_list_nth_data ( control->msg_info, msg - 1 );
if ( minfo != NULL )
{
if ( minfo->deleted == TRUE )
return p3l_respond ( POP3_ERR, "Message already deleted" );
else
{
minfo->deleted = TRUE;
return p3l_respond ( POP3_OK,
g_strdup_printf ( "Message %lu marked for deletion", msg ) );
}
}
else
return p3l_respond ( POP3_ERR, "No such message" );
}
CommandResponse *
mailbox_cmd_trans_retr ( P3LControl *control, const char *args )
{
#ifdef DEBUG
control->system->log ( control, LOG_DEBUG, "%s:%d: handling RETR (%s)",
__FILE__, __LINE__, P3L_GET_DATA ( "USER" ) );
#endif
return TRANS_do_retrieve ( control, args, FALSE );
}
CommandResponse *
mailbox_cmd_trans_top ( P3LControl *control, const char *args )
{
#ifdef DEBUG
control->system->log ( control, LOG_DEBUG, "%s:%d: handling TOP (%s)",
__FILE__, __LINE__, P3L_GET_DATA ( "USER" ) );
#endif
return TRANS_do_retrieve ( control, args, TRUE );
}
/**
* TRANS_do_retrieve: RETR/TOP uniform handler
* @control: the main control struct
* @args: arguments, as passed by the client
* @is_top: TRUE if we're handling TOP, FALSE if we're hadnling RETR
*
* This is an uniform handler for the RETR and TOP commands. This will
* do the hard job by reading the specified message from the mailbox
* file, and echoing it back to the client. If is_top is TRUE, then it
* will only echo the specified number of lines from the mail BODY.
*
* Returns: the final response, probalby an +OK
**/
CommandResponse *
TRANS_do_retrieve ( P3LControl *control, const char *args, gboolean is_top )
{
unsigned long num, msg, x;
size_t max_lines, lines, new_size, size;
MailInfo *minfo, *tinfo;
int mailboxfd;
char *buffer, **argv;
gboolean is_body=FALSE;
P3LString **message;
struct stat stbuf;
char *mailbox = P3L_GET_DATA ( "MAILBOX" );
max_lines = 0;
if ( args == NULL )
{
return p3l_respond ( POP3_ERR, "No such message" );
}
if ( is_top )
{
argv = g_strsplit (args, " ", 1);
if ( argv[0] == NULL )
return p3l_respond ( POP3_ERR, "No such message" );
if ( argv[1] == NULL )
return p3l_respond ( POP3_ERR, "Not enough arguments" );
argv[0]=g_strstrip(argv[0]);
argv[1]=g_strstrip(argv[1]);
if ( ! p3l_is_numeric ( argv[0] ) )
return p3l_respond ( POP3_ERR, "Invalid argument" );
if ( ! p3l_is_numeric ( argv[1] ) )
return p3l_respond ( POP3_ERR, "Invalid argument" );
msg = strtoul ( argv[0], (char **) NULL, 10 ) - 1;
max_lines = strtoul ( argv[1], (char **) NULL, 10 );
g_strfreev ( argv );
}
else
msg = strtoul ( args, (char **) NULL, 10 ) - 1;
mailboxfd = GPOINTER_TO_INT ( g_hash_table_lookup ( control->data, "MAILBOXFD" ) );
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 ) );
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 );
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 );
return p3l_respond ( POP3_FATAL, "mailbox content changed" );
}
minfo = g_list_nth_data ( control->msg_info, msg );
if ( minfo == NULL || ( minfo && minfo->deleted == TRUE ) )
return p3l_respond ( POP3_ERR, "No such message" );
control->send_response ( control, POP3_OK,
g_strdup_printf ( "%u octects", minfo->virtual_size ) );
lseek ( mailboxfd, 0, SEEK_SET );
for ( x = 0; x <= msg; x++ )
{
tinfo = g_list_nth_data ( control->msg_info, x );
lseek ( mailboxfd,
(off_t) GPOINTER_TO_INT ( g_hash_table_lookup ( tinfo->driver_data, "offset" ) ),
SEEK_CUR );
}
size = (size_t) g_hash_table_lookup ( minfo->driver_data, "real_size" );
buffer = TRANS_read_next_message ( mailboxfd, (ssize_t) size );
buffer = p3l_parse_buffer ( buffer, control->data, size, &new_size );
message = p3l_split_lines ( buffer, new_size, &lines );
if ( ! is_top )
max_lines = lines;
num = 0;
x = num;
do
{
if ( message[x] == NULL )
break;
if ( is_body )
{
if ( max_lines == 0 )
break;
else
num++;
}
if ( message[x]->length == 1 && ! memcmp ( message[x]->str, "\n", 1 ) )
is_body = TRUE;
if ( message[x]->str[0] == '.' ) /* Line begins with a dot */
control->send_raw ( control, ".", 1 ); /* ... prepend another one */
control->send_raw ( control, message[x]->str, message[x]->length - 1 );
control->send_raw ( control, "\r\n", 2 );
x++;
} while ( (num < max_lines && num < lines ) || max_lines == 0 );
g_free ( buffer );
return p3l_respond ( POP3_ANSWERED, ".");
}
/**
* TRANS_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 *
TRANS_read_next_message ( int fd, size_t size )
{
char *buffer;
buffer = (char *) g_malloc ( size + 2 );
read ( fd, buffer, size );
return buffer;
}
syntax highlighted by Code2HTML, v. 0.9.1