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