Added -proxy option to inc and msgchk
[mmh] / uip / popi.c
1
2 /*
3  * popi.c -- POP initiator for MPOP
4  *
5  * $Id$
6  *
7  * This code is Copyright (c) 2002, by the authors of nmh.  See the
8  * COPYRIGHT file in the root directory of the nmh distribution for
9  * complete copyright information.
10  */
11
12 #include <h/mh.h>
13 #include <h/fmt_scan.h>
14 #include <h/scansbr.h>
15 #include <h/mts.h>
16 #include <errno.h>
17
18 #ifndef RPOP
19 # define RPOPminc(a) (a)
20 #else
21 # define RPOPminc(a)  0
22 #endif
23
24 #ifndef APOP
25 # define APOPminc(a) (a)
26 #else
27 # define APOPminc(a)  0
28 #endif
29
30 #ifndef BPOP
31 # define BPOPminc(a) (a)
32 #else
33 # define BPOPminc(a)  0
34 #endif
35
36 #ifndef SMTPMTS
37 # define BULKminc(a) (a)
38 #else
39 # define BULKminc(a)  0
40 #endif
41
42 static struct swit  switches[] = {
43 #define APOPSW                  0
44     { "apop", APOPminc (-4) },
45 #define NAPOPSW                 1
46     { "noapop", APOPminc (-6) },
47 #define AUTOSW                  2
48     { "auto", BPOPminc(-4) },
49 #define NAUTOSW                 3
50     { "noauto", BPOPminc(-6) },
51 #define BULKSW                  4
52     { "bulk directory", BULKminc(-4) },
53 #define FORMSW                  5
54     { "form formatfile", 0 },
55 #define FMTSW                   6
56     { "format string", 5 },
57 #define HOSTSW                  7
58     { "host host", 0 },
59 #define PROGSW                  8
60     { "mshproc program", 0 },
61 #define RPOPSW                  9
62     { "rpop", RPOPminc (-4) },
63 #define NRPOPSW                10
64     { "norpop", RPOPminc (-6) },
65 #define USERSW                 11
66     { "user user", 0 },
67 #define WIDTHSW                12
68     { "width columns", 0 },
69 #define VERSIONSW              13
70     { "version", 0 },
71 #define HELPSW                 14
72     { "help", 0 },
73     { NULL, 0 }
74 };
75
76 static char *bulksw = NULL;
77 static int snoop = 0;
78 static int width = 0;
79 static char mailname[BUFSIZ];
80 static char *nfs = NULL;
81 static struct msgs *mp;
82
83 extern char response[];
84
85 /*
86  * prototypes
87  */
88 int sc_width (void);  /* from termsbr.c */
89
90
91 int
92 main (int argc, char **argv)
93 {
94     int autosw = 1, noisy = 1, rpop;
95     char *cp, *maildir, *folder = NULL, *form = NULL;
96     char *format = NULL, *host = NULL, *user = NULL;
97     char *pass = NULL, buf[BUFSIZ], **argp;
98     char **arguments;
99     struct stat st;
100
101     invo_name = r1bindex (argv[0], '/');
102
103     /* read user profile/context */
104     context_read();
105
106     mts_init (invo_name);
107     arguments = getarguments (invo_name, argc, argv, 1);
108     argp = arguments;
109
110     if (pophost && *pophost)
111         host = pophost;
112     if ((cp = getenv ("MHPOPDEBUG")) && *cp)
113         snoop++;
114
115     rpop = getuid() && !geteuid();
116
117     while (cp = *argp++) {
118         if (*cp == '-')
119             switch (smatch (++cp, switches)) {
120                 case AMBIGSW: 
121                     ambigsw (cp, switches);
122                     done (1);
123                 case UNKWNSW: 
124                     adios (NULL, "-%s unknown", cp);
125
126                 case HELPSW: 
127                     snprintf (buf, sizeof(buf), "%s [+folder] [switches]",
128                         invo_name);
129                     print_help (buf, switches, 1);
130                     done (1);
131                 case VERSIONSW:
132                     print_version(invo_name);
133                     done (1);
134
135                 case AUTOSW:
136                     autosw = 1;
137                     continue;
138                 case NAUTOSW:
139                     autosw = 0;
140                     continue;
141
142                 case BULKSW: 
143                     if (!(bulksw = *argp++) || *bulksw == '-')
144                         adios (NULL, "missing argument to %s", argp[-2]);
145                     continue;
146
147                 case FORMSW: 
148                     if (!(form = *argp++) || *form == '-')
149                         adios (NULL, "missing argument to %s", argp[-2]);
150                     format = NULL;
151                     continue;
152                 case FMTSW: 
153                     if (!(format = *argp++) || *format == '-')
154                         adios (NULL, "missing argument to %s", argp[-2]);
155                     form = NULL;
156                     continue;
157
158                 case WIDTHSW: 
159                     if (!(cp = *argp++) || *cp == '-')
160                         adios (NULL, "missing argument to %s", argp[-2]);
161                     width = atoi (cp);
162                     continue;
163
164                 case HOSTSW:
165                     if (!(host = *argp++) || *host == '-')
166                         adios (NULL, "missing argument to %s", argp[-2]);
167                     continue;
168                 case USERSW:
169                     if (!(user = *argp++) || *user == '-')
170                         adios (NULL, "missing argument to %s", argp[-2]);
171                     continue;
172
173                 case APOPSW:
174                     rpop = -1;
175                     continue;
176                 case RPOPSW:
177                     rpop = 1;
178                     continue;
179                 case NAPOPSW:
180                 case NRPOPSW:
181                     rpop = 0;
182                     continue;
183
184                 case PROGSW:
185                     if (!(mshproc = *argp++) || *mshproc == '-')
186                         adios (NULL, "missing argument to %s", argp[-2]);
187                     continue;
188             }
189         if (*cp == '+' || *cp == '@') {
190             if (folder)
191                 adios (NULL, "only one folder at a time!");
192             else
193                 folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
194         }
195         else
196             adios (NULL, "usage: %s [+folder] [switches]", invo_name);
197     }
198
199     if (!host)
200         adios (NULL, "usage: %s -host \"host\"", invo_name);
201
202 #ifdef SMTPMTS
203     if (bulksw)
204         do_bulk (host);
205 #endif
206
207     if (user == NULL)
208         user = getusername ();
209     if (rpop > 0)
210         pass = getusername ();
211     else {
212         setuid (getuid ());
213         ruserpass (host, &user, &pass);
214     }
215     snprintf (mailname, sizeof(mailname), "PO box for %s@%s", user, host);
216
217     if (pop_init (host, user, pass, NULL, snoop, rpop) == NOTOK)
218         adios (NULL, "%s", response);
219     if (rpop > 0)
220         setuid (getuid ());
221
222     /* get new format string */
223     nfs = new_fs (form, format, FORMAT);
224
225     if (!context_find ("path"))
226         free (path ("./", TFOLDER));
227     if (!folder)
228         folder = getfolder (0);
229     maildir = m_maildir (folder);
230
231     if (stat (maildir, &st) == NOTOK) {
232         if (errno != ENOENT)
233             adios (maildir, "error on folder");
234         cp = concat ("Create folder \"", maildir, "\"? ", NULL);
235         if (noisy && !getanswer (cp))
236             done (1);
237         free (cp);
238         if (!makedir (maildir))
239             adios (NULL, "unable to create folder %s", maildir);
240     }
241
242     if (chdir (maildir) == NOTOK)
243         adios (maildir, "unable to change directory to");
244
245     if (!(mp = folder_read (folder)))
246         adios (NULL, "unable to read folder %s", folder);
247
248 #ifdef BPOP
249     if (autosw)
250         msh ();
251     else
252 #endif
253
254     popi();
255     pop_quit();
256
257     context_replace (pfolder, folder);  /* update current folder   */
258     seq_setunseen (mp, 0);              /* set the Unseen-Sequence */
259     seq_save (mp);
260     context_save ();                    /* save the context file   */
261     return done (0);
262 }
263
264
265 static struct swit popicmds[] = {
266 #define DELECMD  0
267     "dele", 0,
268 #define LASTCMD  1
269     "last", 0,
270 #define LISTCMD  2
271     "list", 0,
272 #define NOOPCMD  3
273     "noop", 0,
274 #define QUITCMD  4
275     "quit", 0,
276 #define RETRCMD  5
277     "retr", 0,
278 #define RSETCMD  6
279     "rset", 0,
280 #define SCANCMD  7
281     "scan", 0,
282 #define STATCMD  8
283     "stat", 0,
284 #define TOPCMD   9
285     "top", 0,
286 #ifdef  BPOP
287 #define MSHCMD  10
288     "msh", 0,
289 #endif
290
291     NULL, 0
292 };
293
294
295 static void
296 popi (void)
297 {
298     int eof = 0;
299
300     for (;;) {
301         int i;
302         register char *cp;
303         char buffer[BUFSIZ];
304
305         if (eof)
306             return;
307
308         printf ("(%s) ", invo_name);
309         for (cp = buffer; (i = getchar ()) != '\n'; ) {
310             if (i == EOF) {
311                 putchar ('\n');
312                 if (cp == buffer)
313                     return;
314                 eof = 1;
315                 break;
316             }
317
318             if (cp < buffer + sizeof buffer - 2)
319                 *cp++ = i;
320         }
321         *cp = '\0';
322         if (buffer[0] == '\0')
323             continue;
324         if (buffer[0] == '?') {
325             printf ("commands:\n");
326             print_sw (ALL, popicmds, "");
327             printf ("type CTRL-D or use \"quit\" to leave %s\n", invo_name);
328             continue;
329         }
330
331         if (cp = strchr (buffer, ' '))
332             *cp = '\0';
333         switch (i = smatch (buffer, popicmds)) {
334             case AMBIGSW:
335                 ambigsw (buffer, popicmds);
336                 continue;
337             case UNKWNSW:
338                 printf ("%s unknown -- type \"?\" for help\n", buffer);
339                 continue;
340                 
341             case QUITCMD:
342                 return;
343
344             case STATCMD:
345             case DELECMD:
346             case NOOPCMD:
347             case LASTCMD:
348             case RSETCMD:
349             case TOPCMD:
350                 if (cp)
351                     *cp = ' ';
352                 pop_command ("%s%s", popicmds[i].sw, cp ? cp : "");
353                 printf ("%s\n", response);
354                 break;          
355
356             case LISTCMD:
357                 if (cp)
358                     *cp = ' ';
359                 if (pop_command ("%s%s", popicmds[i].sw, cp ? cp : "")
360                         == OK) {
361                     printf ("%s\n", response);
362                     if (!cp)
363                         for (;;) {
364                             switch (pop_multiline ()) {
365                                 case DONE:
366                                     strcpy (response, ".");
367                                     /* and fall... */
368                                 case NOTOK:
369                                     printf ("%s\n", response);
370                                     break;
371
372                                 case OK:
373                                     printf ("%s\n", response);
374                                     continue;
375                              }
376                             break;
377                         }
378                 }
379                 break;
380
381             case RETRCMD:
382                 if (!cp) {
383                     advise (NULL, "missing argument to %s", buffer);
384                     break;
385                 }
386                 retr_action (NULL, OK);
387                 pop_retr (atoi (++cp), retr_action);
388                 retr_action (NULL, DONE);
389                 printf ("%s\n", response);
390                 break;
391
392             case SCANCMD:
393                 {
394                     char   *dp,
395                            *ep,
396                            *fp;
397
398                     if (width == 0)
399                         width = sc_width ();
400
401                     for (dp = nfs, i = 0; *dp; dp++, i++)
402                         if (*dp == '\\' || *dp == '"' || *dp == '\n')
403                             i++;
404                     i++;
405                     if ((ep = malloc ((unsigned) i)) == NULL)
406                         adios (NULL, "out of memory");
407                     for (dp = nfs, fp = ep; *dp; dp++) {
408                         if (*dp == '\n') {
409                             *fp++ = '\\', *fp++ = 'n';
410                             continue;
411                         }
412                         if (*dp == '"' || *dp == '\\')
413                             *fp++ = '\\';
414                         *fp++ = *dp;
415                     }
416                     *fp = '\0';
417
418                     pop_command ("xtnd scan %d \"%s\"", width, ep);
419                     printf ("%s\n", response);
420
421                     free (ep);
422                 }
423                 break;
424
425 #ifdef  BPOP
426             case MSHCMD:
427                 msh ();
428                 break;
429 #endif
430         }
431     }
432 }
433
434
435 static int
436 retr_action (char *rsp, int flag)
437 {
438     static FILE *fp;
439
440     if (rsp == NULL) {
441         static int msgnum;
442         static char *cp;
443
444         if (flag == OK) {
445             if (!(mp = folder_realloc (mp, mp->lowoff, msgnum = mp->hghmsg + 1)))
446                 adios (NULL, "unable to allocate folder storage");
447
448             cp = getcpy (m_name (mp->hghmsg + 1));
449             if ((fp = fopen (cp, "w+")) == NULL)
450                 adios (cp, "unable to write");
451             chmod (cp, m_gmprot ());
452         }
453         else {
454             struct stat st;
455
456             fflush (fp);
457             if (fstat (fileno (fp), &st) != NOTOK && st.st_size > 0) {
458                 clear_msg_flags (mp, msgnum);
459                 set_exists (mp, msgnum);
460                 set_unseen (mp, msgnum);
461                 mp->msgflags |= SEQMOD;
462
463                 if (ferror (fp))
464                     advise (cp, "write error on");
465                 mp->hghmsg = msgnum;
466             }
467             else
468                 unlink (cp);
469
470             fclose (fp), fp = NULL;
471             free (cp), cp = NULL;
472         }
473
474         return;
475     }
476
477     fprintf (fp, "%s\n", rsp);
478 }
479
480
481 #ifdef BPOP
482 static void
483 msh (void)
484 {
485     int child_id, vecp;
486     char buf1[BUFSIZ], buf2[BUFSIZ], *vec[9];
487
488     if (pop_fd (buf1, sizeof(buf1), buf2, sizeof(buf2)) == NOTOK)
489         adios (NULL, "%s", response);
490
491     vecp = 0;
492     vec[vecp++] = r1bindex (mshproc, '/');
493                     
494     switch (child_id = fork ()) {
495         case NOTOK:
496             adios ("fork", "unable to");
497
498         case OK:
499             vec[vecp++] = "-popread";
500             vec[vecp++] = buf1;
501             vec[vecp++] = "-popwrite";
502             vec[vecp++] = buf2;
503             vec[vecp++] = "-idname";
504             vec[vecp++] = mailname;
505             vec[vecp++] = mailname;
506             vec[vecp] = NULL;
507             execvp (mshproc, vec);
508             fprintf (stderr, "unable to exec ");
509             perror (mshproc);
510             _exit (-1);
511
512        default:
513             pidXwait (child_id, mshproc);
514             break;
515    }
516 }
517 #endif
518
519
520 #ifdef SMTPMTS
521 #include <h/mts.h>
522 #include <mts/smtp/smtp.h>
523
524 static int
525 dselect (struct direct *d)
526 {
527     int i;
528
529     if ((i = strlen (d->d_name)) < sizeof "smtp"
530             || strncmp (d->d_name, "smtp", sizeof "smtp" - 1))
531         return 0;
532     return ((i -= (sizeof ".bulk" - 1)) > 0
533                 && !strcmp (d->d_name + i, ".bulk"));
534 }
535
536
537 static int
538 dcompar (struct direct *d1, struct direct *d2)
539 {
540     struct stat s1, s2;
541
542     if (stat ((*d1)->d_name, &s1) == NOTOK)
543         return 1;
544     if (stat ((*d2)->d_name, &s2) == NOTOK)
545         return -1;
546     return ((int) (s1.st_mtime - s2.st_mtime));
547 }
548
549
550 static void
551 do_bulk (char *host)
552 {
553     register int i;
554     int n, retval, sm;
555     struct direct **namelist;
556
557     if (chdir (bulksw) == NOTOK)
558         adios (bulksw, "unable to change directory to");
559
560     if ((n = scandir (".", &namelist, dselect, dcompar)) == NOTOK)
561         adios (bulksw, "unable to scan directory");
562
563     sm = NOTOK;
564     for (i = 0; i < n; i++) {
565         register struct direct *d = namelist[i];
566
567         if (sm == NOTOK) {
568             if (rp_isbad (retval = sm_init (NULL, host, 1, 1, snoop)))
569                 adios (NULL, "problem initializing server: %s",
570                        rp_string (retval));
571             else
572                 sm = OK;
573         }
574
575         switch (retval = sm_bulk (d->d_name)) {
576             default:
577                 if (rp_isbad (retval))
578                     adios (NULL, "problem delivering msg %s: %s",
579                            d->d_name, rp_string (retval));
580                 /* else fall... */
581             case RP_OK:
582             case RP_NO:
583             case RP_NDEL:
584                 advise (NULL, "msg %s: %s", d->d_name, rp_string (retval));
585                 break;
586         }
587     }
588
589     if (sm == OK) {
590         register int j;
591         int     l,
592                 m;
593         struct direct **newlist;
594
595         while ((l = scandir (".", &newlist, dselect, dcompar)) > OK) {
596             m = 0;
597
598             for (j = 0; j < l; j++) {
599                 register struct direct *d = newlist[j];
600
601                 for (i = 0; i < n; i++)
602                     if (strcmp (d->d_name, namelist[i]->d_name) == 0)
603                         break;
604                 if (i >= n) {
605                     switch (retval = sm_bulk (d->d_name)) {
606                         default:
607                             if (rp_isbad (retval))
608                                 adios (NULL, "problem delivering msg %s: %s",
609                                        d->d_name, rp_string (retval));
610                             /* else fall... */
611                         case RP_OK:
612                         case RP_NO:
613                         case RP_NDEL:
614                             advise (NULL, "msg %s: %s", d->d_name,
615                                     rp_string (retval));
616                             break;
617                     }
618
619                     m = 1;
620                 }
621             }
622
623             for (i = 0; i < n; i++)
624                 free ((char *) namelist[i]);
625             free ((char *) namelist);
626             namelist = newlist, n = l;
627
628             if (!m)
629                 break;
630             newlist = NULL;
631         }
632     }
633
634     if (sm == OK && rp_isbad (retval = sm_end (OK)))
635         adios (NULL, "problem finalizing server: %s", rp_string (retval));
636
637     for (i = 0; i < n; i++)
638         free ((char *) namelist[i]);
639     free ((char *) namelist);
640
641     free ((char *) namelist);
642
643     done (0);
644 }
645 #endif