Put in a prototype for client() to get rid of the "default prototype" warning.
[mmh] / 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 }