314 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			314 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
| /*
 | |
|  * Heirloom mailx - a mail user agent derived from Berkeley Mail.
 | |
|  *
 | |
|  * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
 | |
|  */
 | |
| /*
 | |
|  * Copyright (c) 2004
 | |
|  *	Gunnar Ritter.  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 Gunnar Ritter
 | |
|  *	and his contributors.
 | |
|  * 4. Neither the name of Gunnar Ritter nor the names of his contributors
 | |
|  *    may be used to endorse or promote products derived from this software
 | |
|  *    without specific prior written permission.
 | |
|  *
 | |
|  * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER 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 GUNNAR RITTER 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.
 | |
|  */
 | |
| 
 | |
| #ifndef lint
 | |
| #ifdef	DOSCCS
 | |
| static char sccsid[] = "@(#)maildir.c	1.20 (gritter) 12/28/06";
 | |
| #endif
 | |
| #endif /* not lint */
 | |
| 
 | |
| #include "config.h"
 | |
| 
 | |
| #include "rcv.h"
 | |
| #include "extern.h"
 | |
| #include <time.h>
 | |
| #include <dirent.h>
 | |
| #include <unistd.h>
 | |
| #include <sys/stat.h>
 | |
| #include <errno.h>
 | |
| 
 | |
| /*
 | |
|  * Mail -- a mail program
 | |
|  *
 | |
|  * Maildir folder support.
 | |
|  */
 | |
| 
 | |
| static struct mditem {
 | |
| 	struct message	*md_data;
 | |
| 	unsigned	md_hash;
 | |
| } *mdtable;
 | |
| static long	mdprime;
 | |
| 
 | |
| static sigjmp_buf	maildirjmp;
 | |
| static char *mkname(time_t t, enum mflag f, const char *pref);
 | |
| static enum okay maildir_append1(const char *name, FILE *fp, off_t off1,
 | |
| 		long size, enum mflag flag);
 | |
| static enum okay trycreate(const char *name);
 | |
| static enum okay mkmaildir(const char *name);
 | |
| 
 | |
| static char *
 | |
| mkname(time_t t, enum mflag f, const char *pref)
 | |
| {
 | |
| 	static unsigned long	count;
 | |
| 	static pid_t	mypid;
 | |
| 	char	*cp;
 | |
| 	static char	*node;
 | |
| 	int	size, n, i;
 | |
| 
 | |
| 	if (pref == NULL) {
 | |
| 		if (mypid == 0)
 | |
| 			mypid = getpid();
 | |
| 		if (node == NULL) {
 | |
| 			cp = nodename(0);
 | |
| 			n = size = 0;
 | |
| 			do {
 | |
| 				if (n < size + 8)
 | |
| 					node = srealloc(node, size += 20);
 | |
| 				switch (*cp) {
 | |
| 				case '/':
 | |
| 					node[n++] = '\\', node[n++] = '0',
 | |
| 					node[n++] = '5', node[n++] = '7';
 | |
| 					break;
 | |
| 				case ':':
 | |
| 					node[n++] = '\\', node[n++] = '0',
 | |
| 					node[n++] = '7', node[n++] = '2';
 | |
| 					break;
 | |
| 				default:
 | |
| 					node[n++] = *cp;
 | |
| 				}
 | |
| 			} while (*cp++);
 | |
| 		}
 | |
| 		size = 60 + strlen(node);
 | |
| 		cp = salloc(size);
 | |
| 		n = snprintf(cp, size, "%lu.%06lu_%06lu.%s:2,",
 | |
| 				(unsigned long)t,
 | |
| 				(unsigned long)mypid, ++count, node);
 | |
| 	} else {
 | |
| 		size = (n = strlen(pref)) + 13;
 | |
| 		cp = salloc(size);
 | |
| 		strcpy(cp, pref);
 | |
| 		for (i = n; i > 3; i--)
 | |
| 			if (cp[i-1] == ',' && cp[i-2] == '2' &&
 | |
| 					cp[i-3] == ':') {
 | |
| 				n = i;
 | |
| 				break;
 | |
| 			}
 | |
| 		if (i <= 3) {
 | |
| 			strcpy(&cp[n], ":2,");
 | |
| 			n += 3;
 | |
| 		}
 | |
| 	}
 | |
| 	if (n < size - 7) {
 | |
| 		if (f & MDRAFTED)
 | |
| 			cp[n++] = 'D';
 | |
| 		if (f & MFLAGGED)
 | |
| 			cp[n++] = 'F';
 | |
| 		if (f & MANSWERED)
 | |
| 			cp[n++] = 'R';
 | |
| 		if (f & MREAD)
 | |
| 			cp[n++] = 'S';
 | |
| 		if (f & MDELETED)
 | |
| 			cp[n++] = 'T';
 | |
| 		cp[n] = '\0';
 | |
| 	}
 | |
| 	return cp;
 | |
| }
 | |
