]> diplodocus.org Git - nmh/blob - uip/popsbr.c
* I had alphabetized the --configure options in the --help output
[nmh] / uip / popsbr.c
1
2 /*
3 * popsbr.c -- POP client subroutines
4 *
5 * $Id$
6 */
7
8 #include <h/mh.h>
9
10 extern int client(char *args, char *protocol, char *service, int rproto,
11 char *response, int len_response);
12
13 #if defined(NNTP) && !defined(PSHSBR)
14 # undef NNTP
15 #endif
16
17 #ifdef NNTP /* building pshsbr.o from popsbr.c */
18 # include <h/nntp.h>
19 #endif /* NNTP */
20
21 #if !defined(NNTP) && defined(APOP)
22 # include <h/md5.h>
23 #endif
24
25 #include <h/popsbr.h>
26 #include <h/signals.h>
27 #include <signal.h>
28 #include <errno.h>
29
30 #define TRM "."
31 #define TRMLEN (sizeof TRM - 1)
32
33 static int poprint = 0;
34 static int pophack = 0;
35
36 char response[BUFSIZ];
37
38 static FILE *input;
39 static FILE *output;
40
41 #define targ_t char *
42
43 #if !defined(NNTP) && defined(MPOP)
44 # define command pop_command
45 # define multiline pop_multiline
46 #endif
47
48 #ifdef NNTP
49 # ifdef BPOP /* stupid */
50 static int xtnd_last = -1;
51 static int xtnd_first = 0;
52 static char xtnd_name[512]; /* INCREDIBLE HACK!! */
53 # endif
54 #endif /* NNTP */
55
56 /*
57 * static prototypes
58 */
59 #if !defined(NNTP) && defined(APOP)
60 static char *pop_auth (char *, char *);
61 #endif
62
63 #if defined(NNTP) || !defined(MPOP)
64 /* otherwise they are not static functions */
65 static int command(const char *, ...);
66 static int multiline(void);
67 #endif
68
69 static int traverse (int (*)(), const char *, ...);
70 static int vcommand(const char *, va_list);
71 static int getline (char *, int, FILE *);
72 static int putline (char *, FILE *);
73
74
75 #if !defined(NNTP) && defined(APOP)
76 static char *
77 pop_auth (char *user, char *pass)
78 {
79 int len, buflen;
80 char *cp, *lp;
81 unsigned char *dp, *ep, digest[16];
82 MD5_CTX mdContext;
83 static char buffer[BUFSIZ];
84
85 if ((cp = strchr (response, '<')) == NULL
86 || (lp = strchr (cp, '>')) == NULL) {
87 snprintf (buffer, sizeof(buffer), "APOP not available: %s", response);
88 strncpy (response, buffer, sizeof(response));
89 return NULL;
90 }
91
92 *(++lp) = '\0';
93 snprintf (buffer, sizeof(buffer), "%s%s", cp, pass);
94
95 MD5Init (&mdContext);
96 MD5Update (&mdContext, (unsigned char *) buffer,
97 (unsigned int) strlen (buffer));
98 MD5Final (digest, &mdContext);
99
100 cp = buffer;
101 buflen = sizeof(buffer);
102
103 snprintf (cp, buflen, "%s ", user);
104 len = strlen (cp);
105 cp += len;
106 buflen -= len;
107
108 for (ep = (dp = digest) + sizeof(digest) / sizeof(digest[0]); dp < ep; ) {
109 snprintf (cp, buflen, "%02x", *dp++ & 0xff);
110 cp += 2;
111 buflen -= 2;
112 }
113 *cp = '\0';
114
115 return buffer;
116 }
117 #endif /* !NNTP && APOP */
118
119
120 int
121 pop_init (char *host, char *user, char *pass, int snoop, int rpop, int kpop)
122 {
123 int fd1, fd2;
124 char buffer[BUFSIZ];
125
126 #ifdef APOP
127 int apop;
128
129 if ((apop = rpop) < 0)
130 rpop = 0;
131 #endif
132
133 #ifndef NNTP
134 # ifdef KPOP
135 if ( kpop ) {
136 snprintf (buffer, sizeof(buffer), "%s/%s", KPOP_PRINCIPAL, "kpop");
137 if ((fd1 = client (host, "tcp", buffer, 0, response, sizeof(response))) == NOTOK) {
138 return NOTOK;
139 }
140 } else {
141 # endif /* KPOP */
142 if ((fd1 = client (host, "tcp", POPSERVICE, rpop, response, sizeof(response))) == NOTOK) {
143 return NOTOK;
144 }
145 # ifdef KPOP
146 }
147 # endif /* KPOP */
148 #else /* NNTP */
149 if ((fd1 = client (host, "tcp", "nntp", rpop, response, sizeof(response))) == NOTOK)
150 return NOTOK;
151 #endif
152
153 if ((fd2 = dup (fd1)) == NOTOK) {
154 char *s;
155
156 if ((s = strerror(errno)))
157 snprintf (response, sizeof(response),
158 "unable to dup connection descriptor: %s", s);
159 else
160 snprintf (response, sizeof(response),
161 "unable to dup connection descriptor: unknown error");
162 close (fd1);
163 return NOTOK;
164 }
165 #ifndef NNTP
166 if (pop_set (fd1, fd2, snoop) == NOTOK)
167 #else /* NNTP */
168 if (pop_set (fd1, fd2, snoop, (char *)0) == NOTOK)
169 #endif /* NNTP */
170 return NOTOK;
171
172 SIGNAL (SIGPIPE, SIG_IGN);
173
174 switch (getline (response, sizeof response, input)) {
175 case OK:
176 if (poprint)
177 fprintf (stderr, "<--- %s\n", response);
178 #ifndef NNTP
179 if (*response == '+') {
180 # ifndef KPOP
181 # ifdef APOP
182 if (apop < 0) {
183 char *cp = pop_auth (user, pass);
184
185 if (cp && command ("APOP %s", cp) != NOTOK)
186 return OK;
187 }
188 else
189 # endif /* APOP */
190 if (command ("USER %s", user) != NOTOK
191 && command ("%s %s", rpop ? "RPOP" : (pophack++, "PASS"),
192 pass) != NOTOK)
193 return OK;
194 # else /* KPOP */
195 if (command ("USER %s", user) != NOTOK
196 && command ("PASS %s", pass) != NOTOK)
197 return OK;
198 # endif
199 }
200 #else /* NNTP */
201 if (*response < CHAR_ERR) {
202 command ("MODE READER");
203 return OK;
204 }
205 #endif
206 strncpy (buffer, response, sizeof(buffer));
207 command ("QUIT");
208 strncpy (response, buffer, sizeof(response));
209 /* and fall */
210
211 case NOTOK:
212 case DONE:
213 if (poprint)
214 fprintf (stderr, "%s\n", response);
215 fclose (input);
216 fclose (output);
217 return NOTOK;
218 }
219
220 return NOTOK; /* NOTREACHED */
221 }
222
223 #ifdef NNTP
224 int
225 pop_set (int in, int out, int snoop, char *myname)
226 #else
227 int
228 pop_set (int in, int out, int snoop)
229 #endif
230 {
231
232 #ifdef NNTP
233 if (myname && *myname) {
234 /* interface from bbc to msh */
235 strncpy (xtnd_name, myname, sizeof(xtnd_name));
236 }
237 #endif /* NNTP */
238
239 if ((input = fdopen (in, "r")) == NULL
240 || (output = fdopen (out, "w")) == NULL) {
241 strncpy (response, "fdopen failed on connection descriptor", sizeof(response));
242 if (input)
243 fclose (input);
244 else
245 close (in);
246 close (out);
247 return NOTOK;
248 }
249
250 poprint = snoop;
251
252 return OK;
253 }
254
255
256 int
257 pop_fd (char *in, int inlen, char *out, int outlen)
258 {
259 snprintf (in, inlen, "%d", fileno (input));
260 snprintf (out, outlen, "%d", fileno (output));
261 return OK;
262 }
263
264
265 /*
266 * Find out number of messages available
267 * and their total size.
268 */
269
270 int
271 pop_stat (int *nmsgs, int *nbytes)
272 {
273 #ifdef NNTP
274 char **ap;
275 #endif /* NNTP */
276
277 #ifndef NNTP
278 if (command ("STAT") == NOTOK)
279 return NOTOK;
280
281 *nmsgs = *nbytes = 0;
282 sscanf (response, "+OK %d %d", nmsgs, nbytes);
283
284 #else /* NNTP */
285 if (xtnd_last < 0) { /* in msh, xtnd_name is set from myname */
286 if (command("GROUP %s", xtnd_name) == NOTOK)
287 return NOTOK;
288
289 ap = brkstring (response, " ", "\n"); /* "211 nart first last ggg" */
290 xtnd_first = atoi (ap[2]);
291 xtnd_last = atoi (ap[3]);
292 }
293
294 /* nmsgs is not the real nart, but an incredible simuation */
295 if (xtnd_last > 0)
296 *nmsgs = xtnd_last - xtnd_first + 1; /* because of holes... */
297 else
298 *nmsgs = 0;
299 *nbytes = xtnd_first; /* for subtracting offset in msh() */
300 #endif /* NNTP */
301
302 return OK;
303 }
304
305 #ifdef NNTP
306 int
307 pop_exists (int (*action)())
308 {
309 #ifdef XMSGS /* hacked into NNTP 1.5 */
310 if (traverse (action, "XMSGS %d-%d", (targ_t) xtnd_first, (targ_t) xtnd_last) == OK)
311 return OK;
312 #endif
313 /* provided by INN 1.4 */
314 if (traverse (action, "LISTGROUP") == OK)
315 return OK;
316 return traverse (action, "XHDR NONAME %d-%d", (targ_t) xtnd_first, (targ_t) xtnd_last);
317 }
318 #endif /* NNTP */
319
320
321 #ifdef BPOP
322 int
323 pop_list (int msgno, int *nmsgs, int *msgs, int *bytes, int *ids)
324 #else
325 int
326 pop_list (int msgno, int *nmsgs, int *msgs, int *bytes)
327 #endif
328 {
329 int i;
330 #ifndef BPOP
331 int *ids = NULL;
332 #endif
333
334 if (msgno) {
335 #ifndef NNTP
336 if (command ("LIST %d", msgno) == NOTOK)
337 return NOTOK;
338 *msgs = *bytes = 0;
339 if (ids) {
340 *ids = 0;
341 sscanf (response, "+OK %d %d %d", msgs, bytes, ids);
342 }
343 else
344 sscanf (response, "+OK %d %d", msgs, bytes);
345 #else /* NNTP */
346 *msgs = *bytes = 0;
347 if (command ("STAT %d", msgno) == NOTOK)
348 return NOTOK;
349 if (ids) {
350 *ids = msgno;
351 }
352 #endif /* NNTP */
353 return OK;
354 }
355
356 #ifndef NNTP
357 if (command ("LIST") == NOTOK)
358 return NOTOK;
359
360 for (i = 0; i < *nmsgs; i++)
361 switch (multiline ()) {
362 case NOTOK:
363 return NOTOK;
364 case DONE:
365 *nmsgs = ++i;
366 return OK;
367 case OK:
368 *msgs = *bytes = 0;
369 if (ids) {
370 *ids = 0;
371 sscanf (response, "%d %d %d",
372 msgs++, bytes++, ids++);
373 }
374 else
375 sscanf (response, "%d %d", msgs++, bytes++);
376 break;
377 }
378 for (;;)
379 switch (multiline ()) {
380 case NOTOK:
381 return NOTOK;
382 case DONE:
383 return OK;
384 case OK:
385 break;
386 }
387 #else /* NNTP */
388 return NOTOK;
389 #endif /* NNTP */
390 }
391
392
393 int
394 pop_retr (int msgno, int (*action)())
395 {
396 #ifndef NNTP
397 return traverse (action, "RETR %d", (targ_t) msgno);
398 #else /* NNTP */
399 return traverse (action, "ARTICLE %d", (targ_t) msgno);
400 #endif /* NNTP */
401 }
402
403
404 static int
405 traverse (int (*action)(), const char *fmt, ...)
406 {
407 int result;
408 va_list ap;
409 char buffer[sizeof(response)];
410
411 va_start(ap, fmt);
412 result = vcommand (fmt, ap);
413 va_end(ap);
414
415 if (result == NOTOK)
416 return NOTOK;
417 strncpy (buffer, response, sizeof(buffer));
418
419 for (;;)
420 switch (multiline ()) {
421 case NOTOK:
422 return NOTOK;
423
424 case DONE:
425 strncpy (response, buffer, sizeof(response));
426 return OK;
427
428 case OK:
429 (*action) (response);
430 break;
431 }
432 }
433
434
435 int
436 pop_dele (int msgno)
437 {
438 return command ("DELE %d", msgno);
439 }
440
441
442 int
443 pop_noop (void)
444 {
445 return command ("NOOP");
446 }
447
448
449 #if defined(MPOP) && !defined(NNTP)
450 int
451 pop_last (void)
452 {
453 return command ("LAST");
454 }
455 #endif
456
457
458 int
459 pop_rset (void)
460 {
461 return command ("RSET");
462 }
463
464
465 int
466 pop_top (int msgno, int lines, int (*action)())
467 {
468 #ifndef NNTP
469 return traverse (action, "TOP %d %d", (targ_t) msgno, (targ_t) lines);
470 #else /* NNTP */
471 return traverse (action, "HEAD %d", (targ_t) msgno);
472 #endif /* NNTP */
473 }
474
475
476 #ifdef BPOP
477 int
478 pop_xtnd (int (*action)(), char *fmt, ...)
479 {
480 int result;
481 va_list ap;
482 char buffer[BUFSIZ];
483
484 #ifdef NNTP
485 char **ap;
486 #endif
487
488 va_start(ap, fmt);
489 #ifndef NNTP
490 /* needs to be fixed... va_end needs to be added */
491 snprintf (buffer, sizeof(buffer), "XTND %s", fmt);
492 result = traverse (action, buffer, a, b, c, d);
493 va_end(ap);
494 return result;
495 #else /* NNTP */
496 snprintf (buffer, sizeof(buffer), fmt, a, b, c, d);
497 ap = brkstring (buffer, " ", "\n"); /* a hack, i know... */
498
499 if (!strcasecmp(ap[0], "x-bboards")) { /* XTND "X-BBOARDS group */
500 /* most of these parameters are meaningless under NNTP.
501 * bbc.c was modified to set AKA and LEADERS as appropriate,
502 * the rest are left blank.
503 */
504 return OK;
505 }
506 if (!strcasecmp (ap[0], "archive") && ap[1]) {
507 snprintf (xtnd_name, sizeof(xtnd_name), "%s", ap[1]); /* save the name */
508 xtnd_last = 0;
509 xtnd_first = 1; /* setup to fail in pop_stat */
510 return OK;
511 }
512 if (!strcasecmp (ap[0], "bboards")) {
513
514 if (ap[1]) { /* XTND "BBOARDS group" */
515 snprintf (xtnd_name, sizeof(xtnd_name), "%s", ap[1]); /* save the name */
516 if (command("GROUP %s", xtnd_name) == NOTOK)
517 return NOTOK;
518
519 /* action must ignore extra args */
520 strncpy (buffer, response, sizeof(buffer));
521 ap = brkstring (response, " ", "\n");/* "211 nart first last g" */
522 xtnd_first = atoi (ap[2]);
523 xtnd_last = atoi (ap[3]);
524
525 (*action) (buffer);
526 return OK;
527
528 } else { /* XTND "BBOARDS" */
529 return traverse (action, "LIST", a, b, c, d);
530 }
531 }
532 return NOTOK; /* unknown XTND command */
533 #endif /* NNTP */
534 }
535 #endif BPOP
536
537
538 int
539 pop_quit (void)
540 {
541 int i;
542
543 i = command ("QUIT");
544 pop_done ();
545
546 return i;
547 }
548
549
550 int
551 pop_done (void)
552 {
553 fclose (input);
554 fclose (output);
555
556 return OK;
557 }
558
559
560 #if !defined(MPOP) || defined(NNTP)
561 static
562 #endif
563 int
564 command(const char *fmt, ...)
565 {
566 va_list ap;
567 int result;
568
569 va_start(ap, fmt);
570 result = vcommand(fmt, ap);
571 va_end(ap);
572
573 return result;
574 }
575
576
577 static int
578 vcommand (const char *fmt, va_list ap)
579 {
580 char *cp, buffer[BUFSIZ];
581
582 vsnprintf (buffer, sizeof(buffer), fmt, ap);
583 if (poprint) {
584 if (pophack) {
585 if ((cp = strchr (buffer, ' ')))
586 *cp = 0;
587 fprintf (stderr, "---> %s ********\n", buffer);
588 if (cp)
589 *cp = ' ';
590 pophack = 0;
591 }
592 else
593 fprintf (stderr, "---> %s\n", buffer);
594 }
595
596 if (putline (buffer, output) == NOTOK)
597 return NOTOK;
598
599 switch (getline (response, sizeof response, input)) {
600 case OK:
601 if (poprint)
602 fprintf (stderr, "<--- %s\n", response);
603 #ifndef NNTP
604 return (*response == '+' ? OK : NOTOK);
605 #else /* NNTP */
606 return (*response < CHAR_ERR ? OK : NOTOK);
607 #endif /* NNTP */
608
609 case NOTOK:
610 case DONE:
611 if (poprint)
612 fprintf (stderr, "%s\n", response);
613 return NOTOK;
614 }
615
616 return NOTOK; /* NOTREACHED */
617 }
618
619
620 #if defined(MPOP) && !defined(NNTP)
621 int
622 multiline (void)
623 #else
624 static int
625 multiline (void)
626 #endif
627 {
628 char buffer[BUFSIZ + TRMLEN];
629
630 if (getline (buffer, sizeof buffer, input) != OK)
631 return NOTOK;
632 #ifdef DEBUG
633 if (poprint)
634 fprintf (stderr, "<--- %s\n", response);
635 #endif DEBUG
636 if (strncmp (buffer, TRM, TRMLEN) == 0) {
637 if (buffer[TRMLEN] == 0)
638 return DONE;
639 else
640 strncpy (response, buffer + TRMLEN, sizeof(response));
641 }
642 else
643 strncpy (response, buffer, sizeof(response));
644
645 return OK;
646 }
647
648
649 static int
650 getline (char *s, int n, FILE *iop)
651 {
652 int c;
653 char *p;
654
655 p = s;
656 while (--n > 0 && (c = fgetc (iop)) != EOF)
657 if ((*p++ = c) == '\n')
658 break;
659 if (ferror (iop) && c != EOF) {
660 strncpy (response, "error on connection", sizeof(response));
661 return NOTOK;
662 }
663 if (c == EOF && p == s) {
664 strncpy (response, "connection closed by foreign host", sizeof(response));
665 return DONE;
666 }
667 *p = 0;
668 if (*--p == '\n')
669 *p = 0;
670 if (*--p == '\r')
671 *p = 0;
672
673 return OK;
674 }
675
676
677 static int
678 putline (char *s, FILE *iop)
679 {
680 fprintf (iop, "%s\r\n", s);
681 fflush (iop);
682 if (ferror (iop)) {
683 strncpy (response, "lost connection", sizeof(response));
684 return NOTOK;
685 }
686
687 return OK;
688 }