1e0b8e63eSJohn Marino /*-
2e0b8e63eSJohn Marino * Copyright (c) 1993, 1994
3e0b8e63eSJohn Marino * The Regents of the University of California. All rights reserved.
4e0b8e63eSJohn Marino * Copyright (c) 1993, 1994, 1995, 1996
5e0b8e63eSJohn Marino * Keith Bostic. All rights reserved.
6e0b8e63eSJohn Marino *
7e0b8e63eSJohn Marino * See the LICENSE file for redistribution information.
8e0b8e63eSJohn Marino */
9e0b8e63eSJohn Marino
10e0b8e63eSJohn Marino #include "config.h"
11e0b8e63eSJohn Marino
12e0b8e63eSJohn Marino #include <sys/types.h>
13e0b8e63eSJohn Marino #include <sys/queue.h>
14e0b8e63eSJohn Marino #include <sys/stat.h>
15e0b8e63eSJohn Marino
16e0b8e63eSJohn Marino /*
17e0b8e63eSJohn Marino * We include <sys/file.h>, because the open #defines were found there
18e0b8e63eSJohn Marino * on historical systems. We also include <fcntl.h> because the open(2)
19e0b8e63eSJohn Marino * #defines are found there on newer systems.
20e0b8e63eSJohn Marino */
21e0b8e63eSJohn Marino #include <sys/file.h>
22e0b8e63eSJohn Marino
23e0b8e63eSJohn Marino #include <bitstring.h>
24e0b8e63eSJohn Marino #include <dirent.h>
25e0b8e63eSJohn Marino #include <errno.h>
26e0b8e63eSJohn Marino #include <fcntl.h>
27e0b8e63eSJohn Marino #include <limits.h>
28e0b8e63eSJohn Marino #include <pwd.h>
29e0b8e63eSJohn Marino #include <netinet/in.h> /* Required by resolv.h. */
30e0b8e63eSJohn Marino #include <resolv.h>
31e0b8e63eSJohn Marino #include <stdio.h>
32e0b8e63eSJohn Marino #include <stdlib.h>
33e0b8e63eSJohn Marino #include <string.h>
34e0b8e63eSJohn Marino #include <time.h>
35e0b8e63eSJohn Marino #include <unistd.h>
36e0b8e63eSJohn Marino
37c5dc3490SJohn Marino #include "version.h"
38e0b8e63eSJohn Marino #include "common.h"
39e0b8e63eSJohn Marino #include "pathnames.h"
40e0b8e63eSJohn Marino
41e0b8e63eSJohn Marino /*
42e0b8e63eSJohn Marino * Recovery code.
43e0b8e63eSJohn Marino *
44e0b8e63eSJohn Marino * The basic scheme is as follows. In the EXF structure, we maintain full
45e0b8e63eSJohn Marino * paths of a b+tree file and a mail recovery file. The former is the file
46e0b8e63eSJohn Marino * used as backing store by the DB package. The latter is the file that
47e0b8e63eSJohn Marino * contains an email message to be sent to the user if we crash. The two
48e0b8e63eSJohn Marino * simple states of recovery are:
49e0b8e63eSJohn Marino *
50e0b8e63eSJohn Marino * + first starting the edit session:
51e0b8e63eSJohn Marino * the b+tree file exists and is mode 700, the mail recovery
52e0b8e63eSJohn Marino * file doesn't exist.
53e0b8e63eSJohn Marino * + after the file has been modified:
54e0b8e63eSJohn Marino * the b+tree file exists and is mode 600, the mail recovery
55e0b8e63eSJohn Marino * file exists, and is exclusively locked.
56e0b8e63eSJohn Marino *
57e0b8e63eSJohn Marino * In the EXF structure we maintain a file descriptor that is the locked
58e0b8e63eSJohn Marino * file descriptor for the mail recovery file.
59e0b8e63eSJohn Marino *
60e0b8e63eSJohn Marino * To find out if a recovery file/backing file pair are in use, try to get
61e0b8e63eSJohn Marino * a lock on the recovery file.
62e0b8e63eSJohn Marino *
63e0b8e63eSJohn Marino * To find out if a backing file can be deleted at boot time, check for an
64e0b8e63eSJohn Marino * owner execute bit. (Yes, I know it's ugly, but it's either that or put
65e0b8e63eSJohn Marino * special stuff into the backing file itself, or correlate the files at
66e0b8e63eSJohn Marino * boot time, neither of which looks like fun.) Note also that there's a
67e0b8e63eSJohn Marino * window between when the file is created and the X bit is set. It's small,
68e0b8e63eSJohn Marino * but it's there. To fix the window, check for 0 length files as well.
69e0b8e63eSJohn Marino *
70e0b8e63eSJohn Marino * To find out if a file can be recovered, check the F_RCV_ON bit. Note,
71e0b8e63eSJohn Marino * this DOES NOT mean that any initialization has been done, only that we
72e0b8e63eSJohn Marino * haven't yet failed at setting up or doing recovery.
73e0b8e63eSJohn Marino *
74e0b8e63eSJohn Marino * To preserve a recovery file/backing file pair, set the F_RCV_NORM bit.
75e0b8e63eSJohn Marino * If that bit is not set when ending a file session:
76e0b8e63eSJohn Marino * If the EXF structure paths (rcv_path and rcv_mpath) are not NULL,
77e0b8e63eSJohn Marino * they are unlink(2)'d, and free(3)'d.
78e0b8e63eSJohn Marino * If the EXF file descriptor (rcv_fd) is not -1, it is closed.
79e0b8e63eSJohn Marino *
80e0b8e63eSJohn Marino * The backing b+tree file is set up when a file is first edited, so that
81e0b8e63eSJohn Marino * the DB package can use it for on-disk caching and/or to snapshot the
82e0b8e63eSJohn Marino * file. When the file is first modified, the mail recovery file is created,
83e0b8e63eSJohn Marino * the backing file permissions are updated, the file is sync(2)'d to disk,
84e0b8e63eSJohn Marino * and the timer is started. Then, at RCV_PERIOD second intervals, the
85e0b8e63eSJohn Marino * b+tree file is synced to disk. RCV_PERIOD is measured using SIGALRM, which
86e0b8e63eSJohn Marino * means that the data structures (SCR, EXF, the underlying tree structures)
87e0b8e63eSJohn Marino * must be consistent when the signal arrives.
88e0b8e63eSJohn Marino *
89e0b8e63eSJohn Marino * The recovery mail file contains normal mail headers, with two additional
90e0b8e63eSJohn Marino *
91e0b8e63eSJohn Marino * X-vi-data: <file|path>;<base64 encoded path>
92e0b8e63eSJohn Marino *
93e0b8e63eSJohn Marino * MIME headers; the folding character is limited to ' '.
94e0b8e63eSJohn Marino *
95e0b8e63eSJohn Marino * Btree files are named "vi.XXXXXX" and recovery files are named
96e0b8e63eSJohn Marino * "recover.XXXXXX".
97e0b8e63eSJohn Marino */
98e0b8e63eSJohn Marino
99e0b8e63eSJohn Marino #define VI_DHEADER "X-vi-data:"
100e0b8e63eSJohn Marino
101e0b8e63eSJohn Marino static int rcv_copy(SCR *, int, char *);
102e0b8e63eSJohn Marino static void rcv_email(SCR *, char *);
103e0b8e63eSJohn Marino static int rcv_mailfile(SCR *, int, char *);
104e0b8e63eSJohn Marino static int rcv_mktemp(SCR *, char *, char *);
105e0b8e63eSJohn Marino static int rcv_dlnwrite(SCR *, const char *, const char *, FILE *);
106e0b8e63eSJohn Marino static int rcv_dlnread(SCR *, char **, char **, FILE *);
107e0b8e63eSJohn Marino
108e0b8e63eSJohn Marino /*
109e0b8e63eSJohn Marino * rcv_tmp --
110e0b8e63eSJohn Marino * Build a file name that will be used as the recovery file.
111e0b8e63eSJohn Marino *
112e0b8e63eSJohn Marino * PUBLIC: int rcv_tmp(SCR *, EXF *, char *);
113e0b8e63eSJohn Marino */
114e0b8e63eSJohn Marino int
rcv_tmp(SCR * sp,EXF * ep,char * name)115*b1ac2ebbSDaniel Fojt rcv_tmp(SCR *sp, EXF *ep, char *name)
116e0b8e63eSJohn Marino {
117e0b8e63eSJohn Marino struct stat sb;
118e0b8e63eSJohn Marino int fd;
119e0b8e63eSJohn Marino char *dp, *path;
120e0b8e63eSJohn Marino
121e0b8e63eSJohn Marino /*
122e0b8e63eSJohn Marino * !!!
123e0b8e63eSJohn Marino * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER.
124e0b8e63eSJohn Marino *
125e0b8e63eSJohn Marino *
126e0b8e63eSJohn Marino * If the recovery directory doesn't exist, try and create it. As
127e0b8e63eSJohn Marino * the recovery files are themselves protected from reading/writing
128e0b8e63eSJohn Marino * by other than the owner, the worst that can happen is that a user
129e0b8e63eSJohn Marino * would have permission to remove other user's recovery files. If
130e0b8e63eSJohn Marino * the sticky bit has the BSD semantics, that too will be impossible.
131e0b8e63eSJohn Marino */
132e0b8e63eSJohn Marino if (opts_empty(sp, O_RECDIR, 0))
133e0b8e63eSJohn Marino goto err;
134e0b8e63eSJohn Marino dp = O_STR(sp, O_RECDIR);
135e0b8e63eSJohn Marino if (stat(dp, &sb)) {
136e0b8e63eSJohn Marino if (errno != ENOENT || mkdir(dp, 0)) {
137e0b8e63eSJohn Marino msgq(sp, M_SYSERR, "%s", dp);
138e0b8e63eSJohn Marino goto err;
139e0b8e63eSJohn Marino }
140e0b8e63eSJohn Marino (void)chmod(dp, S_IRWXU | S_IRWXG | S_IRWXO | S_ISVTX);
141e0b8e63eSJohn Marino }
142e0b8e63eSJohn Marino
143e0b8e63eSJohn Marino if ((path = join(dp, "vi.XXXXXX")) == NULL)
144e0b8e63eSJohn Marino goto err;
145e0b8e63eSJohn Marino if ((fd = rcv_mktemp(sp, path, dp)) == -1) {
146e0b8e63eSJohn Marino free(path);
147e0b8e63eSJohn Marino goto err;
148e0b8e63eSJohn Marino }
149e0b8e63eSJohn Marino (void)fchmod(fd, S_IRWXU);
150e0b8e63eSJohn Marino (void)close(fd);
151e0b8e63eSJohn Marino
152e0b8e63eSJohn Marino ep->rcv_path = path;
153e0b8e63eSJohn Marino if (0) {
154e0b8e63eSJohn Marino err: msgq(sp, M_ERR,
155e0b8e63eSJohn Marino "056|Modifications not recoverable if the session fails");
156e0b8e63eSJohn Marino return (1);
157e0b8e63eSJohn Marino }
158e0b8e63eSJohn Marino
159e0b8e63eSJohn Marino /* We believe the file is recoverable. */
160e0b8e63eSJohn Marino F_SET(ep, F_RCV_ON);
161e0b8e63eSJohn Marino return (0);
162e0b8e63eSJohn Marino }
163e0b8e63eSJohn Marino
164e0b8e63eSJohn Marino /*
165e0b8e63eSJohn Marino * rcv_init --
166e0b8e63eSJohn Marino * Force the file to be snapshotted for recovery.
167e0b8e63eSJohn Marino *
168e0b8e63eSJohn Marino * PUBLIC: int rcv_init(SCR *);
169e0b8e63eSJohn Marino */
170e0b8e63eSJohn Marino int
rcv_init(SCR * sp)171e0b8e63eSJohn Marino rcv_init(SCR *sp)
172e0b8e63eSJohn Marino {
173e0b8e63eSJohn Marino EXF *ep;
174e0b8e63eSJohn Marino recno_t lno;
175e0b8e63eSJohn Marino
176e0b8e63eSJohn Marino ep = sp->ep;
177e0b8e63eSJohn Marino
178e0b8e63eSJohn Marino /* Only do this once. */
179e0b8e63eSJohn Marino F_CLR(ep, F_FIRSTMODIFY);
180e0b8e63eSJohn Marino
181e0b8e63eSJohn Marino /* If we already know the file isn't recoverable, we're done. */
182e0b8e63eSJohn Marino if (!F_ISSET(ep, F_RCV_ON))
183e0b8e63eSJohn Marino return (0);
184e0b8e63eSJohn Marino
185e0b8e63eSJohn Marino /* Turn off recoverability until we figure out if this will work. */
186e0b8e63eSJohn Marino F_CLR(ep, F_RCV_ON);
187e0b8e63eSJohn Marino
188e0b8e63eSJohn Marino /* Test if we're recovering a file, not editing one. */
189e0b8e63eSJohn Marino if (ep->rcv_mpath == NULL) {
190e0b8e63eSJohn Marino /* Build a file to mail to the user. */
191e0b8e63eSJohn Marino if (rcv_mailfile(sp, 0, NULL))
192e0b8e63eSJohn Marino goto err;
193e0b8e63eSJohn Marino
194e0b8e63eSJohn Marino /* Force a read of the entire file. */
195e0b8e63eSJohn Marino if (db_last(sp, &lno))
196e0b8e63eSJohn Marino goto err;
197e0b8e63eSJohn Marino
198e0b8e63eSJohn Marino /* Turn on a busy message, and sync it to backing store. */
199e0b8e63eSJohn Marino sp->gp->scr_busy(sp,
200e0b8e63eSJohn Marino "057|Copying file for recovery...", BUSY_ON);
201e0b8e63eSJohn Marino if (ep->db->sync(ep->db, R_RECNOSYNC)) {
202e0b8e63eSJohn Marino msgq_str(sp, M_SYSERR, ep->rcv_path,
203e0b8e63eSJohn Marino "058|Preservation failed: %s");
204e0b8e63eSJohn Marino sp->gp->scr_busy(sp, NULL, BUSY_OFF);
205e0b8e63eSJohn Marino goto err;
206e0b8e63eSJohn Marino }
207e0b8e63eSJohn Marino sp->gp->scr_busy(sp, NULL, BUSY_OFF);
208e0b8e63eSJohn Marino }
209e0b8e63eSJohn Marino
210e0b8e63eSJohn Marino /* Turn off the owner execute bit. */
211e0b8e63eSJohn Marino (void)chmod(ep->rcv_path, S_IRUSR | S_IWUSR);
212e0b8e63eSJohn Marino
213e0b8e63eSJohn Marino /* We believe the file is recoverable. */
214e0b8e63eSJohn Marino F_SET(ep, F_RCV_ON);
215e0b8e63eSJohn Marino return (0);
216e0b8e63eSJohn Marino
217e0b8e63eSJohn Marino err: msgq(sp, M_ERR,
218e0b8e63eSJohn Marino "059|Modifications not recoverable if the session fails");
219e0b8e63eSJohn Marino return (1);
220e0b8e63eSJohn Marino }
221e0b8e63eSJohn Marino
222e0b8e63eSJohn Marino /*
223e0b8e63eSJohn Marino * rcv_sync --
224e0b8e63eSJohn Marino * Sync the file, optionally:
225e0b8e63eSJohn Marino * flagging the backup file to be preserved
226e0b8e63eSJohn Marino * snapshotting the backup file and send email to the user
227e0b8e63eSJohn Marino * sending email to the user if the file was modified
228e0b8e63eSJohn Marino * ending the file session
229e0b8e63eSJohn Marino *
230e0b8e63eSJohn Marino * PUBLIC: int rcv_sync(SCR *, u_int);
231e0b8e63eSJohn Marino */
232e0b8e63eSJohn Marino int
rcv_sync(SCR * sp,u_int flags)233*b1ac2ebbSDaniel Fojt rcv_sync(SCR *sp, u_int flags)
234e0b8e63eSJohn Marino {
235e0b8e63eSJohn Marino EXF *ep;
236e0b8e63eSJohn Marino int fd, rval;
237e0b8e63eSJohn Marino char *dp, *buf;
238e0b8e63eSJohn Marino
239e0b8e63eSJohn Marino /* Make sure that there's something to recover/sync. */
240e0b8e63eSJohn Marino ep = sp->ep;
241e0b8e63eSJohn Marino if (ep == NULL || !F_ISSET(ep, F_RCV_ON))
242e0b8e63eSJohn Marino return (0);
243e0b8e63eSJohn Marino
244e0b8e63eSJohn Marino /* Sync the file if it's been modified. */
245e0b8e63eSJohn Marino if (F_ISSET(ep, F_MODIFIED)) {
246e0b8e63eSJohn Marino if (ep->db->sync(ep->db, R_RECNOSYNC)) {
247e0b8e63eSJohn Marino F_CLR(ep, F_RCV_ON | F_RCV_NORM);
248e0b8e63eSJohn Marino msgq_str(sp, M_SYSERR,
249e0b8e63eSJohn Marino ep->rcv_path, "060|File backup failed: %s");
250e0b8e63eSJohn Marino return (1);
251e0b8e63eSJohn Marino }
252e0b8e63eSJohn Marino
253e0b8e63eSJohn Marino /* REQUEST: don't remove backing file on exit. */
254e0b8e63eSJohn Marino if (LF_ISSET(RCV_PRESERVE))
255e0b8e63eSJohn Marino F_SET(ep, F_RCV_NORM);
256e0b8e63eSJohn Marino
257e0b8e63eSJohn Marino /* REQUEST: send email. */
258e0b8e63eSJohn Marino if (LF_ISSET(RCV_EMAIL))
259e0b8e63eSJohn Marino rcv_email(sp, ep->rcv_mpath);
260e0b8e63eSJohn Marino }
261e0b8e63eSJohn Marino
262e0b8e63eSJohn Marino /*
263e0b8e63eSJohn Marino * !!!
264e0b8e63eSJohn Marino * Each time the user exec's :preserve, we have to snapshot all of
265e0b8e63eSJohn Marino * the recovery information, i.e. it's like the user re-edited the
266e0b8e63eSJohn Marino * file. We copy the DB(3) backing file, and then create a new mail
267e0b8e63eSJohn Marino * recovery file, it's simpler than exiting and reopening all of the
268e0b8e63eSJohn Marino * underlying files.
269e0b8e63eSJohn Marino *
270e0b8e63eSJohn Marino * REQUEST: snapshot the file.
271e0b8e63eSJohn Marino */
272e0b8e63eSJohn Marino rval = 0;
273e0b8e63eSJohn Marino if (LF_ISSET(RCV_SNAPSHOT)) {
274e0b8e63eSJohn Marino if (opts_empty(sp, O_RECDIR, 0))
275e0b8e63eSJohn Marino goto err;
276e0b8e63eSJohn Marino dp = O_STR(sp, O_RECDIR);
277e0b8e63eSJohn Marino if ((buf = join(dp, "vi.XXXXXX")) == NULL) {
278e0b8e63eSJohn Marino msgq(sp, M_SYSERR, NULL);
279e0b8e63eSJohn Marino goto err;
280e0b8e63eSJohn Marino }
281e0b8e63eSJohn Marino if ((fd = rcv_mktemp(sp, buf, dp)) == -1) {
282e0b8e63eSJohn Marino free(buf);
283e0b8e63eSJohn Marino goto err;
284e0b8e63eSJohn Marino }
285e0b8e63eSJohn Marino sp->gp->scr_busy(sp,
286e0b8e63eSJohn Marino "061|Copying file for recovery...", BUSY_ON);
287e0b8e63eSJohn Marino if (rcv_copy(sp, fd, ep->rcv_path) ||
288e0b8e63eSJohn Marino close(fd) || rcv_mailfile(sp, 1, buf)) {
289e0b8e63eSJohn Marino (void)unlink(buf);
290e0b8e63eSJohn Marino (void)close(fd);
291e0b8e63eSJohn Marino rval = 1;
292e0b8e63eSJohn Marino }
293e0b8e63eSJohn Marino free(buf);
294e0b8e63eSJohn Marino sp->gp->scr_busy(sp, NULL, BUSY_OFF);
295e0b8e63eSJohn Marino }
296e0b8e63eSJohn Marino if (0) {
297e0b8e63eSJohn Marino err: rval = 1;
298e0b8e63eSJohn Marino }
299e0b8e63eSJohn Marino
300e0b8e63eSJohn Marino /* REQUEST: end the file session. */
301e0b8e63eSJohn Marino if (LF_ISSET(RCV_ENDSESSION) && file_end(sp, NULL, 1))
302e0b8e63eSJohn Marino rval = 1;
303e0b8e63eSJohn Marino
304e0b8e63eSJohn Marino return (rval);
305e0b8e63eSJohn Marino }
306e0b8e63eSJohn Marino
307e0b8e63eSJohn Marino /*
308e0b8e63eSJohn Marino * rcv_mailfile --
309e0b8e63eSJohn Marino * Build the file to mail to the user.
310e0b8e63eSJohn Marino */
311e0b8e63eSJohn Marino static int
rcv_mailfile(SCR * sp,int issync,char * cp_path)312*b1ac2ebbSDaniel Fojt rcv_mailfile(SCR *sp, int issync, char *cp_path)
313e0b8e63eSJohn Marino {
314e0b8e63eSJohn Marino EXF *ep;
315e0b8e63eSJohn Marino GS *gp;
316e0b8e63eSJohn Marino struct passwd *pw;
317e0b8e63eSJohn Marino int len;
318e0b8e63eSJohn Marino time_t now;
319e0b8e63eSJohn Marino uid_t uid;
320e0b8e63eSJohn Marino int fd;
321e0b8e63eSJohn Marino FILE *fp;
322e0b8e63eSJohn Marino char *dp, *p, *t, *qt, *buf, *mpath;
323e0b8e63eSJohn Marino char *t1, *t2, *t3;
324e0b8e63eSJohn Marino int st;
325e0b8e63eSJohn Marino
326e0b8e63eSJohn Marino /*
327e0b8e63eSJohn Marino * XXX
328e0b8e63eSJohn Marino * MAXHOSTNAMELEN/HOST_NAME_MAX are deprecated. We try sysconf(3)
329e0b8e63eSJohn Marino * first, then fallback to _POSIX_HOST_NAME_MAX.
330e0b8e63eSJohn Marino */
331e0b8e63eSJohn Marino char *host;
332e0b8e63eSJohn Marino long hostmax = sysconf(_SC_HOST_NAME_MAX);
333e0b8e63eSJohn Marino if (hostmax < 0)
334e0b8e63eSJohn Marino hostmax = _POSIX_HOST_NAME_MAX;
335e0b8e63eSJohn Marino
336e0b8e63eSJohn Marino gp = sp->gp;
337e0b8e63eSJohn Marino if ((pw = getpwuid(uid = getuid())) == NULL) {
338e0b8e63eSJohn Marino msgq(sp, M_ERR,
339e0b8e63eSJohn Marino "062|Information on user id %u not found", uid);
340e0b8e63eSJohn Marino return (1);
341e0b8e63eSJohn Marino }
342e0b8e63eSJohn Marino
343e0b8e63eSJohn Marino if (opts_empty(sp, O_RECDIR, 0))
344e0b8e63eSJohn Marino return (1);
345e0b8e63eSJohn Marino dp = O_STR(sp, O_RECDIR);
346e0b8e63eSJohn Marino if ((mpath = join(dp, "recover.XXXXXX")) == NULL) {
347e0b8e63eSJohn Marino msgq(sp, M_SYSERR, NULL);
348e0b8e63eSJohn Marino return (1);
349e0b8e63eSJohn Marino }
350e0b8e63eSJohn Marino if ((fd = rcv_mktemp(sp, mpath, dp)) == -1) {
351e0b8e63eSJohn Marino free(mpath);
352e0b8e63eSJohn Marino return (1);
353e0b8e63eSJohn Marino }
354e0b8e63eSJohn Marino if ((fp = fdopen(fd, "w")) == NULL) {
355e0b8e63eSJohn Marino free(mpath);
356e0b8e63eSJohn Marino close(fd);
357e0b8e63eSJohn Marino return (1);
358e0b8e63eSJohn Marino }
359e0b8e63eSJohn Marino
360e0b8e63eSJohn Marino /*
361e0b8e63eSJohn Marino * XXX
362e0b8e63eSJohn Marino * We keep an open lock on the file so that the recover option can
363e0b8e63eSJohn Marino * distinguish between files that are live and those that need to
364e0b8e63eSJohn Marino * be recovered. There's an obvious window between the mkstemp call
365e0b8e63eSJohn Marino * and the lock, but it's pretty small.
366e0b8e63eSJohn Marino */
367e0b8e63eSJohn Marino ep = sp->ep;
368e0b8e63eSJohn Marino if (file_lock(sp, NULL, fd, 1) != LOCK_SUCCESS)
369e0b8e63eSJohn Marino msgq(sp, M_SYSERR, "063|Unable to lock recovery file");
370e0b8e63eSJohn Marino if (!issync) {
371e0b8e63eSJohn Marino /* Save the recover file descriptor, and mail path. */
372e0b8e63eSJohn Marino ep->rcv_fd = dup(fd);
373e0b8e63eSJohn Marino ep->rcv_mpath = mpath;
374e0b8e63eSJohn Marino cp_path = ep->rcv_path;
375e0b8e63eSJohn Marino }
376e0b8e63eSJohn Marino
377e0b8e63eSJohn Marino t = sp->frp->name;
378e0b8e63eSJohn Marino if ((p = strrchr(t, '/')) == NULL)
379e0b8e63eSJohn Marino p = t;
380e0b8e63eSJohn Marino else
381e0b8e63eSJohn Marino ++p;
382e0b8e63eSJohn Marino (void)time(&now);
383e0b8e63eSJohn Marino
384e0b8e63eSJohn Marino if ((st = rcv_dlnwrite(sp, "file", t, fp))) {
385e0b8e63eSJohn Marino if (st == 1)
386e0b8e63eSJohn Marino goto werr;
387e0b8e63eSJohn Marino goto err;
388e0b8e63eSJohn Marino }
389e0b8e63eSJohn Marino if ((st = rcv_dlnwrite(sp, "path", cp_path, fp))) {
390e0b8e63eSJohn Marino if (st == 1)
391e0b8e63eSJohn Marino goto werr;
392e0b8e63eSJohn Marino goto err;
393e0b8e63eSJohn Marino }
394e0b8e63eSJohn Marino
395*b1ac2ebbSDaniel Fojt MALLOC(sp, host, hostmax + 1);
396e0b8e63eSJohn Marino if (host == NULL)
397e0b8e63eSJohn Marino goto err;
398e0b8e63eSJohn Marino (void)gethostname(host, hostmax + 1);
399e0b8e63eSJohn Marino
400e0b8e63eSJohn Marino len = fprintf(fp, "%s%s%s\n%s%s%s%s\n%s%.40s\n%s\n\n",
401e0b8e63eSJohn Marino "From: root@", host, " (Nvi recovery program)",
402e0b8e63eSJohn Marino "To: ", pw->pw_name, "@", host,
403e0b8e63eSJohn Marino "Subject: Nvi saved the file ", p,
404e0b8e63eSJohn Marino "Precedence: bulk"); /* For vacation(1). */
405e0b8e63eSJohn Marino if (len < 0) {
406e0b8e63eSJohn Marino free(host);
407e0b8e63eSJohn Marino goto werr;
408e0b8e63eSJohn Marino }
409e0b8e63eSJohn Marino
410e0b8e63eSJohn Marino if ((qt = quote(t)) == NULL) {
411e0b8e63eSJohn Marino free(host);
412e0b8e63eSJohn Marino msgq(sp, M_SYSERR, NULL);
413e0b8e63eSJohn Marino goto err;
414e0b8e63eSJohn Marino }
415e0b8e63eSJohn Marino len = asprintf(&buf, "%s%.24s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n\n",
416e0b8e63eSJohn Marino "On ", ctime(&now), ", the user ", pw->pw_name,
417e0b8e63eSJohn Marino " was editing a file named ", t, " on the machine ",
418e0b8e63eSJohn Marino host, ", when it was saved for recovery. ",
419e0b8e63eSJohn Marino "You can recover most, if not all, of the changes ",
420*b1ac2ebbSDaniel Fojt "to this file using the -r option to ", getprogname(), ":\n\n\t",
421*b1ac2ebbSDaniel Fojt getprogname(), " -r ", qt);
422e0b8e63eSJohn Marino free(qt);
423e0b8e63eSJohn Marino free(host);
424*b1ac2ebbSDaniel Fojt if (len == -1) {
425e0b8e63eSJohn Marino msgq(sp, M_SYSERR, NULL);
426e0b8e63eSJohn Marino goto err;
427e0b8e63eSJohn Marino }
428e0b8e63eSJohn Marino
429e0b8e63eSJohn Marino /*
430e0b8e63eSJohn Marino * Format the message. (Yes, I know it's silly.)
431e0b8e63eSJohn Marino * Requires that the message end in a <newline>.
432e0b8e63eSJohn Marino */
433e0b8e63eSJohn Marino #define FMTCOLS 60
434e0b8e63eSJohn Marino for (t1 = buf; len > 0; len -= t2 - t1, t1 = t2) {
435e0b8e63eSJohn Marino /* Check for a short length. */
436e0b8e63eSJohn Marino if (len <= FMTCOLS) {
437e0b8e63eSJohn Marino t2 = t1 + (len - 1);
438e0b8e63eSJohn Marino goto wout;
439e0b8e63eSJohn Marino }
440e0b8e63eSJohn Marino
441e0b8e63eSJohn Marino /* Check for a required <newline>. */
442e0b8e63eSJohn Marino t2 = strchr(t1, '\n');
443e0b8e63eSJohn Marino if (t2 - t1 <= FMTCOLS)
444e0b8e63eSJohn Marino goto wout;
445e0b8e63eSJohn Marino
446e0b8e63eSJohn Marino /* Find the closest space, if any. */
447e0b8e63eSJohn Marino for (t3 = t2; t2 > t1; --t2)
448e0b8e63eSJohn Marino if (*t2 == ' ') {
449e0b8e63eSJohn Marino if (t2 - t1 <= FMTCOLS)
450e0b8e63eSJohn Marino goto wout;
451e0b8e63eSJohn Marino t3 = t2;
452e0b8e63eSJohn Marino }
453e0b8e63eSJohn Marino t2 = t3;
454e0b8e63eSJohn Marino
455e0b8e63eSJohn Marino /* t2 points to the last character to display. */
456e0b8e63eSJohn Marino wout: *t2++ = '\n';
457e0b8e63eSJohn Marino
458e0b8e63eSJohn Marino /* t2 points one after the last character to display. */
459e0b8e63eSJohn Marino if (fwrite(t1, 1, t2 - t1, fp) != t2 - t1) {
460e0b8e63eSJohn Marino free(buf);
461e0b8e63eSJohn Marino goto werr;
462e0b8e63eSJohn Marino }
463e0b8e63eSJohn Marino }
464e0b8e63eSJohn Marino
465e0b8e63eSJohn Marino if (issync) {
466e0b8e63eSJohn Marino fflush(fp);
467e0b8e63eSJohn Marino rcv_email(sp, mpath);
468e0b8e63eSJohn Marino free(mpath);
469e0b8e63eSJohn Marino }
470e0b8e63eSJohn Marino if (fclose(fp)) {
471e0b8e63eSJohn Marino free(buf);
472e0b8e63eSJohn Marino werr: msgq(sp, M_SYSERR, "065|Recovery file");
473e0b8e63eSJohn Marino goto err;
474e0b8e63eSJohn Marino }
475e0b8e63eSJohn Marino free(buf);
476e0b8e63eSJohn Marino return (0);
477e0b8e63eSJohn Marino
478e0b8e63eSJohn Marino err: if (!issync)
479e0b8e63eSJohn Marino ep->rcv_fd = -1;
480e0b8e63eSJohn Marino if (fp != NULL)
481e0b8e63eSJohn Marino (void)fclose(fp);
482e0b8e63eSJohn Marino return (1);
483e0b8e63eSJohn Marino }
484e0b8e63eSJohn Marino
485e0b8e63eSJohn Marino /*
486e0b8e63eSJohn Marino * people making love
487e0b8e63eSJohn Marino * never exactly the same
488e0b8e63eSJohn Marino * just like a snowflake
489e0b8e63eSJohn Marino *
490e0b8e63eSJohn Marino * rcv_list --
491e0b8e63eSJohn Marino * List the files that can be recovered by this user.
492e0b8e63eSJohn Marino *
493e0b8e63eSJohn Marino * PUBLIC: int rcv_list(SCR *);
494e0b8e63eSJohn Marino */
495e0b8e63eSJohn Marino int
rcv_list(SCR * sp)496e0b8e63eSJohn Marino rcv_list(SCR *sp)
497e0b8e63eSJohn Marino {
498e0b8e63eSJohn Marino struct dirent *dp;
499e0b8e63eSJohn Marino struct stat sb;
500e0b8e63eSJohn Marino DIR *dirp;
501e0b8e63eSJohn Marino FILE *fp;
502e0b8e63eSJohn Marino int found;
503e0b8e63eSJohn Marino char *p, *file, *path;
504e0b8e63eSJohn Marino char *dtype, *data;
505e0b8e63eSJohn Marino int st;
506e0b8e63eSJohn Marino
507e0b8e63eSJohn Marino /* Open the recovery directory for reading. */
508e0b8e63eSJohn Marino if (opts_empty(sp, O_RECDIR, 0))
509e0b8e63eSJohn Marino return (1);
510e0b8e63eSJohn Marino p = O_STR(sp, O_RECDIR);
511e0b8e63eSJohn Marino if (chdir(p) || (dirp = opendir(".")) == NULL) {
512e0b8e63eSJohn Marino msgq_str(sp, M_SYSERR, p, "recdir: %s");
513e0b8e63eSJohn Marino return (1);
514e0b8e63eSJohn Marino }
515e0b8e63eSJohn Marino
516e0b8e63eSJohn Marino /* Read the directory. */
517e0b8e63eSJohn Marino for (found = 0; (dp = readdir(dirp)) != NULL;) {
518e0b8e63eSJohn Marino if (strncmp(dp->d_name, "recover.", 8))
519e0b8e63eSJohn Marino continue;
520e0b8e63eSJohn Marino
521e0b8e63eSJohn Marino /* If it's readable, it's recoverable. */
522e0b8e63eSJohn Marino if ((fp = fopen(dp->d_name, "r")) == NULL)
523e0b8e63eSJohn Marino continue;
524e0b8e63eSJohn Marino
525e0b8e63eSJohn Marino switch (file_lock(sp, NULL, fileno(fp), 1)) {
526e0b8e63eSJohn Marino case LOCK_FAILED:
527e0b8e63eSJohn Marino /*
528e0b8e63eSJohn Marino * XXX
529e0b8e63eSJohn Marino * Assume that a lock can't be acquired, but that we
530e0b8e63eSJohn Marino * should permit recovery anyway. If this is wrong,
531e0b8e63eSJohn Marino * and someone else is using the file, we're going to
532e0b8e63eSJohn Marino * die horribly.
533e0b8e63eSJohn Marino */
534e0b8e63eSJohn Marino break;
535e0b8e63eSJohn Marino case LOCK_SUCCESS:
536e0b8e63eSJohn Marino break;
537e0b8e63eSJohn Marino case LOCK_UNAVAIL:
538e0b8e63eSJohn Marino /* If it's locked, it's live. */
539e0b8e63eSJohn Marino (void)fclose(fp);
540e0b8e63eSJohn Marino continue;
541e0b8e63eSJohn Marino }
542e0b8e63eSJohn Marino
543e0b8e63eSJohn Marino /* Check the headers. */
544e0b8e63eSJohn Marino for (file = NULL, path = NULL;
545e0b8e63eSJohn Marino file == NULL || path == NULL;) {
546e0b8e63eSJohn Marino if ((st = rcv_dlnread(sp, &dtype, &data, fp))) {
547e0b8e63eSJohn Marino if (st == 1)
548e0b8e63eSJohn Marino msgq_str(sp, M_ERR, dp->d_name,
549e0b8e63eSJohn Marino "066|%s: malformed recovery file");
550e0b8e63eSJohn Marino goto next;
551e0b8e63eSJohn Marino }
552e0b8e63eSJohn Marino if (dtype == NULL)
553e0b8e63eSJohn Marino continue;
554e0b8e63eSJohn Marino if (!strcmp(dtype, "file"))
555e0b8e63eSJohn Marino file = data;
556e0b8e63eSJohn Marino else if (!strcmp(dtype, "path"))
557e0b8e63eSJohn Marino path = data;
558e0b8e63eSJohn Marino else
559e0b8e63eSJohn Marino free(data);
560e0b8e63eSJohn Marino }
561e0b8e63eSJohn Marino
562e0b8e63eSJohn Marino /*
563e0b8e63eSJohn Marino * If the file doesn't exist, it's an orphaned recovery file,
564e0b8e63eSJohn Marino * toss it.
565e0b8e63eSJohn Marino *
566e0b8e63eSJohn Marino * XXX
567e0b8e63eSJohn Marino * This can occur if the backup file was deleted and we crashed
568e0b8e63eSJohn Marino * before deleting the email file.
569e0b8e63eSJohn Marino */
570e0b8e63eSJohn Marino errno = 0;
571e0b8e63eSJohn Marino if (stat(path, &sb) &&
572e0b8e63eSJohn Marino errno == ENOENT) {
573e0b8e63eSJohn Marino (void)unlink(dp->d_name);
574e0b8e63eSJohn Marino goto next;
575e0b8e63eSJohn Marino }
576e0b8e63eSJohn Marino
577e0b8e63eSJohn Marino /* Get the last modification time and display. */
578e0b8e63eSJohn Marino (void)fstat(fileno(fp), &sb);
579e0b8e63eSJohn Marino (void)printf("%.24s: %s\n",
580e0b8e63eSJohn Marino ctime(&sb.st_mtime), file);
581e0b8e63eSJohn Marino found = 1;
582e0b8e63eSJohn Marino
583e0b8e63eSJohn Marino /* Close, discarding lock. */
584e0b8e63eSJohn Marino next: (void)fclose(fp);
585e0b8e63eSJohn Marino free(file);
586e0b8e63eSJohn Marino free(path);
587e0b8e63eSJohn Marino }
588e0b8e63eSJohn Marino if (found == 0)
589*b1ac2ebbSDaniel Fojt (void)printf("%s: No files to recover\n", getprogname());
590e0b8e63eSJohn Marino (void)closedir(dirp);
591e0b8e63eSJohn Marino return (0);
592e0b8e63eSJohn Marino }
593e0b8e63eSJohn Marino
594e0b8e63eSJohn Marino /*
595e0b8e63eSJohn Marino * rcv_read --
596e0b8e63eSJohn Marino * Start a recovered file as the file to edit.
597e0b8e63eSJohn Marino *
598e0b8e63eSJohn Marino * PUBLIC: int rcv_read(SCR *, FREF *);
599e0b8e63eSJohn Marino */
600e0b8e63eSJohn Marino int
rcv_read(SCR * sp,FREF * frp)601*b1ac2ebbSDaniel Fojt rcv_read(SCR *sp, FREF *frp)
602e0b8e63eSJohn Marino {
603e0b8e63eSJohn Marino struct dirent *dp;
604e0b8e63eSJohn Marino struct stat sb;
605e0b8e63eSJohn Marino DIR *dirp;
606e0b8e63eSJohn Marino FILE *fp;
607e0b8e63eSJohn Marino EXF *ep;
608e0b8e63eSJohn Marino struct timespec rec_mtim = { 0, 0 };
609e0b8e63eSJohn Marino int found, locked = 0, requested, sv_fd;
610e0b8e63eSJohn Marino char *name, *p, *t, *rp, *recp, *pathp;
611e0b8e63eSJohn Marino char *file, *path, *recpath;
612e0b8e63eSJohn Marino char *dtype, *data;
613e0b8e63eSJohn Marino int st;
614e0b8e63eSJohn Marino
615e0b8e63eSJohn Marino if (opts_empty(sp, O_RECDIR, 0))
616e0b8e63eSJohn Marino return (1);
617e0b8e63eSJohn Marino rp = O_STR(sp, O_RECDIR);
618e0b8e63eSJohn Marino if ((dirp = opendir(rp)) == NULL) {
619e0b8e63eSJohn Marino msgq_str(sp, M_SYSERR, rp, "%s");
620e0b8e63eSJohn Marino return (1);
621e0b8e63eSJohn Marino }
622e0b8e63eSJohn Marino
623e0b8e63eSJohn Marino name = frp->name;
624e0b8e63eSJohn Marino sv_fd = -1;
625e0b8e63eSJohn Marino recp = pathp = NULL;
626e0b8e63eSJohn Marino for (found = requested = 0; (dp = readdir(dirp)) != NULL;) {
627e0b8e63eSJohn Marino if (strncmp(dp->d_name, "recover.", 8))
628e0b8e63eSJohn Marino continue;
629e0b8e63eSJohn Marino if ((recpath = join(rp, dp->d_name)) == NULL) {
630e0b8e63eSJohn Marino msgq(sp, M_SYSERR, NULL);
631e0b8e63eSJohn Marino continue;
632e0b8e63eSJohn Marino }
633e0b8e63eSJohn Marino
634e0b8e63eSJohn Marino /* If it's readable, it's recoverable. */
635e0b8e63eSJohn Marino if ((fp = fopen(recpath, "r")) == NULL) {
636e0b8e63eSJohn Marino free(recpath);
637e0b8e63eSJohn Marino continue;
638e0b8e63eSJohn Marino }
639e0b8e63eSJohn Marino
640e0b8e63eSJohn Marino switch (file_lock(sp, NULL, fileno(fp), 1)) {
641e0b8e63eSJohn Marino case LOCK_FAILED:
642e0b8e63eSJohn Marino /*
643e0b8e63eSJohn Marino * XXX
644e0b8e63eSJohn Marino * Assume that a lock can't be acquired, but that we
645e0b8e63eSJohn Marino * should permit recovery anyway. If this is wrong,
646e0b8e63eSJohn Marino * and someone else is using the file, we're going to
647e0b8e63eSJohn Marino * die horribly.
648e0b8e63eSJohn Marino */
649e0b8e63eSJohn Marino locked = 0;
650e0b8e63eSJohn Marino break;
651e0b8e63eSJohn Marino case LOCK_SUCCESS:
652e0b8e63eSJohn Marino locked = 1;
653e0b8e63eSJohn Marino break;
654e0b8e63eSJohn Marino case LOCK_UNAVAIL:
655e0b8e63eSJohn Marino /* If it's locked, it's live. */
656e0b8e63eSJohn Marino (void)fclose(fp);
657e0b8e63eSJohn Marino continue;
658e0b8e63eSJohn Marino }
659e0b8e63eSJohn Marino
660e0b8e63eSJohn Marino /* Check the headers. */
661e0b8e63eSJohn Marino for (file = NULL, path = NULL;
662e0b8e63eSJohn Marino file == NULL || path == NULL;) {
663e0b8e63eSJohn Marino if ((st = rcv_dlnread(sp, &dtype, &data, fp))) {
664e0b8e63eSJohn Marino if (st == 1)
665e0b8e63eSJohn Marino msgq_str(sp, M_ERR, dp->d_name,
666e0b8e63eSJohn Marino "067|%s: malformed recovery file");
667e0b8e63eSJohn Marino goto next;
668e0b8e63eSJohn Marino }
669e0b8e63eSJohn Marino if (dtype == NULL)
670e0b8e63eSJohn Marino continue;
671e0b8e63eSJohn Marino if (!strcmp(dtype, "file"))
672e0b8e63eSJohn Marino file = data;
673e0b8e63eSJohn Marino else if (!strcmp(dtype, "path"))
674e0b8e63eSJohn Marino path = data;
675e0b8e63eSJohn Marino else
676e0b8e63eSJohn Marino free(data);
677e0b8e63eSJohn Marino }
678e0b8e63eSJohn Marino ++found;
679e0b8e63eSJohn Marino
680e0b8e63eSJohn Marino /*
681e0b8e63eSJohn Marino * If the file doesn't exist, it's an orphaned recovery file,
682e0b8e63eSJohn Marino * toss it.
683e0b8e63eSJohn Marino *
684e0b8e63eSJohn Marino * XXX
685e0b8e63eSJohn Marino * This can occur if the backup file was deleted and we crashed
686e0b8e63eSJohn Marino * before deleting the email file.
687e0b8e63eSJohn Marino */
688e0b8e63eSJohn Marino errno = 0;
689e0b8e63eSJohn Marino if (stat(path, &sb) &&
690e0b8e63eSJohn Marino errno == ENOENT) {
691e0b8e63eSJohn Marino (void)unlink(dp->d_name);
692e0b8e63eSJohn Marino goto next;
693e0b8e63eSJohn Marino }
694e0b8e63eSJohn Marino
695e0b8e63eSJohn Marino /* Check the file name. */
696e0b8e63eSJohn Marino if (strcmp(file, name))
697e0b8e63eSJohn Marino goto next;
698e0b8e63eSJohn Marino
699e0b8e63eSJohn Marino ++requested;
700e0b8e63eSJohn Marino
701e0b8e63eSJohn Marino /* If we've found more than one, take the most recent. */
702e0b8e63eSJohn Marino (void)fstat(fileno(fp), &sb);
703e0b8e63eSJohn Marino if (recp == NULL ||
704*b1ac2ebbSDaniel Fojt timespeccmp(&rec_mtim, &sb.st_mtim, <)) {
705e0b8e63eSJohn Marino p = recp;
706e0b8e63eSJohn Marino t = pathp;
707e0b8e63eSJohn Marino recp = recpath;
708e0b8e63eSJohn Marino pathp = path;
709e0b8e63eSJohn Marino if (p != NULL) {
710e0b8e63eSJohn Marino free(p);
711e0b8e63eSJohn Marino free(t);
712e0b8e63eSJohn Marino }
713*b1ac2ebbSDaniel Fojt rec_mtim = sb.st_mtim;
714e0b8e63eSJohn Marino if (sv_fd != -1)
715e0b8e63eSJohn Marino (void)close(sv_fd);
716e0b8e63eSJohn Marino sv_fd = dup(fileno(fp));
717e0b8e63eSJohn Marino } else {
718e0b8e63eSJohn Marino next: free(recpath);
719e0b8e63eSJohn Marino free(path);
720e0b8e63eSJohn Marino }
721e0b8e63eSJohn Marino (void)fclose(fp);
722e0b8e63eSJohn Marino free(file);
723e0b8e63eSJohn Marino }
724e0b8e63eSJohn Marino (void)closedir(dirp);
725e0b8e63eSJohn Marino
726e0b8e63eSJohn Marino if (recp == NULL) {
727e0b8e63eSJohn Marino msgq_str(sp, M_INFO, name,
728e0b8e63eSJohn Marino "068|No files named %s, readable by you, to recover");
729e0b8e63eSJohn Marino return (1);
730e0b8e63eSJohn Marino }
731e0b8e63eSJohn Marino if (found) {
732e0b8e63eSJohn Marino if (requested > 1)
733e0b8e63eSJohn Marino msgq(sp, M_INFO,
734e0b8e63eSJohn Marino "069|There are older versions of this file for you to recover");
735e0b8e63eSJohn Marino if (found > requested)
736e0b8e63eSJohn Marino msgq(sp, M_INFO,
737e0b8e63eSJohn Marino "070|There are other files for you to recover");
738e0b8e63eSJohn Marino }
739e0b8e63eSJohn Marino
740e0b8e63eSJohn Marino /*
741e0b8e63eSJohn Marino * Create the FREF structure, start the btree file.
742e0b8e63eSJohn Marino *
743e0b8e63eSJohn Marino * XXX
744e0b8e63eSJohn Marino * file_init() is going to set ep->rcv_path.
745e0b8e63eSJohn Marino */
746e0b8e63eSJohn Marino if (file_init(sp, frp, pathp, 0)) {
747e0b8e63eSJohn Marino free(recp);
748e0b8e63eSJohn Marino free(pathp);
749e0b8e63eSJohn Marino (void)close(sv_fd);
750e0b8e63eSJohn Marino return (1);
751e0b8e63eSJohn Marino }
752e0b8e63eSJohn Marino free(pathp);
753e0b8e63eSJohn Marino
754e0b8e63eSJohn Marino /*
755e0b8e63eSJohn Marino * We keep an open lock on the file so that the recover option can
756e0b8e63eSJohn Marino * distinguish between files that are live and those that need to
757e0b8e63eSJohn Marino * be recovered. The lock is already acquired, just copy it.
758e0b8e63eSJohn Marino */
759e0b8e63eSJohn Marino ep = sp->ep;
760e0b8e63eSJohn Marino ep->rcv_mpath = recp;
761e0b8e63eSJohn Marino ep->rcv_fd = sv_fd;
762e0b8e63eSJohn Marino if (!locked)
763e0b8e63eSJohn Marino F_SET(frp, FR_UNLOCKED);
764e0b8e63eSJohn Marino
765e0b8e63eSJohn Marino /* We believe the file is recoverable. */
766e0b8e63eSJohn Marino F_SET(ep, F_RCV_ON);
767e0b8e63eSJohn Marino return (0);
768e0b8e63eSJohn Marino }
769e0b8e63eSJohn Marino
770e0b8e63eSJohn Marino /*
771e0b8e63eSJohn Marino * rcv_copy --
772e0b8e63eSJohn Marino * Copy a recovery file.
773e0b8e63eSJohn Marino */
774e0b8e63eSJohn Marino static int
rcv_copy(SCR * sp,int wfd,char * fname)775*b1ac2ebbSDaniel Fojt rcv_copy(SCR *sp, int wfd, char *fname)
776e0b8e63eSJohn Marino {
777e0b8e63eSJohn Marino int nr, nw, off, rfd;
778e0b8e63eSJohn Marino char buf[8 * 1024];
779e0b8e63eSJohn Marino
780e0b8e63eSJohn Marino if ((rfd = open(fname, O_RDONLY, 0)) == -1)
781e0b8e63eSJohn Marino goto err;
782e0b8e63eSJohn Marino while ((nr = read(rfd, buf, sizeof(buf))) > 0)
783e0b8e63eSJohn Marino for (off = 0; nr; nr -= nw, off += nw)
784e0b8e63eSJohn Marino if ((nw = write(wfd, buf + off, nr)) < 0)
785e0b8e63eSJohn Marino goto err;
786e0b8e63eSJohn Marino if (nr == 0)
787e0b8e63eSJohn Marino return (0);
788e0b8e63eSJohn Marino
789e0b8e63eSJohn Marino err: msgq_str(sp, M_SYSERR, fname, "%s");
790e0b8e63eSJohn Marino return (1);
791e0b8e63eSJohn Marino }
792e0b8e63eSJohn Marino
793e0b8e63eSJohn Marino /*
794e0b8e63eSJohn Marino * rcv_mktemp --
795e0b8e63eSJohn Marino * Paranoid make temporary file routine.
796e0b8e63eSJohn Marino */
797e0b8e63eSJohn Marino static int
rcv_mktemp(SCR * sp,char * path,char * dname)798*b1ac2ebbSDaniel Fojt rcv_mktemp(SCR *sp, char *path, char *dname)
799e0b8e63eSJohn Marino {
800e0b8e63eSJohn Marino int fd;
801e0b8e63eSJohn Marino
802e0b8e63eSJohn Marino if ((fd = mkstemp(path)) == -1)
803e0b8e63eSJohn Marino msgq_str(sp, M_SYSERR, dname, "%s");
804e0b8e63eSJohn Marino return (fd);
805e0b8e63eSJohn Marino }
806e0b8e63eSJohn Marino
807e0b8e63eSJohn Marino /*
808e0b8e63eSJohn Marino * rcv_email --
809e0b8e63eSJohn Marino * Send email.
810e0b8e63eSJohn Marino */
811e0b8e63eSJohn Marino static void
rcv_email(SCR * sp,char * fname)812*b1ac2ebbSDaniel Fojt rcv_email(SCR *sp, char *fname)
813e0b8e63eSJohn Marino {
814e0b8e63eSJohn Marino char *buf;
815e0b8e63eSJohn Marino
816*b1ac2ebbSDaniel Fojt if (asprintf(&buf, _PATH_SENDMAIL " -odb -t < %s", fname) == -1) {
817e0b8e63eSJohn Marino msgq_str(sp, M_ERR, strerror(errno),
818e0b8e63eSJohn Marino "071|not sending email: %s");
819e0b8e63eSJohn Marino return;
820e0b8e63eSJohn Marino }
821e0b8e63eSJohn Marino (void)system(buf);
822e0b8e63eSJohn Marino free(buf);
823e0b8e63eSJohn Marino }
824e0b8e63eSJohn Marino
825e0b8e63eSJohn Marino /*
826e0b8e63eSJohn Marino * rcv_dlnwrite --
827e0b8e63eSJohn Marino * Encode a string into an X-vi-data line and write it.
828e0b8e63eSJohn Marino */
829e0b8e63eSJohn Marino static int
rcv_dlnwrite(SCR * sp,const char * dtype,const char * src,FILE * fp)830*b1ac2ebbSDaniel Fojt rcv_dlnwrite(SCR *sp, const char *dtype, const char *src, FILE *fp)
831e0b8e63eSJohn Marino {
832e0b8e63eSJohn Marino char *bp = NULL, *p;
833e0b8e63eSJohn Marino size_t blen = 0;
834e0b8e63eSJohn Marino size_t dlen, len;
835e0b8e63eSJohn Marino int plen, xlen;
836e0b8e63eSJohn Marino
837e0b8e63eSJohn Marino len = strlen(src);
838e0b8e63eSJohn Marino dlen = strlen(dtype);
839e0b8e63eSJohn Marino GET_SPACE_GOTOC(sp, bp, blen, (len + 2) / 3 * 4 + dlen + 2);
840e0b8e63eSJohn Marino (void)memcpy(bp, dtype, dlen);
841e0b8e63eSJohn Marino bp[dlen] = ';';
842e0b8e63eSJohn Marino if ((xlen = b64_ntop((u_char *)src,
843e0b8e63eSJohn Marino len, bp + dlen + 1, blen)) == -1)
844e0b8e63eSJohn Marino goto err;
845e0b8e63eSJohn Marino xlen += dlen + 1;
846e0b8e63eSJohn Marino
847e0b8e63eSJohn Marino /* Output as an MIME folding header. */
848e0b8e63eSJohn Marino if ((plen = fprintf(fp, VI_DHEADER " %.*s\n",
849e0b8e63eSJohn Marino FMTCOLS - (int)sizeof(VI_DHEADER), bp)) < 0)
850e0b8e63eSJohn Marino goto err;
851e0b8e63eSJohn Marino plen -= (int)sizeof(VI_DHEADER) + 1;
852e0b8e63eSJohn Marino for (p = bp, xlen -= plen; xlen > 0; xlen -= plen) {
853e0b8e63eSJohn Marino p += plen;
854e0b8e63eSJohn Marino if ((plen = fprintf(fp, " %.*s\n", FMTCOLS - 1, p)) < 0)
855e0b8e63eSJohn Marino goto err;
856e0b8e63eSJohn Marino plen -= 2;
857e0b8e63eSJohn Marino }
858e0b8e63eSJohn Marino FREE_SPACE(sp, bp, blen);
859e0b8e63eSJohn Marino return (0);
860e0b8e63eSJohn Marino
861e0b8e63eSJohn Marino err: FREE_SPACE(sp, bp, blen);
862e0b8e63eSJohn Marino return (1);
863e0b8e63eSJohn Marino alloc_err:
864e0b8e63eSJohn Marino msgq(sp, M_SYSERR, NULL);
865e0b8e63eSJohn Marino return (-1);
866e0b8e63eSJohn Marino }
867e0b8e63eSJohn Marino
868e0b8e63eSJohn Marino /*
869e0b8e63eSJohn Marino * rcv_dlnread --
870e0b8e63eSJohn Marino * Read an X-vi-data line and decode it.
871e0b8e63eSJohn Marino */
872e0b8e63eSJohn Marino static int
rcv_dlnread(SCR * sp,char ** dtypep,char ** datap,FILE * fp)873*b1ac2ebbSDaniel Fojt rcv_dlnread(SCR *sp, char **dtypep,
874e0b8e63eSJohn Marino char **datap, /* free *datap if != NULL after use. */
875e0b8e63eSJohn Marino FILE *fp)
876e0b8e63eSJohn Marino {
877e0b8e63eSJohn Marino int ch;
878e0b8e63eSJohn Marino char buf[1024];
879e0b8e63eSJohn Marino char *bp = NULL, *p, *src;
880e0b8e63eSJohn Marino size_t blen = 0;
881e0b8e63eSJohn Marino size_t len, off, dlen;
882e0b8e63eSJohn Marino char *dtype, *data;
883e0b8e63eSJohn Marino int xlen;
884e0b8e63eSJohn Marino
885e0b8e63eSJohn Marino if (fgets(buf, sizeof(buf), fp) == NULL)
886e0b8e63eSJohn Marino return (1);
887e0b8e63eSJohn Marino if (strncmp(buf, VI_DHEADER, sizeof(VI_DHEADER) - 1)) {
888e0b8e63eSJohn Marino *dtypep = NULL;
889e0b8e63eSJohn Marino *datap = NULL;
890e0b8e63eSJohn Marino return (0);
891e0b8e63eSJohn Marino }
892e0b8e63eSJohn Marino
893e0b8e63eSJohn Marino /* Fetch an MIME folding header. */
894e0b8e63eSJohn Marino len = strlen(buf) - sizeof(VI_DHEADER) + 1;
895e0b8e63eSJohn Marino GET_SPACE_GOTOC(sp, bp, blen, len);
896e0b8e63eSJohn Marino (void)memcpy(bp, buf + sizeof(VI_DHEADER) - 1, len);
897e0b8e63eSJohn Marino p = bp + len;
898e0b8e63eSJohn Marino while ((ch = fgetc(fp)) == ' ') {
899e0b8e63eSJohn Marino if (fgets(buf, sizeof(buf), fp) == NULL)
900e0b8e63eSJohn Marino goto err;
901e0b8e63eSJohn Marino off = strlen(buf);
902e0b8e63eSJohn Marino len += off;
903e0b8e63eSJohn Marino ADD_SPACE_GOTOC(sp, bp, blen, len);
904e0b8e63eSJohn Marino p = bp + len - off;
905e0b8e63eSJohn Marino (void)memcpy(p, buf, off);
906e0b8e63eSJohn Marino }
907e0b8e63eSJohn Marino bp[len] = '\0';
908e0b8e63eSJohn Marino (void)ungetc(ch, fp);
909e0b8e63eSJohn Marino
910e0b8e63eSJohn Marino for (p = bp; *p == ' ' || *p == '\n'; p++);
911e0b8e63eSJohn Marino if ((src = strchr(p, ';')) == NULL)
912e0b8e63eSJohn Marino goto err;
913e0b8e63eSJohn Marino dlen = src - p;
914e0b8e63eSJohn Marino src += 1;
915e0b8e63eSJohn Marino len -= src - bp;
916e0b8e63eSJohn Marino
917e0b8e63eSJohn Marino /* Memory looks like: "<data>\0<dtype>\0". */
918*b1ac2ebbSDaniel Fojt MALLOC(sp, data, dlen + len / 4 * 3 + 2);
919e0b8e63eSJohn Marino if (data == NULL)
920e0b8e63eSJohn Marino goto err;
921e0b8e63eSJohn Marino if ((xlen = (b64_pton(p + dlen + 1,
922e0b8e63eSJohn Marino (u_char *)data, len / 4 * 3 + 1))) == -1) {
923e0b8e63eSJohn Marino free(data);
924e0b8e63eSJohn Marino goto err;
925e0b8e63eSJohn Marino }
926e0b8e63eSJohn Marino data[xlen] = '\0';
927e0b8e63eSJohn Marino dtype = data + xlen + 1;
928e0b8e63eSJohn Marino (void)memcpy(dtype, p, dlen);
929e0b8e63eSJohn Marino dtype[dlen] = '\0';
930e0b8e63eSJohn Marino FREE_SPACE(sp, bp, blen);
931e0b8e63eSJohn Marino *dtypep = dtype;
932e0b8e63eSJohn Marino *datap = data;
933e0b8e63eSJohn Marino return (0);
934e0b8e63eSJohn Marino
935e0b8e63eSJohn Marino err: FREE_SPACE(sp, bp, blen);
936e0b8e63eSJohn Marino return (1);
937e0b8e63eSJohn Marino alloc_err:
938e0b8e63eSJohn Marino msgq(sp, M_SYSERR, NULL);
939e0b8e63eSJohn Marino return (-1);
940e0b8e63eSJohn Marino }
941