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