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