Home | History | Annotate | Line # | Download | only in mail
fio.c revision 1.14
      1 /*	$NetBSD: fio.c,v 1.14 2001/12/19 00:06:11 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1980, 1993
      5  *	The Regents of the University of California.  All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. All advertising materials mentioning features or use of this software
     16  *    must display the following acknowledgement:
     17  *	This product includes software developed by the University of
     18  *	California, Berkeley and its contributors.
     19  * 4. Neither the name of the University nor the names of its contributors
     20  *    may be used to endorse or promote products derived from this software
     21  *    without specific prior written permission.
     22  *
     23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     33  * SUCH DAMAGE.
     34  */
     35 
     36 #include <sys/cdefs.h>
     37 #ifndef lint
     38 #if 0
     39 static char sccsid[] = "@(#)fio.c	8.2 (Berkeley) 4/20/95";
     40 #else
     41 __RCSID("$NetBSD: fio.c,v 1.14 2001/12/19 00:06:11 christos Exp $");
     42 #endif
     43 #endif /* not lint */
     44 
     45 #include "rcv.h"
     46 #include "extern.h"
     47 
     48 /*
     49  * Mail -- a mail program
     50  *
     51  * File I/O.
     52  */
     53 extern int wait_status;
     54 extern char *tmpdir;
     55 
     56 /*
     57  * Set up the input pointers while copying the mail file into /tmp.
     58  */
     59 void
     60 setptr(ibuf, offset)
     61 	FILE *ibuf;
     62 	off_t offset;
     63 {
     64 	int c, count;
     65 	char *cp, *cp2;
     66 	struct message this;
     67 	FILE *mestmp;
     68 	int maybe, inhead;
     69 	char linebuf[LINESIZE];
     70 	int omsgCount;
     71 
     72 	/* Get temporary file. */
     73 	(void)snprintf(linebuf, LINESIZE, "%s/mail.XXXXXX", tmpdir);
     74 	if ((c = mkstemp(linebuf)) == -1 ||
     75 	    (mestmp = Fdopen(c, "r+")) == NULL) {
     76 		(void)fprintf(stderr, "mail: can't open %s\n", linebuf);
     77 		exit(1);
     78 	}
     79 	(void)unlink(linebuf);
     80 
     81 	if (offset == 0) {
     82 		 msgCount = 0;
     83 	} else {
     84 		/* Seek into the file to get to the new messages */
     85 		(void) fseek(ibuf, offset, 0);
     86 		/*
     87 		 * We need to make "offset" a pointer to the end of
     88 		 * the temp file that has the copy of the mail file.
     89 		 * If any messages have been edited, this will be
     90 		 * different from the offset into the mail file.
     91 		 */
     92 		(void) fseek(otf, 0L, 2);
     93 		offset = ftell(otf);
     94 	}
     95 	omsgCount = msgCount;
     96 	maybe = 1;
     97 	inhead = 0;
     98 	this.m_flag = MUSED|MNEW;
     99 	this.m_size = 0;
    100 	this.m_lines = 0;
    101 	this.m_block = 0;
    102 	this.m_offset = 0;
    103 	for (;;) {
    104 		if (fgets(linebuf, LINESIZE, ibuf) == NULL) {
    105 			if (append(&this, mestmp)) {
    106 				perror("temporary file");
    107 				exit(1);
    108 			}
    109 			makemessage(mestmp, omsgCount);
    110 			return;
    111 		}
    112 		count = strlen(linebuf);
    113 		/*
    114 		 * Transforms lines ending in <CR><LF> to just <LF>.
    115 		 * This allows mail to be able to read Eudora mailboxes
    116 		 * that reside on a DOS partition.
    117 		 */
    118 		if (count >= 2 && linebuf[count-1] == '\n' && linebuf[count-2] == '\r') {
    119 			linebuf[count-2] = '\n';
    120 			count--;
    121 		}
    122 		(void) fwrite(linebuf, sizeof *linebuf, count, otf);
    123 		if (ferror(otf)) {
    124 			perror("/tmp");
    125 			exit(1);
    126 		}
    127 		if(count)
    128 			linebuf[count - 1] = 0;
    129 		if (maybe && linebuf[0] == 'F' && ishead(linebuf)) {
    130 			msgCount++;
    131 			if (append(&this, mestmp)) {
    132 				perror("temporary file");
    133 				exit(1);
    134 			}
    135 			this.m_flag = MUSED|MNEW;
    136 			this.m_size = 0;
    137 			this.m_lines = 0;
    138 			this.m_block = blockof(offset);
    139 			this.m_offset = offsetof(offset);
    140 			inhead = 1;
    141 		} else if (linebuf[0] == 0) {
    142 			inhead = 0;
    143 		} else if (inhead) {
    144 			for (cp = linebuf, cp2 = "status";; cp++) {
    145 				if ((c = *cp2++) == 0) {
    146 					while (isspace((unsigned char)*cp++))
    147 						;
    148 					if (cp[-1] != ':')
    149 						break;
    150 					while ((c = *cp++) != '\0')
    151 						if (c == 'R')
    152 							this.m_flag |= MREAD;
    153 						else if (c == 'O')
    154 							this.m_flag &= ~MNEW;
    155 					inhead = 0;
    156 					break;
    157 				}
    158 				if (*cp != c && *cp != toupper(c))
    159 					break;
    160 			}
    161 		}
    162 		offset += count;
    163 		this.m_size += count;
    164 		this.m_lines++;
    165 		maybe = linebuf[0] == 0;
    166 	}
    167 }
    168 
    169 /*
    170  * Drop the passed line onto the passed output buffer.
    171  * If a write error occurs, return -1, else the count of
    172  * characters written, including the newline if requested.
    173  */
    174 int
    175 putline(obuf, linebuf, outlf)
    176 	FILE *obuf;
    177 	char *linebuf;
    178 	int   outlf;
    179 {
    180 	int c;
    181 
    182 	c = strlen(linebuf);
    183 	(void) fwrite(linebuf, sizeof *linebuf, c, obuf);
    184 	if (outlf) {
    185 		(void) putc('\n', obuf);
    186 		c++;
    187 	}
    188 	if (ferror(obuf))
    189 		return (-1);
    190 	return (c);
    191 }
    192 
    193 /*
    194  * Read up a line from the specified input into the line
    195  * buffer.  Return the number of characters read.  Do not
    196  * include the newline at the end.
    197  */
    198 int
    199 readline(ibuf, linebuf, linesize)
    200 	FILE *ibuf;
    201 	char *linebuf;
    202 	int linesize;
    203 {
    204 	int n;
    205 
    206 	clearerr(ibuf);
    207 	if (fgets(linebuf, linesize, ibuf) == NULL)
    208 		return -1;
    209 	n = strlen(linebuf);
    210 	if (n > 0 && linebuf[n - 1] == '\n')
    211 		linebuf[--n] = '\0';
    212 	return n;
    213 }
    214 
    215 /*
    216  * Return a file buffer all ready to read up the
    217  * passed message pointer.
    218  */
    219 FILE *
    220 setinput(mp)
    221 	struct message *mp;
    222 {
    223 
    224 	fflush(otf);
    225 	if (fseek(itf, (long)positionof(mp->m_block, mp->m_offset), 0) < 0)
    226 		err(1, "fseek");
    227 	return (itf);
    228 }
    229 
    230 /*
    231  * Take the data out of the passed ghost file and toss it into
    232  * a dynamically allocated message structure.
    233  */
    234 void
    235 makemessage(f, omsgCount)
    236 	FILE *f;
    237 	int omsgCount;
    238 {
    239 	int size = (msgCount + 1) * sizeof (struct message);
    240 	struct message *nmessage = realloc(message, size);
    241 
    242 	if (nmessage == NULL)
    243 		err(1, "Insufficient memory for %d messages", msgCount);
    244 	if (omsgCount == 0 || message == NULL)
    245 		dot = nmessage;
    246 	else
    247 		dot = nmessage + (dot - message);
    248 	message = nmessage;
    249 	size -= (omsgCount + 1) * sizeof (struct message);
    250 	fflush(f);
    251 	(void) lseek(fileno(f), (off_t)sizeof *message, 0);
    252 	if (read(fileno(f), (char *) &message[omsgCount], size) != size)
    253 		errx(1, "Message temporary file corrupted");
    254 	message[msgCount].m_size = 0;
    255 	message[msgCount].m_lines = 0;
    256 	Fclose(f);
    257 }
    258 
    259 /*
    260  * Append the passed message descriptor onto the temp file.
    261  * If the write fails, return 1, else 0
    262  */
    263 int
    264 append(mp, f)
    265 	struct message *mp;
    266 	FILE *f;
    267 {
    268 	return fwrite((char *) mp, sizeof *mp, 1, f) != 1;
    269 }
    270 
    271 /*
    272  * Delete a file, but only if the file is a plain file.
    273  */
    274 int
    275 rm(name)
    276 	char *name;
    277 {
    278 	struct stat sb;
    279 
    280 	if (stat(name, &sb) < 0)
    281 		return(-1);
    282 	if (!S_ISREG(sb.st_mode)) {
    283 		errno = EISDIR;
    284 		return(-1);
    285 	}
    286 	return(unlink(name));
    287 }
    288 
    289 static int sigdepth;		/* depth of holdsigs() */
    290 static sigset_t nset, oset;
    291 /*
    292  * Hold signals SIGHUP, SIGINT, and SIGQUIT.
    293  */
    294 void
    295 holdsigs()
    296 {
    297 
    298 	if (sigdepth++ == 0) {
    299 		sigemptyset(&nset);
    300 		sigaddset(&nset, SIGHUP);
    301 		sigaddset(&nset, SIGINT);
    302 		sigaddset(&nset, SIGQUIT);
    303 		sigprocmask(SIG_BLOCK, &nset, &oset);
    304 	}
    305 }
    306 
    307 /*
    308  * Release signals SIGHUP, SIGINT, and SIGQUIT.
    309  */
    310 void
    311 relsesigs()
    312 {
    313 
    314 	if (--sigdepth == 0)
    315 		sigprocmask(SIG_SETMASK, &oset, NULL);
    316 }
    317 
    318 /*
    319  * Determine the size of the file possessed by
    320  * the passed buffer.
    321  */
    322 off_t
    323 fsize(iob)
    324 	FILE *iob;
    325 {
    326 	struct stat sbuf;
    327 
    328 	if (fstat(fileno(iob), &sbuf) < 0)
    329 		return 0;
    330 	return sbuf.st_size;
    331 }
    332 
    333 /*
    334  * Evaluate the string given as a new mailbox name.
    335  * Supported meta characters:
    336  *	%	for my system mail box
    337  *	%user	for user's system mail box
    338  *	#	for previous file
    339  *	&	invoker's mbox file
    340  *	+file	file in folder directory
    341  *	any shell meta character
    342  * Return the file name as a dynamic string.
    343  */
    344 char *
    345 expand(name)
    346 	char *name;
    347 {
    348 	char xname[PATHSIZE];
    349 	char cmdbuf[PATHSIZE];		/* also used for file names */
    350 	int pid, l;
    351 	char *cp, *shell;
    352 	int pivec[2];
    353 	struct stat sbuf;
    354 
    355 	/*
    356 	 * The order of evaluation is "%" and "#" expand into constants.
    357 	 * "&" can expand into "+".  "+" can expand into shell meta characters.
    358 	 * Shell meta characters expand into constants.
    359 	 * This way, we make no recursive expansion.
    360 	 */
    361 	switch (*name) {
    362 	case '%':
    363 		findmail(name[1] ? name + 1 : myname, xname);
    364 		return savestr(xname);
    365 	case '#':
    366 		if (name[1] != 0)
    367 			break;
    368 		if (prevfile[0] == 0) {
    369 			printf("No previous file\n");
    370 			return NOSTR;
    371 		}
    372 		return savestr(prevfile);
    373 	case '&':
    374 		if (name[1] == 0 && (name = value("MBOX")) == NOSTR)
    375 			name = "~/mbox";
    376 		/* fall through */
    377 	}
    378 	if (name[0] == '+' && getfold(cmdbuf) >= 0) {
    379 		snprintf(xname, PATHSIZE, "%s/%s", cmdbuf, name + 1);
    380 		name = savestr(xname);
    381 	}
    382 	/* catch the most common shell meta character */
    383 	if (name[0] == '~' && (name[1] == '/' || name[1] == '\0')) {
    384 		snprintf(xname, PATHSIZE, "%s%s", homedir, name + 1);
    385 		name = savestr(xname);
    386 	}
    387 	if (!anyof(name, "~{[*?$`'\"\\"))
    388 		return name;
    389 	if (pipe(pivec) < 0) {
    390 		perror("pipe");
    391 		return name;
    392 	}
    393 	snprintf(cmdbuf, PATHSIZE, "echo %s", name);
    394 	if ((shell = value("SHELL")) == NOSTR)
    395 		shell = _PATH_CSHELL;
    396 	pid = start_command(shell, 0, -1, pivec[1], "-c", cmdbuf, NOSTR);
    397 	if (pid < 0) {
    398 		close(pivec[0]);
    399 		close(pivec[1]);
    400 		return NOSTR;
    401 	}
    402 	close(pivec[1]);
    403 	l = read(pivec[0], xname, PATHSIZE);
    404 	close(pivec[0]);
    405 	if (wait_child(pid) < 0 && WTERMSIG(wait_status) != SIGPIPE) {
    406 		fprintf(stderr, "\"%s\": Expansion failed.\n", name);
    407 		return NOSTR;
    408 	}
    409 	if (l < 0) {
    410 		perror("read");
    411 		return NOSTR;
    412 	}
    413 	if (l == 0) {
    414 		fprintf(stderr, "\"%s\": No match.\n", name);
    415 		return NOSTR;
    416 	}
    417 	if (l == PATHSIZE) {
    418 		fprintf(stderr, "\"%s\": Expansion buffer overflow.\n", name);
    419 		return NOSTR;
    420 	}
    421 	xname[l] = '\0';
    422 	for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--)
    423 		;
    424 	cp[1] = '\0';
    425 	if (strchr(xname, ' ') && stat(xname, &sbuf) < 0) {
    426 		fprintf(stderr, "\"%s\": Ambiguous.\n", name);
    427 		return NOSTR;
    428 	}
    429 	return savestr(xname);
    430 }
    431 
    432 /*
    433  * Determine the current folder directory name.
    434  */
    435 int
    436 getfold(name)
    437 	char *name;
    438 {
    439 	char *folder;
    440 
    441 	if ((folder = value("folder")) == NOSTR)
    442 		return (-1);
    443 	if (*folder == '/') {
    444 		strncpy(name, folder, PATHSIZE - 1);
    445 		name[PATHSIZE - 1] = '\0' ;
    446 	}
    447 	else
    448 		snprintf(name, PATHSIZE, "%s/%s", homedir, folder);
    449 	return (0);
    450 }
    451 
    452 /*
    453  * Return the name of the dead.letter file.
    454  */
    455 char *
    456 getdeadletter()
    457 {
    458 	char *cp;
    459 
    460 	if ((cp = value("DEAD")) == NOSTR || (cp = expand(cp)) == NOSTR)
    461 		cp = expand("~/dead.letter");
    462 	else if (*cp != '/') {
    463 		char buf[PATHSIZE];
    464 
    465 		(void) snprintf(buf, PATHSIZE, "~/%s", cp);
    466 		cp = expand(buf);
    467 	}
    468 	return cp;
    469 }
    470