cosmetic fixes from Rudi
[sxemacs] / lib-src / movemail.c
1 /* movemail foo bar -- move file foo to file bar,
2    locking file foo.
3    Copyright (C) 1986, 1992, 1993, 1994, 1996 Free Software Foundation, Inc.
4
5    Copyright (C) 2005 Johann "Myrkraverk" Oskarsson <johann@myrkraverk.com>
6
7 This file is part of SXEmacs.
8
9 SXEmacs is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 SXEmacs is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
22  Please mail bugs and suggestions to the SXEmacs maintainer.
23 */
24
25 /* Important notice:
26  *
27  *  You *must* coordinate the locking method used by movemail with that
28  *  used by your mail delivery agent, as well as that of the other mail
29  *  user agents on your system.  movemail allows you to do this at run
30  *  time via the -m flag.  Moreover, it uses a default determined by
31  *  the MAIL_LOCK_DOT, MAIL_LOCK_LOCKF, MAIL_LOCK_FLOCK,
32  *  MAIL_LOCK_LOCKING, and MAIL_LOCK_MMDF preprocessor settings.
33  */
34
35 /*
36  * Mike Sperber <sperber@informatik.uni-tuebingen.de> reorganized
37  * everything that has to with locking in December 1999.
38  */
39
40 /*
41  * Modified January, 1986 by Michael R. Gretzinger (Project Athena)
42  *
43  * Added POP (Post Office Protocol) service.  When compiled
44  * -DMAIL_USE_POP movemail will accept input filename arguments of the
45  * form "po:username".  This will cause movemail to open a connection
46  * to a pop server running on $MAILHOST (environment variable).
47  * Movemail must be setuid to root in order to work with POP.
48  *
49  * New module: popmail.c
50  * Modified routines:
51  *      main - added code within #ifdef MAIL_USE_POP; added setuid
52  *             (getuid ()) after POP code.
53  * New routines in movemail.c:
54  *      get_errmsg - return pointer to system error message
55  *
56  * Modified August, 1993 by Jonathan Kamens (OpenVision Technologies)
57  *
58  * Move all of the POP code into a separate file, "pop.c".
59  * Use strerror instead of get_errmsg.
60  *
61  */
62
63 #define NO_SHORTNAMES           /* Tell config not to load remap.h */
64 #define DONT_ENCAPSULATE
65 #include <config.h>
66 #include <sys/types.h>
67 #include <sys/stat.h>
68 #include <stdio.h>
69 #include <errno.h>
70 #include "../src/sysfile.h"
71 #include "../src/syswait.h"
72 #include "../src/systime.h"
73 #include <stdlib.h>
74 #include <string.h>
75 #ifdef HAVE_GETOPT_H
76 #include <getopt.h>
77 #endif
78 #ifdef MAIL_USE_POP
79 #include "pop.h"
80 #include "../src/regex.h"
81 #endif
82
83 #include <assert.h>
84
85 extern char *optarg;
86 extern int optind, opterr;
87
88 #ifndef HAVE_STRERROR
89 char *strerror(int errnum);
90 #endif                          /* HAVE_STRERROR */
91
92 #ifndef DIRECTORY_SEP
93 #define DIRECTORY_SEP '/'
94 #endif
95 #ifndef IS_DIRECTORY_SEP
96 #define IS_DIRECTORY_SEP(_c_) ((_c_) == DIRECTORY_SEP)
97 #endif
98
99 #if defined (HAVE_UNISTD_H)
100 #include <unistd.h>
101 #endif                          /* unistd.h */
102 #ifndef F_OK
103 #define F_OK 0
104 #define X_OK 1
105 #define W_OK 2
106 #define R_OK 4
107 #endif                          /* No F_OK */
108
109 #if defined (HAVE_FCNTL_H)
110 #include <fcntl.h>
111 #endif                          /* fcntl.h */
112
113 #ifdef HAVE_LOCKING
114 #include <sys/locking.h>
115 #endif
116
117 #ifdef HAVE_MMDF
118 extern int lk_open(), lk_close();
119 #endif
120
121 /* Cancel substitutions made by config.h for Emacs.  */
122 #undef open
123 #undef read
124 #undef write
125 #undef close
126
127 static void fatal(char *, char *);
128 static void error(const char *, const char *, const char *);
129 static void usage(int);
130 static void pfatal_with_name(char *);
131 static void pfatal_and_delete(char *);
132 static char *concat(char *, char *, char *);
133 static long *xmalloc(unsigned int);
134 #ifdef MAIL_USE_POP
135 static int popmail(char *, char *, char *);
136 static int pop_retr(popserver server, int msgno,
137                     int (*action) (char *, FILE *), FILE * arg);
138 static int mbx_write(char *, FILE *);
139 static int mbx_delimit_begin(FILE *);
140 static int mbx_delimit_end(FILE *);
141 static struct re_pattern_buffer *compile_regex(char *regexp_pattern);
142 static int pop_search_top(popserver server, int msgno, int lines,
143                           struct re_pattern_buffer *regexp);
144 #endif
145
146
147 #define xstrncpy(d_,s_,l_)                      \
148         do {                                    \
149                 char* dst_=d_;                  \
150                 dst_[0]='\0';                   \
151                 strncat((dst_),(s_),(l_)-1);    \
152         } while(0)
153
154 int verbose = 0;
155 #ifdef MAIL_USE_POP
156 int reverse = 0;
157 int keep_messages = 0;
158 struct re_pattern_buffer *regexp_pattern = 0;
159 int match_lines = 10;
160 #endif
161
162 #define VERBOSE(x) if (verbose) { printf x; fflush(stdout); }
163
164 #ifdef HAVE_GETOPT_LONG
165 struct option longopts[] = {
166         {"inbox", required_argument, NULL, 'i'},
167         {"outfile", required_argument, NULL, 'o'},
168 #ifdef MAIL_USE_POP
169         {"password", required_argument, NULL, 'p'},
170         {"reverse-pop-order", no_argument, NULL, 'x'},
171         {"keep-messages", no_argument, NULL, 'k'},
172         {"regex", required_argument, NULL, 'r'},
173         {"match-lines", required_argument, NULL, 'l'},
174 #endif
175         {"lock-method", required_argument, NULL, 'm'},
176         {"help", no_argument, NULL, 'h'},
177         {"verbose", no_argument, NULL, 'v'},
178         {0}
179 };
180 #endif
181
182 #define DOTLOCKING      0
183 #define FLOCKING        1
184 #define LOCKFING        2
185 #define MMDF            3
186 #define LOCKING         4
187
188 #if defined(MAIL_LOCK_FLOCK) && defined(HAVE_FLOCK)
189 #define DEFAULT_LOCKING FLOCKING
190 #elif defined(MAIL_LOCK_LOCKF) && defined(HAVE_LOCKF)
191 #define DEFAULT_LOCKING LOCKFING
192 #elif defined(MAIL_LOCK_MMDF) && defined(HAVE_MMDF)
193 #define DEFAULT_LOCKING MMDF
194 #elif defined(MAIL_LOCK_LOCKING) && defined(HAVE_LOCKING)
195 #define DEFAULT_LOCKING LOCKING
196 #else
197 #define DEFAULT_LOCKING DOTLOCKING
198 #endif
199
200 #ifndef DISABLE_DIRECT_ACCESS
201 static void lock_dot(char *);
202 #endif
203 static void unlock_dot(char *);
204 static int parse_lock_method(char *);
205 static char *unparse_lock_method(int);
206
207 int main(int argc, char *argv[])
208 {
209         char *inname = 0, *outname = 0;
210 #if defined MAIL_USE_POP
211         char *poppass = 0;
212 #endif  /* MAIL_USE_POP */
213 #ifndef DISABLE_DIRECT_ACCESS
214         int indesc, outdesc;
215         int nread;
216         int status;
217 #endif
218
219         int lock_method = DEFAULT_LOCKING;
220
221         char *maybe_lock_env;
222
223         maybe_lock_env = getenv("EMACSLOCKMETHOD");
224         if (maybe_lock_env) {
225                 printf("maybe-lock_env: %s\n", maybe_lock_env);
226                 lock_method = parse_lock_method(maybe_lock_env);
227         }
228
229         for (;;) {
230 #ifdef MAIL_USE_POP
231                 char *optstring = "i:o:m:p:l:r:xvhk";
232 #else
233                 char *optstring = "i:o:m:vh";
234 #endif
235
236 #if HAVE_GETOPT_LONG
237                 int opt = getopt_long(argc, argv, optstring, longopts, 0);
238 #elif HAVE_GETOPT
239                 int opt = getopt(argc, argv, optstring);
240 #else
241 # error         "movemail cannot be built without getopt, preferably getopt_long"
242 #endif
243                 if (opt == EOF)
244                         break;
245
246                 switch (opt) {
247                 case 0:
248                         break;
249                 case 1: /* one of the standard arguments seen */
250                         if (!inname) {
251                                 inname = optarg;
252                         } else if (!outname) {
253                                 outname = optarg;
254 #if defined MAIL_USE_POP
255                         } else {
256                                 poppass = optarg;
257 #endif  /* MAIL_USE_POP */
258                         }
259                         break;
260
261                 case 'i':       /* infile */
262                         inname = optarg;
263                         break;
264
265                 case 'o':       /* outfile */
266                         outname = optarg;
267                         break;
268 #ifdef MAIL_USE_POP
269                 case 'p':       /* pop password */
270                         poppass = optarg;
271                         break;
272                 case 'k':
273                         keep_messages = 1;
274                         break;
275                 case 'x':
276                         reverse = 1;
277                         break;
278                 case 'l':       /* lines to match */
279                         match_lines = atoi(optarg);
280                         break;
281
282                 case 'r':       /* regular expression */
283                         regexp_pattern = compile_regex(optarg);
284                         break;
285 #endif
286
287                 case 'm':
288                         lock_method = parse_lock_method(optarg);
289                         break;
290                 case 'h':
291                         usage(lock_method);
292                         exit(0);
293                 case 'v':
294                         verbose = 1;
295                         break;
296                 default:
297                         break;
298                 }
299         }
300
301         while (optind < argc) {
302                 assert(argv[optind] != NULL);
303                 if (!inname) {
304                         inname = argv[optind];
305                 } else if (!outname) {
306                         outname = argv[optind];
307 #if defined MAIL_USE_POP
308                 } else {
309                         poppass = argv[optind];
310 #endif  /* MAIL_USE_POP */
311                 }
312                 optind++;
313         }
314
315         if (!inname || !outname) {
316                 usage(lock_method);
317                 exit(1);
318         }
319 #ifdef HAVE_MMDF
320         if (lock_method == MMDF)
321                 mmdf_init(argv[0]);
322 #endif
323
324         if (*outname == 0)
325                 fatal("Destination file name is empty", 0);
326
327         /* Also check that outname's directory is writable to the real uid.  */
328         {
329                 char *buf = (char *)xmalloc(strlen(outname) + 1);
330                 char *cp;
331                 strcpy(buf, outname);
332                 cp = buf + strlen(buf);
333                 while (cp > buf && !IS_DIRECTORY_SEP(cp[-1]))
334                         *--cp = 0;
335                 if (cp == buf)
336                         *cp++ = '.';
337                 if (access(buf, W_OK) != 0)
338                         pfatal_with_name(buf);
339                 free(buf);
340         }
341
342 #ifdef MAIL_USE_POP
343         if (!strncmp(inname, "po:", 3)) {
344                 int retcode = popmail(inname + 3, outname, poppass);
345                 exit(retcode);
346         }
347         setuid(getuid());
348 #endif                          /* MAIL_USE_POP */
349
350 #ifndef DISABLE_DIRECT_ACCESS
351
352
353         if (fork() == 0) {
354                 setuid(getuid());
355
356                 VERBOSE(("opening input file\n"));
357
358                 switch (lock_method) {
359                 case DOTLOCKING:
360                         indesc = open(inname, O_RDONLY);
361                         break;
362 #ifdef HAVE_LOCKF
363                 case LOCKFING:
364                         indesc = open(inname, O_RDWR);
365                         break;
366 #endif
367 #ifdef HAVE_FLOCK
368                 case FLOCKING:
369                         indesc = open(inname, O_RDWR);
370                         break;
371 #endif
372 #ifdef HAVE_LOCKING
373                 case LOCKING:
374                         indesc = open(inname, O_RDWR);
375                         break;
376 #endif
377 #ifdef HAVE_MMDF
378                 case MMDF:
379                         indesc = lk_open(inname, O_RDONLY, 0, 0, 10);
380                         break;
381 #endif
382                 default:
383                         abort();
384                 }
385
386                 if (indesc < 0)
387                         pfatal_with_name(inname);
388
389 #ifdef HAVE_UMASK
390                 /* In case movemail is setuid to root, make sure the user can
391                    read the output file.  */
392                 umask(umask(0) & 0333);
393 #endif
394
395                 outdesc = open(outname, O_WRONLY | O_CREAT | O_EXCL, 0666);
396                 if (outdesc < 0)
397                         pfatal_with_name(outname);
398
399                 VERBOSE(("locking input file\n"));
400
401                 switch (lock_method) {
402 #ifdef HAVE_LOCKF
403                 case LOCKFING:
404                         if (lockf(indesc, F_LOCK, 0) < 0)
405                                 pfatal_with_name(inname);
406