Add maildir support.
git-svn-id: svn://svn.h5l.se/heimdal/trunk/heimdal@4798 ec53bebd-3082-4978-b11e-865c3cabbd6b
This commit is contained in:
220
appl/popper/maildir.c
Normal file
220
appl/popper/maildir.c
Normal file
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* Copyright (c) 1998 Kungliga Tekniska H<>gskolan
|
||||
* (Royal Institute of Technology, Stockholm, Sweden).
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by Kungliga Tekniska
|
||||
* H<>gskolan and its contributors.
|
||||
*
|
||||
* 4. Neither the name of the Institute nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <popper.h>
|
||||
#include <dirent.h>
|
||||
RCSID("$Id$");
|
||||
|
||||
static int
|
||||
scan_file(POP *p, MsgInfoList *mp)
|
||||
{
|
||||
char path[MAXDROPLEN];
|
||||
FILE *f;
|
||||
char buf[1024];
|
||||
int eoh = 0;
|
||||
|
||||
snprintf(path, sizeof(path), "%s/%s/%s",
|
||||
p->drop_name,
|
||||
(mp->flags & NEW_FLAG) ? "new" : "old",
|
||||
mp->name);
|
||||
f = fopen(path, "r");
|
||||
|
||||
if(f == NULL) {
|
||||
#ifdef DEBUG
|
||||
if(p->debug)
|
||||
pop_log(p, POP_DEBUG,
|
||||
"Failed to open message file `%s': %s",
|
||||
path, strerror(errno));
|
||||
#endif
|
||||
return pop_msg (p, POP_FAILURE,
|
||||
"Failed to open message file `%s'", path);
|
||||
}
|
||||
while(fgets(buf, sizeof(buf), f)) {
|
||||
if(buf[strlen(buf) - 1] == '\n')
|
||||
mp->lines++;
|
||||
mp->length += strlen(buf);
|
||||
if(eoh)
|
||||
continue;
|
||||
if(strcmp(buf, "\n") == 0)
|
||||
eoh = 1;
|
||||
parse_header(mp, buf);
|
||||
}
|
||||
fclose(f);
|
||||
return add_missing_headers(p, mp);
|
||||
}
|
||||
|
||||
static int
|
||||
scan_dir(POP *p, int new)
|
||||
{
|
||||
char tmp[MAXDROPLEN];
|
||||
DIR *dir;
|
||||
struct dirent *dent;
|
||||
MsgInfoList *mp = p->mlp;
|
||||
int n_mp = p->msg_count;
|
||||
int e;
|
||||
|
||||
snprintf(tmp, sizeof(tmp), "%s/%s", p->drop_name, new ? "new" : "old");
|
||||
mkdir(tmp, 0700);
|
||||
dir = opendir(tmp);
|
||||
while((dent = readdir(dir)) != NULL) {
|
||||
if(strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0)
|
||||
continue;
|
||||
mp = realloc(mp, (n_mp + 1) * sizeof(*mp));
|
||||
if(mp == NULL) {
|
||||
p->msg_count = 0;
|
||||
return pop_msg (p, POP_FAILURE,
|
||||
"Can't build message list for '%s': Out of memory",
|
||||
p->user);
|
||||
}
|
||||
memset(mp + n_mp, 0, sizeof(*mp));
|
||||
mp[n_mp].name = strdup(dent->d_name);
|
||||
if(mp[n_mp].name == NULL) {
|
||||
p->msg_count = 0;
|
||||
return pop_msg (p, POP_FAILURE,
|
||||
"Can't build message list for '%s': Out of memory",
|
||||
p->user);
|
||||
}
|
||||
mp[n_mp].number = n_mp + 1;
|
||||
mp[n_mp].flags = 0;
|
||||
if(new)
|
||||
mp[n_mp].flags |= NEW_FLAG;
|
||||
e = scan_file(p, &mp[n_mp]);
|
||||
if(e != POP_SUCCESS)
|
||||
return e;
|
||||
p->drop_size += mp[n_mp].length;
|
||||
n_mp++;
|
||||
}
|
||||
closedir(dir);
|
||||
p->mlp = mp;
|
||||
p->msg_count = n_mp;
|
||||
return POP_SUCCESS;
|
||||
}
|
||||
|
||||
int
|
||||
pop_maildir_info(POP *p)
|
||||
{
|
||||
int e;
|
||||
|
||||
p->temp_drop[0] = '\0';
|
||||
p->mlp = NULL;
|
||||
p->msg_count = 0;
|
||||
|
||||
e = scan_dir(p, 0);
|
||||
if(e != POP_SUCCESS) return e;
|
||||
|
||||
e = scan_dir(p, 1);
|
||||
if(e != POP_SUCCESS) return e;
|
||||
return POP_SUCCESS;
|
||||
}
|
||||
|
||||
int
|
||||
pop_maildir_update(POP *p)
|
||||
{
|
||||
int i;
|
||||
char tmp1[MAXDROPLEN], tmp2[MAXDROPLEN];
|
||||
for(i = 0; i < p->msg_count; i++) {
|
||||
snprintf(tmp1, sizeof(tmp1), "%s/%s/%s", p->drop_name,
|
||||
(p->mlp[i].flags & NEW_FLAG) ? "new" : "old",
|
||||
p->mlp[i].name);
|
||||
if(p->mlp[i].flags & DEL_FLAG) {
|
||||
#ifdef DEBUG
|
||||
if(p->debug)
|
||||
pop_log(p, POP_DEBUG, "Removing `%s'", tmp1);
|
||||
#endif
|
||||
if(unlink(tmp1) < 0) {
|
||||
#ifdef DEBUG
|
||||
if(p->debug)
|
||||
pop_log(p, POP_DEBUG, "Failed to remove `%s': %s",
|
||||
tmp1, strerror(errno));
|
||||
#endif
|
||||
/* return failure? */
|
||||
}
|
||||
} else if((p->mlp[i].flags & NEW_FLAG) &&
|
||||
(p->mlp[i].flags & RETR_FLAG)) {
|
||||
snprintf(tmp2, sizeof(tmp2), "%s/old/%s", p->drop_name,
|
||||
p->mlp[i].name);
|
||||
#ifdef DEBUG
|
||||
if(p->debug)
|
||||
pop_log(p, POP_DEBUG, "Linking `%s' to `%s'", tmp1, tmp2);
|
||||
#endif
|
||||
if(link(tmp1, tmp2) == 0) {
|
||||
#ifdef DEBUG
|
||||
if(p->debug)
|
||||
pop_log(p, POP_DEBUG, "Removing `%s'", tmp1);
|
||||
#endif
|
||||
if(unlink(tmp1) < 0) {
|
||||
#ifdef DEBUG
|
||||
if(p->debug)
|
||||
pop_log(p, POP_DEBUG, "Failed to remove `%s'", tmp1);
|
||||
#endif
|
||||
/* return failure? */
|
||||
}
|
||||
} else {
|
||||
if(errno == EXDEV) {
|
||||
#ifdef DEBUG
|
||||
if(p->debug)
|
||||
pop_log(p, POP_DEBUG, "Trying to rename `%s' to `%s'",
|
||||
tmp1, tmp2);
|
||||
#endif
|
||||
if(rename(tmp1, tmp2) < 0) {
|
||||
#ifdef DEBUG
|
||||
if(p->debug)
|
||||
pop_log(p, POP_DEBUG, "Failed to rename `%s' to `%s'",
|
||||
tmp1, tmp2);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return(pop_quit(p));
|
||||
}
|
||||
|
||||
int
|
||||
pop_maildir_open(POP *p, MsgInfoList *mp)
|
||||
{
|
||||
char tmp[MAXDROPLEN];
|
||||
snprintf(tmp, sizeof(tmp), "%s/%s/%s", p->drop_name,
|
||||
(mp->flags & NEW_FLAG) ? "new" : "old", mp->name);
|
||||
if(p->drop)
|
||||
fclose(p->drop);
|
||||
p->drop = fopen(tmp, "r");
|
||||
if(p->drop == NULL)
|
||||
return pop_msg(p, POP_FAILURE, "Failed to open message file");
|
||||
return POP_SUCCESS;
|
||||
}
|
@@ -107,6 +107,7 @@ pop_pass (POP *p)
|
||||
{
|
||||
struct passwd *pw;
|
||||
int i;
|
||||
struct stat st;
|
||||
|
||||
/* Make one string of all these parameters */
|
||||
|
||||
@@ -198,13 +199,17 @@ pop_pass (POP *p)
|
||||
/* Build the name of the user's maildrop */
|
||||
snprintf(p->drop_name, sizeof(p->drop_name), "%s/%s", POP_MAILDIR, p->user);
|
||||
|
||||
/* Make a temporary copy of the user's maildrop */
|
||||
/* and set the group and user id */
|
||||
if (pop_dropcopy(p,pw) != POP_SUCCESS) return (POP_FAILURE);
|
||||
|
||||
/* Get information about the maildrop */
|
||||
if (pop_dropinfo(p) != POP_SUCCESS) return(POP_FAILURE);
|
||||
|
||||
if(stat(p->drop_name, &st) < 0 || !S_ISDIR(st.st_mode)){
|
||||
/* Make a temporary copy of the user's maildrop */
|
||||
/* and set the group and user id */
|
||||
if (pop_dropcopy(p,pw) != POP_SUCCESS) return (POP_FAILURE);
|
||||
|
||||
/* Get information about the maildrop */
|
||||
if (pop_dropinfo(p) != POP_SUCCESS) return(POP_FAILURE);
|
||||
} else {
|
||||
changeuser(p, pw);
|
||||
if(pop_maildir_info(p) != POP_SUCCESS) return POP_FAILURE;
|
||||
}
|
||||
/* Initialize the last-message-accessed number */
|
||||
p->last_msg = 0;
|
||||
|
||||
|
@@ -68,7 +68,7 @@ pop_send(POP *p)
|
||||
/* Is the message flagged for deletion? */
|
||||
if (mp->flags & DEL_FLAG)
|
||||
return (pop_msg (p,POP_FAILURE,
|
||||
"Message %d has been deleted.",msg_num));
|
||||
"Message %d has been deleted.",msg_num));
|
||||
|
||||
/* If this is a TOP command, get the number of lines to send */
|
||||
if (strcmp(p->pop_command,"top") == 0) {
|
||||
@@ -85,61 +85,71 @@ pop_send(POP *p)
|
||||
/* Display the number of bytes in the message */
|
||||
pop_msg(p, POP_SUCCESS, "%ld octets", mp->length);
|
||||
|
||||
if(IS_MAILDIR(p)){
|
||||
int e = pop_maildir_open(p, mp);
|
||||
if(e != POP_SUCCESS)
|
||||
return e;
|
||||
}
|
||||
|
||||
/* Position to the start of the message */
|
||||
fseek(p->drop,mp->offset,0);
|
||||
|
||||
/* Skip the first line (the sendmail "From" line) */
|
||||
fgets (buffer,MAXMSGLINELEN,p->drop);
|
||||
return_path_sent = 0;
|
||||
|
||||
if(!IS_MAILDIR(p)) {
|
||||
/* Skip the first line (the sendmail "From" line) */
|
||||
fgets (buffer,MAXMSGLINELEN,p->drop);
|
||||
|
||||
#ifdef RETURN_PATH_HANDLING
|
||||
return_path_sent = 0;
|
||||
if (strncmp(buffer,"From ",5) == 0) {
|
||||
return_path_linlen = strlen(buffer);
|
||||
for (return_path_adr = buffer+5;
|
||||
(*return_path_adr == ' ' || *return_path_adr == '\t') &&
|
||||
return_path_adr < buffer + return_path_linlen;
|
||||
return_path_adr++)
|
||||
;
|
||||
if (return_path_adr < buffer + return_path_linlen) {
|
||||
if ((return_path_end = strchr(return_path_adr, ' ')) != NULL)
|
||||
*return_path_end = '\0';
|
||||
if (strlen(return_path_adr) != 0 && *return_path_adr != '\n') {
|
||||
static char tmpbuf[MAXMSGLINELEN + 20];
|
||||
strcpy(tmpbuf, "Return-Path: ");
|
||||
strcat(tmpbuf, return_path_adr);
|
||||
strcat(tmpbuf, "\n");
|
||||
if (strlen(tmpbuf) < MAXMSGLINELEN) {
|
||||
pop_sendline (p,tmpbuf);
|
||||
if (hangup)
|
||||
return (pop_msg (p,POP_FAILURE,"SIGHUP or SIGPIPE flagged"));
|
||||
return_path_sent++;
|
||||
if (strncmp(buffer,"From ",5) == 0) {
|
||||
return_path_linlen = strlen(buffer);
|
||||
for (return_path_adr = buffer+5;
|
||||
(*return_path_adr == ' ' || *return_path_adr == '\t') &&
|
||||
return_path_adr < buffer + return_path_linlen;
|
||||
return_path_adr++)
|
||||
;
|
||||
if (return_path_adr < buffer + return_path_linlen) {
|
||||
if ((return_path_end = strchr(return_path_adr, ' ')) != NULL)
|
||||
*return_path_end = '\0';
|
||||
if (strlen(return_path_adr) != 0 && *return_path_adr != '\n') {
|
||||
static char tmpbuf[MAXMSGLINELEN + 20];
|
||||
strcpy(tmpbuf, "Return-Path: ");
|
||||
strcat(tmpbuf, return_path_adr);
|
||||
strcat(tmpbuf, "\n");
|
||||
if (strlen(tmpbuf) < MAXMSGLINELEN) {
|
||||
pop_sendline (p,tmpbuf);
|
||||
if (hangup)
|
||||
return pop_msg (p, POP_FAILURE,
|
||||
"SIGHUP or SIGPIPE flagged");
|
||||
return_path_sent++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Send the header of the message followed by a blank line */
|
||||
while (fgets(buffer,MAXMSGLINELEN,p->drop)) {
|
||||
#ifdef RETURN_PATH_HANDLING
|
||||
/* Don't send existing Return-Path-header if already sent own */
|
||||
if (strncasecmp(buffer,"Return-Path:", 12) != 0 ||
|
||||
!return_path_sent)
|
||||
if (!return_path_sent || strncasecmp(buffer, "Return-Path:", 12) != 0)
|
||||
#endif
|
||||
pop_sendline (p,buffer);
|
||||
pop_sendline (p,buffer);
|
||||
/* A single newline (blank line) signals the
|
||||
end of the header. sendline() converts this to a NULL,
|
||||
so that's what we look for. */
|
||||
if (*buffer == 0) break;
|
||||
if (hangup)
|
||||
return (pop_msg (p,POP_FAILURE,"SIGHUP or SIGPIPE flagged"));
|
||||
return (pop_msg (p,POP_FAILURE,"SIGHUP or SIGPIPE flagged"));
|
||||
}
|
||||
/* Send the message body */
|
||||
{
|
||||
int blank_line = 0;
|
||||
while (fgets(buffer,MAXMSGLINELEN,p->drop)) {
|
||||
while (fgets(buffer, MAXMSGLINELEN, p->drop)) {
|
||||
/* Look for the start of the next message */
|
||||
if (blank_line && strncmp(buffer,"From ",5) == 0) break;
|
||||
if (!IS_MAILDIR(p) && blank_line && strncmp(buffer,"From ",5) == 0)
|
||||
break;
|
||||
blank_line = (strncmp(buffer, "\n", 1) == 0);
|
||||
/* Decrement the lines sent (for a TOP command) */
|
||||
if (msg_lines >= 0 && msg_lines-- == 0) break;
|
||||
|
@@ -42,6 +42,9 @@ pop_updt (POP *p)
|
||||
}
|
||||
#endif /* DEBUG */
|
||||
|
||||
if(IS_MAILDIR(p))
|
||||
return pop_maildir_update(p);
|
||||
|
||||
if (p->msgs_deleted == p->msg_count) {
|
||||
/* Truncate before close, to avoid race condition, DO NOT UNLINK!
|
||||
Another process may have opened, and not yet tried to lock */
|
||||
|
@@ -191,6 +191,7 @@ typedef enum { /* POP processing states */
|
||||
|
||||
#define DEL_FLAG 1
|
||||
#define RETR_FLAG 2
|
||||
#define NEW_FLAG 4
|
||||
|
||||
typedef struct { /* Message information */
|
||||
int number; /* Message number relative to
|
||||
@@ -210,8 +211,11 @@ typedef struct { /* Message information */
|
||||
char *from;
|
||||
char *date;
|
||||
#endif
|
||||
char *name;
|
||||
} MsgInfoList;
|
||||
|
||||
#define IS_MAILDIR(P) ((P)->temp_drop[0] == '\0')
|
||||
|
||||
typedef struct { /* POP parameter block */
|
||||
int debug; /* Debugging requested */
|
||||
char * myname; /* The name of this POP
|
||||
@@ -327,3 +331,13 @@ int pop_msg(POP *p, int stat, char *format, ...)
|
||||
__attribute__ ((format (printf, 3, 4)))
|
||||
#endif
|
||||
;
|
||||
|
||||
void pop_sendline(POP*, char*);
|
||||
|
||||
int pop_maildir_info (POP*);
|
||||
int pop_maildir_open (POP*, MsgInfoList*);
|
||||
int pop_maildir_update (POP*);
|
||||
|
||||
void changeuser(POP*, struct passwd*);
|
||||
void parse_header(MsgInfoList*, char*);
|
||||
int add_missing_headers(POP*, MsgInfoList*);
|
||||
|
Reference in New Issue
Block a user