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