diff options
Diffstat (limited to 'gnu/libexec/uucp/uucico/rec.c')
-rw-r--r-- | gnu/libexec/uucp/uucico/rec.c | 1162 |
1 files changed, 1162 insertions, 0 deletions
diff --git a/gnu/libexec/uucp/uucico/rec.c b/gnu/libexec/uucp/uucico/rec.c new file mode 100644 index 0000000..160aab7 --- /dev/null +++ b/gnu/libexec/uucp/uucico/rec.c @@ -0,0 +1,1162 @@ +/* rec.c + Routines to receive a file. + + 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 Infinity Development Systems, P.O. Box 520, Waltham, MA 02254. + */ + +#include "uucp.h" + +#if USE_RCS_ID +const char rec_rcsid[] = "$Id: rec.c,v 1.1 1993/08/04 19:36:28 jtc Exp $"; +#endif + +#include <errno.h> + +#include "uudefs.h" +#include "uuconf.h" +#include "system.h" +#include "prot.h" +#include "trans.h" + +/* If the other side does not tell us the size of a file it wants to + send us, we assume it is this long. This is only used for free + space checking. */ +#define CASSUMED_FILE_SIZE (10240) + +/* We keep this information in the pinfo field of the stransfer + structure. */ +struct srecinfo +{ + /* Local user to send mail to (may be NULL). */ + char *zmail; + /* Full file name. */ + char *zfile; + /* Temporary file name. */ + char *ztemp; + /* TRUE if this is a spool directory file. */ + boolean fspool; + /* TRUE if this was a local request. */ + boolean flocal; + /* TRUE if the file has been completely received. */ + boolean freceived; + /* TRUE if remote request has been replied to. */ + boolean freplied; + /* TRUE if we moved the file to the final destination. */ + boolean fmoved; +}; + +/* This structure is kept in the pinfo field if we are refusing a + remote request. */ +struct srecfailinfo +{ + /* Reason for refusal. */ + enum tfailure twhy; + /* TRUE if we have sent the reason for refusal. */ + boolean fsent; + /* TRUE if we have seen the end of the file. */ + boolean freceived; +}; + +/* Local functions. */ + +static void urrec_free P((struct stransfer *qtrans)); +static boolean flocal_rec_fail P((struct stransfer *qtrans, + struct scmd *qcmd, + const struct uuconf_system *qsys, + const char *zwhy)); +static boolean flocal_rec_send_request P((struct stransfer *qtrans, + struct sdaemon *qdaemon)); +static boolean flocal_rec_await_reply P((struct stransfer *qtrans, + struct sdaemon *qdaemon, + const char *zdata, + size_t cdata)); +static boolean fremote_send_reply P((struct stransfer *qtrans, + struct sdaemon *qdaemon)); +static boolean fremote_send_fail P((struct sdaemon *qdaemon, + struct scmd *qcmd, + enum tfailure twhy, + int iremote)); +static boolean fremote_send_fail_send P((struct stransfer *qtrans, + struct sdaemon *qdaemon)); +static boolean fremote_discard P((struct stransfer *qtrans, + struct sdaemon *qdaemon, + const char *zdata, size_t cdata)); +static boolean frec_file_end P((struct stransfer *qtrans, + struct sdaemon *qdaemon, + const char *zdata, size_t cdata)); +static boolean frec_file_send_confirm P((struct stransfer *qtrans, + struct sdaemon *qdaemon)); + +/* Free up a receive stransfer structure. */ + +static void +urrec_free (qtrans) + struct stransfer *qtrans; +{ + struct srecinfo *qinfo = (struct srecinfo *) qtrans->pinfo; + + if (qinfo != NULL) + { + ubuffree (qinfo->zmail); + ubuffree (qinfo->zfile); + ubuffree (qinfo->ztemp); + xfree (qtrans->pinfo); + } + + utransfree (qtrans); +} + +/* Set up a request for a file from the remote system. This may be + called before the remote system has been called. + + This is the order of function calls: + + flocal_rec_file_init --> fqueue_local + flocal_rec_send_request (send R ...) --> fqueue_receive + flocal_rec_await_reply (open file, call pffile) --> fqueue_receive + receive file + frec_file_end (close and move file, call pffile) --> fqueue_send + frec_file_send_confirm (send CY) + */ + +boolean +flocal_rec_file_init (qdaemon, qcmd) + struct sdaemon *qdaemon; + struct scmd *qcmd; +{ + const struct uuconf_system *qsys; + boolean fspool; + char *zfile; + struct srecinfo *qinfo; + struct stransfer *qtrans; + + qsys = qdaemon->qsys; + + /* Make sure we are permitted to transfer files. */ + if (qdaemon->fcaller + ? ! qsys->uuconf_fcall_transfer + : ! qsys->uuconf_fcalled_transfer) + { + /* This case will have been checked by uucp or uux, but it could + have changed. */ + if (! qsys->uuconf_fcall_transfer + && ! qsys->uuconf_fcalled_transfer) + return flocal_rec_fail ((struct stransfer *) NULL, qcmd, qsys, + "not permitted to request files"); + return TRUE; + } + + fspool = fspool_file (qcmd->zto); + + if (fspool) + { + pointer puuconf; + int iuuconf; + const char *zlocalname; + struct uuconf_system slocalsys; + + /* Normal users are not allowed to request files to be received + into the spool directory. To support uux forwarding, we use + the special option '9'. This permits a file to be received + into the spool directory for the local system only without + the usual checking. This is only done for local requests, of + course. */ + if (qcmd->zto[0] != 'D' + || strchr (qcmd->zoptions, '9') == NULL) + return flocal_rec_fail ((struct stransfer *) NULL, qcmd, qsys, + "not permitted to receive"); + + puuconf = qdaemon->puuconf; + iuuconf = uuconf_localname (puuconf, &zlocalname); + if (iuuconf == UUCONF_NOT_FOUND) + { + zlocalname = zsysdep_localname (); + if (zlocalname == NULL) + return FALSE; + } + else if (iuuconf != UUCONF_SUCCESS) + { + ulog_uuconf (LOG_ERROR, puuconf, iuuconf); + return FALSE; + } + + iuuconf = uuconf_system_info (puuconf, zlocalname, &slocalsys); + if (iuuconf == UUCONF_NOT_FOUND) + { + iuuconf = uuconf_system_local (puuconf, &slocalsys); + if (iuuconf != UUCONF_SUCCESS) + { + ulog_uuconf (LOG_ERROR, puuconf, iuuconf); + return FALSE; + } + } + else if (iuuconf != UUCONF_SUCCESS) + { + ulog_uuconf (LOG_ERROR, puuconf, iuuconf); + return FALSE; + } + + zfile = zsysdep_spool_file_name (&slocalsys, qcmd->zto, qcmd->pseq); + + (void) uuconf_system_free (puuconf, &slocalsys); + + if (zfile == NULL) + return FALSE; + } + else + { + zfile = zsysdep_add_base (qcmd->zto, qcmd->zfrom); + if (zfile == NULL) + return FALSE; + + /* Check permissions. */ + if (! fin_directory_list (zfile, qsys->uuconf_pzlocal_receive, + qsys->uuconf_zpubdir, TRUE, + FALSE, qcmd->zuser)) + { + ubuffree (zfile); + return flocal_rec_fail ((struct stransfer *) NULL, qcmd, qsys, + "not permitted to receive"); + } + + /* The 'f' option means that directories should not + be created if they do not already exist. */ + if (strchr (qcmd->zoptions, 'f') == NULL) + { + if (! fsysdep_make_dirs (zfile, TRUE)) + { + ubuffree (zfile); + return flocal_rec_fail ((struct stransfer *) NULL, qcmd, + qsys, "cannot create directories"); + } + } + } + + qinfo = (struct srecinfo *) xmalloc (sizeof (struct srecinfo)); + if (strchr (qcmd->zoptions, 'm') == NULL) + qinfo->zmail = NULL; + else + qinfo->zmail = zbufcpy (qcmd->zuser); + qinfo->zfile = zfile; + qinfo->ztemp = NULL; + qinfo->fspool = fspool; + qinfo->flocal = TRUE; + qinfo->freceived = FALSE; + qinfo->freplied = TRUE; + + qtrans = qtransalc (qcmd); + qtrans->psendfn = flocal_rec_send_request; + qtrans->pinfo = (pointer) qinfo; + + return fqueue_local (qdaemon, qtrans); +} + +/* Report an error for a local receive request. */ + +static boolean +flocal_rec_fail (qtrans, qcmd, qsys, zwhy) + struct stransfer *qtrans; + struct scmd *qcmd; + const struct uuconf_system *qsys; + const char *zwhy; +{ + if (zwhy != NULL) + { + ulog (LOG_ERROR, "%s: %s", qcmd->zfrom, zwhy); + (void) fmail_transfer (FALSE, qcmd->zuser, (const char *) NULL, zwhy, + qcmd->zfrom, qsys->uuconf_zname, + qcmd->zto, (const char *) NULL, + (const char *) NULL); + (void) fsysdep_did_work (qcmd->pseq); + } + if (qtrans != NULL) + urrec_free (qtrans); + return TRUE; +} + +/* This is called when we are ready to send the actual request to the + other system. */ + +static boolean +flocal_rec_send_request (qtrans, qdaemon) + struct stransfer *qtrans; + struct sdaemon *qdaemon; +{ + struct srecinfo *qinfo = (struct srecinfo *) qtrans->pinfo; + long cbytes, cbytes2; + size_t clen; + char *zsend; + boolean fret; + + qinfo->ztemp = zsysdep_receive_temp (qdaemon->qsys, qinfo->zfile, + (const char *) NULL); + if (qinfo->ztemp == NULL) + { + urrec_free (qtrans); + return FALSE; + } + + /* Check the amount of free space available for both the temporary + file and the real file. */ + cbytes = csysdep_bytes_free (qinfo->ztemp); + cbytes2 = csysdep_bytes_free (qinfo->zfile); + if (cbytes < cbytes2) + cbytes = cbytes2; + if (cbytes != -1) + { + cbytes -= qdaemon->qsys->uuconf_cfree_space; + if (cbytes < 0) + cbytes = 0; + } + + if (qdaemon->clocal_size != -1 + && (cbytes == -1 || qdaemon->clocal_size < cbytes)) + cbytes = qdaemon->clocal_size; + + /* We send the string + R from to user options + + We put a dash in front of options. If we are talking to a + counterpart, we also send the maximum size file we are prepared + to accept, as returned by esysdep_open_receive. */ + clen = (strlen (qtrans->s.zfrom) + strlen (qtrans->s.zto) + + strlen (qtrans->s.zuser) + strlen (qtrans->s.zoptions) + 30); + zsend = zbufalc (clen); + if ((qdaemon->ifeatures & FEATURE_SIZES) == 0) + sprintf (zsend, "R %s %s %s -%s", qtrans->s.zfrom, qtrans->s.zto, + qtrans->s.zuser, qtrans->s.zoptions); + else if ((qdaemon->ifeatures & FEATURE_V103) == 0) + sprintf (zsend, "R %s %s %s -%s 0x%lx", qtrans->s.zfrom, qtrans->s.zto, + qtrans->s.zuser, qtrans->s.zoptions, (unsigned long) cbytes); + else + sprintf (zsend, "R %s %s %s -%s %ld", qtrans->s.zfrom, qtrans->s.zto, + qtrans->s.zuser, qtrans->s.zoptions, cbytes); + + fret = (*qdaemon->qproto->pfsendcmd) (qdaemon, zsend, qtrans->ilocal, + qtrans->iremote); + ubuffree (zsend); + if (! fret) + { + urrec_free (qtrans); + return FALSE; + } + + qtrans->fcmd = TRUE; + qtrans->precfn = flocal_rec_await_reply; + + return fqueue_receive (qdaemon, qtrans); +} + +/* This is called when a reply is received for the request. */ + +/*ARGSUSED*/ +static boolean +flocal_rec_await_reply (qtrans, qdaemon, zdata, cdata) + struct stransfer *qtrans; + struct sdaemon *qdaemon; + const char *zdata; + size_t cdata; +{ + struct srecinfo *qinfo = (struct srecinfo *) qtrans->pinfo; + long crestart; + const char *zlog; + + if (zdata[0] != 'R' + || (zdata[1] != 'Y' && zdata[1] != 'N')) + { + ulog (LOG_ERROR, "%s: bad response to receive request: \"%s\"", + qtrans->s.zfrom, zdata); + urrec_free (qtrans); + return FALSE; + } + + if (zdata[1] == 'N') + { + boolean fnever; + const char *zerr; + + fnever = TRUE; + if (zdata[2] == '2') + zerr = "no such file"; + else if (zdata[2] == '6') + { + /* We sent over the maximum file size we were prepared to + receive, and the remote system is telling us that the + file is larger than that. Try again later. It would be + better if we could know whether there will ever be enough + room. */ + zerr = "too large to receive now"; + fnever = FALSE; + } + else + zerr = "unknown reason"; + + if (fnever) + return flocal_rec_fail (qtrans, &qtrans->s, qdaemon->qsys, zerr); + + ulog (LOG_ERROR, "%s: %s", qtrans->s.zfrom, zerr); + + urrec_free (qtrans); + + return TRUE; + } + + /* The mode should have been sent as "RY 0%o". If it wasn't, we use + 0666. */ + qtrans->s.imode = (unsigned int) strtol ((char *) (zdata + 2), + (char **) NULL, 8); + if (qtrans->s.imode == 0) + qtrans->s.imode = 0666; + + /* Open the file to receive into. We just ignore any restart count, + since we have no way to tell it to the other side. SVR4 may have + some way to do this, but I don't know what it is. */ + qtrans->e = esysdep_open_receive (qdaemon->qsys, qinfo->zfile, + (const char *) NULL, qinfo->ztemp, + &crestart); + if (! ffileisopen (qtrans->e)) + return flocal_rec_fail (qtrans, &qtrans->s, qdaemon->qsys, + "cannot open file"); + + if (qinfo->fspool) + zlog = qtrans->s.zto; + else + zlog = qinfo->zfile; + qtrans->zlog = zbufalc (sizeof "Receiving " + strlen (zlog)); + sprintf (qtrans->zlog, "Receiving %s", zlog); + + if (qdaemon->qproto->pffile != NULL) + { + boolean fhandled; + + if (! (*qdaemon->qproto->pffile) (qdaemon, qtrans, TRUE, FALSE, + (long) -1, &fhandled)) + { + (void) ffileclose (qtrans->e); + return flocal_rec_fail (qtrans, &qtrans->s, qdaemon->qsys, + (const char *) NULL); + } + if (fhandled) + return TRUE; + } + + qtrans->frecfile = TRUE; + qtrans->psendfn = frec_file_send_confirm; + qtrans->precfn = frec_file_end; + + return fqueue_receive (qdaemon, qtrans); +} + +/* Make sure there is still enough disk space available to receive a + file. */ + +boolean +frec_check_free (qtrans, cfree_space) + struct stransfer *qtrans; + long cfree_space; +{ + struct srecinfo *qinfo = (struct srecinfo *) qtrans->pinfo; + long cfree1, cfree2; + + cfree1 = csysdep_bytes_free (qinfo->ztemp); + cfree2 = csysdep_bytes_free (qinfo->zfile); + if (cfree1 < cfree2) + cfree1 = cfree2; + if (cfree1 != -1 && cfree1 < cfree_space) + { + ulog (LOG_ERROR, "%s: too big to receive now", qinfo->zfile); + return FALSE; + } + + return TRUE; +} + +/* A remote request to send a file to the local system, meaning that + we are going to receive a file. + + If we are using a protocol which does not support multiple + channels, the remote system will not start sending us the file + until it has received our confirmation. In that case, the order of + functions is as follows: + + fremote_send_file_init (open file) --> fqueue_remote + fremote_send_reply (send SY, call pffile) --> fqueue_receive + receive file + frec_file_end (close and move file, call pffile) --> fqueue_send + frec_file_send_confirm (send CY) + + If the protocol supports multiple channels, then the remote system + will start sending the file immediately after the send request. + That means that the data may come in before remote_send_reply is + called, so frec_file_end may be called before fremote_send_reply. + Note that this means the pffile entry points may be called in + reverse order for such a protocol. + + If the send request is rejected, via fremote_send_fail, and the + protocol supports multiple channels, we must accept and discard + data until a zero byte buffer is received from the other side, + indicating that it has received our rejection. + + This code also handles execution requests, which are very similar + to send requests. */ + +boolean +fremote_send_file_init (qdaemon, qcmd, iremote) + struct sdaemon *qdaemon; + struct scmd *qcmd; + int iremote; +{ + const struct uuconf_system *qsys; + boolean fspool; + char *zfile; + openfile_t e; + char *ztemp; + long cbytes, cbytes2; + long crestart; + struct srecinfo *qinfo; + struct stransfer *qtrans; + const char *zlog; + + qsys = qdaemon->qsys; + + if (! qsys->uuconf_frec_request) + { + ulog (LOG_ERROR, "%s: not permitted to receive files from remote", + qcmd->zfrom); + return fremote_send_fail (qdaemon, qcmd, FAILURE_PERM, iremote); + } + + fspool = fspool_file (qcmd->zto); + + /* We don't accept remote command files. An execution request may + only send a simple data file. */ + if ((fspool && qcmd->zto[0] == 'C') + || (qcmd->bcmd == 'E' + && (! fspool || qcmd->zto[0] != 'D'))) + { + ulog (LOG_ERROR, "%s: not permitted to receive", qcmd->zfrom); + return fremote_send_fail (qdaemon, qcmd, FAILURE_PERM, iremote); + } + + /* See if we have already received this file in a previous + conversation. */ + if (fsysdep_already_received (qsys, qcmd->zto, qcmd->ztemp)) + return fremote_send_fail (qdaemon, qcmd, FAILURE_RECEIVED, iremote); + + if (fspool) + { + zfile = zsysdep_spool_file_name (qsys, qcmd->zto, (pointer) NULL); + if (zfile == NULL) + return FALSE; + } + else + { + zfile = zsysdep_local_file (qcmd->zto, qsys->uuconf_zpubdir); + if (zfile != NULL) + { + char *zadd; + + zadd = zsysdep_add_base (zfile, qcmd->zfrom); + ubuffree (zfile); + zfile = zadd; + } + if (zfile == NULL) + return FALSE; + + /* Check permissions. */ + if (! fin_directory_list (zfile, qsys->uuconf_pzremote_receive, + qsys->uuconf_zpubdir, TRUE, + FALSE, (const char *) NULL)) + { + ulog (LOG_ERROR, "%s: not permitted to receive", zfile); + ubuffree (zfile); + return fremote_send_fail (qdaemon, qcmd, FAILURE_PERM, iremote); + } + + if (strchr (qcmd->zoptions, 'f') == NULL) + { + if (! fsysdep_make_dirs (zfile, TRUE)) + { + ubuffree (zfile); + return fremote_send_fail (qdaemon, qcmd, FAILURE_OPEN, + iremote); + } + } + } + + ztemp = zsysdep_receive_temp (qsys, zfile, qcmd->ztemp); + + /* Adjust the number of bytes we are prepared to receive according + to the amount of free space we are supposed to leave available + and the maximum file size we are permitted to transfer. */ + cbytes = csysdep_bytes_free (ztemp); + cbytes2 = csysdep_bytes_free (zfile); + if (cbytes < cbytes2) + cbytes = cbytes2; + + if (cbytes != -1) + { + cbytes -= qsys->uuconf_cfree_space; + if (cbytes < 0) + cbytes = 0; + } + + if (qdaemon->cremote_size != -1 + && (cbytes == -1 || qdaemon->cremote_size < cbytes)) + cbytes = qdaemon->cremote_size; + + /* If the number of bytes we are prepared to receive is less than + the file size, we must fail. If the remote did not tell us the + file size, arbitrarily assumed that it is 10240 bytes. */ + if (cbytes != -1) + { + long csize; + + csize = qcmd->cbytes; + if (csize == -1) + csize = CASSUMED_FILE_SIZE; + if (cbytes < csize) + { + ulog (LOG_ERROR, "%s: too big to receive", zfile); + ubuffree (ztemp); + ubuffree (zfile); + return fremote_send_fail (qdaemon, qcmd, FAILURE_SIZE, iremote); + } + } + + /* Open the file to receive into. This may find an old copy of the + file, which will be used for file restart if the other side + supports it. */ + e = esysdep_open_receive (qsys, zfile, qcmd->ztemp, ztemp, &crestart); + if (! ffileisopen (e)) + { + ubuffree (ztemp); + ubuffree (zfile); + return fremote_send_fail (qdaemon, qcmd, FAILURE_OPEN, iremote); + } + + if (crestart > 0) + { + if ((qdaemon->ifeatures & FEATURE_RESTART) == 0) + crestart = -1; + else + { + DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO, + "fremote_send_file_init: Restarting receive from %ld", + crestart); + if (! ffileseek (e, crestart)) + { + ulog (LOG_ERROR, "seek: %s", strerror (errno)); + (void) ffileclose (e); + ubuffree (ztemp); + ubuffree (zfile); + return FALSE; + } + } + } + + qinfo = (struct srecinfo *) xmalloc (sizeof (struct srecinfo)); + if (strchr (qcmd->zoptions, 'n') == NULL) + qinfo->zmail = NULL; + else + qinfo->zmail = zbufcpy (qcmd->znotify); + qinfo->zfile = zfile; + qinfo->ztemp = ztemp; + qinfo->fspool = fspool; + qinfo->flocal = FALSE; + qinfo->freceived = FALSE; + qinfo->freplied = FALSE; + + qtrans = qtransalc (qcmd); + qtrans->psendfn = fremote_send_reply; + qtrans->precfn = frec_file_end; + qtrans->iremote = iremote; + qtrans->pinfo = (pointer) qinfo; + qtrans->frecfile = TRUE; + qtrans->e = e; + if (crestart > 0) + qtrans->ipos = crestart; + + if (qcmd->bcmd == 'E') + zlog = qcmd->zcmd; + else + { + if (qinfo->fspool) + zlog = qcmd->zto; + else + zlog = qinfo->zfile; + } + qtrans->zlog = zbufalc (sizeof "Receiving " + strlen (zlog)); + sprintf (qtrans->zlog, "Receiving %s", zlog); + + return fqueue_remote (qdaemon, qtrans); +} + +/* Reply to a send request, and prepare to receive the file. */ + +static boolean +fremote_send_reply (qtrans, qdaemon) + struct stransfer *qtrans; + struct sdaemon *qdaemon; +{ + struct srecinfo *qinfo = (struct srecinfo *) qtrans->pinfo; + char ab[50]; + + ab[0] = qtrans->s.bcmd; + ab[1] = 'Y'; + if (qtrans->ipos <= 0) + ab[2] = '\0'; + else + sprintf (ab + 2, " 0x%lx", (unsigned long) qtrans->ipos); + + if (! (*qdaemon->qproto->pfsendcmd) (qdaemon, ab, qtrans->ilocal, + qtrans->iremote)) + { + (void) ffileclose (qtrans->e); + (void) remove (qinfo->ztemp); + urrec_free (qtrans); + return FALSE; + } + + qinfo->freplied = TRUE; + + if (qdaemon->qproto->pffile != NULL) + { + boolean fhandled; + + if (! (*qdaemon->qproto->pffile) (qdaemon, qtrans, TRUE, FALSE, + (long) -1, &fhandled)) + { + (void) ffileclose (qtrans->e); + (void) remove (qinfo->ztemp); + urrec_free (qtrans); + return FALSE; + } + if (fhandled) + return TRUE; + } + + /* If the file has been completely received, we just want to send + the final confirmation. Otherwise, we must wait for the file + first. */ + qtrans->psendfn = frec_file_send_confirm; + if (qinfo->freceived) + return fqueue_send (qdaemon, qtrans); + else + return fqueue_receive (qdaemon, qtrans); +} + +/* If we can't receive a file, queue up a response to the remote + system. */ + +static boolean +fremote_send_fail (qdaemon, qcmd, twhy, iremote) + struct sdaemon *qdaemon; + struct scmd *qcmd; + enum tfailure twhy; + int iremote; +{ + struct srecfailinfo *qinfo; + struct stransfer *qtrans; + + qinfo = (struct srecfailinfo *) xmalloc (sizeof (struct srecfailinfo)); + qinfo->twhy = twhy; + qinfo->fsent = FALSE; + + /* If the protocol does not support multiple channels (cchans <= 1), + then we have essentially already received the entire file. */ + qinfo->freceived = qdaemon->qproto->cchans <= 1; + + qtrans = qtransalc (qcmd); + qtrans->psendfn = fremote_send_fail_send; + qtrans->precfn = fremote_discard; + qtrans->iremote = iremote; + qtrans->pinfo = (pointer) qinfo; + + return fqueue_remote (qdaemon, qtrans); +} + +/* Send a failure string for a send command to the remote system; + this is called when we are ready to reply to the command. */ + +static boolean +fremote_send_fail_send (qtrans, qdaemon) + struct stransfer *qtrans; + struct sdaemon *qdaemon; +{ + struct srecfailinfo *qinfo = (struct srecfailinfo *) qtrans->pinfo; + char ab[4]; + boolean fret; + + ab[0] = qtrans->s.bcmd; + ab[1] = 'N'; + + switch (qinfo->twhy) + { + case FAILURE_PERM: + ab[2] = '2'; + break; + case FAILURE_OPEN: + ab[2] = '4'; + break; + case FAILURE_SIZE: + ab[2] = '6'; + break; + case FAILURE_RECEIVED: + /* Remember this file as though we successfully received it; + when the other side acknowledges our rejection, we know that + we no longer have to remember that we received this file. */ + usent_receive_ack (qdaemon, qtrans); + ab[2] = '8'; + break; + default: + ab[2] = '\0'; + break; + } + + ab[3] = '\0'; + + fret = (*qdaemon->qproto->pfsendcmd) (qdaemon, ab, qtrans->ilocal, + qtrans->iremote); + + qinfo->fsent = TRUE; + + /* Wait for the end of file marker if we haven't gotten it yet. */ + if (! qinfo->freceived) + { + if (! fqueue_receive (qdaemon, qtrans)) + fret = FALSE; + } + else + { + xfree (qtrans->pinfo); + utransfree (qtrans); + } + + return fret; +} + +/* Discard data until we reach the end of the file. This is used for + a protocol with multiple channels, since the remote system may + start sending the file before the confirmation is sent. If we + refuse the file, the remote system will get us back in synch by + sending an empty buffer, which is what we look for here. */ + +/*ARGSUSED*/ +static boolean +fremote_discard (qtrans, qdaemon, zdata, cdata) + struct stransfer *qtrans; + struct sdaemon *qdaemon; + const char *zdata; + size_t cdata; +{ + struct srecfailinfo *qinfo = (struct srecfailinfo *) qtrans->pinfo; + + DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO, + "fremote_discard: Discarding %lu bytes", + (unsigned long) cdata); + + if (cdata != 0) + return TRUE; + + qinfo->freceived = TRUE; + + /* If we have already sent the denial, we are done. */ + if (qinfo->fsent) + { + xfree (qtrans->pinfo); + utransfree (qtrans); + } + + return TRUE; +} + +/* This is called when a file has been completely received. It sends + a response to the remote system. */ + +/*ARGSUSED*/ +static boolean +frec_file_end (qtrans, qdaemon, zdata, cdata) + struct stransfer *qtrans; + struct sdaemon *qdaemon; + const char *zdata; + size_t cdata; +{ + struct srecinfo *qinfo = (struct srecinfo *) qtrans->pinfo; + const char *zerr; + boolean fnever; + + DEBUG_MESSAGE3 (DEBUG_UUCP_PROTO, "frec_file_end: %s to %s (freplied %s)", + qtrans->s.zfrom, qtrans->s.zto, + qinfo->freplied ? "TRUE" : "FALSE"); + + if (qdaemon->qproto->pffile != NULL) + { + boolean fhandled; + + if (! (*qdaemon->qproto->pffile) (qdaemon, qtrans, FALSE, FALSE, + (long) -1, &fhandled)) + { + (void) ffileclose (qtrans->e); + (void) remove (qinfo->ztemp); + urrec_free (qtrans); + return FALSE; + } + if (fhandled) + return TRUE; + } + + qinfo->freceived = TRUE; + + fnever = FALSE; + + if (! ffileclose (qtrans->e)) + { + zerr = strerror (errno); + ulog (LOG_ERROR, "%s: close: %s", qtrans->s.zto, zerr); + } + else if (! fsysdep_move_file (qinfo->ztemp, qinfo->zfile, qinfo->fspool, + FALSE, ! qinfo->fspool, + (qinfo->flocal + ? qtrans->s.zuser + : (const char *) NULL))) + { + zerr = "could not move to final location"; + ulog (LOG_ERROR, "%s: %s", qinfo->zfile, zerr); + fnever = TRUE; + } + else + { + if (! qinfo->fspool) + { + unsigned int imode; + + /* Unless we can change the ownership of the file, the only + choice to make about these bits is whether to set the + execute bit or not. */ + if ((qtrans->s.imode & 0111) != 0) + imode = 0777; + else + imode = 0666; + (void) fsysdep_change_mode (qinfo->zfile, imode); + } + + zerr = NULL; + } + + if (zerr != NULL) + (void) remove (qinfo->ztemp); + + ustats (zerr == NULL, qtrans->s.zuser, qdaemon->qsys->uuconf_zname, + FALSE, qtrans->cbytes, qtrans->isecs, qtrans->imicros, + qdaemon->fmaster); + + if (zerr == NULL) + { + if (qinfo->zmail != NULL && *qinfo->zmail != '\0') + (void) fmail_transfer (TRUE, qtrans->s.zuser, qinfo->zmail, + (const char *) NULL, + qtrans->s.zfrom, qdaemon->qsys->uuconf_zname, + qtrans->s.zto, (const char *) NULL, + (const char *) NULL); + + if (qtrans->s.pseq != NULL) + (void) fsysdep_did_work (qtrans->s.pseq); + + if (! qinfo->flocal) + { + /* Remember that we have received this file, so that if the + connection drops at this point we won't receive it again. + We could check the return value here, but if we return + FALSE we couldn't do anything but drop the connection, + which would hardly be reasonable. Instead we trust that + the administrator will notice and handle any error + messages, which are very unlikely to occur if everything + is set up correctly. */ + (void) fsysdep_remember_reception (qdaemon->qsys, qtrans->s.zto, + qtrans->s.ztemp); + } + } + else + { + /* If the transfer failed, we send mail if it was requested + locally and if it can never succeed. */ + if (qinfo->flocal && fnever) + { + (void) fmail_transfer (FALSE, qtrans->s.zuser, qinfo->zmail, + zerr, qtrans->s.zfrom, + qdaemon->qsys->uuconf_zname, + qtrans->s.zto, (const char *) NULL, + (const char *) NULL); + (void) fsysdep_did_work (qtrans->s.pseq); + } + } + + /* If this is an execution request, we must create the execution + file itself. */ + if (qtrans->s.bcmd == 'E' && zerr == NULL) + { + char *zxqt, *zxqtfile, *ztemp; + FILE *e; + boolean fbad; + + /* We get an execution file name by simply replacing the leading + D in the received file name with an X. This pretty much + always has to work since we can always receive a file name + starting with X, so the system dependent code must be + prepared to see one. */ + zxqt = zbufcpy (qtrans->s.zto); + zxqt[0] = 'X'; + zxqtfile = zsysdep_spool_file_name (qdaemon->qsys, zxqt, + (pointer) NULL); + ubuffree (zxqt); + + if (zxqtfile == NULL) + { + urrec_free (qtrans); + return FALSE; + } + + /* We have to write via a temporary file, because otherwise + uuxqt might pick up the file before we have finished writing + it. */ + e = NULL; + ztemp = zsysdep_receive_temp (qdaemon->qsys, zxqtfile, "D.0"); + if (ztemp != NULL) + e = esysdep_fopen (ztemp, FALSE, FALSE, TRUE); + + if (e == NULL) + { + ubuffree (zxqtfile); + ubuffree (ztemp); + urrec_free (qtrans); + return FALSE; + } + + fprintf (e, "U %s %s\n", qtrans->s.zuser, qdaemon->qsys->uuconf_zname); + fprintf (e, "F %s\n", qtrans->s.zto); + fprintf (e, "I %s\n", qtrans->s.zto); + if (strchr (qtrans->s.zoptions, 'N') != NULL) + fprintf (e, "N\n"); + if (strchr (qtrans->s.zoptions, 'Z') != NULL) + fprintf (e, "Z\n"); + if (strchr (qtrans->s.zoptions, 'R') != NULL) + fprintf (e, "R %s\n", qtrans->s.znotify); + if (strchr (qtrans->s.zoptions, 'e') != NULL) + fprintf (e, "e\n"); + fprintf (e, "C %s\n", qtrans->s.zcmd); + + fbad = FALSE; + if (fclose (e) == EOF) + { + ulog (LOG_ERROR, "fclose: %s", strerror (errno)); + (void) remove (ztemp); + fbad = TRUE; + } + + if (! fbad) + { + if (! fsysdep_move_file (ztemp, zxqtfile, TRUE, FALSE, FALSE, + (const char *) NULL)) + fbad = TRUE; + } + + ubuffree (zxqtfile); + ubuffree (ztemp); + + if (fbad) + { + urrec_free (qtrans); + return FALSE; + } + } + + /* Prepare to send the completion string to the remote system. If + we have not yet replied to the remote send request, we leave the + transfer structure on the remote queue. Otherwise we add it to + the send queue. The psendfn field will already be set. */ + qinfo->fmoved = zerr == NULL; + if (qinfo->freplied) + return fqueue_send (qdaemon, qtrans); + + return TRUE; +} + +/* Send the final confirmation string to the remote system. */ + +static boolean +frec_file_send_confirm (qtrans, qdaemon) + struct stransfer *qtrans; + struct sdaemon *qdaemon; +{ + struct srecinfo *qinfo = (struct srecinfo *) qtrans->pinfo; + const char *zsend; + boolean fret; + + if (! qinfo->fmoved) + zsend = "CN5"; + else if (! qdaemon->frequest_hangup) + zsend = "CY"; + else + { +#if DEBUG > 0 + if (qdaemon->fmaster) + ulog (LOG_FATAL, "frec_file_send_confirm: Can't happen"); +#endif + + DEBUG_MESSAGE0 (DEBUG_UUCP_PROTO, + "frec_send_file_confirm: Requesting remote to transfer control"); + zsend = "CYM"; + } + + fret = (*qdaemon->qproto->pfsendcmd) (qdaemon, zsend, + qtrans->ilocal, qtrans->iremote); + + /* Now, if that was a remote command, then when the confirmation + message is acked we no longer have to remember that we received + that file. */ + if (! qinfo->flocal && qinfo->fmoved) + usent_receive_ack (qdaemon, qtrans); + + urrec_free (qtrans); + return fret; +} + +/* Discard a temporary file if it is not useful. A temporary file is + useful if it could be used to restart a receive. This is called if + the connection is lost. It is only called if qtrans->frecfile is + TRUE. */ + +boolean +frec_discard_temp (qdaemon, qtrans) + struct sdaemon *qdaemon; + struct stransfer *qtrans; +{ + struct srecinfo *qinfo = (struct srecinfo *) qtrans->pinfo; + + if ((qdaemon->ifeatures & FEATURE_RESTART) == 0 + || qtrans->s.ztemp == NULL + || qtrans->s.ztemp[0] != 'D' + || strcmp (qtrans->s.ztemp, "D.0") == 0) + (void) remove (qinfo->ztemp); + return TRUE; +} |