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