1 /* movemail foo bar -- move file foo to file bar,
2 locking file foo the way /bin/mail respects.
3 Copyright (C) 1986 Free Software Foundation, Inc.
5 This file is part of GNU Emacs.
7 GNU Emacs is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 1, or (at your option)
12 GNU Emacs is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GNU Emacs; see the file COPYING. If not, write to
19 the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
22 * Modified January, 1986 by Michael R. Gretzinger (Project Athena)
24 * Added POP (Post Office Protocol) service. When compiled -DPOP
25 * movemail will accept input filename arguments of the form
26 * "po:username". This will cause movemail to open a connection to
27 * a pop server running on $MAILHOST (environment variable). Movemail
28 * must be setuid to root in order to work with POP.
30 * New module: popmail.c
32 * main - added code within #ifdef MAIL_USE_POP; added setuid(getuid())
34 * New routines in movemail.c:
35 * get_errmsg - return pointer to system error message
39 #include <sys/types.h>
43 #define NO_SHORTNAMES /* Tell config not to load remap.h */
44 #include "../src/config.h"
58 #include <sys/locking.h>
62 extern int lk_open (), lk_close ();
65 /* Cancel substitutions made by config.h for Emacs. */
74 /* Nonzero means this is name of a lock file to delete on fatal error. */
75 char *delete_lockname
;
81 char *inname
, *outname
;
86 #ifndef MAIL_USE_FLOCK
93 #endif /* not MAIL_USE_FLOCK */
98 fatal ("two arguments required");
107 /* Check access to input and output file. */
108 if (access (inname
, R_OK
| W_OK
) != 0)
109 pfatal_with_name (inname
);
110 if (access (outname
, F_OK
) == 0 && access (outname
, W_OK
) != 0)
111 pfatal_with_name (outname
);
113 /* Also check that outname's directory is writeable to the real uid. */
115 char *buf
= (char *) malloc (strlen (outname
) + 1);
117 strcpy (buf
, outname
);
118 p
= buf
+ strlen (buf
);
119 while (p
> buf
&& p
[-1] != '/')
123 if (access (buf
, W_OK
) != 0)
124 pfatal_with_name (buf
);
129 if (!bcmp (inname
, "po:", 3))
131 int status
; char *user
;
133 user
= (char *) rindex (inname
, ':') + 1;
134 status
= popmail (user
, outname
);
139 #endif /* MAIL_USE_POP */
141 #ifndef MAIL_USE_MMDF
142 #ifndef MAIL_USE_FLOCK
143 /* Use a lock file named /usr/spool/mail/$USER.lock:
144 If it exists, the mail file is locked. */
145 lockname
= concat (inname
, ".lock", "");
146 strcpy (tempname
, inname
);
147 p
= tempname
+ strlen (tempname
);
148 while (p
!= tempname
&& p
[-1] != '/')
151 strcpy (p
, "EXXXXXX");
153 (void) unlink (tempname
);
157 /* Create the lock file, but not under the lock file name. */
158 /* Give up if cannot do that. */
159 desc
= open (tempname
, O_WRONLY
| O_CREAT
, 0666);
161 pfatal_with_name (concat ("temporary file \"", tempname
, "\""));
164 tem
= link (tempname
, lockname
);
165 (void) unlink (tempname
);
170 /* If lock file is a minute old, unlock it. */
171 if (stat (lockname
, &st
) >= 0)
174 if (st
.st_ctime
< now
- 60)
175 (void) unlink (lockname
);
179 delete_lockname
= lockname
;
180 #endif /* not MAIL_USE_FLOCK */
182 #ifdef MAIL_USE_FLOCK
183 indesc
= open (inname
, O_RDWR
);
184 #else /* if not MAIL_USE_FLOCK */
185 indesc
= open (inname
, O_RDONLY
);
186 #endif /* not MAIL_USE_FLOCK */
187 #else /* MAIL_USE_MMDF */
188 indesc
= lk_open (inname
, O_RDONLY
, 0, 0, 10);
189 #endif /* MAIL_USE_MMDF */
192 pfatal_with_name (inname
);
194 #if defined(BSD) || defined(XENIX)
195 /* In case movemail is setuid to root, make sure the user can
196 read the output file. */
197 /* This is desirable for all systems
198 but I don't want to assume all have the umask system call */
199 umask (umask (0) & 0333);
200 #endif /* BSD or Xenix */
201 outdesc
= open (outname
, O_WRONLY
| O_CREAT
| O_EXCL
, 0666);
203 pfatal_with_name (outname
);
204 #ifdef MAIL_USE_FLOCK
206 if (locking (indesc
, LK_RLCK
, 0L) < 0) pfatal_with_name (inname
);
208 if (flock (indesc
, LOCK_EX
) < 0) pfatal_with_name (inname
);
210 #endif /* MAIL_USE_FLOCK */
214 nread
= read (indesc
, buf
, sizeof buf
);
215 if (nread
!= write (outdesc
, buf
, nread
))
217 int saved_errno
= errno
;
218 (void) unlink (outname
);
220 pfatal_with_name (outname
);
222 if (nread
< sizeof buf
)
230 /* Check to make sure no errors before we zap the inbox. */
231 if (close (outdesc
) != 0)
233 int saved_errno
= errno
;
234 (void) unlink (outname
);
236 pfatal_with_name (outname
);
239 #ifdef MAIL_USE_FLOCK
240 #if defined(STRIDE) || defined(XENIX)
241 /* Stride, xenix have file locking, but no ftruncate. This mess will do. */
242 (void) close (open (inname
, O_CREAT
| O_TRUNC
| O_RDWR
, 0666));
244 (void) ftruncate (indesc
, 0L);
245 #endif /* STRIDE or XENIX */
246 #endif /* MAIL_USE_FLOCK */
249 lk_close (indesc
, 0, 0, 0);
254 #ifndef MAIL_USE_FLOCK
255 /* Delete the input file; if we can't, at least get rid of its contents. */
256 if (unlink (inname
) < 0)
258 creat (inname
, 0666);
259 #ifndef MAIL_USE_MMDF
261 #endif /* not MAIL_USE_MMDF */
262 #endif /* not MAIL_USE_FLOCK */
266 /* Print error message and exit. */
272 unlink (delete_lockname
);
277 /* Print error message. `s1' is printf control string, `s2' is arg for it. */
282 printf ("movemail: ");
287 pfatal_with_name (name
)
290 extern int errno
, sys_nerr
;
291 extern char *sys_errlist
[];
294 if (errno
< sys_nerr
)
295 s
= concat ("", sys_errlist
[errno
], " for %s");
297 s
= "cannot open %s";
301 /* Return a newly-allocated string whose contents concatenate those of s1, s2, s3. */
307 int len1
= strlen (s1
), len2
= strlen (s2
), len3
= strlen (s3
);
308 char *result
= (char *) xmalloc (len1
+ len2
+ len3
+ 1);
311 strcpy (result
+ len1
, s2
);
312 strcpy (result
+ len1
+ len2
, s3
);
313 *(result
+ len1
+ len2
+ len3
) = 0;
318 /* Like malloc but get fatal error if memory is exhausted. */
324 int result
= malloc (size
);
326 fatal ("virtual memory exhausted", 0);
330 /* This is the guts of the interface to the Post Office Protocol. */
334 #include <sys/socket.h>
335 #include <netinet/in.h>
341 /* Cancel substitutions made by config.h for Emacs. */
357 static int debug
= 0;
359 popmail(user
, outfile
)
373 host
= getenv("MAILHOST");
375 fatal("no MAILHOST defined");
378 if (pop_init(host
) == NOTOK
) {
383 if (getline(response
, sizeof response
, sfi
) != OK
) {
388 if (pop_command("USER %s", user
) == NOTOK
||
389 pop_command("RPOP %s", user
) == NOTOK
) {
395 if (pop_stat(&nmsgs
, &nbytes
) == NOTOK
) {
407 mbfi
= open (outfile
, O_WRONLY
| O_CREAT
| O_EXCL
, 0666);
411 error("Error in open: %s, %s", get_errmsg(), outfile
);
414 fchown(mbfi
, getuid(), -1);
416 if ((mbf
= fdopen(mbfi
, "w")) == NULL
)
419 error("Error in fdopen: %s", get_errmsg());
425 for (i
= 1; i
<= nmsgs
; i
++) {
426 mbx_delimit_begin(mbf
);
427 if (pop_retr(i
, mbx_write
, mbf
) != OK
) {
433 mbx_delimit_end(mbf
);
437 for (i
= 1; i
<= nmsgs
; i
++) {
438 if (pop_command("DELE %d", i
) == NOTOK
) {
454 register struct hostent
*hp
;
455 register struct servent
*sp
;
456 int lport
= IPPORT_RESERVED
- 1;
457 struct sockaddr_in sin
;
461 hp
= gethostbyname(host
);
463 sprintf(Errmsg
, "MAILHOST unknown: %s", host
);
467 sp
= getservbyname("pop", "tcp");
469 strcpy(Errmsg
, "tcp/pop: unknown service");
473 sin
.sin_family
= hp
->h_addrtype
;
474 bcopy(hp
->h_addr
, (char *)&sin
.sin_addr
, hp
->h_length
);
475 sin
.sin_port
= sp
->s_port
;
476 s
= rresvport(&lport
);
478 sprintf(Errmsg
, "error creating socket: %s", get_errmsg());
482 if (connect(s
, (char *)&sin
, sizeof sin
) < 0) {
483 sprintf(Errmsg
, "error during connect: %s", get_errmsg());
488 sfi
= fdopen(s
, "r");
489 sfo
= fdopen(s
, "w");
490 if (sfi
== NULL
|| sfo
== NULL
) {
491 sprintf(Errmsg
, "error in fdopen: %s", get_errmsg());
499 pop_command(fmt
, a
, b
, c
, d
)
505 sprintf(buf
, fmt
, a
, b
, c
, d
);
507 if (debug
) fprintf(stderr
, "---> %s\n", buf
);
508 if (putline(buf
, Errmsg
, sfo
) == NOTOK
) return(NOTOK
);
510 if (getline(buf
, sizeof buf
, sfi
) != OK
) {
515 if (debug
) fprintf(stderr
, "<--- %s\n", buf
);
525 pop_stat(nmsgs
, nbytes
)
530 if (debug
) fprintf(stderr
, "---> STAT\n");
531 if (putline("STAT", Errmsg
, sfo
) == NOTOK
) return(NOTOK
);
533 if (getline(buf
, sizeof buf
, sfi
) != OK
) {
538 if (debug
) fprintf(stderr
, "<--- %s\n", buf
);
543 sscanf(buf
, "+OK %d %d", nmsgs
, nbytes
);
548 pop_retr(msgno
, action
, arg
)
553 sprintf(buf
, "RETR %d", msgno
);
554 if (debug
) fprintf(stderr
, "%s\n", buf
);
555 if (putline(buf
, Errmsg
, sfo
) == NOTOK
) return(NOTOK
);
557 if (getline(buf
, sizeof buf
, sfi
) != OK
) {
563 switch (multiline(buf
, sizeof buf
, sfi
)) {
585 while (--n
> 0 && (c
= fgetc(f
)) != EOF
)
586 if ((*p
++ = c
) == '\n') break;
589 strcpy(buf
, "error on connection");
593 if (c
== EOF
&& p
== buf
) {
594 strcpy(buf
, "connection closed by foreign host");
599 if (*--p
== '\n') *p
= NULL
;
600 if (*--p
== '\r') *p
= NULL
;
609 if (getline(buf
, n
, f
) != OK
) return (NOTOK
);
611 if (*(buf
+1) == NULL
) {
623 extern int errno
, sys_nerr
;
624 extern char *sys_errlist
[];
627 if (errno
< sys_nerr
)
628 s
= sys_errlist
[errno
];
639 fprintf(f
, "%s\r\n", buf
);
642 strcpy(err
, "lost connection");
656 mbx_delimit_begin(mbf
)
659 fputs("\f\n0,unseen,,\n", mbf
);
668 #endif /* MAIL_USE_POP */