Commit | Line | Data |
---|---|---|
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 | |
79 | char 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. */ | |
87 | openfile_t eSendfile = EFILECLOSED; | |
88 | ||
89 | /* Current file being received. */ | |
90 | openfile_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 | ||
101 | struct 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__ | |
114 | static 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__ | |
135 | static 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 | |
145 | uffreestring (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. */ | |
159 | static pointer pSendseq; | |
160 | /* File name being transferred from. */ | |
161 | static struct sstring sSendfrom; | |
162 | /* File name being transferred to. */ | |
163 | static struct sstring sSendto; | |
164 | /* System being transferred to. */ | |
165 | static struct sstring sSendtosys; | |
166 | /* Requesting user name. */ | |
167 | static struct sstring sSenduser; | |
168 | /* User to send mail to. */ | |
169 | static struct sstring sSendmail; | |
170 | /* Start time. */ | |
171 | static long iSendstart_secs; | |
172 | static long iSendstart_micros; | |
173 | /* Indicate whether we had an error during the transfer. */ | |
174 | static boolean fSenderror; | |
175 | ||
176 | /* Store information about a file being sent. */ | |
177 | ||
178 | boolean | |
179 | fstore_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 | ||
219 | boolean | |
220 | fsent_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 | ||
314 | void | |
315 | usendfile_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. */ | |
328 | static pointer pRecseq; | |
329 | /* File name being transferred from. */ | |
330 | static struct sstring sRecfrom; | |
331 | /* File name being transferred to. */ | |
332 | static struct sstring sRecto; | |
333 | /* System being transferred from. */ | |
334 | static struct sstring sRecfromsys; | |
335 | /* Requesting user name. */ | |
336 | static struct sstring sRecuser; | |
337 | /* Final file mode. */ | |
338 | static unsigned int iRecmode; | |
339 | /* User to send mail to. */ | |
340 | static struct sstring sRecmail; | |
341 | /* Temporary file name (as returned by esysdep_open_receive). */ | |
342 | static struct sstring sRectemp; | |
343 | /* Start time. */ | |
344 | static long iRecstart_secs; | |
345 | static long iRecstart_micros; | |
346 | /* Indicate whether we had an error during the transfer. */ | |
347 | static boolean fRecerror; | |
348 | ||
349 | /* Store information about a file being received. */ | |
350 | ||
351 | boolean | |
352 | fstore_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 | ||
398 | boolean | |
399 | freceived_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 | ||
511 | void | |
512 | urecfile_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 | ||
520 | boolean | |
521 | frecfile_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 | ||
535 | boolean | |
536 | fmail_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 | } |