--- Makefile Tue Nov 26 06:38:18 2002 +++ Makefile Mon Dec 16 20:08:43 2002 @@ -35,7 +35,7 @@ virtual.o \ auth_passwd.o auth_shadow.o auth_pam.o \ pop_root.o pop_auth.o pop_trans.o \ - protocol.o database.o mailbox.o \ + protocol.o database.o mailbox.o maildir.o \ misc.o \ md5/md5.o --- database.h Tue Nov 26 06:38:18 2002 +++ database.h Tue Nov 26 06:32:42 2002 @@ -7,6 +7,8 @@ #include "params.h" +#include <sys/time.h> + /* * Message flags. */ @@ -30,6 +32,10 @@ unsigned long data_offset; /* Just the message itself */ unsigned long data_size; unsigned char hash[16]; /* MD5 hash, to be used for UIDL */ +#if POP_MAILDIR + time_t mtime; + char *filename; +#endif }; struct db_main { --- maildir.c Thu Jan 1 00:00:00 1970 +++ maildir.c Mon Dec 16 20:57:07 2002 @@ -0,0 +1,275 @@ +/* + * Maildir access + */ + +#include "params.h" + +#if POP_MAILDIR + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <dirent.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <syslog.h> +#include <errno.h> +#include <time.h> + +#include "md5/md5.h" + +#include "misc.h" +#include "protocol.h" +#include "database.h" +#include "maildir.h" + +int maildir_msgcmp(const void *p1, const void *p2) +{ + struct db_message *m1, *m2; + + m1 = *(struct db_message **) p1; + m2 = *(struct db_message **) p2; + + if (m1->mtime < m2->mtime) + return -1; + else if (m1->mtime == m2->mtime) + return 0; + else + return 1; +} + +int maildir_close(void) +{ + struct db_message *msg; + unsigned int i; + char *p, *pathname; + + for (i = 0; i < db.total_count; i++) { + msg = db.array[i]; + +/* + * Move all messages from new/ to cur/ and add info + * to them, currently only :2,. Maybe later we'll also + * add S to RETR'd messages, but the database doesn't + * keep track of RETR's. + * + * (see info: http://cr.yp.to/proto/maildir.html) + */ + + if (strncmp(msg->filename, "new/", 4)) continue; + if (!(p = strchr(msg->filename, '/'))) continue; + + pathname = malloc(strlen(msg->filename) + strlen(":2,") + 1); + + if (!pathname) return 1; + + strcpy(pathname, msg->filename); + + memcpy(pathname, "cur/", 4); + strcat(pathname, ":2,"); +/* + * This method was suggested by + * Peter van Dijk <peter@dataloss.nl> + * http://www.dataloss.nl/docs/maildir/ + */ + if (link(msg->filename, pathname)) { + free(pathname); + continue; + } + + if (unlink(msg->filename)) { + db.flags |= DB_STALE; + free(pathname); + continue; + } + + free(msg->filename); + msg->filename = pathname; + } + + return 0; +} + +static int maildir_changed(void) +{ + struct stat stat; + unsigned int i; + struct db_message *msg; + + for (i = 0; i < db.total_count; i++) { + msg = db.array[i]; + + if (lstat(msg->filename, &stat) || + stat.st_mtime != msg->mtime || + stat.st_size != msg->size) { + db.flags |= DB_STALE; + return 1; + } + + if (time(NULL) == msg->mtime) + if (sleep_select(1, 0)) return 1; + } + + return 0; +} + +int maildir_get(struct db_message *msg, int lines) +{ + int fd; + struct stat stat, stat_after; + + if (maildir_changed()) return POP_CRASH_SERVER; + + if (lstat(msg->filename, &stat)) return POP_CRASH_SERVER; + + if (!S_ISREG(stat.st_mode)) return POP_CRASH_SERVER; + + if (!stat.st_size) return POP_CRASH_SERVER; + + if ((fd = open(msg->filename, O_RDONLY | O_NOCTTY | O_NONBLOCK)) < 0) + return POP_CRASH_SERVER; + + if (fstat(fd, &stat_after) < 0) return POP_CRASH_SERVER; + if (stat.st_mode != stat_after.st_mode || + stat.st_ino != stat_after.st_ino || + stat.st_dev != stat_after.st_dev) return POP_CRASH_SERVER; + + if (pop_reply_multiline(fd, msg->size, lines)) { + close(fd); + return POP_CRASH_NETFAIL; + } + + close(fd); + + if (maildir_changed()) return POP_CRASH_SERVER; + + if (pop_reply_terminate()) return POP_CRASH_NETFAIL; + + return POP_OK; +} + +int maildir_update(void) +{ + unsigned int i; + struct db_message *msg; + + for (i = 0; i < db.total_count; i++) { + msg = db.array[i]; + + if (msg->flags & MSG_DELETED) + { + if (unlink(msg->filename) < 0) { + if (errno == ENOENT) + continue; + + return 1; + } + } + } + + return 0; +} + +int maildir_scandir(char *dir) +{ + char *pathname; + DIR *dirp; + struct dirent *de; + struct db_message msg; + MD5_CTX hash; + struct stat stat; + char *p, *uidl; + + dirp = opendir(dir); + + if (!dirp) return 1; + + while ((de = readdir(dirp))) { + if (de->d_name[0] == '.') continue; + + pathname = malloc(strlen(dir) + strlen(de->d_name) + 2); + if (!pathname) return 1; + + sprintf(pathname, "%s/%s", dir, de->d_name); + + if (lstat(pathname, &stat)) { + free(pathname); + continue; + } + + if (!S_ISREG(stat.st_mode)) { + free(pathname); + continue; + } + + if (!stat.st_size) { + free(pathname); + continue; + } + + memset(&msg, 0, sizeof(msg)); + + msg.size = stat.st_size; + msg.mtime = stat.st_mtime; + msg.filename = pathname; +/* + * According to RFC1939 the message uidl can be from 1 to 70 characters + * in the range 0x21 to 0x7E. If your MTA generates valid unique filenames + * you could just as well use the filename instead of a md5 hash. + */ + +/* We use the basename of pathname as hash data. */ + + if (!(p = strrchr(pathname, '/'))) return 1; + + uidl = strdup(p); + +/* We trim any trailing * maildir info that might exist. */ + + if ((p = strrchr(uidl, ':'))) *p = '\0'; + + MD5_Init(&hash); + MD5_Update(&hash, uidl, strlen(uidl)); + MD5_Final(msg.hash, &hash); + + free(uidl); + + if (db_add(&msg)) { + closedir(dirp); + return 1; + } + } + + closedir(dirp); + + return 0; +} + +int maildir_open(char *spool, char *mailbox) +{ + char *pathname; + + pathname = malloc(strlen(spool) + strlen(mailbox) + 2); + + if (!pathname) return 1; + + sprintf(pathname, "%s/%s", spool, mailbox); + + if (chdir(pathname) == -1) { + free(pathname); + return 1; + } + + free(pathname); + + if (maildir_scandir("new")) + return 1; + if (maildir_scandir("cur")) + return 1; + + return 0; +} + +#endif --- maildir.h Thu Jan 1 00:00:00 1970 +++ maildir.h Tue Nov 26 07:40:49 2002 @@ -0,0 +1,35 @@ +/* + * Maildir access. + */ + +#ifndef _POP_MAILDIR_H +#define _POP_MAILDIR_H + +/* + * Opens the maildir, filling in the maildir database. Returns a non-zero + * value on error. + */ +extern int maildir_open(char *spool, char *mailbox); + +/* + * Sends (first lines of) a message to the POP client. Returns one of the + * POP_* event codes. + */ +extern int maildir_get(struct db_message *msg, int lines); + +/* + * Move new messages to cur/ and clean out tmp. + */ +extern int maildir_close(void); + +/* + * Updates the maildir according to flags in the database. + */ +extern int maildir_update(void); + +/* + * Compares the modified timestamp of two maildir messages + */ +extern int maildir_msgcmp(const void *p1, const void *p2); + +#endif Common subdirectories: popa3d-0.5.9/md5 and popa3d-0.5.9-maildir/md5 --- params.h Tue Nov 26 06:38:18 2002 +++ params.h Mon Dec 16 20:12:37 2002 @@ -59,6 +59,12 @@ #endif /* + * Maildir support by Hallgrimur H. Gunnarsson <hhg@data.is> + * Improvements by Cory Visi <cory@visi.name> + */ +#define POP_MAILDIR 1 + +/* * Do we want to support virtual domains? */ #define POP_VIRTUAL 0 --- pop_trans.c Tue Nov 26 06:38:18 2002 +++ pop_trans.c Tue Nov 26 07:38:42 2002 @@ -3,12 +3,14 @@ */ #include <stdio.h> +#include <stdlib.h> #include <syslog.h> #include "params.h" #include "protocol.h" #include "database.h" #include "mailbox.h" +#include "maildir.h" static int pop_trans_quit(char *params) { @@ -104,7 +106,11 @@ if (number < 1 || number > db.total_count || params) return POP_ERROR; msg = db.array[number - 1]; if (msg->flags & MSG_DELETED) return POP_ERROR; +#if POP_MAILDIR + if ((event = maildir_get(msg, -1)) != POP_OK) return event; +#else if ((event = mailbox_get(msg, -1)) != POP_OK) return event; +#endif #if POP_SUPPORT_LAST if (number > db.last) db.last = number; #endif @@ -123,7 +129,11 @@ if (lines < 0 || params) return POP_ERROR; msg = db.array[number - 1]; if (msg->flags & MSG_DELETED) return POP_ERROR; +#if POP_MAILDIR + if ((event = maildir_get(msg, lines)) != POP_OK) return event; +#else if ((event = mailbox_get(msg, lines)) != POP_OK) return event; +#endif return POP_QUIET; } @@ -192,13 +202,25 @@ { db_init(); +#if POP_MAILDIR + if (maildir_open(spool, mailbox)) return 1; +#else if (mailbox_open(spool, mailbox)) return 1; +#endif if (db_fix()) { +#if POP_MAILDIR + maildir_close(); +#else mailbox_close(); +#endif return 1; } +#if POP_MAILDIR + qsort(&db.array[0], db.total_count, sizeof(db.array[0]), maildir_msgcmp); +#endif + return 0; } @@ -225,7 +247,11 @@ else switch ((result = pop_handle_state(pop_trans_commands))) { case POP_STATE: +#if POP_MAILDIR + if (maildir_update()) { +#else if (mailbox_update()) { +#endif if (db.flags & DB_STALE) break; syslog(SYSLOG_PRI_ERROR, "Failed to update %s/%s", @@ -258,7 +284,11 @@ "Server failure accessing %s/%s", spool, mailbox); +#if POP_MAILDIR + maildir_close(); +#else mailbox_close(); +#endif return 0; }