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