]> diplodocus.org Git - nmh/blob - uip/popi.c
Fix stupid accidental dependence on a bash quirk in previous
[nmh] / 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