1 
2 static char rcsid[] = "@(#)$Id: fileio.c,v 1.9 1996/10/28 16:58:05 wfp5p Exp $";
3 
4 /*******************************************************************************
5  *  The Elm Mail System  -  $Revision: 1.9 $   $State: Exp $
6  *
7  *                      Copyright (c) 1988-1995 USENET Community Trust
8  *			Copyright (c) 1986,1987 Dave Taylor
9  *******************************************************************************
10  * Bug reports, patches, comments, suggestions should be sent to:
11  *
12  *      Bill Pemberton, Elm Coordinator
13  *      flash@virginia.edu
14  *
15  *******************************************************************************
16  * $Log: fileio.c,v $
17  * Revision 1.9  1996/10/28  16:58:05  wfp5p
18  * Beta 1
19  *
20  * Revision 1.8  1996/08/08  19:49:25  wfp5p
21  * Alpha 11
22  *
23  * Revision 1.7  1996/03/14  17:29:34  wfp5p
24  * Alpha 9
25  *
26  * Revision 1.6  1995/09/29  17:42:10  wfp5p
27  * Alpha 8 (Chip's big changes)
28  *
29  * Revision 1.5  1995/09/11  15:19:07  wfp5p
30  * Alpha 7
31  *
32  * Revision 1.4  1995/06/22  14:48:47  wfp5p
33  * Performance enhancements from Paul Close
34  *
35  * Revision 1.3  1995/06/08  13:41:04  wfp5p
36  * A few mostly cosmetic changes
37  *
38  * Revision 1.2  1995/04/20  21:01:47  wfp5p
39  * Added the showreply feature and emacs key bindings.
40  *
41  * Revision 1.1.1.1  1995/04/19  20:38:36  wfp5p
42  * Initial import of elm 2.4 PL0 as base for elm 2.5.
43  *
44  ******************************************************************************/
45 
46 /** File I/O routines, including deletion from the folder!
47 
48 **/
49 
50 #include "elm_defs.h"
51 #include "elm_globals.h"
52 #include "s_elm.h"
53 #include "mailfile.h"
54 #include "port_stat.h"
55 
56 static char *makeAttString
57     P_((char *, int, const char *, int, const struct header_rec *));
58 
copy_write_error_exit(err)59 static void copy_write_error_exit(err)
60 int err;
61 {
62 	ShutdownTerm();
63 	error1(catgets(elm_msg_cat, ElmSet, ElmWriteCopyMessageFailed,
64 		"Write failed in copy_message()! [%s]"), strerror(err));
65 	leave(LEAVE_EMERGENCY);
66 }
67 
68 void
copy_message(dest_file,msgnum,cm_options)69 copy_message(dest_file, msgnum, cm_options)
70 FILE *dest_file;
71 int msgnum, cm_options;
72 {
73 	/** Copy selected message to destination file, with optional 'prefix'
74 	    as the prefix for each line.  If remove_header is true, it will
75 	    skip lines in the message until it finds the end of header line...
76 	    then it will start copying into the file... If remote is true
77 	    then it will append "remote from <hostname>" at the end of the
78 	    very first line of the file (for remailing)
79 
80 	    If "update_status" is true then it will write a new Status:
81 	    line at the end of the headers.  It never copies an existing one.
82 
83 	    If "decode" is true, prompt for key if the message is encrypted,
84 	    else just copy it as it is.
85 	**/
86 
87     /*
88      *	Changed buffer[SLEN] to buffer[VERY_LONG_STRING] to make it
89      *	big enough to contain a full length header line. Any header
90      *	is allowed to be at least 1024 bytes in length. (r.t.f. RFC)
91      *	14-Sep-1993 Jukka Ukkonen <ukkonen@csc.fi>
92      */
93 
94     char *buffer;
95     char *prefix;
96     char attrbuf[SLEN];
97     register struct header_rec *msg_header;
98     register int  lines, front_line, next_front,
99 		  in_header = 1, first_line = TRUE, ignoring = FALSE;
100     int forwarding	= !!(cm_options & CM_FORWARDING);
101     int remove_header	= !!(cm_options & CM_REMOVE_HEADER);
102     int remote		= !!(cm_options & CM_REMOTE);
103     int update_status	= !!(cm_options & CM_UPDATE_STATUS);
104     int remail		= !!(cm_options & CM_REMAIL);
105     int decode		= !!(cm_options & CM_DECODE);
106 #ifdef DONT_ADD_FROM
107     int strip_from = remail;
108 #else /* DONT_ADD_FROM */
109     int strip_from = FALSE;
110 #endif /* DONT_ADD_FROM */
111     int	end_header = 0;
112     int sender_added = 0;
113     int crypt_line = OFF, crypted = OFF;
114     static int start_encode_len = 0, end_encode_len = 0;
115     int bytes_seen = 0;
116     int buf_len, err;
117     struct mailFile mailFile;
118     FAST_COMP_DECLARE;
119 
120     /** set up lengths of encode/decode strings so we don't have to strlen()
121      ** every time */
122     if (start_encode_len == 0) {
123       start_encode_len = strlen(MSSG_START_ENCODE);
124       end_encode_len = strlen(MSSG_END_ENCODE);
125     }
126 
127     msg_header = curr_folder.headers[msgnum-1];
128     prefix = ((cm_options & CM_PREFIX) ? prefixchars : "");
129 
130       /** get to the first line of the message desired **/
131 
132     if (fseek(curr_folder.fp, msg_header->offset, 0) == -1) {
133        dprint(1, (debugfile,
134 		"ERROR: Attempt to seek %d bytes into file failed (%s)",
135 		msg_header->offset, "copy_message"));
136        error1(catgets(elm_msg_cat, ElmSet, ElmSeekFailed,
137 	     "ELM [seek] failed trying to read %d bytes into file."),
138 	     msg_header->offset);
139        return;
140     }
141 
142     /* how many lines in message? */
143 
144     lines = msg_header->lines;
145 
146     if (msg_header->encrypted && decode) {
147 	get_encode_key(OFF);
148     }
149 
150     /* now while not EOF & still in message... copy it! */
151 
152     next_front = TRUE;
153     mailFile_attach(&mailFile, curr_folder.fp);
154 
155     /* emit the opening attribution string */
156     if (cm_options & CM_ATTRIBUTION) {
157       if (forwarding) {
158 	if (fwdattribution[0] != '\0') {
159 	  prefix = "";
160 	  fputs(makeAttString(attrbuf, sizeof(attrbuf),
161 		  fwdattribution, 0, msg_header), dest_file);
162 	  putc('\n', dest_file);
163 	} else {
164 	  fputs(catgets(elm_msg_cat, ElmSet, ElmCopyMssgAttributionForwarded,
165 		  "Forwarded message:\n"), dest_file);
166 	}
167       } else if (attribution[0]) {
168 	fputs(makeAttString(attrbuf, sizeof(attrbuf),
169 	    attribution, 0, msg_header), dest_file);
170 	putc('\n', dest_file);
171       }
172     }
173 
174     while (lines) {
175       if (! (buf_len = mailFile_gets(&buffer, &mailFile)))
176         break;
177 
178       bytes_seen += buf_len;
179       front_line = next_front;
180 
181       if(buffer[buf_len - 1] == '\n') {
182 	lines--;	/* got a full line */
183 	next_front = TRUE;
184       }
185       else
186 	next_front = FALSE;
187 
188       if (front_line && ignoring)
189 	ignoring = whitespace(buffer[0]);
190 
191       if (ignoring)
192 	continue;
193 
194       /* preload the first char of the line for fast comparisons */
195       fast_comp_load(buffer[0]);
196 
197 #ifdef MMDF
198       if ((cm_options & CM_MMDF_HEAD) && strcmp(buffer, MSG_SEPARATOR) == 0)
199 	continue;
200 #endif /* MMDF */
201 
202       /* are we still in the header? */
203 
204       if (in_header && front_line) {
205 	if (buf_len < 2) {
206 	  in_header = 0;
207 	  bytes_seen = 0;
208 	  end_header = -1;
209 	  if (remail && !sender_added) {
210 	    if (fprintf(dest_file, "%sSender: %s\n", prefix, user_name) == EOF) {
211 	      copy_write_error_exit(errno);
212 	    }
213 	  }
214 	}
215 	else if (!isspace(*buffer)
216 	      && index(buffer, ':') == NULL
217 #ifdef MMDF
218 	      && strcmp(buffer, MSG_SEPARATOR) != 0
219 #endif /* MMDF */
220 		) {
221 	  in_header = 0;
222 	  bytes_seen = 0;
223 	  end_header = 1;
224 	  if (remail && !sender_added) {
225 	    if (fprintf(dest_file, "%sSender: %s\n", prefix, user_name) == EOF) {
226 	      copy_write_error_exit(errno);
227 	    }
228 	  }
229 	} else if (in_header && remote && fast_header_cmp(buffer, "Sender", (char *)NULL)) {
230 	  if (remail)
231 	    if (fprintf(dest_file, "%sSender: %s\n", prefix, user_name) == EOF) {
232 	      copy_write_error_exit(errno);
233 	    }
234 	  sender_added = TRUE;
235 	  continue;
236 	}
237 	if (end_header) {
238 	  if (update_status) {
239 	      if (isoff(msg_header->status, NEW)) {
240 		if (ison(msg_header->status, UNREAD)) {
241 		  if (fprintf(dest_file, "%sStatus: O\n", prefix) == EOF) {
242 		    copy_write_error_exit(errno);
243 		  }
244 		} else	{ /* read */
245 		  int x;
246 #ifdef BSD
247 		  if (fprintf(dest_file, "%sStatus: OR", prefix) == EOF)
248 #else
249 		  if (fprintf(dest_file, "%sStatus: RO", prefix) == EOF)
250 #endif
251 		  {
252 		    copy_write_error_exit(errno);
253 		  }
254 
255 		  if (ison(msg_header->status, REPLIED_TO))
256 		  {
257                    if (putc('r', dest_file) == EOF)
258 		       copy_write_error_exit (errno);
259 		  }
260 
261 		  for (x=0;msg_header->mailx_status[x] != '\0'; x++)
262                   {
263 		     if ( strchr("ROr",msg_header->mailx_status[x]) == NULL)
264 		     {
265 		        if (putc(msg_header->mailx_status[x], dest_file) == EOF)
266 			{
267 			   copy_write_error_exit(errno);
268 			}
269 		     }
270 		  }
271 
272                   if (putc('\n', dest_file) == EOF)
273 		     copy_write_error_exit (errno);
274 
275 		}
276 
277 		update_status = FALSE; /* do it only once */
278                }  /* else if NEW - indicate NEW with no Status: line. This is
279 		 * important if we resync a mailfile - we don't want
280 		 * NEW status lost when we copy each message out.
281 		 * It is the responsibility of the function that calls
282 		 * this function to unset NEW as appropriate to its
283 		 * reason for using this function to copy a message
284 		 */
285 
286 		/*
287 		 * add the missing newline for RFC 822
288 		 */
289 	      if (end_header > 0) {
290 		/* add the missing newline for RFC 822 */
291 		if (putc('\n', dest_file) == EOF) {
292 		  copy_write_error_exit(errno);
293 		}
294 	      }
295 	  }
296 	}
297       }
298 
299       if (in_header) {
300 	/* Process checks while in header area */
301 
302 	if (remove_header) {
303 	  ignoring = TRUE;
304 	  continue;
305 	}
306 
307 	/* add remote on to front? */
308 	if (first_line && remote) {
309 	  no_ret(buffer);
310 #ifndef MMDF
311 	  if (!strip_from && fprintf(dest_file, "%s%s remote from %s\n",
312 		  prefix, buffer, host_name) == EOF) {
313 		copy_write_error_exit(errno);
314 	  }
315 #else
316 	  if (fast_strbegConst(buffer, "From ")) {
317 	    if (!strip_from && fprintf(dest_file, "%s%s remote from %s\n",
318 		    prefix, buffer, host_name) == EOF) {
319 		copy_write_error_exit(errno);
320 	    }
321 	  } else {
322 	    if (fprintf(dest_file, "%s%s\n", prefix, buffer) == EOF) {
323 		copy_write_error_exit(errno);
324 	    }
325 	  }
326 #endif /* MMDF */
327 	  first_line = FALSE;
328 	  continue;
329 	}
330 
331 	if (!forwarding) {
332 	  if (fast_header_cmp(buffer, "Status", (char *)NULL) ||
333 		    /* we will output a new Status: line later, if desired. */
334 		    (strip_from && fast_stribegConst(buffer, ">From"))) {
335 	    ignoring = TRUE;
336 	    continue;
337 	  } else {
338 	    err = 0;
339 	    if (*prefix) err = fputs(prefix, dest_file);
340 	    if (err != EOF) err = fwrite(buffer, 1, buf_len, dest_file);
341 	    if (err != buf_len) copy_write_error_exit(errno);
342 	    continue;
343 	  }
344 	}
345 	else { /* forwarding */
346 
347 	  if (fast_header_cmp(buffer, "Received", (char *)NULL)
348 		  || fast_stribegConst(buffer, ">From")
349 		  || fast_header_cmp(buffer, "Status", (char *)NULL)
350 		  || fast_header_cmp(buffer, "Return-Path", (char *)NULL))
351 	      ignoring = TRUE;
352 	  else
353 	    if (remail && fast_header_cmp(buffer, "To", (char *)NULL)) {
354 	      if (fprintf(dest_file, "%sOrig-%s", prefix, buffer) == EOF) {
355 		copy_write_error_exit(errno);
356 	      }
357 	    } else {
358 	      err = 0;
359 	      if (*prefix) err = fputs(prefix, dest_file);
360 	      if (err != EOF) err = fwrite(buffer, 1, buf_len, dest_file);
361 	      if (err != buf_len) copy_write_error_exit(errno);
362 	    }
363 	}
364       }
365       else { /* not in header */
366         /* Process checks that occur after the header area */
367 
368 	/* perform encryption checks */
369 	if (buffer[0] == '[' && decode) {
370 		if (!strncmp(buffer, MSSG_START_ENCODE, start_encode_len)) {
371 			crypted = ON;
372 			crypt_line = ON;
373 		} else if (!strncmp(buffer, MSSG_END_ENCODE, end_encode_len)) {
374 			crypted = OFF;
375 			crypt_line = ON;
376 		} else if (crypted) {
377 			no_ret(buffer);
378 			encode(buffer);
379 			strcat(buffer, "\n");
380 		}
381 	} else if (crypted) {
382 		no_ret(buffer);
383 		encode(buffer);
384 		strcat(buffer, "\n");
385 	}
386 
387 
388 #ifndef MMDF
389 #ifndef DONT_ESCAPE_MESSAGES
390 	if (msg_header->content_length <= bytes_seen &&
391 	    fast_strbegConst(buffer, "From ") && (real_from(buffer, NULL))) {
392 	  /* If we have a content-length > bytes_seen and there is lines left
393 	  ** this is probably a From line that is part of the body, as an
394 	  ** included message. A simple heuristic test.
395 	  */
396 	  dprint(1, (debugfile,
397 		 "\n*** Internal Problem...Tried to add the following;\n"));
398 	  dprint(1, (debugfile,
399 		 "  '%s'\nto output file (copy_message) ***\n", buffer));
400 	  break;	/* STOP NOW! */
401 	}
402 #endif /* DONT_ESCAPE_MESSAGES */
403 #endif /* MMDF */
404 
405 	err = 0;
406 	if (*prefix && !crypt_line) err = fputs(prefix, dest_file);
407 	crypt_line = OFF;
408 	if (err != EOF) err = fwrite(buffer, 1, buf_len, dest_file);
409 	if (err != buf_len) copy_write_error_exit(errno);
410       }
411     }
412 #ifndef MMDF
413     if (buf_len + strlen(prefix) > 1)
414 	if (putc('\n', dest_file) == EOF) {	/* blank line to keep mailx happy *sigh* */
415 	  copy_write_error_exit(errno);
416 	}
417 #endif /* MMDF */
418 
419     /* emit the closing attribution */
420     if ((cm_options & CM_ATTRIBUTION) && forwarding && fwdattribution[0]) {
421       fputs(makeAttString(attrbuf, sizeof(attrbuf),
422 	      fwdattribution, 1, msg_header), dest_file);
423       putc('\n', dest_file);
424     }
425 
426     /* Since fprintf is buffered, its return value is only useful for
427      * writes which exceed the blocksize.  Do a fflush to ensure that
428      * the message has, in fact, been written.
429      */
430     if (fflush(dest_file) == EOF) {
431       copy_write_error_exit(errno);
432     }
433     mailFile_detach(&mailFile);
434 }
435 
436 
437 static char *
makeAttString(retbuf,retbuflen,attribution,sel_field,messageHeader)438 makeAttString(retbuf, retbuflen, attribution, sel_field, messageHeader)
439 char *retbuf;			/* storage space for result		*/
440 int retbuflen;			/* size of result buffer		*/
441 const char *attribution;	/* attribution string to expand		*/
442 int sel_field;			/* field to select in "%[...]" list	*/
443 const struct header_rec *messageHeader; /* current message header info	*/
444 {
445     const char *aptr = attribution; /* cursor into the attribution spec	*/
446     char *rptr = retbuf;	/* cursor into the result buffer	*/
447     const char *expval;		/* next item to add to result buffer	*/
448     int explen;			/* length of expansion value		*/
449     int in_selection;		/* currently doing %[...] list?		*/
450     int curr_field;		/* field number of current %[...] list	*/
451     char fromfield[STRING];	/* room for parsed from address         */
452 
453     --retbuflen;		/* reserve space for '\0' */
454     in_selection = FALSE;
455 
456     /*
457      * Process the attribution string.
458      */
459     for ( ; retbuflen > 1 && *aptr != '\0' ; ++aptr) {
460 
461 	/*
462 	 * Handle the character if it is not a %-expansion.
463 	 */
464 	switch (*aptr) {
465 
466 	case '|':		/* next choice of "%[sel0|sel1|...]" list */
467 	    if (in_selection) {
468 		++curr_field;
469 		expval = NULL;
470 	    } else {
471 		expval = aptr;
472 		explen = 1;
473 	    }
474 	    break;
475 
476 	case ']':		/* end of "%[sel0|sel1|...]" list */
477 	    if (in_selection) {
478 		in_selection = FALSE;
479 		expval = NULL;
480 	    } else {
481 		expval = aptr;
482 		explen = 1;
483 	    }
484 	    break;
485 
486 	case '\\':		/* backslash-quoting */
487 	    switch (*++aptr) {
488 	    case 't':
489 		expval = "\t";
490 		explen = 1;
491 		break;
492 	    case 'n':
493 		expval = "\n";
494 		explen = 1;
495 		break;
496 	    case '\0':
497 		--aptr;
498 		/*FALLTHRU*/
499 	    default:
500 		expval = aptr;
501 		explen = 1;
502 		break;
503 	    }
504 	    break;
505 
506 	case '%':		/* special %-expansion */
507 
508 	    switch (*++aptr) {
509 
510 	    case 's':  /* backward compatibility with 2.4 */
511 	    case 'F':  /* expand from */
512 		expval = messageHeader->from;
513 		explen = strlen(expval);
514 		break;
515 
516 	    case 'D': /* expand date */
517 		expval = ctime(&messageHeader->time_sent);
518 		explen = strlen(expval)-1; /* exclude newline */
519 		break;
520 
521 	    case 'I': /* expand message ID */
522 		expval = messageHeader->messageid;
523 		explen = strlen(expval);
524 		break;
525 
526 	    case 'S': /* expand subject */
527 		expval = messageHeader->subject;
528 		explen = strlen(expval);
529 		break;
530 
531 	    case '[': /* %[sel0|sel1|...] */
532 		in_selection = TRUE;
533 		curr_field = 0;
534 		expval = NULL;
535 		break;
536 
537 	    case ')': /* special case for %)F - from name */
538 		/*FALLTHROUGH*/
539 	    case '>': /* special case for %>F - from address */
540 		if (*++aptr != 'F') {
541 		    expval = aptr-2;
542 		    explen = 3;
543 		}
544 		else {
545 		    int ret;
546 		    if (*(aptr-1) == ')')		/* from name */
547 			ret = parse_arpa_mailbox(messageHeader->allfrom, NULL, 0,
548 			    fromfield, sizeof(fromfield), NULL);
549 		    else
550 		    if (*(aptr-1) == '>')		/* from addr */
551 			ret = parse_arpa_mailbox(messageHeader->allfrom,
552 			     fromfield, sizeof(fromfield), NULL, 0, NULL);
553 		    else {
554 			sprintf(fromfield, "error: %c", *(aptr-1));
555 		    }
556 		    if (ret < 0 || *fromfield == '\0') {
557 			explen = 0;
558 			while (*(aptr+1) && whitespace(*(aptr+1)))
559 			    aptr++;
560 		    }
561 		    else {
562 			expval = fromfield;
563 			explen = strlen(expval);
564 		    }
565 		}
566 		break;
567 
568 	    case '%': /* add a % and skip on past... */
569 		expval = aptr;
570 		explen = 1;
571 		break;
572 
573 	    case '\0':
574 		expval = --aptr;
575 		explen = 1;
576 		break;
577 
578 	    default:
579 		expval = aptr-1;
580 		explen = 2;
581 		break;
582 
583 	    }
584 
585 	    break;
586 
587 	default:			/* just a regular char */
588 	    expval = aptr;
589 	    explen = 1;
590 
591 	}
592 
593 	/*
594 	 * Add the expansion value to the result.
595 	 */
596 	if (expval != NULL && (!in_selection || curr_field == sel_field)) {
597 	    if (explen < retbuflen) {
598 		(void) strncpy(rptr, expval, explen);
599 		rptr += explen;
600 	    }
601 	    retbuflen -= explen;
602 	}
603 
604     }
605 
606     *rptr = '\0';
607     return(retbuf);
608 }
609 
610 
611 static struct stat saved_buf;
612 static char saved_fname[SLEN];
613 
614 int
save_file_stats(fname)615 save_file_stats(fname)
616 char *fname;
617 {
618 	/* if fname exists, save the owner, group, mode and filename.
619 	 * otherwise flag nothing saved. Return 0 if saved, else -1.
620 	 */
621 
622 	if (stat(fname, &saved_buf) == 0) {
623 	  (void)strfcpy(saved_fname, fname, sizeof(saved_fname));
624 	  dprint(2, (debugfile,
625 	    "save_file_stats(%s) successful - owner=%d group=%d mode=%o\n",
626 	    fname, saved_buf.st_uid, saved_buf.st_gid, saved_buf.st_mode));
627 	  return(0);
628 	}
629         saved_fname[0] = '\0';
630 	dprint(2, (debugfile, "save_file_stats(%s) FAILED [%s]\n",
631 	  fname, strerror(errno)));
632 	return(-1);
633 
634 }
635 
restore_file_stats(fname)636 restore_file_stats(fname)
637 char *fname;
638 {
639 	/* if fname matches the saved file name, set the owner and group
640 	 * of fname to the saved owner, group and mode,
641 	 * else to the userid and groupid of the user and to 700.
642 	 * Return	-1 if the  either mode or owner/group not set
643 	 *		0 if the default values were used
644 	 *		1 if the saved values were used
645 	 */
646 
647 	int old_umask, i, new_mode, new_owner, new_group, ret_code;
648 	int chown_restricted;
649 
650 
651 	new_mode = 0600;
652 	new_owner = userid;
653 	new_group = groupid;
654 	ret_code = 0;
655 
656 	if(strcmp(fname, saved_fname) == 0) {
657 	  new_mode = saved_buf.st_mode;
658 	  new_owner = saved_buf.st_uid;
659 	  new_group = saved_buf.st_gid;
660 	  ret_code = 1;
661 	}
662 	dprint(2, (debugfile, "restore_file_stats(%s) - %s file stats\n",
663 	  fname, (ret_code ? "restoring previous" : "setting new")));
664 
665 	old_umask = umask(0);
666 	if (chmod(fname, new_mode & 0777) < 0) {
667 	  ret_code = -1;
668 	  dprint(4, (debugfile, "  chmod(%s, %.3o) FAILED [%s]\n",
669 	      fname, (new_mode & 0777), strerror(errno)));
670 	} else {
671 	  dprint(4, (debugfile, "  chmod(%s, %.3o) succeeded\n",
672 	      fname, (new_mode & 0777)));
673 	}
674 	(void) umask(old_umask);
675 
676 #ifdef	BSD
677 # define CHOWN_IS_RESTRICTED	TRUE	/* BSD restricts chown() to root */
678 #else
679 # ifdef _PC_CHOWN_RESTRICTED
680 #  define CHOWN_IS_RESTRICTED	pathconf(fname, _PC_CHOWN_RESTRICTED)
681 # else
682 #  define CHOWN_IS_RESTRICTED	FALSE	/* or so we would like to think... */
683 # endif
684 #endif
685 
686 	if (elm_chown(fname, new_owner, new_group) < 0) {
687 	  if (!CHOWN_IS_RESTRICTED)
688 	    ret_code = -1;
689 	  dprint(4, (debugfile, "  elm_chown(%s, %d, %d) FAILED [%s]\n",
690 	      fname, new_owner, new_group, strerror(errno)));
691 	} else {
692 	  dprint(4, (debugfile, "  elm_chown(%s, %d, %d) succeeded\n",
693 	      fname, new_owner, new_group));
694 	}
695 
696 	return(ret_code);
697 }
698 
699