/* file.c Generic routines to handle files. Copyright (C) 1991, 1992 Ian Lance Taylor This file is part of the Taylor UUCP package. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. The author of the program may be contacted at ian@airs.com or c/o AIRS, P.O. Box 520, Waltham, MA 02254. $Log: file.c,v $ Revision 1.1.1.1 1993/03/21 09:45:37 cgd initial import of 386bsd-0.1 sources Revision 1.16 1992/03/30 04:07:13 ian Dirk Musstopf: remove temporary file if receive fails Revision 1.15 1992/03/17 00:31:33 ian Changed iRecmode to unsigned int Revision 1.14 1992/03/11 01:18:15 ian Niels Baggesen: drop the connection on a write failure Revision 1.13 1992/03/11 00:18:50 ian Save temporary file if file send fails Revision 1.12 1992/02/29 01:06:59 ian Chip Salzenberg: recheck file permissions before sending Revision 1.11 1992/02/19 19:36:07 ian Rearranged time functions Revision 1.10 1992/02/08 03:54:18 ian Include only in , added 1992 copyright Revision 1.9 1992/01/18 22:48:53 ian Reworked sending of mail and general handling of failed transfers Revision 1.8 1991/12/21 23:10:43 ian Terry Gardner: record failed file transfers in statistics file Revision 1.7 1991/12/17 07:09:58 ian Record statistics in fractions of a second Revision 1.6 1991/12/10 19:29:02 ian Move statistics file stuff from file.c to log.c Revision 1.5 1991/11/10 19:24:22 ian Added pffile protocol entry point for file level control Revision 1.4 1991/11/08 04:30:50 ian Hannu Strang: flush statistics file after each line Revision 1.3 1991/11/07 19:42:16 ian Chip Salzenberg: declare inline functions consistently Revision 1.2 1991/09/19 03:23:34 ian Chip Salzenberg: append to private debugging file, don't overwrite it Revision 1.1 1991/09/10 19:40:31 ian Initial revision */ #include "uucp.h" #if USE_RCS_ID char file_rcsid[] = "$Id: file.c,v 1.1.1.1 1993/03/21 09:45:37 cgd Exp $"; #endif #include #include "system.h" /* Current file being sent. */ openfile_t eSendfile = EFILECLOSED; /* Current file being received. */ openfile_t eRecfile = EFILECLOSED; /* This file has to keep several strings. I don't want to pay for the overhead (small as it is) of calling malloc for each one, and I also don't want to truncate the strings. So I use a structure which holds strings up to a certain length, and call malloc for longer strings. NULL strings are indistinguishable from empty strings, which turns out not to matter. */ #define CSTRLEN (30) struct sstring { char ab[CSTRLEN]; char *zalloc; }; /* Local functions. */ __inline__ static boolean ffsetstring P((struct sstring *, const char *)); __inline__ static const char *zfgetstring P((const struct sstring *)); __inline__ static void uffreestring P((struct sstring *)); __inline__ static boolean ffsetstring (q, z) struct sstring *q; const char *z; { if (z == NULL) q->ab[0] = '\0'; else if (strlen (z) < CSTRLEN) strcpy (q->ab, z); else { q->zalloc = strdup (z); if (q->zalloc == NULL) { ulog (LOG_ERROR, "Not enough memory to store command"); return FALSE; } } return TRUE; } __inline__ static const char *zfgetstring (q) const struct sstring *q; { if (q->zalloc != NULL) return q->zalloc; else return q->ab; } __inline__ static void uffreestring (q) struct sstring *q; { xfree ((pointer) q->zalloc); q->zalloc = NULL; } /* Information we keep for the file being sent. We have to be able to send mail to the user when the transfer is finished, we have to be able to report error messages sensibly, and we have to be able to clear this send request out of the work queue if we initiated the send. */ /* Work queue sequence number. */ static pointer pSendseq; /* File name being transferred from. */ static struct sstring sSendfrom; /* File name being transferred to. */ static struct sstring sSendto; /* System being transferred to. */ static struct sstring sSendtosys; /* Requesting user name. */ static struct sstring sSenduser; /* User to send mail to. */ static struct sstring sSendmail; /* Start time. */ static long iSendstart_secs; static long iSendstart_micros; /* Indicate whether we had an error during the transfer. */ static boolean fSenderror; /* Store information about a file being sent. */ boolean fstore_sendfile (e, pseq, zfrom, zto, ztosys, zuser, zmail) openfile_t e; pointer pseq; const char *zfrom; const char *zto; const char *ztosys; const char *zuser; const char *zmail; { #if DEBUG > 0 if (ffileisopen (eSendfile)) ulog (LOG_FATAL, "fstore_sendfile: In progress"); #endif if (! ffsetstring (&sSendfrom, zfrom) || ! ffsetstring (&sSendto, zto) || ! ffsetstring (&sSendtosys, ztosys) || ! ffsetstring (&sSenduser, zuser) || ! ffsetstring (&sSendmail, zmail)) { (void) ffileclose (e); return FALSE; } eSendfile = e; pSendseq = pseq; iSendstart_secs = isysdep_process_time (&iSendstart_micros); fSenderror = FALSE; return TRUE; } /* Finish up after sending a file. The freceived argument indicates whether the other file received it successfully. The cbytes argument is the number of bytes that were sent (for the statistics entry). The zwhy argument holds a reason for failure if freceived is FALSE. The fnever argument is TRUE if the file was not received correctly and, moreover, will never be received correctly. */ boolean fsent_file (freceived, cbytes, zwhy, fnever) boolean freceived; long cbytes; const char *zwhy; boolean fnever; { boolean f; long isecs, imicros; f = TRUE; if (fSenderror) freceived = FALSE; if (ffileisopen (eSendfile)) (void) ffileclose (eSendfile); isecs = isysdep_process_time (&imicros); imicros -= iSendstart_micros; if (imicros < 0) { imicros += 1000000; isecs--; } isecs -= iSendstart_secs; ustats (freceived, zfgetstring (&sSenduser), zfgetstring (&sSendtosys), TRUE, cbytes, isecs, imicros); if (freceived) { const char *zmail; zmail = zfgetstring (&sSendmail); if (*zmail != '\0') { if (! fmail_transfer (TRUE, zfgetstring (&sSenduser), zmail, zwhy, zfgetstring (&sSendfrom), zLocalname, zfgetstring (&sSendto), zfgetstring (&sSendtosys), (const char *) NULL)) f = FALSE; } if (pSendseq != NULL) { if (! fsysdep_did_work (pSendseq)) f = FALSE; pSendseq = NULL; } } else { /* If the transfer failed, we only try to save the file and send mail if it was requested locally (in which case pSendseq != NULL) and it will never succeed. We send mail to sSendmail if defined, otherwise to sSenduser. I hope this is reasonable. */ if (pSendseq != NULL && fnever) { if (! fmail_transfer (FALSE, zfgetstring (&sSenduser), zfgetstring (&sSendmail), zwhy, zfgetstring (&sSendfrom), zLocalname, zfgetstring (&sSendto), zfgetstring (&sSendtosys), zsysdep_save_temp_file (pSendseq))) f = FALSE; if (! fsysdep_did_work (pSendseq)) f = FALSE; pSendseq = NULL; } } eSendfile = EFILECLOSED; uffreestring (&sSendfrom); uffreestring (&sSendto); uffreestring (&sSendtosys); uffreestring (&sSenduser); uffreestring (&sSendmail); return f; } /* Some error occurred while sending a file. Mark an error and let fsent_file handle everything else. This used to differentiate between temporary and permanent errors, but I've decided that all transmission errors are temporary. */ void usendfile_error () { fSenderror = TRUE; } /* Information we keep for the file being received. We have to be able to move the file into its final location with the correct mode, we have to be able to send mail to the user when the transfer is finished, we have to be able to report error messages sensibly, and we have to be able to clear this receive request out of the work queue if we initiated the receive. */ /* Work queue sequence number. */ static pointer pRecseq; /* File name being transferred from. */ static struct sstring sRecfrom; /* File name being transferred to. */ static struct sstring sRecto; /* System being transferred from. */ static struct sstring sRecfromsys; /* Requesting user name. */ static struct sstring sRecuser; /* Final file mode. */ static unsigned int iRecmode; /* User to send mail to. */ static struct sstring sRecmail; /* Temporary file name (as returned by esysdep_open_receive). */ static struct sstring sRectemp; /* Start time. */ static long iRecstart_secs; static long iRecstart_micros; /* Indicate whether we had an error during the transfer. */ static boolean fRecerror; /* Store information about a file being received. */ boolean fstore_recfile (e, pseq, zfrom, zto, zfromsys, zuser, imode, zmail, ztemp) openfile_t e; pointer pseq; const char *zfrom; const char *zto; const char *zfromsys; const char *zuser; unsigned int imode; const char *zmail; const char *ztemp; { #if DEBUG > 0 if (ffileisopen (eRecfile)) ulog (LOG_FATAL, "fstore_recfile: In progress"); #endif if (! ffsetstring (&sRecfrom, zfrom) || ! ffsetstring (&sRecto, zto) || ! ffsetstring (&sRecfromsys, zfromsys) || ! ffsetstring (&sRecuser, zuser) || ! ffsetstring (&sRecmail, zmail) || ! ffsetstring (&sRectemp, ztemp)) { (void) ffileclose (e); (void) remove (ztemp); return FALSE; } eRecfile = e; pRecseq = pseq; iRecmode = imode; iRecstart_secs = isysdep_process_time (&iRecstart_micros); fRecerror = FALSE; return TRUE; } /* Finish up after receiving a file. The argument indicates whether the data was received correctly. We do not confirm the file reception to the other system unless this function returns TRUE. This may be called when no file receive is in progress if a fatal program error occurs. The zwhy and fnever arguments are valid if fsent is FALSE; fnever is TRUE if the file receive can never succeed. */ boolean freceived_file (fsent, cbytes, zwhy, fnever) boolean fsent; long cbytes; const char *zwhy; boolean fnever; { long isecs, imicros; if (fRecerror) fsent = FALSE; if (ffileisopen (eRecfile)) { if (! ffileclose (eRecfile)) { if (fsent) { zwhy = strerror (errno); fnever = FALSE; ulog (LOG_ERROR, "close: %s", zwhy); } fsent = FALSE; } } if (fsent) { if (! fsysdep_move_file (zfgetstring (&sRectemp), zfgetstring (&sRecto), iRecmode, TRUE, (pRecseq != NULL ? zfgetstring (&sRecuser) : (const char *) NULL))) { zwhy = "could not move to final location"; fnever = TRUE; fsent = FALSE; } } else (void) remove (zfgetstring (&sRectemp)); isecs = isysdep_process_time (&imicros); imicros -= iRecstart_micros; if (imicros < 0) { imicros += 1000000; isecs--; } isecs -= iRecstart_secs; ustats (fsent, zfgetstring (&sRecuser), zfgetstring (&sRecfromsys), FALSE, cbytes, isecs, imicros); if (fsent) { const char *zmail; zmail = zfgetstring (&sRecmail); if (*zmail != '\0') (void) fmail_transfer (TRUE, zfgetstring (&sRecuser), zmail, zwhy, zfgetstring (&sRecfrom), zfgetstring (&sRecfromsys), zfgetstring (&sRecto), zLocalname, (const char *) NULL); if (pRecseq != NULL) { if (! fsysdep_did_work (pRecseq)) fsent = FALSE; pRecseq = NULL; } } else { /* If the transfer failed, we send mail if it was requested locally and if it can never succeed. */ if (pRecseq != NULL && fnever) { (void) fmail_transfer (FALSE, zfgetstring (&sRecuser), zfgetstring (&sRecmail), zwhy, zfgetstring (&sRecfrom), zfgetstring (&sRecfromsys), zfgetstring (&sRecto), zLocalname, (const char *) NULL); if (! fsysdep_did_work (pRecseq)) fsent = FALSE; pRecseq = NULL; } } eRecfile = EFILECLOSED; uffreestring (&sRecfrom); uffreestring (&sRecto); uffreestring (&sRecfromsys); uffreestring (&sRecuser); uffreestring (&sRecmail); uffreestring (&sRectemp); return fsent; } /* Some error occurred while receiving a file. Note that we had an error, so that when we close up we know that something went wrong. We leave the file open because it's easier to handle everything in freceived_file. This used to differentiate between permanent errors and temporary errors, but I've decided that all errors that occur while the file is being transferred are temporary. */ void urecfile_error () { fRecerror = TRUE; } /* There was some sort of protocol error while receiving the file, so we want to try it again. We must truncate the file. */ boolean frecfile_rewind () { eRecfile = esysdep_truncate (eRecfile, zfgetstring (&sRectemp)); if (! ffileisopen (eRecfile)) { urecfile_error (); return FALSE; } return TRUE; } /* Send mail about a file transfer. We send to the given mailing address if there is one, otherwise to the user. */ boolean fmail_transfer (fsuccess, zuser, zmail, zwhy, zfromfile, zfromsys, ztofile, ztosys, zsaved) boolean fsuccess; const char *zuser; const char *zmail; const char *zwhy; const char *zfromfile; const char *zfromsys; const char *ztofile; const char *ztosys; const char *zsaved; { const char *zsendto; const char *az[20]; int i; if (zmail != NULL && *zmail != '\0') zsendto = zmail; else zsendto = zuser; i = 0; az[i++] = "The file\n\t"; az[i++] = zfromsys; az[i++] = "!"; az[i++] = zfromfile; if (fsuccess) az[i++] = "\nwas successfully transferred to\n\t"; else az[i++] = "\ncould not be transferred to\n\t"; az[i++] = ztosys; az[i++] = "!"; az[i++] = ztofile; az[i++] = "\nas requested by\n\t"; az[i++] = zuser; if (! fsuccess) { az[i++] = "\nfor the following reason:\n\t"; az[i++] = zwhy; az[i++] = "\n"; } if (zsaved != NULL) { az[i++] = zsaved; az[i++] = "\n"; } return fsysdep_mail (zsendto, fsuccess ? "UUCP succeeded" : "UUCP failed", i, az); }