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