386BSD 0.1 development
[unix-history] / usr / src / libexec / uucp / file.c
CommitLineData
af364716
WJ
1/* file.c
2 Generic routines to handle files.
3
4 Copyright (C) 1991, 1992 Ian Lance Taylor
5
6 This file is part of the Taylor UUCP package.
7
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License as
10 published by the Free Software Foundation; either version 2 of the
11 License, or (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21
22 The author of the program may be contacted at ian@airs.com or
23 c/o AIRS, P.O. Box 520, Waltham, MA 02254.
24
25 $Log: file.c,v $
26 Revision 1.16 1992/03/30 04:07:13 ian
27 Dirk Musstopf: remove temporary file if receive fails
28
29 Revision 1.15 1992/03/17 00:31:33 ian
30 Changed iRecmode to unsigned int
31
32 Revision 1.14 1992/03/11 01:18:15 ian
33 Niels Baggesen: drop the connection on a write failure
34
35 Revision 1.13 1992/03/11 00:18:50 ian
36 Save temporary file if file send fails
37
38 Revision 1.12 1992/02/29 01:06:59 ian
39 Chip Salzenberg: recheck file permissions before sending
40
41 Revision 1.11 1992/02/19 19:36:07 ian
42 Rearranged time functions
43
44 Revision 1.10 1992/02/08 03:54:18 ian
45 Include <string.h> only in <uucp.h>, added 1992 copyright
46
47 Revision 1.9 1992/01/18 22:48:53 ian
48 Reworked sending of mail and general handling of failed transfers
49
50 Revision 1.8 1991/12/21 23:10:43 ian
51 Terry Gardner: record failed file transfers in statistics file
52
53 Revision 1.7 1991/12/17 07:09:58 ian
54 Record statistics in fractions of a second
55
56 Revision 1.6 1991/12/10 19:29:02 ian
57 Move statistics file stuff from file.c to log.c
58
59 Revision 1.5 1991/11/10 19:24:22 ian
60 Added pffile protocol entry point for file level control
61
62 Revision 1.4 1991/11/08 04:30:50 ian
63 Hannu Strang: flush statistics file after each line
64
65 Revision 1.3 1991/11/07 19:42:16 ian
66 Chip Salzenberg: declare inline functions consistently
67
68 Revision 1.2 1991/09/19 03:23:34 ian
69 Chip Salzenberg: append to private debugging file, don't overwrite it
70
71 Revision 1.1 1991/09/10 19:40:31 ian
72 Initial revision
73
74 */
75
76#include "uucp.h"
77
78#if USE_RCS_ID
79char file_rcsid[] = "$Id: file.c,v 1.16 1992/03/30 04:07:13 ian Rel $";
80#endif
81
82#include <errno.h>
83
84#include "system.h"
85\f
86/* Current file being sent. */
87openfile_t eSendfile = EFILECLOSED;
88
89/* Current file being received. */
90openfile_t eRecfile = EFILECLOSED;
91
92/* This file has to keep several strings. I don't want to pay for the
93 overhead (small as it is) of calling malloc for each one, and I also
94 don't want to truncate the strings. So I use a structure which holds
95 strings up to a certain length, and call malloc for longer strings.
96 NULL strings are indistinguishable from empty strings, which turns
97 out not to matter. */
98
99#define CSTRLEN (30)
100
101struct sstring
102{
103 char ab[CSTRLEN];
104 char *zalloc;
105};
106
107/* Local functions. */
108
109__inline__ static boolean ffsetstring P((struct sstring *, const char *));
110__inline__ static const char *zfgetstring P((const struct sstring *));
111__inline__ static void uffreestring P((struct sstring *));
112\f
113__inline__
114static boolean ffsetstring (q, z)
115 struct sstring *q;
116 const char *z;
117{
118 if (z == NULL)
119 q->ab[0] = '\0';
120 else if (strlen (z) < CSTRLEN)
121 strcpy (q->ab, z);
122 else
123 {
124 q->zalloc = strdup (z);
125 if (q->zalloc == NULL)
126 {
127 ulog (LOG_ERROR, "Not enough memory to store command");
128 return FALSE;
129 }
130 }
131 return TRUE;
132}
133
134__inline__
135static const char *zfgetstring (q)
136 const struct sstring *q;
137{
138 if (q->zalloc != NULL)
139 return q->zalloc;
140 else
141 return q->ab;
142}
143
144__inline__ static void
145uffreestring (q)
146 struct sstring *q;
147{
148 xfree ((pointer) q->zalloc);
149 q->zalloc = NULL;
150}
151\f
152/* Information we keep for the file being sent. We have to be able to
153 send mail to the user when the transfer is finished, we have to be
154 able to report error messages sensibly, and we have to be able to
155 clear this send request out of the work queue if we initiated the
156 send. */
157
158/* Work queue sequence number. */
159static pointer pSendseq;
160/* File name being transferred from. */
161static struct sstring sSendfrom;
162/* File name being transferred to. */
163static struct sstring sSendto;
164/* System being transferred to. */
165static struct sstring sSendtosys;
166/* Requesting user name. */
167static struct sstring sSenduser;
168/* User to send mail to. */
169static struct sstring sSendmail;
170/* Start time. */
171static long iSendstart_secs;
172static long iSendstart_micros;
173/* Indicate whether we had an error during the transfer. */
174static boolean fSenderror;
175
176/* Store information about a file being sent. */
177
178boolean
179fstore_sendfile (e, pseq, zfrom, zto, ztosys, zuser, zmail)
180 openfile_t e;
181 pointer pseq;
182 const char *zfrom;
183 const char *zto;
184 const char *ztosys;
185 const char *zuser;
186 const char *zmail;
187{
188#if DEBUG > 0
189 if (ffileisopen (eSendfile))
190 ulog (LOG_FATAL, "fstore_sendfile: In progress");
191#endif
192
193 if (! ffsetstring (&sSendfrom, zfrom)
194 || ! ffsetstring (&sSendto, zto)
195 || ! ffsetstring (&sSendtosys, ztosys)
196 || ! ffsetstring (&sSenduser, zuser)
197 || ! ffsetstring (&sSendmail, zmail))
198 {
199 (void) ffileclose (e);
200 return FALSE;
201 }
202
203 eSendfile = e;
204 pSendseq = pseq;
205 iSendstart_secs = isysdep_process_time (&iSendstart_micros);
206
207 fSenderror = FALSE;
208
209 return TRUE;
210}
211
212/* Finish up after sending a file. The freceived argument indicates
213 whether the other file received it successfully. The cbytes
214 argument is the number of bytes that were sent (for the statistics
215 entry). The zwhy argument holds a reason for failure if freceived
216 is FALSE. The fnever argument is TRUE if the file was not received
217 correctly and, moreover, will never be received correctly. */
218
219boolean
220fsent_file (freceived, cbytes, zwhy, fnever)
221 boolean freceived;
222 long cbytes;
223 const char *zwhy;
224 boolean fnever;
225{
226 boolean f;
227 long isecs, imicros;
228
229 f = TRUE;
230
231 if (fSenderror)
232 freceived = FALSE;
233
234 if (ffileisopen (eSendfile))
235 (void) ffileclose (eSendfile);
236
237 isecs = isysdep_process_time (&imicros);
238 imicros -= iSendstart_micros;
239 if (imicros < 0)
240 {
241 imicros += 1000000;
242 isecs--;
243 }
244 isecs -= iSendstart_secs;
245
246 ustats (freceived, zfgetstring (&sSenduser), zfgetstring (&sSendtosys),
247 TRUE, cbytes, isecs, imicros);
248
249 if (freceived)
250 {
251 const char *zmail;
252
253 zmail = zfgetstring (&sSendmail);
254 if (*zmail != '\0')
255 {
256 if (! fmail_transfer (TRUE, zfgetstring (&sSenduser),
257 zmail, zwhy,
258 zfgetstring (&sSendfrom),
259 zLocalname,
260 zfgetstring (&sSendto),
261 zfgetstring (&sSendtosys),
262 (const char *) NULL))
263 f = FALSE;
264 }
265
266 if (pSendseq != NULL)
267 {
268 if (! fsysdep_did_work (pSendseq))
269 f = FALSE;
270 pSendseq = NULL;
271 }
272 }
273 else
274 {
275 /* If the transfer failed, we only try to save the file and send
276 mail if it was requested locally (in which case pSendseq !=
277 NULL) and it will never succeed. We send mail to sSendmail
278 if defined, otherwise to sSenduser. I hope this is
279 reasonable. */
280 if (pSendseq != NULL && fnever)
281 {
282 if (! fmail_transfer (FALSE,
283 zfgetstring (&sSenduser),
284 zfgetstring (&sSendmail),
285 zwhy,
286 zfgetstring (&sSendfrom),
287 zLocalname,
288 zfgetstring (&sSendto),
289 zfgetstring (&sSendtosys),
290 zsysdep_save_temp_file (pSendseq)))
291 f = FALSE;
292
293 if (! fsysdep_did_work (pSendseq))
294 f = FALSE;
295 pSendseq = NULL;
296 }
297 }
298
299 eSendfile = EFILECLOSED;
300 uffreestring (&sSendfrom);
301 uffreestring (&sSendto);
302 uffreestring (&sSendtosys);
303 uffreestring (&sSenduser);
304 uffreestring (&sSendmail);
305
306 return f;
307}
308
309/* Some error occurred while sending a file. Mark an error and let
310 fsent_file handle everything else. This used to differentiate
311 between temporary and permanent errors, but I've decided that all
312 transmission errors are temporary. */
313
314void
315usendfile_error ()
316{
317 fSenderror = TRUE;
318}
319\f
320/* Information we keep for the file being received. We have to be
321 able to move the file into its final location with the correct
322 mode, we have to be able to send mail to the user when the transfer
323 is finished, we have to be able to report error messages sensibly,
324 and we have to be able to clear this receive request out of the
325 work queue if we initiated the receive. */
326
327/* Work queue sequence number. */
328static pointer pRecseq;
329/* File name being transferred from. */
330static struct sstring sRecfrom;
331/* File name being transferred to. */
332static struct sstring sRecto;
333/* System being transferred from. */
334static struct sstring sRecfromsys;
335/* Requesting user name. */
336static struct sstring sRecuser;
337/* Final file mode. */
338static unsigned int iRecmode;
339/* User to send mail to. */
340static struct sstring sRecmail;
341/* Temporary file name (as returned by esysdep_open_receive). */
342static struct sstring sRectemp;
343/* Start time. */
344static long iRecstart_secs;
345static long iRecstart_micros;
346/* Indicate whether we had an error during the transfer. */
347static boolean fRecerror;
348
349/* Store information about a file being received. */
350
351boolean
352fstore_recfile (e, pseq, zfrom, zto, zfromsys, zuser, imode, zmail, ztemp)
353 openfile_t e;
354 pointer pseq;
355 const char *zfrom;
356 const char *zto;
357 const char *zfromsys;
358 const char *zuser;
359 unsigned int imode;
360 const char *zmail;
361 const char *ztemp;
362{
363#if DEBUG > 0
364 if (ffileisopen (eRecfile))
365 ulog (LOG_FATAL, "fstore_recfile: In progress");
366#endif
367
368 if (! ffsetstring (&sRecfrom, zfrom)
369 || ! ffsetstring (&sRecto, zto)
370 || ! ffsetstring (&sRecfromsys, zfromsys)
371 || ! ffsetstring (&sRecuser, zuser)
372 || ! ffsetstring (&sRecmail, zmail)
373 || ! ffsetstring (&sRectemp, ztemp))
374 {
375 (void) ffileclose (e);
376 (void) remove (ztemp);
377 return FALSE;
378 }
379
380 eRecfile = e;
381 pRecseq = pseq;
382 iRecmode = imode;
383 iRecstart_secs = isysdep_process_time (&iRecstart_micros);
384
385 fRecerror = FALSE;
386
387 return TRUE;
388}
389
390/* Finish up after receiving a file. The argument indicates whether
391 the data was received correctly. We do not confirm the file
392 reception to the other system unless this function returns TRUE.
393 This may be called when no file receive is in progress if a fatal
394 program error occurs. The zwhy and fnever arguments are valid if
395 fsent is FALSE; fnever is TRUE if the file receive can never
396 succeed. */
397
398boolean
399freceived_file (fsent, cbytes, zwhy, fnever)
400 boolean fsent;
401 long cbytes;
402 const char *zwhy;
403 boolean fnever;
404{
405 long isecs, imicros;
406
407 if (fRecerror)
408 fsent = FALSE;
409
410 if (ffileisopen (eRecfile))
411 {
412 if (! ffileclose (eRecfile))
413 {
414 if (fsent)
415 {
416 zwhy = strerror (errno);
417 fnever = FALSE;
418 ulog (LOG_ERROR, "close: %s", zwhy);
419 }
420 fsent = FALSE;
421 }
422 }
423
424 if (fsent)
425 {
426 if (! fsysdep_move_file (zfgetstring (&sRectemp),
427 zfgetstring (&sRecto),
428 iRecmode, TRUE,
429 (pRecseq != NULL
430 ? zfgetstring (&sRecuser)
431 : (const char *) NULL)))
432 {
433 zwhy = "could not move to final location";
434 fnever = TRUE;
435 fsent = FALSE;
436 }
437 }
438 else
439 (void) remove (zfgetstring (&sRectemp));
440
441 isecs = isysdep_process_time (&imicros);
442 imicros -= iRecstart_micros;
443 if (imicros < 0)
444 {
445 imicros += 1000000;
446 isecs--;
447 }
448 isecs -= iRecstart_secs;
449
450 ustats (fsent, zfgetstring (&sRecuser), zfgetstring (&sRecfromsys),
451 FALSE, cbytes, isecs, imicros);
452
453 if (fsent)
454 {
455 const char *zmail;
456
457 zmail = zfgetstring (&sRecmail);
458 if (*zmail != '\0')
459 (void) fmail_transfer (TRUE, zfgetstring (&sRecuser),
460 zmail, zwhy,
461 zfgetstring (&sRecfrom),
462 zfgetstring (&sRecfromsys),
463 zfgetstring (&sRecto),
464 zLocalname, (const char *) NULL);
465
466 if (pRecseq != NULL)
467 {
468 if (! fsysdep_did_work (pRecseq))
469 fsent = FALSE;
470 pRecseq = NULL;
471 }
472 }
473 else
474 {
475 /* If the transfer failed, we send mail if it was requested
476 locally and if it can never succeed. */
477 if (pRecseq != NULL && fnever)
478 {
479 (void) fmail_transfer (FALSE,
480 zfgetstring (&sRecuser),
481 zfgetstring (&sRecmail),
482 zwhy,
483 zfgetstring (&sRecfrom),
484 zfgetstring (&sRecfromsys),
485 zfgetstring (&sRecto),
486 zLocalname, (const char *) NULL);
487 if (! fsysdep_did_work (pRecseq))
488 fsent = FALSE;
489 pRecseq = NULL;
490 }
491 }
492
493 eRecfile = EFILECLOSED;
494 uffreestring (&sRecfrom);
495 uffreestring (&sRecto);
496 uffreestring (&sRecfromsys);
497 uffreestring (&sRecuser);
498 uffreestring (&sRecmail);
499 uffreestring (&sRectemp);
500
501 return fsent;
502}
503
504/* Some error occurred while receiving a file. Note that we had an
505 error, so that when we close up we know that something went wrong.
506 We leave the file open because it's easier to handle everything in
507 freceived_file. This used to differentiate between permanent
508 errors and temporary errors, but I've decided that all errors that
509 occur while the file is being transferred are temporary. */
510
511void
512urecfile_error ()
513{
514 fRecerror = TRUE;
515}
516
517/* There was some sort of protocol error while receiving the file,
518 so we want to try it again. We must truncate the file. */
519
520boolean
521frecfile_rewind ()
522{
523 eRecfile = esysdep_truncate (eRecfile, zfgetstring (&sRectemp));
524 if (! ffileisopen (eRecfile))
525 {
526 urecfile_error ();
527 return FALSE;
528 }
529 return TRUE;
530}
531\f
532/* Send mail about a file transfer. We send to the given mailing
533 address if there is one, otherwise to the user. */
534
535boolean
536fmail_transfer (fsuccess, zuser, zmail, zwhy, zfromfile, zfromsys,
537 ztofile, ztosys, zsaved)
538 boolean fsuccess;
539 const char *zuser;
540 const char *zmail;
541 const char *zwhy;
542 const char *zfromfile;
543 const char *zfromsys;
544 const char *ztofile;
545 const char *ztosys;
546 const char *zsaved;
547{
548 const char *zsendto;
549 const char *az[20];
550 int i;
551
552 if (zmail != NULL && *zmail != '\0')
553 zsendto = zmail;
554 else
555 zsendto = zuser;
556
557 i = 0;
558 az[i++] = "The file\n\t";
559 az[i++] = zfromsys;
560 az[i++] = "!";
561 az[i++] = zfromfile;
562 if (fsuccess)
563 az[i++] = "\nwas successfully transferred to\n\t";
564 else
565 az[i++] = "\ncould not be transferred to\n\t";
566 az[i++] = ztosys;
567 az[i++] = "!";
568 az[i++] = ztofile;
569 az[i++] = "\nas requested by\n\t";
570 az[i++] = zuser;
571 if (! fsuccess)
572 {
573 az[i++] = "\nfor the following reason:\n\t";
574 az[i++] = zwhy;
575 az[i++] = "\n";
576 }
577 if (zsaved != NULL)
578 {
579 az[i++] = zsaved;
580 az[i++] = "\n";
581 }
582
583 return fsysdep_mail (zsendto,
584 fsuccess ? "UUCP succeeded" : "UUCP failed",
585 i, az);
586}