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