Add/update copyright notice in all source code files.
[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 int errno;
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, 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     if (stat (maildir, &st) == NOTOK) {
233         if (errno != ENOENT)
234             adios (maildir, "error on folder");
235         cp = concat ("Create folder \"", maildir, "\"? ", NULL);
236         if (noisy && !getanswer (cp))
237             done (1);
238         free (cp);
239         if (!makedir (maildir))
240             adios (NULL, "unable to create folder %s", maildir);
241     }
242
243     if (chdir (maildir) == NOTOK)
244         adios (maildir, "unable to change directory to");
245
246     if (!(mp = folder_read (folder)))
247         adios (NULL, "unable to read folder %s", folder);
248
249 #ifdef BPOP
250     if (autosw)
251         msh ();
252     else
253 #endif
254
255     popi();
256     pop_quit();
257
258     context_replace (pfolder, folder);  /* update current folder   */
259     seq_setunseen (mp, 0);              /* set the Unseen-Sequence */
260     seq_save (mp);
261     context_save ();                    /* save the context file   */
262     return done (0);
263 }
264
265
266 static struct swit popicmds[] = {
267 #define DELECMD  0
268     "dele", 0,
269 #define LASTCMD  1
270     "last", 0,
271 #define LISTCMD  2
272     "list", 0,
273 #define NOOPCMD  3
274     "noop", 0,
275 #define QUITCMD  4
276     "quit", 0,
277 #define RETRCMD  5
278     "retr", 0,
279 #define RSETCMD  6
280     "rset", 0,
281 #define SCANCMD  7
282     "scan", 0,
283 #define STATCMD  8
284     "stat", 0,
285 #define TOPCMD   9
286     "top", 0,
287 #ifdef  BPOP
288 #define MSHCMD  10
289     "msh", 0,
290 #endif
291
292     NULL, 0
293 };
294
295
296 static void
297 popi (void)
298 {
299     int eof = 0;
300
301     for (;;) {
302         int i;
303         register char *cp;
304         char buffer[BUFSIZ];
305
306         if (eof)
307             return;
308
309         printf ("(%s) ", invo_name);
310         for (cp = buffer; (i = getchar ()) != '\n'; ) {
311             if (i == EOF) {
312                 putchar ('\n');
313                 if (cp == buffer)
314                     return;
315                 eof = 1;
316                 break;
317             }
318
319             if (cp < buffer + sizeof buffer - 2)
320                 *cp++ = i;
321         }
322         *cp = '\0';
323         if (buffer[0] == '\0')
324             continue;
325         if (buffer[0] == '?') {
326             printf ("commands:\n");
327             print_sw (ALL, popicmds, "");
328             printf ("type CTRL-D or use \"quit\" to leave %s\n", invo_name);
329             continue;
330         }
331
332         if (cp = strchr (buffer, ' '))
333             *cp = '\0';
334         switch (i = smatch (buffer, popicmds)) {
335             case AMBIGSW:
336                 ambigsw (buffer, popicmds);
337                 continue;
338             case UNKWNSW:
339                 printf ("%s unknown -- type \"?\" for help\n", buffer);
340                 continue;
341                 
342             case QUITCMD:
343                 return;
344
345             case STATCMD:
346             case DELECMD:
347             case NOOPCMD:
348             case LASTCMD:
349             case RSETCMD:
350             case TOPCMD:
351                 if (cp)
352                     *cp = ' ';
353                 pop_command ("%s%s", popicmds[i].sw, cp ? cp : "");
354                 printf ("%s\n", response);
355                 break;          
356
357             case LISTCMD:
358                 if (cp)
359                     *cp = ' ';
360                 if (pop_command ("%s%s", popicmds[i].sw, cp ? cp : "")
361                         == OK) {
362                     printf ("%s\n", response);
363                     if (!cp)
364                         for (;;) {
365                             switch (pop_multiline ()) {
366                                 case DONE:
367                                     strcpy (response, ".");
368                                     /* and fall... */
369                                 case NOTOK:
370                                     printf ("%s\n", response);
371                                     break;
372
373                                 case OK:
374                                     printf ("%s\n", response);
375                                     continue;
376                              }
377                             break;
378                         }
379                 }
380                 break;
381
382             case RETRCMD:
383                 if (!cp) {
384                     advise (NULL, "missing argument to %s", buffer);
385                     break;
386                 }
387                 retr_action (NULL, OK);
388                 pop_retr (atoi (++cp), retr_action);
389                 retr_action (NULL, DONE);
390                 printf ("%s\n", response);
391                 break;
392
393             case SCANCMD:
394                 {
395                     char   *dp,
396                            *ep,
397                            *fp;
398
399                     if (width == 0)
400                         width = sc_width ();
401
402                     for (dp = nfs, i = 0; *dp; dp++, i++)
403                         if (*dp == '\\' || *dp == '"' || *dp == '\n')
404                             i++;
405                     i++;
406                     if ((ep = malloc ((unsigned) i)) == NULL)
407                         adios (NULL, "out of memory");
408                     for (dp = nfs, fp = ep; *dp; dp++) {
409                         if (*dp == '\n') {
410                             *fp++ = '\\', *fp++ = 'n';
411                             continue;
412                         }
413                         if (*dp == '"' || *dp == '\\')
414                             *fp++ = '\\';
415                         *fp++ = *dp;
416                     }
417                     *fp = '\0';
418
419                     pop_command ("xtnd scan %d \"%s\"", width, ep);
420                     printf ("%s\n", response);
421
422                     free (ep);
423                 }
424                 break;
425
426 #ifdef  BPOP
427             case MSHCMD:
428                 msh ();
429                 break;
430 #endif
431         }
432     }
433 }
434
435
436 static int
437 retr_action (char *rsp, int flag)
438 {
439     static FILE *fp;
440
441     if (rsp == NULL) {
442         static int msgnum;
443         static char *cp;
444
445         if (flag == OK) {
446             if (!(mp = folder_realloc (mp, mp->lowoff, msgnum = mp->hghmsg + 1)))
447                 adios (NULL, "unable to allocate folder storage");
448
449             cp = getcpy (m_name (mp->hghmsg + 1));
450             if ((fp = fopen (cp, "w+")) == NULL)
451                 adios (cp, "unable to write");
452             chmod (cp, m_gmprot ());
453         }
454         else {
455             struct stat st;
456
457             fflush (fp);
458             if (fstat (fileno (fp), &st) != NOTOK && st.st_size > 0) {
459                 clear_msg_flags (mp, msgnum);
460                 set_exists (mp, msgnum);
461                 set_unseen (mp, msgnum);
462                 mp->msgflags |= SEQMOD;
463
464                 if (ferror (fp))
465                     advise (cp, "write error on");
466                 mp->hghmsg = msgnum;
467             }
468             else
469                 unlink (cp);
470
471             fclose (fp), fp = NULL;
472             free (cp), cp = NULL;
473         }
474
475         return;
476     }
477
478     fprintf (fp, "%s\n", rsp);
479 }
480
481
482 #ifdef BPOP
483 static void
484 msh (void)
485 {
486     int child_id, vecp;
487     char buf1[BUFSIZ], buf2[BUFSIZ], *vec[9];
488
489     if (pop_fd (buf1, sizeof(buf1), buf2, sizeof(buf2)) == NOTOK)
490         adios (NULL, "%s", response);
491
492     vecp = 0;
493     vec[vecp++] = r1bindex (mshproc, '/');
494                     
495     switch (child_id = fork ()) {
496         case NOTOK:
497             adios ("fork", "unable to");
498
499         case OK:
500             vec[vecp++] = "-popread";
501             vec[vecp++] = buf1;
502             vec[vecp++] = "-popwrite";
503             vec[vecp++] = buf2;
504             vec[vecp++] = "-idname";
505             vec[vecp++] = mailname;
506             vec[vecp++] = mailname;
507             vec[vecp] = NULL;
508             execvp (mshproc, vec);
509             fprintf (stderr, "unable to exec ");
510             perror (mshproc);
511             _exit (-1);
512
513        default:
514             pidXwait (child_id, mshproc);
515             break;
516    }
517 }
518 #endif
519
520
521 #ifdef SMTPMTS
522 #include <h/mts.h>
523 #include <mts/smtp/smtp.h>
524
525 static int
526 dselect (struct direct *d)
527 {
528     int i;
529
530     if ((i = strlen (d->d_name)) < sizeof "smtp"
531             || strncmp (d->d_name, "smtp", sizeof "smtp" - 1))
532         return 0;
533     return ((i -= (sizeof ".bulk" - 1)) > 0
534                 && !strcmp (d->d_name + i, ".bulk"));
535 }
536
537
538 static int
539 dcompar (struct direct *d1, struct direct *d2)
540 {
541     struct stat s1, s2;
542
543     if (stat ((*d1)->d_name, &s1) == NOTOK)
544         return 1;
545     if (stat ((*d2)->d_name, &s2) == NOTOK)
546         return -1;
547     return ((int) (s1.st_mtime - s2.st_mtime));
548 }
549
550
551 static void
552 do_bulk (char *host)
553 {
554     register int i;
555     int n, retval, sm;
556     struct direct **namelist;
557
558     if (chdir (bulksw) == NOTOK)
559         adios (bulksw, "unable to change directory to");
560
561     if ((n = scandir (".", &namelist, dselect, dcompar)) == NOTOK)
562         adios (bulksw, "unable to scan directory");
563
564     sm = NOTOK;
565     for (i = 0; i < n; i++) {
566         register struct direct *d = namelist[i];
567
568         if (sm == NOTOK) {
569             if (rp_isbad (retval = sm_init (NULL, host, 1, 1, snoop)))
570                 adios (NULL, "problem initializing server: %s",
571                        rp_string (retval));
572             else
573                 sm = OK;
574         }
575
576         switch (retval = sm_bulk (d->d_name)) {
577             default:
578                 if (rp_isbad (retval))
579                     adios (NULL, "problem delivering msg %s: %s",
580                            d->d_name, rp_string (retval));
581                 /* else fall... */
582             case RP_OK:
583             case RP_NO:
584             case RP_NDEL:
585                 advise (NULL, "msg %s: %s", d->d_name, rp_string (retval));
586                 break;
587         }
588     }
589
590     if (sm == OK) {
591         register int j;
592         int     l,
593                 m;
594         struct direct **newlist;
595
596         while ((l = scandir (".", &newlist, dselect, dcompar)) > OK) {
597             m = 0;
598
599             for (j = 0; j < l; j++) {
600                 register struct direct *d = newlist[j];
601
602                 for (i = 0; i < n; i++)
603                     if (strcmp (d->d_name, namelist[i]->d_name) == 0)
604                         break;
605                 if (i >= n) {
606                     switch (retval = sm_bulk (d->d_name)) {
607                         default:
608                             if (rp_isbad (retval))
609                                 adios (NULL, "problem delivering msg %s: %s",
610                                        d->d_name, rp_string (retval));
611                             /* else fall... */
612                         case RP_OK:
613                         case RP_NO:
614                         case RP_NDEL:
615                             advise (NULL, "msg %s: %s", d->d_name,
616                                     rp_string (retval));
617                             break;
618                     }
619
620                     m = 1;
621                 }
622             }
623
624             for (i = 0; i < n; i++)
625                 free ((char *) namelist[i]);
626             free ((char *) namelist);
627             namelist = newlist, n = l;
628
629             if (!m)
630                 break;
631             newlist = NULL;
632         }
633     }
634
635     if (sm == OK && rp_isbad (retval = sm_end (OK)))
636         adios (NULL, "problem finalizing server: %s", rp_string (retval));
637
638     for (i = 0; i < n; i++)
639         free ((char *) namelist[i]);
640     free ((char *) namelist);
641
642     free ((char *) namelist);
643
644     done (0);
645 }
646 #endif