source: trunk/third/nmh/uip/popsbr.c @ 19067

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