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