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