1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the BSD package               *
4 *Copyright (c) 1978-2010 The Regents of the University of California an*
5 *                                                                      *
6 * Redistribution and use in source and binary forms, with or           *
7 * without modification, are permitted provided that the following      *
8 * conditions are met:                                                  *
9 *                                                                      *
10 *    1. Redistributions of source code must retain the above           *
11 *       copyright notice, this list of conditions and the              *
12 *       following disclaimer.                                          *
13 *                                                                      *
14 *    2. Redistributions in binary form must reproduce the above        *
15 *       copyright notice, this list of conditions and the              *
16 *       following disclaimer in the documentation and/or other         *
17 *       materials provided with the distribution.                      *
18 *                                                                      *
19 *    3. Neither the name of The Regents of the University of California*
20 *       names of its contributors may be used to endorse or            *
21 *       promote products derived from this software without            *
22 *       specific prior written permission.                             *
23 *                                                                      *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND               *
25 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,          *
26 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF             *
27 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE             *
28 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS    *
29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,             *
30 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED      *
31 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,        *
32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON    *
33 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,      *
34 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY       *
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE              *
36 * POSSIBILITY OF SUCH DAMAGE.                                          *
37 *                                                                      *
38 * Redistribution and use in source and binary forms, with or without   *
39 * modification, are permitted provided that the following conditions   *
40 * are met:                                                             *
41 * 1. Redistributions of source code must retain the above copyright    *
42 *    notice, this list of conditions and the following disclaimer.     *
43 * 2. Redistributions in binary form must reproduce the above copyright *
44 *    notice, this list of conditions and the following disclaimer in   *
45 *    the documentation and/or other materials provided with the        *
46 *    distribution.                                                     *
47 * 3. Neither the name of the University nor the names of its           *
48 *    contributors may be used to endorse or promote products derived   *
49 *    from this software without specific prior written permission.     *
50 *                                                                      *
51 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS"    *
52 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED    *
53 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A      *
54 * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS    *
55 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,      *
56 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT     *
57 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF     *
58 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND  *
59 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,   *
60 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT   *
61 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF   *
62 * SUCH DAMAGE.                                                         *
63 *                                                                      *
64 *                          Kurt Shoens (UCB)                           *
65 *                                 gsf                                  *
66 *                                                                      *
67 ***********************************************************************/
68 #pragma prototyped
69 /*
70  * Mail -- a mail program
71  *
72  * Perform message editing functions.
73  */
74 
75 #include "mailx.h"
76 
77 /*
78  * Edit a message list.
79  */
80 static int
edit1(struct msg * msgvec,int type)81 edit1(struct msg* msgvec, int type)
82 {
83 	register struct msg*	ip;
84 	register struct msg*	mp;
85 	FILE*			fp;
86 	off_t			size;
87 	sig_t			sigint;
88 
89 	/*
90 	 * Deal with each message to be edited . . .
91 	 */
92 	for (ip = msgvec; ip->m_index; ip++) {
93 		if (ip > msgvec) {
94 			char	buf[100];
95 			char*	p;
96 
97 			note(PROMPT, "Edit message %d [ynq]? ", ip->m_index);
98 			if (!fgets(buf, sizeof buf, stdin))
99 				break;
100 			for (p = buf; isspace(*p); p++);
101 			if (*p == 'q' || *p == 'Q')
102 				break;
103 			if (*p == 'n' || *p == 'N')
104 				continue;
105 		}
106 		state.msg.dot = mp = state.msg.list + ip->m_index - 1;
107 		touchmsg(mp);
108 		sigint = signal(SIGINT, SIG_IGN);
109 		if (fp = run_editor(setinput(mp), mp->m_size, NiL, type, state.readonly)) {
110 			if (!state.msg.op)
111 				settmp(NiL, 0);
112 			fseek(state.msg.op, (off_t)0, SEEK_END);
113 			size = ftell(state.msg.op);
114 			mp->m_block = blocknumber(size);
115 			mp->m_offset = blockoffset(size);
116 			msgflags(mp, MODIFY, 0);
117 			rewind(fp);
118 			filecopy(NiL, fp, state.tmp.dir, state.msg.op, NiL, (off_t)0, &mp->m_lines, &mp->m_size, 0);
119 			fileclose(fp);
120 		}
121 		signal(SIGINT, sigint);
122 	}
123 	return 0;
124 }
125 
126 /*
127  * Edit a message list.
128  */
129 int
editor(struct msg * msgvec)130 editor(struct msg* msgvec)
131 {
132 	return edit1(msgvec, 'e');
133 }
134 
135 /*
136  * Invoke the visual editor on a message list.
137  */
138 int
visual(struct msg * msgvec)139 visual(struct msg* msgvec)
140 {
141 	return edit1(msgvec, 'v');
142 }
143 
144 /*
145  * Run an editor on the file at "fp" of "size" bytes,
146  * and return a new file pointer.
147  * Signals must be handled by the caller.
148  * "Type" is 'e' for state.var.editor, 'v' for state.var.visual.
149  */
150 FILE*
run_editor(register FILE * fp,off_t size,struct header * hp,int type,int readonly)151 run_editor(register FILE* fp, off_t size, struct header* hp, int type, int readonly)
152 {
153 	FILE*		ep;
154 	time_t		modtime;
155 	int		lc;
156 	int		err;
157 	char*		edit;
158 	unsigned long	editheaders = 0;
159 	struct parse	pp;
160 
161 	/*
162 	 * Create and copy to the temporary file.
163 	 */
164 	if (!(ep = fileopen(state.tmp.edit, "EMw")))
165 		goto ret1;
166 	if (size) {
167 		if (size < 0)
168 			size = 0;
169 		if (hp && state.var.editheaders) {
170 			editheaders = GEDIT|GRULE;
171 			headout(ep, hp, editheaders|GNL);
172 		}
173 		if (filecopy(NiL, fp, state.tmp.edit, ep, NiL, size, NiL, NiL, 0))
174 			goto ret2;
175 	}
176 	modtime = state.openstat.st_mtime;
177 	fileclose(ep);
178 	ep = 0;
179 	/*
180 	 * Edit the file.
181 	 */
182 	edit = type == 'e' ? state.var.editor : state.var.visual;
183 	err = run_command(edit, 0, -1, -1, state.tmp.edit, NiL, NiL) < 0;
184 	/*
185 	 * If in readonly mode or file unchanged, clean up and return.
186 	 */
187 	if (readonly)
188 		goto ret2;
189 	if (stat(state.tmp.edit, &state.openstat) < 0)
190 		goto ret1;
191 	if (modtime == state.openstat.st_mtime)
192 		goto ret2;
193 	if (err)
194 		note(0, "%s did not exit normally but did do some changes -- you may want to re-edit", edit);
195 	/*
196 	 * Now, switch to the temporary file.
197 	 */
198 	if (!(ep = fileopen(state.tmp.edit, "Ea+")))
199 		goto ret2;
200 	if (editheaders && headset(&pp, NiL, ep, hp, NiL, editheaders|GTO|GMETOO)) {
201 		while (headget(&pp));
202 		remove(state.tmp.edit);
203 		if (!(ep = fileopen(state.tmp.edit, "EMa+")))
204 			goto ret1;
205 		filecopy(NiL, pp.fp, state.tmp.edit, ep, NiL, (off_t)0, NiL, NiL, 0);
206 		fileclose(pp.fp);
207 	}
208 	/*
209 	 * Ensure that the tempEdit file ends with two newlines.
210 	 *
211 	 * XXX
212 	 * Probably ought to have a `From' line, as well.
213 	 *
214 	 * XXX
215 	 * If the file is only a single byte long, the seek is going to
216 	 * fail, but I'm not sure we care.  In the case of any error, we
217 	 * pass back the file descriptor -- if we fail because the disk
218 	 * is too full, reads should continue to work and I see no reason
219 	 * to discard the user's work.
220 	 */
221 	if (fseek(ep, (off_t)-2, SEEK_END) < 0)
222 		return ep;
223 	lc = getc(ep) == '\n' ? 1 : 0;
224 	if (getc(ep) == '\n')
225 		++lc;
226 	else
227 		lc = 0;
228 
229 	switch (lc) {
230 	case 0:
231 		if (putc('\n', ep) == EOF)
232 			break;
233 		/* FALLTHROUGH */
234 	case 1:
235 		putc('\n', ep);
236 		/* FALLTHROUGH */
237 	case 2:
238 		break;
239 	default:
240 		abort();
241 		break;
242 	}
243 	/*
244 	 * XXX: fflush() is necessary, so future stat(2) succeeds.
245 	 */
246 	fflush(ep);
247 	remove(state.tmp.edit);
248 	return ep;
249 
250  ret1:	note(SYSTEM, "%s", state.tmp.edit);
251  ret2:	remove(state.tmp.edit);
252 	if (ep)
253 		fileclose(ep);
254 	return 0;
255 }
256