Eliminated compilation warnings.
[mmh] / 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
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 }