| 
 | |
| enum okay
 | |
| maildir_append(const char *name, FILE *fp)
 | |
| {
 | |
| 	char	*buf, *bp, *lp;
 | |
| 	size_t	bufsize, buflen, count;
 | |
| 	off_t	off1 = -1, offs;
 | |
| 	int	inhead = 1;
 | |
| 	int	flag = MNEW|MNEWEST;
 | |
| 	long	size = 0;
 | |
| 	enum okay	ok;
 | |
| 
 | |
| 	if (mkmaildir(name) != OKAY)
 | |
| 		return STOP;
 | |
| 	buf = smalloc(bufsize = LINESIZE);
 | |
| 	buflen = 0;
 | |
| 	count = fsize(fp);
 | |
| 	offs = ftell(fp);
 | |
| 	do {
 | |
| 		bp = fgetline(&buf, &bufsize, &count, &buflen, fp, 1);
 | |
| 		if (bp == NULL || strncmp(buf, "From ", 5) == 0) {
 | |
| 			if (off1 != (off_t)-1) {
 | |
| 				ok = maildir_append1(name, fp, off1,
 | |
| 						size, flag);
 | |
| 				if (ok == STOP)
 | |
| 					return STOP;
 | |
| 				fseek(fp, offs+buflen, SEEK_SET);
 | |
| 			}
 | |
| 			off1 = offs + buflen;
 | |
| 			size = 0;
 | |
| 			inhead = 1;
 | |
| 			flag = MNEW;
 | |
| 		} else
 | |
| 			size += buflen;
 | |
| 		offs += buflen;
 | |
| 		if (bp && buf[0] == '\n')
 | |
| 			inhead = 0;
 | |
| 		else if (bp && inhead && ascncasecmp(buf, "status", 6) == 0) {
 | |
| 			lp = &buf[6];
 | |
| 			while (whitechar(*lp&0377))
 | |
| 				lp++;
 | |
| 			if (*lp == ':')
 | |
| 				while (*++lp != '\0')
 | |
| 					switch (*lp) {
 | |
| 					case 'R':
 | |
| 						flag |= MREAD;
 | |
| 						break;
 | |
| 					case 'O':
 | |
| 						flag &= ~MNEW;
 | |
| 						break;
 | |
| 					}
 | |
| 		} else if (bp && inhead &&
 | |
| 				ascncasecmp(buf, "x-status", 8) == 0) {
 | |
| 			lp = &buf[8];
 | |
| 			while (whitechar(*lp&0377))
 | |
| 				lp++;
 | |
| 			if (*lp == ':')
 | |
| 				while (*++lp != '\0')
 | |
| 					switch (*lp) {
 | |
| 					case 'F':
 | |
| 						flag |= MFLAGGED;
 | |
| 						break;
 | |
| 					case 'A':
 | |
| 						flag |= MANSWERED;
 | |
| 						break;
 | |
| 					case 'T':
 | |
| 						flag |= MDRAFTED;
 | |
| 						break;
 | |
| 					}
 | |
| 		}
 | |
| 	} while (bp != NULL);
 | |
| 	free(buf);
 | |
| 	return OKAY;
 | |
| }
 | |
| 
 | |
| static enum okay
 | |
| maildir_append1(const char *name, FILE *fp, off_t off1, long size,
 | |
| 		enum mflag flag)
 | |
