summaryrefslogtreecommitdiff
path: root/gnu/libexec/uucp/file.c
blob: 5fc34456ac0b9db08f27688c4cd054f5c9bbc8a6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
/* 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 <string.h> only in <uucp.h>, 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 <errno.h>

#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);
}