diff --git a/appl/popper/maildir.c b/appl/popper/maildir.c new file mode 100644 index 000000000..c650e6b47 --- /dev/null +++ b/appl/popper/maildir.c @@ -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 +#include +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; +} diff --git a/appl/popper/pop_pass.c b/appl/popper/pop_pass.c index 639f03921..b7a9f2614 100644 --- a/appl/popper/pop_pass.c +++ b/appl/popper/pop_pass.c @@ -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; diff --git a/appl/popper/pop_send.c b/appl/popper/pop_send.c index 5eae1e0f9..986191b32 100644 --- a/appl/popper/pop_send.c +++ b/appl/popper/pop_send.c @@ -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; diff --git a/appl/popper/pop_updt.c b/appl/popper/pop_updt.c index b8e6733a8..16102fa52 100644 --- a/appl/popper/pop_updt.c +++ b/appl/popper/pop_updt.c @@ -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 */ diff --git a/appl/popper/popper.h b/appl/popper/popper.h index 263af388a..fd46de3f8 100644 --- a/appl/popper/popper.h +++ b/appl/popper/popper.h @@ -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*);