| {
 | |
| 	const int	attempts = 43200;
 | |
| 	struct stat	st;
 | |
| 	char	buf[4096];
 | |
| 	char	*fn, *tmp, *new;
 | |
| 	FILE	*op;
 | |
| 	long	n, z;
 | |
| 	int	i;
 | |
| 	time_t	now;
 | |
| 
 | |
| 	for (i = 0; i < attempts; i++) {
 | |
| 		time(&now);
 | |
| 		fn = mkname(now, flag, NULL);
 | |
| 		tmp = salloc(n = strlen(name) + strlen(fn) + 6);
 | |
| 		snprintf(tmp, n, "%s/tmp/%s", name, fn);
 | |
| 		if (stat(tmp, &st) < 0 && errno == ENOENT)
 | |
| 			break;
 | |
| 		sleep(2);
 | |
| 	}
 | |
| 	if (i >= attempts) {
 | |
| 		DEBUG_ERROR(
 | |
| 			"Cannot create unique file name in \"%s/tmp\".\n",
 | |
| 			name);
 | |
| 		return STOP;
 | |
| 	}
 | |
| 	if ((op = Fopen(tmp, "w")) == NULL) {
 | |
| 		DEBUG_ERROR("Cannot write to \"%s\".\n", tmp);
 | |
| 		return STOP;
 | |
| 	}
 | |
| 	fseek(fp, off1, SEEK_SET);
 | |
| 	while (size > 0) {
 | |
| 		z = size > sizeof buf ? sizeof buf : size;
 | |
| 		if ((n = fread(buf, 1, z, fp)) != z ||
 | |
| 				fwrite(buf, 1, n, op) != n) {
 | |
| 			DEBUG_ERROR("Error writing to \"%s\".\n", tmp);
 | |
| 			Fclose(op);
 | |
| 			unlink(tmp);
 | |
| 			return STOP;
 | |
| 		}
 | |
| 		size -= n;
 | |
| 	}
 | |
| 	Fclose(op);
 | |
| 	new = salloc(n = strlen(name) + strlen(fn) + 6);
 | |
| 	snprintf(new, n, "%s/new/%s", name, fn);
 | |
| 	if (link(tmp, new) < 0) {
 | |
| 		DEBUG_ERROR("Cannot link \"%s\" to \"%s\".\n", tmp, new);
 | |
| 		return STOP;
 | |
| 	}
 | |
| 	if (unlink(tmp) < 0)
 | |
| 		DEBUG_ERROR("Cannot unlink \"%s\".\n", tmp);
 | |
| 	return OKAY;
 | |
| }
 | |
| 
 | |
| static enum okay 
 | |
| trycreate(const char *name)
 | |
| {
 | |
| 	struct stat	st;
 | |
| 
 | |
| 	if (stat(name, &st) == 0) {
 | |
| 		if (!S_ISDIR(st.st_mode)) {
 | |
| 			DEBUG_ERROR("\"%s\" is not a directory.\n", name);
 | |
| 			return STOP;
 | |
| 		}
 | |
| 	} else if (makedir(name) != OKAY) {
 | |
| 		DEBUG_ERROR("Cannot create directory \"%s\".\n", name);
 | |
| 		return STOP;
 | |
| 	} else
 | |
| 		imap_created_mailbox++;
 | |
| 	return OKAY;
 | |
| }
 | |
| 
 | |
| static enum okay 
 | |
| mkmaildir(const char *name)
 | |
| {
 | |
| 	char	*np;
 | |
| 	size_t	sz;
 | |
| 	enum okay	ok = STOP;
 | |
| 
 | |
| 	if (trycreate(name) == OKAY) {
 | |
| 		np = ac_alloc((sz = strlen(name)) + 5);
 | |
| 		strcpy(np, name);
 | |
| 		strcpy(&np[sz], "/tmp");
 | |
| 		if (trycreate(np) == OKAY) {
 | |
| 			strcpy(&np[sz], "/new");
 | |
| 			if (trycreate(np) == OKAY) {
 | |
| 				strcpy(&np[sz], "/cur");
 | |
| 				if (trycreate(np) == OKAY)
 | |
| 					ok = OKAY;
 | |
| 			}
 | |
| 		}
 | |
| 		ac_free(np);
 | |
| 	}
 | |
| 	return ok;
 | |
| }
 | |
| 
 |