This source file includes following definitions.
- xmalloc
- main
- mail_spool_name
- fatal
- error
- pfatal_with_name
- pfatal_and_delete
- popmail
- pop_retr
- mbx_write
- movemail_strftime
- mbx_delimit_begin
- mbx_delimit_end
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 #include <config.h>
59 #include <sys/types.h>
60 #include <sys/stat.h>
61 #include <sys/file.h>
62 #include <stdlib.h>
63 #include <errno.h>
64 #include <time.h>
65
66 #include <getopt.h>
67 #include <unistd.h>
68 #include <fcntl.h>
69 #include <signal.h>
70 #include <string.h>
71
72 #include <attribute.h>
73 #include <unlocked-io.h>
74
75 #include "syswait.h"
76 #ifdef MAIL_USE_POP
77 #include "pop.h"
78 #endif
79
80 #ifdef MSDOS
81 #undef access
82 #endif
83
84 #ifdef WINDOWSNT
85 #include "ntlib.h"
86 #undef access
87 #undef unlink
88 #define fork() 0
89 #define waitpid(child, var, flags) (*(var) = 0)
90
91
92
93
94
95
96
97
98
99
100
101 #include <fcntl.h>
102 #endif
103
104 #ifdef WINDOWSNT
105 #include <sys/locking.h>
106 #endif
107
108
109
110
111
112
113 #ifdef MAIL_USE_LOCKF
114 #define MAIL_USE_SYSTEM_LOCK
115 #endif
116
117 #ifdef MAIL_USE_FLOCK
118 #define MAIL_USE_SYSTEM_LOCK
119 #endif
120
121 #if (!defined MAIL_USE_SYSTEM_LOCK \
122 && (defined HAVE_LIBMAIL || defined HAVE_LIBLOCKFILE) \
123 && defined HAVE_MAILLOCK_H)
124 #include <maillock.h>
125
126
127 #ifdef MAILDIR
128 #define MAIL_USE_MAILLOCK
129 static char *mail_spool_name (char *);
130 #endif
131 #endif
132
133 static _Noreturn void fatal (const char *s1, const char *s2, const char *s3);
134 static void error (const char *s1, const char *s2, const char *s3);
135 static _Noreturn void pfatal_with_name (char *name);
136 static _Noreturn void pfatal_and_delete (char *name);
137 #ifdef MAIL_USE_POP
138 static int popmail (char *, char *, bool, char *, bool);
139 static bool pop_retr (popserver, int, FILE *);
140 static bool mbx_write (char *, int, FILE *);
141 static bool mbx_delimit_begin (FILE *);
142 static bool mbx_delimit_end (FILE *);
143 #endif
144
145 #if (defined MAIL_USE_MAILLOCK \
146 || (!defined DISABLE_DIRECT_ACCESS && !defined MAIL_USE_SYSTEM_LOCK))
147
148
149 static void * ATTRIBUTE_MALLOC
150 xmalloc (size_t size)
151 {
152 void *result = malloc (size);
153 if (!result)
154 fatal ("virtual memory exhausted", 0, 0);
155 return result;
156 }
157 #endif
158
159
160 static char *delete_lockname;
161
162 int
163 main (int argc, char **argv)
164 {
165 char *inname, *outname;
166 int indesc, outdesc;
167 ssize_t nread;
168 int wait_status;
169 int c;
170 bool preserve_mail = false;
171
172 #ifndef MAIL_USE_SYSTEM_LOCK
173 struct stat st;
174 int tem;
175 char *tempname;
176 size_t inname_len, inname_dirlen;
177 int desc;
178 #endif
179
180 #ifdef MAIL_USE_POP
181 bool pop_reverse_order = false;
182 # define ARGSTR "pr"
183 #else
184 # define ARGSTR "p"
185 #endif
186
187 uid_t real_gid = getgid ();
188 uid_t priv_gid = getegid ();
189
190 delete_lockname = 0;
191
192 while (0 <= (c = getopt (argc, argv, ARGSTR)))
193 {
194 switch (c) {
195 #ifdef MAIL_USE_POP
196 case 'r':
197 pop_reverse_order = true;
198 break;
199 #endif
200 case 'p':
201 preserve_mail = true;
202 break;
203 default:
204 return EXIT_FAILURE;
205 }
206 }
207
208 if (
209 #ifdef MAIL_USE_POP
210 (argc - optind < 2) || (argc - optind > 3)
211 #else
212 (argc - optind != 2)
213 #endif
214 )
215 {
216 #ifdef MAIL_USE_POP
217 fprintf (stderr, "Usage: movemail [-p] [-r] inbox destfile%s\n",
218 " [POP-password]");
219 #else
220 fprintf (stderr, "Usage: movemail [-p] inbox destfile%s\n", "");
221 #endif
222 return EXIT_FAILURE;
223 }
224
225 inname = argv[optind];
226 outname = argv[optind+1];
227
228 if (*outname == 0)
229 fatal ("Destination file name is empty", 0, 0);
230
231 #ifdef MAIL_USE_POP
232 if (!strncmp (inname, "po:", 3))
233 {
234 int status;
235
236 status = popmail (inname + 3, outname, preserve_mail,
237 (argc - optind == 3) ? argv[optind+2] : NULL,
238 pop_reverse_order);
239 return status;
240 }
241
242 if (setuid (getuid ()) < 0)
243 fatal ("Failed to drop privileges", 0, 0);
244
245 #endif
246
247 #ifndef DISABLE_DIRECT_ACCESS
248
249 char *lockname = 0;
250 char *spool_name = 0;
251
252 #ifdef MAIL_USE_MAILLOCK
253 spool_name = mail_spool_name (inname);
254 #endif
255 if (! spool_name)
256 {
257 #ifndef MAIL_USE_SYSTEM_LOCK
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274 bool lockname_unlinked = false;
275 inname_len = strlen (inname);
276 lockname = xmalloc (inname_len + sizeof ".lock");
277 strcpy (lockname, inname);
278 strcpy (lockname + inname_len, ".lock");
279 for (inname_dirlen = inname_len;
280 inname_dirlen && !IS_DIRECTORY_SEP (inname[inname_dirlen - 1]);
281 inname_dirlen--)
282 continue;
283 tempname = xmalloc (inname_dirlen + sizeof "EXXXXXX");
284
285 while (true)
286 {
287
288
289
290 memcpy (tempname, inname, inname_dirlen);
291 strcpy (tempname + inname_dirlen, "EXXXXXX");
292 desc = mkostemp (tempname, O_BINARY);
293 if (desc < 0)
294 {
295 int mkostemp_errno = errno;
296 error ("error while creating what would become the lock file",
297 0, 0);
298 errno = mkostemp_errno;
299 pfatal_with_name (tempname);
300 }
301 close (desc);
302
303 tem = link (tempname, lockname);
304
305 if (tem < 0 && errno != EEXIST)
306 pfatal_with_name (lockname);
307
308 unlink (tempname);
309 if (tem >= 0)
310 break;
311 sleep (1);
312
313
314
315
316
317 if (!lockname_unlinked
318 && stat (lockname, &st) == 0
319 && st.st_ctime < time (0) - 300)
320 lockname_unlinked = unlink (lockname) == 0 || errno == ENOENT;
321 }
322
323 delete_lockname = lockname;
324 #endif
325 }
326
327 #ifdef SIGCHLD
328 signal (SIGCHLD, SIG_DFL);
329 #endif
330
331 pid_t child = fork ();
332 if (child < 0)
333 fatal ("Error in fork; %s", strerror (errno), 0);
334
335 if (child == 0)
336 {
337 int lockcount = 0;
338 int status = 0;
339 #if defined (MAIL_USE_MAILLOCK) && defined (HAVE_TOUCHLOCK)
340 time_t touched_lock;
341 #endif
342
343 if (setuid (getuid ()) < 0 || setregid (-1, real_gid) < 0)
344 fatal ("Failed to drop privileges", 0, 0);
345
346 #ifdef MAIL_USE_SYSTEM_LOCK
347 indesc = open (inname, O_RDWR | O_BINARY);
348 #else
349 indesc = open (inname, O_RDONLY | O_BINARY);
350 #endif
351
352 if (indesc < 0)
353 pfatal_with_name (inname);
354
355
356 umask (umask (0) & 0377);
357
358 outdesc = open (outname, O_WRONLY | O_BINARY | O_CREAT | O_EXCL, 0666);
359 if (outdesc < 0)
360 pfatal_with_name (outname);
361
362 if (setregid (-1, priv_gid) < 0)
363 fatal ("Failed to regain privileges", 0, 0);
364
365
366
367 retry_lock:
368
369
370 #ifdef MAIL_USE_MAILLOCK
371 if (spool_name)
372 {
373
374
375 status = - maillock (spool_name, 1);
376 #ifdef HAVE_TOUCHLOCK
377 touched_lock = time (0);
378 #endif
379 lockcount = 5;
380 }
381 else
382 #endif
383 {
384 #ifdef MAIL_USE_SYSTEM_LOCK
385 #ifdef MAIL_USE_LOCKF
386 status = lockf (indesc, F_LOCK, 0);
387 #else
388 #ifdef WINDOWSNT
389 status = locking (indesc, LK_RLCK, -1L);
390 #else
391 status = flock (indesc, LOCK_EX);
392 #endif
393 #endif
394 #endif
395 }
396
397
398
399 if (status < 0)
400 {
401 if (++lockcount <= 5 && (errno == EAGAIN || errno == EBUSY))
402 {
403 sleep (1);
404 goto retry_lock;
405 }
406
407 pfatal_with_name (inname);
408 }
409
410 {
411 char buf[1024];
412
413 while (true)
414 {
415 nread = read (indesc, buf, sizeof buf);
416 if (nread < 0)
417 pfatal_with_name (inname);
418 if (nread != write (outdesc, buf, nread))
419 {
420 int saved_errno = errno;
421 unlink (outname);
422 errno = saved_errno;
423 pfatal_with_name (outname);
424 }
425 if (nread < sizeof buf)
426 break;
427 #if defined (MAIL_USE_MAILLOCK) && defined (HAVE_TOUCHLOCK)
428 if (spool_name)
429 {
430 time_t now = time (0);
431 if (now - touched_lock > 60)
432 {
433 touchlock ();
434 touched_lock = now;
435 }
436 }
437 #endif
438 }
439 }
440
441 if (fsync (outdesc) != 0 && errno != EINVAL)
442 pfatal_and_delete (outname);
443
444
445 if (setregid (-1, real_gid) < 0)
446 fatal ("Failed to drop privileges", 0, 0);
447
448
449 if (close (outdesc) != 0)
450 pfatal_and_delete (outname);
451
452 #ifdef MAIL_USE_SYSTEM_LOCK
453 if (! preserve_mail)
454 {
455 if (ftruncate (indesc, 0) != 0)
456 pfatal_with_name (inname);
457 }
458 #endif
459
460 close (indesc);
461
462 #ifndef MAIL_USE_SYSTEM_LOCK
463 if (! preserve_mail)
464 {
465
466
467 #ifdef MAIL_UNLINK_SPOOL
468
469
470 if (unlink (inname) < 0 && errno != ENOENT)
471 #endif
472 creat (inname, 0600);
473 }
474 #endif
475
476
477 if (setregid (-1, priv_gid) < 0)
478 fatal ("Failed to regain privileges", 0, 0);
479
480 #ifdef MAIL_USE_MAILLOCK
481
482
483 if (spool_name)
484 mailunlock ();
485 #endif
486 return EXIT_SUCCESS;
487 }
488
489 if (waitpid (child, &wait_status, 0) < 0)
490 fatal ("Error in waitpid; %s", strerror (errno), 0);
491 if (!WIFEXITED (wait_status))
492 return EXIT_FAILURE;
493 else if (WEXITSTATUS (wait_status) != 0)
494 return WEXITSTATUS (wait_status);
495
496 if (lockname)
497 unlink (lockname);
498
499 #endif
500
501 return EXIT_SUCCESS;
502 }
503
504 #ifdef MAIL_USE_MAILLOCK
505
506
507
508
509 static char *
510 mail_spool_name (char *inname)
511 {
512 struct stat stat1, stat2;
513 char *indir, *fname;
514 int status;
515
516 if (! (fname = strrchr (inname, '/')))
517 return NULL;
518
519 fname++;
520
521 if (stat (MAILDIR, &stat1) < 0)
522 return NULL;
523
524 indir = xmalloc (fname - inname + 1);
525 memcpy (indir, inname, fname - inname);
526 indir[fname-inname] = '\0';
527
528
529 status = stat (indir, &stat2);
530
531 free (indir);
532
533 if (status < 0)
534 return NULL;
535
536 if (stat1.st_dev == stat2.st_dev
537 && stat1.st_ino == stat2.st_ino)
538 return fname;
539
540 return NULL;
541 }
542 #endif
543
544
545
546 static void
547 fatal (const char *s1, const char *s2, const char *s3)
548 {
549 if (delete_lockname)
550 unlink (delete_lockname);
551 error (s1, s2, s3);
552 exit (EXIT_FAILURE);
553 }
554
555
556
557
558 static void
559 error (const char *s1, const char *s2, const char *s3)
560 {
561 fprintf (stderr, "movemail: ");
562 if (s3)
563 fprintf (stderr, s1, s2, s3);
564 else if (s2)
565 fprintf (stderr, s1, s2);
566 else
567 fprintf (stderr, "%s", s1);
568 fprintf (stderr, "\n");
569 }
570
571 static void
572 pfatal_with_name (char *name)
573 {
574 fatal ("%s for %s", strerror (errno), name);
575 }
576
577 static void
578 pfatal_and_delete (char *name)
579 {
580 const char *s = strerror (errno);
581 unlink (name);
582 fatal ("%s for %s", s, name);
583 }
584
585
586
587 #ifdef MAIL_USE_POP
588
589 #ifndef WINDOWSNT
590 #include <sys/socket.h>
591 #include <netinet/in.h>
592 #include <netdb.h>
593 #else
594 #undef _WINSOCKAPI_
595 #include <winsock.h>
596 #endif
597 #include <pwd.h>
598 #include <string.h>
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614 static int
615 popmail (char *mailbox, char *outfile, bool preserve, char *password,
616 bool reverse_order)
617 {
618 int nmsgs, nbytes;
619 int i;
620 int mbfi;
621 FILE *mbf;
622 popserver server;
623 int start, end, increment;
624 char *user, *hostname;
625
626 user = mailbox;
627 if ((hostname = strchr (mailbox, ':')))
628 *hostname++ = '\0';
629
630 server = pop_open (hostname, user, password, POP_NO_GETPASS);
631 if (! server)
632 {
633 error ("Error connecting to POP server: %s", pop_error, 0);
634 return EXIT_FAILURE;
635 }
636
637 if (pop_stat (server, &nmsgs, &nbytes))
638 {
639 error ("Error getting message count from POP server: %s", pop_error, 0);
640 return EXIT_FAILURE;
641 }
642
643 if (!nmsgs)
644 {
645 pop_close (server);
646 return EXIT_SUCCESS;
647 }
648
649 mbfi = open (outfile, O_WRONLY | O_BINARY | O_CREAT | O_EXCL, 0666);
650 if (mbfi < 0)
651 {
652 pop_close (server);
653 error ("Error in open: %s, %s", strerror (errno), outfile);
654 return EXIT_FAILURE;
655 }
656
657 if (fchown (mbfi, getuid (), -1) != 0)
658 {
659 int fchown_errno = errno;
660 struct stat st;
661 if (fstat (mbfi, &st) != 0 || st.st_uid != getuid ())
662 {
663 pop_close (server);
664 error ("Error in fchown: %s, %s", strerror (fchown_errno), outfile);
665 return EXIT_FAILURE;
666 }
667 }
668
669 mbf = fdopen (mbfi, "wb");
670 if (!mbf)
671 {
672 pop_close (server);
673 error ("Error in fdopen: %s", strerror (errno), 0);
674 close (mbfi);
675 unlink (outfile);
676 return EXIT_FAILURE;
677 }
678
679 if (reverse_order)
680 {
681 start = nmsgs;
682 end = 1;
683 increment = -1;
684 }
685 else
686 {
687 start = 1;
688 end = nmsgs;
689 increment = 1;
690 }
691
692 for (i = start; i * increment <= end * increment; i += increment)
693 if (! (mbx_delimit_begin (mbf)
694 && pop_retr (server, i, mbf)
695 && mbx_delimit_end (mbf)
696 && fflush (mbf) == 0))
697 {
698 if (errno)
699 error ("Error in POP retrieving: %s", strerror (errno), 0);
700 pop_close (server);
701 fclose (mbf);
702 return EXIT_FAILURE;
703 }
704
705 if (fsync (mbfi) != 0 && errno != EINVAL)
706 {
707 error ("Error in fsync: %s", strerror (errno), 0);
708 fclose (mbf);
709 return EXIT_FAILURE;
710 }
711
712 if (fclose (mbf) != 0)
713 {
714 error ("Error in fclose: %s", strerror (errno), 0);
715 return EXIT_FAILURE;
716 }
717
718 if (! preserve)
719 for (i = 1; i <= nmsgs; i++)
720 {
721 if (pop_delete (server, i))
722 {
723 error ("Error from POP server: %s", pop_error, 0);
724 pop_close (server);
725 return EXIT_FAILURE;
726 }
727 }
728
729 if (pop_quit (server))
730 {
731 error ("Error from POP server: %s", pop_error, 0);
732 return EXIT_FAILURE;
733 }
734
735 return EXIT_SUCCESS;
736 }
737
738 static bool
739 pop_retr (popserver server, int msgno, FILE *arg)
740 {
741 char *line;
742 int ret;
743
744 if (pop_retrieve_first (server, msgno, &line))
745 {
746 error ("Error from POP server: %s", pop_error, 0);
747 errno = 0;
748 return false;
749 }
750
751 while ((ret = pop_retrieve_next (server, &line)) >= 0)
752 {
753 if (! line)
754 break;
755
756 if (! mbx_write (line, ret, arg))
757 {
758 int write_errno = errno;
759 pop_close (server);
760 errno = write_errno;
761 return false;
762 }
763 }
764
765 if (ret)
766 {
767 error ("Error from POP server: %s", pop_error, 0);
768 errno = 0;
769 return false;
770 }
771
772 return true;
773 }
774
775 static bool
776 mbx_write (char *line, int len, FILE *mbf)
777 {
778 #ifdef MOVEMAIL_QUOTE_POP_FROM_LINES
779
780 # define IS_FROM_LINE(a) ((a[0] == 'F') \
781 && (a[1] == 'r') \
782 && (a[2] == 'o') \
783 && (a[3] == 'm') \
784 && (a[4] == ' '))
785 if (IS_FROM_LINE (line))
786 {
787 if (fputc ('>', mbf) < 0)
788 return false;
789 }
790 #endif
791 if (line[0] == '\037')
792 {
793 if (fputs ("^_", mbf) < 0)
794 return false;
795 line++;
796 len--;
797 }
798 return fwrite (line, 1, len, mbf) == len && 0 <= fputc ('\n', mbf);
799 }
800
801 #ifdef WINDOWSNT
802
803
804
805 static size_t
806 movemail_strftime (char *s, size_t size, char const *format,
807 struct tm const *tm)
808 {
809 char fmt[size + 6], *q;
810 const char *p;
811
812 for (p = format, q = &fmt[0]; *p; )
813 {
814 if (*p == '%' && p[1] == 'e')
815 {
816 memcpy (q, "%d", 2);
817 q += 2;
818 p += 2;
819 }
820 else if (*p == '%' && p[1] == 'T')
821 {
822 memcpy (q, "%H:%M:%S", 8);
823 q += 8;
824 p += 2;
825 }
826 else if (*p == '%' && p[1] == '%')
827 {
828 memcpy (q, p, 2);
829 q += 2;
830 p += 2;
831 }
832 else
833 *q++ = *p++;
834 }
835
836 size_t n = strftime (s, size, fmt, tm);
837 char *mday = s + sizeof "From movemail Sun Jan " - 1;
838 if (*mday == '0')
839 *mday = ' ';
840 return n;
841 }
842 # undef strftime
843 # define strftime movemail_strftime
844 #endif
845
846 static bool
847 mbx_delimit_begin (FILE *mbf)
848 {
849 time_t now = time (NULL);
850 struct tm *ltime = localtime (&now);
851 if (!ltime)
852 return false;
853
854 char fromline[100];
855 if (! strftime (fromline, sizeof fromline,
856 "From movemail %a %b %e %T %Y\n", ltime))
857 {
858 errno = EOVERFLOW;
859 return false;
860 }
861 return 0 <= fputs (fromline, mbf);
862 }
863
864 static bool
865 mbx_delimit_end (FILE *mbf)
866 {
867 return 0 <= putc ('\n', mbf);
868 }
869
870 #endif