xref: /386bsd/usr/src/libexec/uucp/prot.c (revision a2142627)
1 /* prot.c
2    Protocol support routines to move commands and data around.
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: prot.c,v $
26    Revision 1.21  1992/04/02  22:51:09  ian
27    Add gcc 2.0 format checking to ulog, and fixed discovered problems
28 
29    Revision 1.20  1992/03/30  04:49:10  ian
30    Niels Baggesen: added debugging types abnormal and uucp-proto
31 
32    Revision 1.19  1992/03/30  04:07:13  ian
33    Dirk Musstopf: remove temporary file if receive fails
34 
35    Revision 1.18  1992/03/13  22:59:25  ian
36    Have breceive_char go through freceive_data
37 
38    Revision 1.17  1992/03/12  19:56:10  ian
39    Debugging based on types rather than number
40 
41    Revision 1.16  1992/03/11  01:18:15  ian
42    Niels Baggesen: drop the connection on a write failure
43 
44    Revision 1.15  1992/03/11  00:18:50  ian
45    Save temporary file if file send fails
46 
47    Revision 1.14  1992/02/09  05:21:55  ian
48    Bob Denny: call fmail_transfer before fsysdep_did_work
49 
50    Revision 1.13  1992/02/08  19:41:24  ian
51    Simplify pffile calls for ancient stupid compilers
52 
53    Revision 1.12  1992/02/08  03:54:18  ian
54    Include <string.h> only in <uucp.h>, added 1992 copyright
55 
56    Revision 1.11  1992/01/18  22:48:53  ian
57    Reworked sending of mail and general handling of failed transfers
58 
59    Revision 1.10  1992/01/16  18:16:58  ian
60    Niels Baggesen: add some debugging messages
61 
62    Revision 1.9  1991/12/31  19:34:19  ian
63    Added number of bytes to pffile protocol entry point
64 
65    Revision 1.8  1991/12/30  04:28:30  ian
66    John Theus: check for EOF to work around bug in fread
67 
68    Revision 1.7  1991/12/21  23:10:43  ian
69    Terry Gardner: record failed file transfers in statistics file
70 
71    Revision 1.6  1991/12/13  04:33:38  ian
72    Franc,ois Pinard: don't bother to warn if the final HY doesn't come in
73 
74    Revision 1.5  1991/11/15  21:00:59  ian
75    Efficiency hacks for 'f' and 't' protocols
76 
77    Revision 1.4  1991/11/11  19:32:03  ian
78    Added breceive_char to read characters through protocol buffering
79 
80    Revision 1.3  1991/11/11  04:21:16  ian
81    Added 'f' protocol
82 
83    Revision 1.2  1991/11/10  19:24:22  ian
84    Added pffile protocol entry point for file level control
85 
86    Revision 1.1  1991/11/09  18:51:50  ian
87    Initial revision
88 
89    */
90 
91 #include "uucp.h"
92 
93 #if USE_RCS_ID
94 char prot_rcsid[] = "$Id: prot.c,v 1.21 1992/04/02 22:51:09 ian Rel $";
95 #endif
96 
97 #include <errno.h>
98 
99 #include "system.h"
100 #include "port.h"
101 #include "prot.h"
102 
103 /* This file implements the generic UUCP protocol for making and
104    confirming file transfer requests.  This involves sending ASCII
105    strings back and forth between the communicating daemons.  It would
106    be possible to use a different scheme when designing a new
107    protocol, but this scheme is used by all traditional UUCP
108    protocols.  */
109 
110 /* Local functions.  */
111 
112 static boolean fpsendfile_confirm P((void));
113 static boolean fprecfile_confirm P((void));
114 static boolean fploop P((void));
115 static void upadd_cmd P((const char *z, int clen, boolean flast));
116 
117 /* Variables visible to the protocol-specific routines.  */
118 
119 /* Protocol structure.  */
120 const struct sprotocol *qProto;
121 
122 /* Buffer to hold received data.  */
123 char abPrecbuf[CRECBUFLEN];
124 
125 /* Index of start of data in abPrecbuf.  */
126 int iPrecstart;
127 
128 /* Index of end of data (first byte not included in data) in abPrecbuf.  */
129 int iPrecend;
130 
131 /* Whether an unexpected shutdown is OK now; this is used to avoid
132    giving a warning for systems that hang up in a hurry.  */
133 boolean fPerror_ok;
134 
135 /* Amount of data sent for current send file; -1 means there is no
136    current send file.  */
137 static long cPsent_bytes = -1;
138 
139 /* Amount of data received for current receive file; -1 means there is
140    no current receive file.  */
141 static long cPreceived_bytes = -1;
142 
143 /* Send a file.  If we are the master, we must send a command to
144    transfer the file and wait for a confirmation that we can begin
145    sending the file.  If we are the slave, the master has sent us a
146    command and is waiting for a reply; we must confirm that we will
147    send the file.  Either way, we begin transferring data.
148 
149    This function returns FALSE if there is a communication failure.
150    It returns TRUE otherwise, even if the file transfer failed.  */
151 
152 boolean
fsend_file(fmaster,e,qcmd,zmail,ztosys,fnew)153 fsend_file (fmaster, e, qcmd, zmail, ztosys, fnew)
154      boolean fmaster;
155      openfile_t e;
156      const struct scmd *qcmd;
157      const char *zmail;
158      const char *ztosys;
159      boolean fnew;
160 {
161   if (fmaster)
162     {
163       int clen;
164       char *zsend;
165       const char *zrec;
166 
167       /* Send the string
168 	 S zfrom zto zuser zoptions ztemp imode znotify
169 	 to the remote system.  We put a '-' in front of the (possibly
170 	 empty) options and a '0' in front of the mode.  The remote
171 	 system will ignore ztemp, but it is supposed to be sent anyhow.
172 	 If fnew is TRUE, we also send the size; in this case if ztemp
173 	 is empty we must send it as "".  */
174       clen = (strlen (qcmd->zfrom) + strlen (qcmd->zto)
175 	      + strlen (qcmd->zuser) + strlen (qcmd->zoptions)
176 	      + strlen (qcmd->ztemp) + strlen (qcmd->znotify)
177 	      + 50);
178       zsend = (char *) alloca (clen);
179       if (! fnew)
180 	sprintf (zsend, "S %s %s %s -%s %s 0%o %s", qcmd->zfrom, qcmd->zto,
181 		 qcmd->zuser, qcmd->zoptions, qcmd->ztemp, qcmd->imode,
182 		 qcmd->znotify);
183       else
184 	{
185 	  const char *znotify;
186 
187 	  if (qcmd->znotify[0] != '\0')
188 	    znotify = qcmd->znotify;
189 	  else
190 	    znotify = "\"\"";
191 	  sprintf (zsend, "S %s %s %s -%s %s 0%o %s %ld", qcmd->zfrom,
192 		   qcmd->zto, qcmd->zuser, qcmd->zoptions, qcmd->ztemp,
193 		   qcmd->imode, znotify, qcmd->cbytes);
194 	}
195 
196       if (! (qProto->pfsendcmd) (zsend))
197 	{
198 	  (void) ffileclose (e);
199 	  return FALSE;
200 	}
201 
202       /* Now we must await a reply.  */
203 
204       zrec = zgetcmd ();
205       if (zrec == NULL)
206 	{
207 	  (void) ffileclose (e);
208 	  return FALSE;
209 	}
210 
211       if (zrec[0] != 'S'
212 	  || (zrec[1] != 'Y' && zrec[1] != 'N'))
213 	{
214 	  ulog (LOG_ERROR, "Bad response to send request");
215 	  (void) ffileclose (e);
216 	  return FALSE;
217 	}
218 
219       if (zrec[1] == 'N')
220 	{
221 	  const char *zerr;
222 	  boolean fnever;
223 
224 	  fnever = TRUE;
225 	  if (zrec[2] == '2')
226 	    zerr = "permission denied";
227 	  else if (zrec[2] == '4')
228 	    {
229 	      zerr = "remote cannot create work files";
230 	      fnever = FALSE;
231 	    }
232 	  else if (zrec[2] == '6')
233 	    {
234 	      zerr = "too large for receiver now";
235 	      fnever = FALSE;
236 	    }
237 	  else if (zrec[2] == '7')
238 	    {
239 	      /* The file is too large to ever send.  */
240 	      zerr = "too large for receiver";
241 	    }
242 	  else
243 	    {
244 	      char *zset;
245 
246 	      zset = (char *) alloca (sizeof "unknown reason: "
247 				      + strlen (zrec));
248 	      sprintf (zset, "unknown reason: %s", zrec);
249 	      zerr = zset;
250 	    }
251 
252 	  ulog (LOG_ERROR, "Can't send %s: %s", qcmd->zfrom, zerr);
253 	  (void) ffileclose (e);
254 	  if (fnever)
255 	    {
256 	      (void) fmail_transfer (FALSE, qcmd->zuser, zmail, zerr,
257 				     qcmd->zfrom, zLocalname,
258 				     qcmd->zto, ztosys,
259 				     zsysdep_save_temp_file (qcmd->pseq));
260 	      (void) fsysdep_did_work (qcmd->pseq);
261 	    }
262 	  return TRUE;
263 	}
264     }
265   else
266     {
267       char absend[20];
268 
269       /* We are the slave; confirm that we will send the file.  We
270 	 send the file mode in the confirmation string.  */
271 
272       sprintf (absend, "RY 0%o", qcmd->imode);
273 
274       if (! (qProto->pfsendcmd) (absend))
275 	{
276 	  (void) ffileclose (e);
277 	  return FALSE;
278 	}
279     }
280 
281   /* Record the file we are sending, and let the protocol take over.  */
282 
283   if (! fstore_sendfile (e, qcmd->pseq, qcmd->zfrom, qcmd->zto, ztosys,
284 			 qcmd->zuser, zmail))
285     return FALSE;
286 
287   cPsent_bytes = 0;
288 
289   /* Tell the protocol that we are starting to send a file.  */
290   if (qProto->pffile != NULL)
291     {
292       boolean (*pffile) P((boolean, boolean, boolean *, long));
293 
294       /* Simplify expression for ancient compilers.  */
295       pffile = qProto->pffile;
296       if (! pffile (TRUE, TRUE, (boolean *) NULL, qcmd->cbytes))
297 	return FALSE;
298     }
299 
300   return fploop ();
301 }
302 
303 /* Confirm that a file has been received correctly by the other side.
304    Return FALSE for a communication error.  We expect the receiving
305    system to send back CY; if an error occurred while moving the
306    received file into its final location, the receiving system will
307    send back CN5.  */
308 
309 static boolean
fpsendfile_confirm()310 fpsendfile_confirm ()
311 {
312   const char *zrec;
313   long cbytes;
314   const char *zerr;
315 
316   zrec = zgetcmd ();
317   if (zrec == NULL)
318     return FALSE;
319 
320   cbytes = cPsent_bytes;
321   cPsent_bytes = -1;
322 
323   if (zrec[0] != 'C'
324       || (zrec[1] != 'Y' && zrec[1] != 'N'))
325     {
326       zerr = "Bad confirmation for sent file";
327       ulog (LOG_ERROR, zerr);
328       (void) fsent_file (FALSE, cbytes, zerr, FALSE);
329     }
330   else if (zrec[1] == 'N')
331     {
332       if (zrec[2] == '5')
333 	zerr = "File could not be stored in final location";
334       else
335 	{
336 	  char *zset;
337 
338 	  zset = (char *) alloca (sizeof "File send failed: " + strlen (zrec));
339 	  sprintf (zset, "File send failed: %s", zrec);
340 	  zerr = zset;
341 	}
342       ulog (LOG_ERROR, zerr);
343       (void) fsent_file (FALSE, cbytes, zerr, TRUE);
344     }
345   else
346     (void) fsent_file (TRUE, cbytes, (const char *) NULL, FALSE);
347 
348   return TRUE;
349 }
350 
351 /* Receive a file.  If we are the master, we must set up a file
352    request and wait for the other side to confirm it.  If we are the
353    slave, we must confirm a request made by the other side.  We then
354    start receiving the file.
355 
356    This function must return FALSE if there is a communication error
357    and TRUE otherwise.  We return TRUE even if the file transfer
358    fails.  */
359 
360 boolean
freceive_file(fmaster,e,qcmd,zmail,zfromsys,fnew)361 freceive_file (fmaster, e, qcmd, zmail, zfromsys, fnew)
362      boolean fmaster;
363      openfile_t e;
364      const struct scmd *qcmd;
365      const char *zmail;
366      const char *zfromsys;
367      boolean fnew;
368 {
369   unsigned int imode;
370 
371   if (fmaster)
372     {
373       int clen;
374       char *zsend;
375       const char *zrec;
376 
377       /* We send the string
378 	 R from to user options
379 	 We put a dash in front of options.  If we are talking to a
380 	 counterpart, we also send the maximum size file we are
381 	 prepared to accept, as returned by esysdep_open_receive.  */
382 
383       clen = (strlen (qcmd->zfrom) + strlen (qcmd->zto)
384 	      + strlen (qcmd->zuser) + strlen (qcmd->zoptions) + 30);
385       zsend = (char *) alloca (clen);
386 
387       if (! fnew)
388 	sprintf (zsend, "R %s %s %s -%s", qcmd->zfrom, qcmd->zto,
389 		 qcmd->zuser, qcmd->zoptions);
390       else
391 	sprintf (zsend, "R %s %s %s -%s %ld", qcmd->zfrom, qcmd->zto,
392 		 qcmd->zuser, qcmd->zoptions, qcmd->cbytes);
393 
394       if (! (qProto->pfsendcmd) (zsend))
395 	{
396 	  (void) ffileclose (e);
397 	  (void) remove (qcmd->ztemp);
398 	  return FALSE;
399 	}
400 
401       /* Wait for a reply.  */
402 
403       zrec = zgetcmd ();
404       if (zrec == NULL)
405 	{
406 	  (void) ffileclose (e);
407 	  (void) remove (qcmd->ztemp);
408 	  return FALSE;
409 	}
410 
411       if (zrec[0] != 'R'
412 	  || (zrec[1] != 'Y' && zrec[1] != 'N'))
413 	{
414 	  ulog (LOG_ERROR, "Bad response to receive request");
415 	  (void) ffileclose (e);
416 	  (void) remove (qcmd->ztemp);
417 	  return FALSE;
418 	}
419 
420       if (zrec[1] == 'N')
421 	{
422 	  const char *zerr;
423 
424 	  if (zrec[2] == '2')
425 	    zerr = "no such file";
426 	  else if (zrec[2] == '6')
427 	    {
428 	      /* We sent over the maximum file size we were prepared
429 		 to receive, and the remote system is telling us that
430 		 the file is larger than that.  Try again later.  It
431 		 would be better if we could know whether there will
432 		 ever be enough room.  */
433 	      ulog (LOG_ERROR, "Can't receive %s: too large",
434 		    qcmd->zfrom);
435 	      (void) ffileclose (e);
436 	      (void) remove (qcmd->ztemp);
437 	      return TRUE;
438 	    }
439 	  else
440 	    {
441 	      char *zset;
442 
443 	      zset = (char *) alloca (sizeof "unknown reason: "
444 				      + strlen (zrec));
445 	      sprintf (zset, "unknown reason: %s", zrec);
446 	      zerr = zset;
447 	    }
448 	  ulog (LOG_ERROR, "Can't receive %s: %s", qcmd->zfrom, zerr);
449 	  (void) ffileclose (e);
450 	  (void) remove (qcmd->ztemp);
451 	  (void) fmail_transfer (FALSE, qcmd->zuser, zmail, zerr,
452 				 qcmd->zfrom, zfromsys,
453 				 qcmd->zto, zLocalname,
454 				 (const char *) NULL);
455 	  (void) fsysdep_did_work (qcmd->pseq);
456 	  return TRUE;
457 	}
458 
459       /* The mode should have been sent as "RY 0%o".  If it wasn't,
460 	 we use 0666.  */
461       imode = (unsigned int) strtol (zrec + 2, (char **) NULL, 8);
462       if (imode == 0)
463 	imode = 0666;
464     }
465   else
466     {
467       /* Tell the other system to go ahead and send.  */
468 
469       if (! (qProto->pfsendcmd) ("SY"))
470 	{
471 	  (void) ffileclose (e);
472 	  (void) remove (qcmd->ztemp);
473 	  return FALSE;
474 	}
475       imode = qcmd->imode;
476     }
477 
478   if (! fstore_recfile (e, qcmd->pseq, qcmd->zfrom, qcmd->zto, zfromsys,
479 			qcmd->zuser, imode, zmail, qcmd->ztemp))
480     return FALSE;
481 
482   cPreceived_bytes = 0;
483 
484   /* Tell the protocol that we are starting to receive a file.  */
485   if (qProto->pffile != NULL)
486     {
487       boolean (*pffile) P((boolean, boolean, boolean *, long));
488 
489       /* Simplify expression for ancient compilers.  */
490       pffile = qProto->pffile;
491       if (! pffile (TRUE, FALSE, (boolean *) NULL, (long) -1))
492 	return FALSE;
493     }
494 
495   return fploop ();
496 }
497 
498 /* Confirm that a file was received correctly.  */
499 
500 static boolean
fprecfile_confirm()501 fprecfile_confirm ()
502 {
503   long cbytes;
504 
505   cbytes = cPreceived_bytes;
506   cPreceived_bytes = -1;
507 
508   if (freceived_file (TRUE, cbytes, (const char *) NULL, FALSE))
509     return (qProto->pfsendcmd) ("CY");
510   else
511     return (qProto->pfsendcmd) ("CN5");
512 }
513 
514 /* Send a transfer request.  This is only called by the master.  It
515    ignored the pseq entry in the scmd structure.  */
516 
517 boolean
fxcmd(qcmd,pfnever)518 fxcmd (qcmd, pfnever)
519      const struct scmd *qcmd;
520      boolean *pfnever;
521 {
522   int clen;
523   char *zsend;
524   const char *zrec;
525 
526   *pfnever = FALSE;
527 
528   /* We send the string
529      X from to user options
530      We put a dash in front of options.  */
531 
532   clen = (strlen (qcmd->zfrom) + strlen (qcmd->zto)
533 	  + strlen (qcmd->zuser) + strlen (qcmd->zoptions) + 7);
534   zsend = (char *) alloca (clen);
535 
536   sprintf (zsend, "X %s %s %s -%s", qcmd->zfrom, qcmd->zto,
537 	   qcmd->zuser, qcmd->zoptions);
538 
539   if (! (qProto->pfsendcmd) (zsend))
540     return FALSE;
541 
542   /* Wait for a reply.  */
543 
544   zrec = zgetcmd ();
545   if (zrec == NULL)
546     return FALSE;
547 
548   if (zrec[0] != 'X'
549       || (zrec[1] != 'Y' && zrec[1] != 'N'))
550     {
551       ulog (LOG_ERROR, "Bad response to wildcard request");
552       return FALSE;
553     }
554 
555   if (zrec[1] == 'N')
556     {
557       ulog (LOG_ERROR, "Work request denied");
558       *pfnever = TRUE;
559       return TRUE;
560     }
561 
562   return TRUE;
563 }
564 
565 /* Confirm a transfer request.  */
566 
567 boolean
fxcmd_confirm()568 fxcmd_confirm ()
569 {
570   return (qProto->pfsendcmd) ("XY");
571 }
572 
573 /* Signal a file transfer failure to the other side.  This is only called
574    by the slave.  */
575 
576 boolean
ftransfer_fail(bcmd,twhy)577 ftransfer_fail (bcmd, twhy)
578      int bcmd;
579      enum tfailure twhy;
580 {
581   const char *z;
582 
583   switch (bcmd)
584     {
585     case 'S':
586       switch (twhy)
587 	{
588 	case FAILURE_PERM:
589 	  z = "SN2";
590 	  break;
591 	case FAILURE_OPEN:
592 	  z = "SN4";
593 	  break;
594 	case FAILURE_SIZE:
595 	  z = "SN6";
596 	  break;
597 	default:
598 	  z = "SN";
599 	  break;
600 	}
601       break;
602     case 'R':
603       switch (twhy)
604 	{
605 	case FAILURE_PERM:
606 	case FAILURE_OPEN:
607 	  z = "RN2";
608 	  break;
609 	case FAILURE_SIZE:
610 	  z = "RN6";
611 	  break;
612 	default:
613 	  z = "RN";
614 	  break;
615 	}
616       break;
617     case 'X':
618       z = "XN";
619       break;
620     default:
621 #if DEBUG > 0
622       ulog (LOG_ERROR, "ftransfer_fail: Can't happen");
623 #endif
624       return FALSE;
625     }
626 
627   return (qProto->pfsendcmd) (z);
628 }
629 
630 /* Get and parse a command from the other system.  Handle hangups
631    specially.  */
632 
633 boolean
fgetcmd(fmaster,qcmd)634 fgetcmd (fmaster, qcmd)
635      boolean fmaster;
636      struct scmd *qcmd;
637 {
638   static char *z;
639   static int c;
640 
641   while (TRUE)
642     {
643       const char *zcmd;
644       int clen;
645 
646       zcmd = zgetcmd ();
647       if (zcmd == NULL)
648 	return FALSE;
649 
650       clen = strlen (zcmd);
651       if (clen + 1 > c)
652 	{
653 	  c = clen + 1;
654 	  z = (char *) xrealloc ((pointer) z, c);
655 	}
656       strcpy (z, zcmd);
657 
658       if (! fparse_cmd (z, qcmd))
659 	continue;
660 
661       /* Handle hangup commands specially.  If it's just 'H', return
662 	 it.  If it's 'N', the other side is denying a hangup request
663 	 which we can just ignore (since the top level code assumes
664 	 that hangup requests are denied).  If it's 'Y', the other
665 	 side is confirming a hangup request.  In this case we confirm
666 	 with an "HY", wait for yet another "HY" from the other side,
667 	 and then finally shut down the protocol (I don't know why it
668 	 works this way, but it does).  We then return a 'Y' command
669 	 to the top level code.  */
670 
671       if (qcmd->bcmd == 'N')
672 	{
673 #if DEBUG > 0
674 	  if (fmaster)
675 	    ulog (LOG_ERROR, "Got hangup reply as master");
676 #endif
677 	  continue;
678 	}
679 
680       if (qcmd->bcmd == 'Y')
681 	{
682 #if DEBUG > 0
683 	  if (fmaster)
684 	    ulog (LOG_ERROR, "Got hangup reply as master");
685 #endif
686 	  /* Don't check errors rigorously here, since the other side
687 	     might jump the gun and hang up.  */
688 
689 	  if (! (qProto->pfsendcmd) ("HY"))
690 	    return TRUE;
691 	  fPerror_ok = TRUE;
692 	  zcmd = zgetcmd ();
693 	  fPerror_ok = FALSE;
694 	  if (zcmd == NULL)
695 	    return TRUE;
696 #if DEBUG > 1
697 	  if (strcmp (zcmd, "HY") != 0)
698 	    DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO | DEBUG_ABNORMAL,
699 			    "fgetcmd: Got \"%s\" when expecting \"HY\"",
700 			    zcmd);
701 #endif
702 	  (void) (qProto->pfshutdown) ();
703 	  return TRUE;
704 	}
705 
706       return TRUE;
707     }
708 
709   /*NOTREACHED*/
710 }
711 
712 /* Hangup.  */
713 
714 boolean
fhangup_request()715 fhangup_request ()
716 {
717   return (qProto->pfsendcmd) ("H");
718 }
719 
720 /* Reply to a hangup request.  This is only called by the slave.  If
721    fconfirm is TRUE, we are closing down the protocol.  We send an HY
722    message.  The master responds with an HY message.  We send another
723    HY message, and then shut down the protocol.  */
724 
725 boolean
fhangup_reply(fconfirm)726 fhangup_reply (fconfirm)
727      boolean fconfirm;
728 {
729   if (! fconfirm)
730     return (qProto->pfsendcmd) ("HN");
731   else
732     {
733       const char *z;
734 
735       if (! (qProto->pfsendcmd) ("HY"))
736 	return FALSE;
737 
738       z = zgetcmd ();
739       if (z == NULL)
740 	return FALSE;
741       if (strcmp (z, "HY") != 0)
742 	ulog (LOG_ERROR, "Got \"%s\" when expecting \"HY\"", z);
743       else
744 	{
745 	  if (! (qProto->pfsendcmd) ("HY"))
746 	    return FALSE;
747 	}
748 
749       return (qProto->pfshutdown) ();
750     }
751 }
752 
753 /* Loop sending and/or receiving data.  If there is a file to send,
754    this will send it until the entire file has been sent or a command
755    has been received from the remote system or a complete file has
756    been received from the remote system.  Otherwise this will simply
757    call the protocol to wait until a complete file or command has been
758    received from the remote system.  */
759 
760 static boolean
fploop()761 fploop ()
762 {
763   boolean fexit;
764 
765   DEBUG_MESSAGE0 (DEBUG_UUCP_PROTO, "fploop: Main protocol loop");
766 
767   if (ffileisopen (eSendfile))
768     {
769       int iend;
770 
771       iend = iPrecend;
772 
773       while (TRUE)
774 	{
775 	  /* We keep sending out packets until we have something
776 	     in the receive buffer.  */
777 	  while (iend == iPrecend)
778 	    {
779 	      char *zdata;
780 	      int cdata;
781 
782 	      /* Get a packet and fill it with data.  */
783 
784 	      zdata = (qProto->pzgetspace) (&cdata);
785 	      if (zdata == NULL)
786 		return FALSE;
787 
788 	      if (ffileeof (eSendfile))
789 		cdata = 0;
790 	      else
791 		{
792 		  cdata = cfileread (eSendfile, zdata, cdata);
793 		  if (ffilereaderror (eSendfile, cdata))
794 		    {
795 		      /* The protocol gives us no way to report a file
796 			 sending error, so we just drop the connection.
797 			 What else can we do?  */
798 		      ulog (LOG_ERROR, "read: %s", strerror (errno));
799 		      usendfile_error ();
800 		      return FALSE;
801 		    }
802 		}
803 
804 	      if (! (qProto->pfsenddata) (zdata, cdata))
805 		return FALSE;
806 
807 	      cPsent_bytes += cdata;
808 
809 	      /* If we have reached the end of the file, tell the
810 		 protocol that the file is finished (the protocol
811 		 could also detect this by looking for zero passed as
812 		 the data length to the send data routine, but would
813 		 have no convenient way to tell us to redo the file
814 		 send).  If we are not supposed to redo the file
815 		 transfer, wait for confirmation and return out to get
816 		 the next file.  */
817 
818 	      if (cdata == 0)
819 		{
820 		  if (qProto->pffile != NULL)
821 		    {
822 		      boolean fredo;
823 		      boolean (*pffile) P((boolean, boolean, boolean *,
824 					   long));
825 
826 		      /* Simplify expression for ancient compilers.  */
827 		      pffile = qProto->pffile;
828 		      if (! pffile (FALSE, TRUE, &fredo, (long) -1))
829 			return FALSE;
830 
831 		      if (fredo)
832 			{
833 			  ulog (LOG_NORMAL, "Resending file");
834 			  if (! ffilerewind (eSendfile))
835 			    {
836 			      ulog (LOG_ERROR, "rewind: %s",
837 				    strerror (errno));
838 			      usendfile_error ();
839 			      return FALSE;
840 			    }
841 			  continue;
842 			}
843 		    }
844 
845 		  return fpsendfile_confirm ();
846 		}
847 	    }
848 
849 	  /* Process the data in the receive buffer, and decide
850 	     whether it's time to get out.  */
851 	  if (! (qProto->pfprocess) (&fexit))
852 	    return FALSE;
853 	  if (fexit)
854 	    return TRUE;
855 
856 	  iend = iPrecend;
857 	}
858     }
859 
860 #if DEBUG > 0
861   /* If there is no file to send, there really should be a file to
862      receive.  */
863 
864   if (! ffileisopen(eRecfile))
865     ulog (LOG_FATAL, "fploop: No send or receive file");
866 #endif
867 
868   /* We have no file to send.  Wait for data to come in.  */
869 
870   return (qProto->pfwait) ();
871 }
872 
873 /* This function is called by the protocol routines when data has
874    arrived.  Some protocols may know whether the data is for a command
875    or a file; for others, if a receive file is open it is for the file
876    and is otherwise for a command.  This function will set *pfexit to
877    TRUE if it has received a complete file (assumed to be true if
878    cdata is zero) or a complete command (assumed to be true if the
879    argument data contains a null byte).  It will return FALSE on
880    error.  */
881 
882 boolean
fgot_data(zdata,cdata,fcmd,ffile,pfexit)883 fgot_data (zdata, cdata, fcmd, ffile, pfexit)
884      const char *zdata;
885      int cdata;
886      boolean fcmd;
887      boolean ffile;
888      boolean *pfexit;
889 {
890   *pfexit = FALSE;
891 
892   if (! fcmd && ! ffile)
893     {
894       if (ffileisopen (eRecfile))
895 	ffile = TRUE;
896       else
897 	fcmd = TRUE;
898     }
899 
900 #if DEBUG > 0
901   if (ffile && ! ffileisopen (eRecfile))
902     ulog (LOG_FATAL, "fgot_data: No file to receive into");
903 #endif
904 
905   if (ffile)
906     {
907       if (cdata == 0)
908 	{
909 	  /* The file transfer is complete.  If the protocol has a
910 	     file level routine, call it to see whether we have to
911 	     receive the file again.  */
912 	  if (qProto->pffile != NULL)
913 	    {
914 	      boolean fredo;
915 	      boolean (*pffile) P((boolean, boolean, boolean *, long));
916 
917 	      /* Simplify expression for ancient compilers.  */
918 	      pffile = qProto->pffile;
919 	      if (! pffile (FALSE, FALSE, &fredo, (long) -1))
920 		return FALSE;
921 
922 	      if (fredo)
923 		{
924 		  ulog (LOG_NORMAL, "File being resent");
925 		  if (! frecfile_rewind ())
926 		    return FALSE;
927 		  return TRUE;
928 		}
929 	    }
930 
931 	  if (! fprecfile_confirm ())
932 	    return FALSE;
933 	  *pfexit = TRUE;
934 	  return TRUE;
935 	}
936       else
937 	{
938 	  int cwrote;
939 
940 	  /* Cast zdata to avoid warnings because of erroneous
941 	     prototypes on Ultrix.  */
942 	  cwrote = cfilewrite (eRecfile, (char *) zdata, cdata);
943 	  if (cwrote != cdata)
944 	    {
945 	      const char *zerr;
946 
947 	      if (cwrote < 0)
948 		zerr = strerror (errno);
949 	      else
950 		zerr = "could not write all data";
951 	      ulog (LOG_ERROR, "write: %s", zerr);
952 	      urecfile_error ();
953 
954 	      /* Any write error is almost certainly a temporary
955 		 condition, or else UUCP would not be functioning at
956 		 all.  If we continue to accept the file, we will wind
957 		 up rejecting it at the end (what else could we do?)
958 		 and the remote system will throw away the request.
959 		 We're better off just dropping the connection, which
960 		 is what happens when we return FALSE, and trying
961 		 again later.  */
962 	      return FALSE;
963 	    }
964 
965 	  cPreceived_bytes += cdata;
966 
967 	  return TRUE;
968 	}
969     }
970   else
971     {
972       const char *z;
973 
974       /* We want to add this data to the current command string.  If
975 	 there is no null character in the data, this string will be
976 	 continued by the next packet.  Otherwise this must be the
977 	 last string in the command, and we don't care about what
978 	 comes after the null byte.  */
979 
980       z = (const char *) memchr ((constpointer) zdata, '\0', cdata);
981       if (z == NULL)
982 	upadd_cmd (zdata, cdata, FALSE);
983       else
984 	{
985 	  upadd_cmd (zdata, z - zdata, TRUE);
986 	  *pfexit = TRUE;
987 	}
988 
989       return TRUE;
990     }
991 }
992 
993 /* This function is called by fgot_data when a command string is
994    received.  We must queue up received commands since we don't know
995    when we'll be able to get to them (for example, the
996    acknowledgements for the last few packets of a sent file may
997    contain the string indicating whether the file was received
998    correctly).  */
999 
1000 struct spcmdqueue
1001 {
1002   struct spcmdqueue *qnext;
1003   int csize;
1004   int clen;
1005   char *z;
1006 };
1007 
1008 static struct spcmdqueue *qPcmd_queue;
1009 static struct spcmdqueue *qPcmd_free;
1010 
1011 static void
upadd_cmd(z,clen,flast)1012 upadd_cmd (z, clen, flast)
1013      const char *z;
1014      int clen;
1015      boolean flast;
1016 {
1017   struct spcmdqueue *q;
1018 
1019   q = qPcmd_free;
1020   if (q == NULL)
1021     {
1022       q = (struct spcmdqueue *) xmalloc (sizeof (struct spcmdqueue));
1023       q->qnext = NULL;
1024       q->csize = 0;
1025       q->clen = 0;
1026       q->z = NULL;
1027       qPcmd_free = q;
1028     }
1029 
1030   if (q->clen + clen + 1 > q->csize)
1031     {
1032       q->csize = q->clen + clen + 1;
1033       q->z = (char *) xrealloc ((pointer) q->z, q->csize);
1034     }
1035 
1036   memcpy (q->z + q->clen, z, clen);
1037   q->clen += clen;
1038   q->z[q->clen] = '\0';
1039 
1040   /* If the last string in this command, add it to the queue of
1041      finished commands.  */
1042 
1043   if (flast)
1044     {
1045       struct spcmdqueue **pq;
1046 
1047       for (pq = &qPcmd_queue; *pq != NULL; pq = &(*pq)->qnext)
1048 	;
1049       *pq = q;
1050       qPcmd_free = q->qnext;
1051       q->qnext = NULL;
1052     }
1053 }
1054 
1055 /* Get a command string.  We just have to wait until the receive
1056    packet function gives us something in qPcmd_queue.  The return
1057    value of this may be treated as a static buffer; it will last
1058    at least until the next packet is received.  */
1059 
1060 const char *
zgetcmd()1061 zgetcmd ()
1062 {
1063   struct spcmdqueue *q;
1064 
1065   /* Wait until a command comes in.  */
1066   while (qPcmd_queue == NULL)
1067     {
1068       DEBUG_MESSAGE0 (DEBUG_UUCP_PROTO, "zgetcmd: Waiting for packet");
1069 
1070       if (! (qProto->pfwait) ())
1071 	return NULL;
1072     }
1073 
1074   q = qPcmd_queue;
1075   qPcmd_queue = q->qnext;
1076 
1077   q->clen = 0;
1078 
1079   /* We must not replace qPcmd_free, because it may already be
1080      receiving a new command string.  */
1081   if (qPcmd_free == NULL)
1082     {
1083       q->qnext = NULL;
1084       qPcmd_free = q;
1085     }
1086   else
1087     {
1088       q->qnext = qPcmd_free->qnext;
1089       qPcmd_free->qnext = q;
1090     }
1091 
1092   DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO, "zgetcmd: Got command \"%s\"", q->z);
1093 
1094   return q->z;
1095 }
1096 
1097 /* We want to output and input at the same time, if supported on this
1098    machine.  If we have something to send, we send it all while
1099    accepting a large amount of data.  Once we have sent everything we
1100    look at whatever we have received.  If data comes in faster than we
1101    can send it, we may run out of buffer space.  */
1102 
1103 boolean
fsend_data(zsend,csend,fdoread)1104 fsend_data (zsend, csend, fdoread)
1105      const char *zsend;
1106      int csend;
1107      boolean fdoread;
1108 {
1109   if (! fdoread)
1110     return fport_write (zsend, csend);
1111 
1112   while (csend > 0)
1113     {
1114       char *zrec;
1115       int crec, csent;
1116 
1117       if (iPrecend < iPrecstart)
1118 	{
1119 	  zrec = abPrecbuf + iPrecend;
1120 	  crec = iPrecstart - iPrecend - 1;
1121 	}
1122       else if (iPrecend < CRECBUFLEN)
1123 	{
1124 	  zrec = abPrecbuf + iPrecend;
1125 	  crec = CRECBUFLEN - iPrecend;
1126 	}
1127       else
1128 	{
1129 	  zrec = abPrecbuf;
1130 	  crec = iPrecstart - 1;
1131 	}
1132 
1133       csent = csend;
1134 
1135       if (! fport_io (zsend, &csent, zrec, &crec))
1136 	return FALSE;
1137 
1138       csend -= csent;
1139       zsend += csent;
1140 
1141       iPrecend = (iPrecend + crec) % CRECBUFLEN;
1142     }
1143 
1144   return TRUE;
1145 }
1146 
1147 /* Read data from the other system when we have nothing to send.  The
1148    argument cneed is the amount of data the caller wants, and ctimeout
1149    is the timeout in seconds.  The function sets *pcrec to the amount
1150    of data which was actually received, which may be less than cneed
1151    if there isn't enough room in the receive buffer.  If no data is
1152    received before the timeout expires, *pcrec will be returned as 0.
1153    If an error occurs, the function returns FALSE.  If the freport
1154    argument is FALSE, no error should be reported.  */
1155 
1156 boolean
freceive_data(cneed,pcrec,ctimeout,freport)1157 freceive_data (cneed, pcrec, ctimeout, freport)
1158      int cneed;
1159      int *pcrec;
1160      int ctimeout;
1161      boolean freport;
1162 {
1163   /* Set *pcrec to the maximum amount of data we can read.  fport_read
1164      expects *pcrec to be the buffer size, and sets it to the amount
1165      actually received.  */
1166   if (iPrecend < iPrecstart)
1167     *pcrec = iPrecstart - iPrecend - 1;
1168   else
1169     {
1170       *pcrec = CRECBUFLEN - iPrecend;
1171       if (iPrecstart == 0)
1172 	--(*pcrec);
1173     }
1174 
1175 #if DEBUG > 0
1176   /* If we have no room in the buffer, we're in trouble.  The
1177      protocols must be written to ensure that this can't happen.  */
1178   if (*pcrec == 0)
1179     ulog (LOG_FATAL, "freceive_data: No room in buffer");
1180 #endif
1181 
1182   /* If we don't have room for all the data the caller wants, we
1183      simply have to expect less.  We'll get the rest later.  */
1184   if (*pcrec < cneed)
1185     cneed = *pcrec;
1186 
1187   if (! fport_read (abPrecbuf + iPrecend, pcrec, cneed, ctimeout, freport))
1188     return FALSE;
1189 
1190   iPrecend = (iPrecend + *pcrec) % CRECBUFLEN;
1191 
1192   return TRUE;
1193 }
1194 
1195 /* Read a single character.  Get it out of the receive buffer if it's
1196    there, otherwise ask freceive_data for at least one character.
1197    This is used because as a protocol is shutting down freceive_data
1198    may read ahead and eat characters that should be read outside the
1199    protocol routines.  We call freceive_data rather than fport_read
1200    with an argument of 1 so that we can get all the available data in
1201    a single system call.  The ctimeout argument is the timeout in
1202    seconds; the freport argument is FALSE if no error should be
1203    reported.  This returns a character, or -1 on timeout or -2 on
1204    error.  */
1205 
1206 int
breceive_char(ctimeout,freport)1207 breceive_char (ctimeout, freport)
1208      int ctimeout;
1209      boolean freport;
1210 {
1211   char b;
1212 
1213   if (iPrecstart == iPrecend)
1214     {
1215       int crec;
1216 
1217       if (! freceive_data (1, &crec, ctimeout, freport))
1218 	return -2;
1219       if (crec == 0)
1220 	return -1;
1221     }
1222 
1223   b = abPrecbuf[iPrecstart];
1224   iPrecstart = (iPrecstart + 1) % CRECBUFLEN;
1225   return BUCHAR (b);
1226 }
1227 
1228 /* This routine is called when an error occurred and we are crashing
1229    out of the connection.  It is only used to report statistics on
1230    failed transfers to the statistics file.  Note that the number of
1231    bytes we report as having been sent has little or nothing to do
1232    with the number of bytes the remote site actually received.  */
1233 
1234 void
ustats_failed()1235 ustats_failed ()
1236 {
1237   long cbytes;
1238 
1239   if (cPsent_bytes != -1)
1240     {
1241       cbytes = cPsent_bytes;
1242       cPsent_bytes = -1;
1243       (void) fsent_file (FALSE, cbytes, "connection failure", FALSE);
1244     }
1245 
1246   if (cPreceived_bytes != -1)
1247     {
1248       cbytes = cPreceived_bytes;
1249       cPreceived_bytes = -1;
1250       (void) freceived_file (FALSE, cbytes, "connection failure", FALSE);
1251     }
1252 }
1253