push.c revision 55682
155682Smarkm/*
255682Smarkm * Copyright (c) 1997-1999 Kungliga Tekniska H�gskolan
355682Smarkm * (Royal Institute of Technology, Stockholm, Sweden).
455682Smarkm * All rights reserved.
555682Smarkm *
655682Smarkm * Redistribution and use in source and binary forms, with or without
755682Smarkm * modification, are permitted provided that the following conditions
855682Smarkm * are met:
955682Smarkm *
1055682Smarkm * 1. Redistributions of source code must retain the above copyright
1155682Smarkm *    notice, this list of conditions and the following disclaimer.
1255682Smarkm *
1355682Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1455682Smarkm *    notice, this list of conditions and the following disclaimer in the
1555682Smarkm *    documentation and/or other materials provided with the distribution.
1655682Smarkm *
1755682Smarkm * 3. Neither the name of the Institute nor the names of its contributors
1855682Smarkm *    may be used to endorse or promote products derived from this software
1955682Smarkm *    without specific prior written permission.
2055682Smarkm *
2155682Smarkm * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
2255682Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2355682Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2455682Smarkm * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
2555682Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2655682Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2755682Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2855682Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2955682Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3055682Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3155682Smarkm * SUCH DAMAGE.
3255682Smarkm */
3355682Smarkm
3455682Smarkm#include "push_locl.h"
3555682SmarkmRCSID("$Id: push.c,v 1.38 1999/12/28 03:46:06 assar Exp $");
3655682Smarkm
3755682Smarkm#ifdef KRB4
3855682Smarkmstatic int use_v4 = -1;
3955682Smarkm#endif
4055682Smarkm
4155682Smarkm#ifdef KRB5
4255682Smarkmstatic int use_v5 = -1;
4355682Smarkmstatic krb5_context context;
4455682Smarkm#endif
4555682Smarkm
4655682Smarkmstatic char *port_str;
4755682Smarkmstatic int verbose_level;
4855682Smarkmstatic int do_fork;
4955682Smarkmstatic int do_leave;
5055682Smarkmstatic int do_version;
5155682Smarkmstatic int do_help;
5255682Smarkmstatic int do_from;
5355682Smarkmstatic int do_count;
5455682Smarkmstatic char *header_str;
5555682Smarkm
5655682Smarkmstruct getargs args[] = {
5755682Smarkm#ifdef KRB4
5855682Smarkm    { "krb4",	'4', arg_flag,		&use_v4,	"Use Kerberos V4",
5955682Smarkm      NULL },
6055682Smarkm#endif
6155682Smarkm#ifdef KRB5
6255682Smarkm    { "krb5",	'5', arg_flag,		&use_v5,	"Use Kerberos V5",
6355682Smarkm      NULL },
6455682Smarkm#endif
6555682Smarkm    { "verbose",'v', arg_counter,	&verbose_level,	"Verbose",
6655682Smarkm      NULL },
6755682Smarkm    { "fork",	'f', arg_flag,		&do_fork,	"Fork deleting proc",
6855682Smarkm      NULL },
6955682Smarkm    { "leave",	'l', arg_flag,		&do_leave,	"Leave mail on server",
7055682Smarkm      NULL },
7155682Smarkm    { "port",	'p', arg_string,	&port_str,	"Use this port",
7255682Smarkm      "number-or-service" },
7355682Smarkm    { "from",	 0,  arg_flag,		&do_from,	"Behave like from",
7455682Smarkm      NULL },
7555682Smarkm    { "header",	 0,  arg_string,	&header_str,	"Header string to print", NULL },
7655682Smarkm    { "count", 'c',  arg_flag,		&do_count,	"Print number of messages", NULL},
7755682Smarkm    { "version", 0,  arg_flag,		&do_version,	"Print version",
7855682Smarkm      NULL },
7955682Smarkm    { "help",	 0,  arg_flag,		&do_help,	NULL,
8055682Smarkm      NULL }
8155682Smarkm
8255682Smarkm};
8355682Smarkm
8455682Smarkmstatic void
8555682Smarkmusage (int ret)
8655682Smarkm{
8755682Smarkm    arg_printusage (args,
8855682Smarkm		    sizeof(args) / sizeof(args[0]),
8955682Smarkm		    NULL,
9055682Smarkm		    "[[{po:username[@hostname] | hostname[:username]}] ...]"
9155682Smarkm		    "filename");
9255682Smarkm    exit (ret);
9355682Smarkm}
9455682Smarkm
9555682Smarkmstatic int
9655682Smarkmdo_connect (const char *hostname, int port, int nodelay)
9755682Smarkm{
9855682Smarkm    struct addrinfo *ai, *a;
9955682Smarkm    struct addrinfo hints;
10055682Smarkm    int error;
10155682Smarkm    int s;
10255682Smarkm    char portstr[NI_MAXSERV];
10355682Smarkm
10455682Smarkm    memset (&hints, 0, sizeof(hints));
10555682Smarkm    hints.ai_socktype = SOCK_STREAM;
10655682Smarkm    hints.ai_protocol = IPPROTO_TCP;
10755682Smarkm
10855682Smarkm    snprintf (portstr, sizeof(portstr), "%u", ntohs(port));
10955682Smarkm
11055682Smarkm    error = getaddrinfo (hostname, portstr, &hints, &ai);
11155682Smarkm    if (error)
11255682Smarkm	errx (1, "getaddrinfo(%s): %s", hostname, gai_strerror(error));
11355682Smarkm
11455682Smarkm    for (a = ai; a != NULL; a = a->ai_next) {
11555682Smarkm	s = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
11655682Smarkm	if (s < 0)
11755682Smarkm	    continue;
11855682Smarkm	if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
11955682Smarkm	    warn ("connect(%s)", hostname);
12055682Smarkm 	    close (s);
12155682Smarkm 	    continue;
12255682Smarkm	}
12355682Smarkm	break;
12455682Smarkm    }
12555682Smarkm    freeaddrinfo (ai);
12655682Smarkm    if (a == NULL) {
12755682Smarkm	warnx ("failed to contact %s", hostname);
12855682Smarkm	return -1;
12955682Smarkm    }
13055682Smarkm
13155682Smarkm    if(setsockopt(s, IPPROTO_TCP, TCP_NODELAY,
13255682Smarkm		  (void *)&nodelay, sizeof(nodelay)) < 0)
13355682Smarkm	err (1, "setsockopt TCP_NODELAY");
13455682Smarkm    return s;
13555682Smarkm}
13655682Smarkm
13755682Smarkmtypedef enum { INIT = 0, GREET, USER, PASS, STAT, RETR, TOP,
13855682Smarkm	       DELE, XDELE, QUIT} pop_state;
13955682Smarkm
14055682Smarkm#define PUSH_BUFSIZ 65536
14155682Smarkm
14255682Smarkm#define STEP 16
14355682Smarkm
14455682Smarkmstruct write_state {
14555682Smarkm    struct iovec *iovecs;
14655682Smarkm    size_t niovecs, maxiovecs, allociovecs;
14755682Smarkm    int fd;
14855682Smarkm};
14955682Smarkm
15055682Smarkmstatic void
15155682Smarkmwrite_state_init (struct write_state *w, int fd)
15255682Smarkm{
15355682Smarkm#ifdef UIO_MAXIOV
15455682Smarkm    w->maxiovecs = UIO_MAXIOV;
15555682Smarkm#else
15655682Smarkm    w->maxiovecs = 16;
15755682Smarkm#endif
15855682Smarkm    w->allociovecs = min(STEP, w->maxiovecs);
15955682Smarkm    w->niovecs = 0;
16055682Smarkm    w->iovecs = malloc(w->allociovecs * sizeof(*w->iovecs));
16155682Smarkm    if (w->iovecs == NULL)
16255682Smarkm	err (1, "malloc");
16355682Smarkm    w->fd = fd;
16455682Smarkm}
16555682Smarkm
16655682Smarkmstatic void
16755682Smarkmwrite_state_add (struct write_state *w, void *v, size_t len)
16855682Smarkm{
16955682Smarkm    if(w->niovecs == w->allociovecs) {
17055682Smarkm	if(w->niovecs == w->maxiovecs) {
17155682Smarkm	    if(writev (w->fd, w->iovecs, w->niovecs) < 0)
17255682Smarkm		err(1, "writev");
17355682Smarkm	    w->niovecs = 0;
17455682Smarkm	} else {
17555682Smarkm	    w->allociovecs = min(w->allociovecs + STEP, w->maxiovecs);
17655682Smarkm	    w->iovecs = realloc (w->iovecs,
17755682Smarkm				 w->allociovecs * sizeof(*w->iovecs));
17855682Smarkm	    if (w->iovecs == NULL)
17955682Smarkm		errx (1, "realloc");
18055682Smarkm	}
18155682Smarkm    }
18255682Smarkm    w->iovecs[w->niovecs].iov_base = v;
18355682Smarkm    w->iovecs[w->niovecs].iov_len  = len;
18455682Smarkm    ++w->niovecs;
18555682Smarkm}
18655682Smarkm
18755682Smarkmstatic void
18855682Smarkmwrite_state_flush (struct write_state *w)
18955682Smarkm{
19055682Smarkm    if (w->niovecs) {
19155682Smarkm	if (writev (w->fd, w->iovecs, w->niovecs) < 0)
19255682Smarkm	    err (1, "writev");
19355682Smarkm	w->niovecs = 0;
19455682Smarkm    }
19555682Smarkm}
19655682Smarkm
19755682Smarkmstatic void
19855682Smarkmwrite_state_destroy (struct write_state *w)
19955682Smarkm{
20055682Smarkm    free (w->iovecs);
20155682Smarkm}
20255682Smarkm
20355682Smarkmstatic int
20455682Smarkmdoit(int s,
20555682Smarkm     const char *host,
20655682Smarkm     const char *user,
20755682Smarkm     const char *outfilename,
20855682Smarkm     const char *header_str,
20955682Smarkm     int leavep,
21055682Smarkm     int verbose,
21155682Smarkm     int forkp)
21255682Smarkm{
21355682Smarkm    int ret;
21455682Smarkm    char out_buf[PUSH_BUFSIZ];
21555682Smarkm    size_t out_len = 0;
21655682Smarkm    char in_buf[PUSH_BUFSIZ + 1];	/* sentinel */
21755682Smarkm    size_t in_len = 0;
21855682Smarkm    char *in_ptr = in_buf;
21955682Smarkm    pop_state state = INIT;
22055682Smarkm    unsigned count, bytes;
22155682Smarkm    unsigned asked_for = 0, retrieved = 0, asked_deleted = 0, deleted = 0;
22255682Smarkm    unsigned sent_xdele = 0;
22355682Smarkm    int out_fd;
22455682Smarkm    char from_line[128];
22555682Smarkm    size_t from_line_length;
22655682Smarkm    time_t now;
22755682Smarkm    struct write_state write_state;
22855682Smarkm
22955682Smarkm    if (do_from) {
23055682Smarkm	out_fd = -1;
23155682Smarkm	if (verbose)
23255682Smarkm	    fprintf (stderr, "%s@%s\n", user, host);
23355682Smarkm    } else {
23455682Smarkm	out_fd = open(outfilename, O_WRONLY | O_APPEND | O_CREAT, 0666);
23555682Smarkm	if (out_fd < 0)
23655682Smarkm	    err (1, "open %s", outfilename);
23755682Smarkm	if (verbose)
23855682Smarkm	    fprintf (stderr, "%s@%s -> %s\n", user, host, outfilename);
23955682Smarkm    }
24055682Smarkm
24155682Smarkm    now = time(NULL);
24255682Smarkm    from_line_length = snprintf (from_line, sizeof(from_line),
24355682Smarkm				 "From %s %s", "push", ctime(&now));
24455682Smarkm
24555682Smarkm    out_len = snprintf (out_buf, sizeof(out_buf),
24655682Smarkm			"USER %s\r\nPASS hej\r\nSTAT\r\n",
24755682Smarkm			user);
24855682Smarkm    if (net_write (s, out_buf, out_len) != out_len)
24955682Smarkm	err (1, "write");
25055682Smarkm    if (verbose > 1)
25155682Smarkm	write (STDERR_FILENO, out_buf, out_len);
25255682Smarkm
25355682Smarkm    if (!do_from)
25455682Smarkm	write_state_init (&write_state, out_fd);
25555682Smarkm
25655682Smarkm    while(state != QUIT) {
25755682Smarkm	fd_set readset, writeset;
25855682Smarkm
25955682Smarkm	FD_ZERO(&readset);
26055682Smarkm	FD_ZERO(&writeset);
26155682Smarkm	FD_SET(s,&readset);
26255682Smarkm	if (((state == STAT || state == RETR || state == TOP)
26355682Smarkm	     && asked_for < count)
26455682Smarkm	    || (state == XDELE && !sent_xdele)
26555682Smarkm	    || (state == DELE && asked_deleted < count))
26655682Smarkm	    FD_SET(s,&writeset);
26755682Smarkm	ret = select (s + 1, &readset, &writeset, NULL, NULL);
26855682Smarkm	if (ret < 0) {
26955682Smarkm	    if (errno == EAGAIN)
27055682Smarkm		continue;
27155682Smarkm	    else
27255682Smarkm		err (1, "select");
27355682Smarkm	}
27455682Smarkm
27555682Smarkm	if (FD_ISSET(s, &readset)) {
27655682Smarkm	    char *beg, *p;
27755682Smarkm	    size_t rem;
27855682Smarkm	    int blank_line = 0;
27955682Smarkm
28055682Smarkm	    ret = read (s, in_ptr, sizeof(in_buf) - in_len - 1);
28155682Smarkm	    if (ret < 0)
28255682Smarkm		err (1, "read");
28355682Smarkm	    else if (ret == 0)
28455682Smarkm		errx (1, "EOF during read");
28555682Smarkm
28655682Smarkm	    in_len += ret;
28755682Smarkm	    in_ptr += ret;
28855682Smarkm	    *in_ptr = '\0';
28955682Smarkm
29055682Smarkm	    beg = in_buf;
29155682Smarkm	    rem = in_len;
29255682Smarkm	    while(rem > 1
29355682Smarkm		  && (p = strstr(beg, "\r\n")) != NULL) {
29455682Smarkm		if (state == TOP) {
29555682Smarkm		    char *copy = beg;
29655682Smarkm
29755682Smarkm		    if (strncasecmp(copy,
29855682Smarkm				    header_str,
29955682Smarkm				    min(p - copy + 1, strlen(header_str))) == 0) {
30055682Smarkm			fprintf (stdout, "%.*s\n", (int)(p - copy), copy);
30155682Smarkm		    }
30255682Smarkm		    if (beg[0] == '.' && beg[1] == '\r' && beg[2] == '\n') {
30355682Smarkm			state = STAT;
30455682Smarkm			if (++retrieved == count) {
30555682Smarkm			    state = QUIT;
30655682Smarkm			    net_write (s, "QUIT\r\n", 6);
30755682Smarkm			    if (verbose > 1)
30855682Smarkm				net_write (STDERR_FILENO, "QUIT\r\n", 6);
30955682Smarkm			}
31055682Smarkm		    }
31155682Smarkm		    rem -= p - beg + 2;
31255682Smarkm		    beg = p + 2;
31355682Smarkm		} else if (state == RETR) {
31455682Smarkm		    char *copy = beg;
31555682Smarkm		    if (beg[0] == '.') {
31655682Smarkm			if (beg[1] == '\r' && beg[2] == '\n') {
31755682Smarkm			    if(!blank_line)
31855682Smarkm				write_state_add(&write_state, "\n", 1);
31955682Smarkm			    state = STAT;
32055682Smarkm			    rem -= p - beg + 2;
32155682Smarkm			    beg = p + 2;
32255682Smarkm			    if (++retrieved == count) {
32355682Smarkm				write_state_flush (&write_state);
32455682Smarkm				if (fsync (out_fd) < 0)
32555682Smarkm				    err (1, "fsync");
32655682Smarkm				close(out_fd);
32755682Smarkm				if (leavep) {
32855682Smarkm				    state = QUIT;
32955682Smarkm				    net_write (s, "QUIT\r\n", 6);
33055682Smarkm				    if (verbose > 1)
33155682Smarkm					net_write (STDERR_FILENO, "QUIT\r\n", 6);
33255682Smarkm				} else {
33355682Smarkm				    if (forkp) {
33455682Smarkm					pid_t pid;
33555682Smarkm
33655682Smarkm					pid = fork();
33755682Smarkm					if (pid < 0)
33855682Smarkm					    warn ("fork");
33955682Smarkm					else if(pid != 0) {
34055682Smarkm					    if(verbose)
34155682Smarkm						fprintf (stderr,
34255682Smarkm							 "(exiting)");
34355682Smarkm					    return 0;
34455682Smarkm					}
34555682Smarkm				    }
34655682Smarkm
34755682Smarkm				    state = XDELE;
34855682Smarkm				    if (verbose)
34955682Smarkm					fprintf (stderr, "deleting... ");
35055682Smarkm				}
35155682Smarkm			    }
35255682Smarkm			    continue;
35355682Smarkm			} else
35455682Smarkm			    ++copy;
35555682Smarkm		    }
35655682Smarkm		    *p = '\n';
35755682Smarkm		    if(blank_line &&
35855682Smarkm		       strncmp(copy, "From ", min(p - copy + 1, 5)) == 0)
35955682Smarkm			write_state_add(&write_state, ">", 1);
36055682Smarkm		    write_state_add(&write_state, copy, p - copy + 1);
36155682Smarkm		    blank_line = (*copy == '\n');
36255682Smarkm		    rem -= p - beg + 2;
36355682Smarkm		    beg = p + 2;
36455682Smarkm		} else if (rem >= 3 && strncmp (beg, "+OK", 3) == 0) {
36555682Smarkm		    if (state == STAT) {
36655682Smarkm			if (!do_from)
36755682Smarkm			    write_state_add(&write_state,
36855682Smarkm					    from_line, from_line_length);
36955682Smarkm			blank_line = 0;
37055682Smarkm			if (do_from)
37155682Smarkm			    state = TOP;
37255682Smarkm			else
37355682Smarkm			    state = RETR;
37455682Smarkm		    } else if (state == XDELE) {
37555682Smarkm			state = QUIT;
37655682Smarkm			net_write (s, "QUIT\r\n", 6);
37755682Smarkm			if (verbose > 1)
37855682Smarkm			    net_write (STDERR_FILENO, "QUIT\r\n", 6);
37955682Smarkm			break;
38055682Smarkm		    } else if (state == DELE) {
38155682Smarkm			if (++deleted == count) {
38255682Smarkm			    state = QUIT;
38355682Smarkm			    net_write (s, "QUIT\r\n", 6);
38455682Smarkm			    if (verbose > 1)
38555682Smarkm				net_write (STDERR_FILENO, "QUIT\r\n", 6);
38655682Smarkm			    break;
38755682Smarkm			}
38855682Smarkm		    } else if (++state == STAT) {
38955682Smarkm			if(sscanf (beg + 4, "%u %u", &count, &bytes) != 2)
39055682Smarkm			    errx(1, "Bad STAT-line: %.*s", (int)(p - beg), beg);
39155682Smarkm			if (verbose) {
39255682Smarkm			    fprintf (stderr, "%u message(s) (%u bytes). "
39355682Smarkm				     "fetching... ",
39455682Smarkm				     count, bytes);
39555682Smarkm			    if (do_from)
39655682Smarkm				fprintf (stderr, "\n");
39755682Smarkm			} else if (do_count) {
39855682Smarkm			    fprintf (stderr, "%u message(s) (%u bytes).\n",
39955682Smarkm				     count, bytes);
40055682Smarkm			}
40155682Smarkm			if (count == 0) {
40255682Smarkm			    state = QUIT;
40355682Smarkm			    net_write (s, "QUIT\r\n", 6);
40455682Smarkm			    if (verbose > 1)
40555682Smarkm				net_write (STDERR_FILENO, "QUIT\r\n", 6);
40655682Smarkm			    break;
40755682Smarkm			}
40855682Smarkm		    }
40955682Smarkm
41055682Smarkm		    rem -= p - beg + 2;
41155682Smarkm		    beg = p + 2;
41255682Smarkm		} else {
41355682Smarkm		    if(state == XDELE) {
41455682Smarkm			state = DELE;
41555682Smarkm			rem -= p - beg + 2;
41655682Smarkm			beg = p + 2;
41755682Smarkm		    } else
41855682Smarkm			errx (1, "Bad response: %.*s", (int)(p - beg), beg);
41955682Smarkm		}
42055682Smarkm	    }
42155682Smarkm	    if (!do_from)
42255682Smarkm		write_state_flush (&write_state);
42355682Smarkm
42455682Smarkm	    memmove (in_buf, beg, rem);
42555682Smarkm	    in_len = rem;
42655682Smarkm	    in_ptr = in_buf + rem;
42755682Smarkm	}
42855682Smarkm	if (FD_ISSET(s, &writeset)) {
42955682Smarkm	    if ((state == STAT && !do_from) || state == RETR)
43055682Smarkm		out_len = snprintf (out_buf, sizeof(out_buf),
43155682Smarkm				    "RETR %u\r\n", ++asked_for);
43255682Smarkm	    else if ((state == STAT && do_from) || state == TOP)
43355682Smarkm		out_len = snprintf (out_buf, sizeof(out_buf),
43455682Smarkm				    "TOP %u 0\r\n", ++asked_for);
43555682Smarkm	    else if(state == XDELE) {
43655682Smarkm		out_len = snprintf(out_buf, sizeof(out_buf),
43755682Smarkm				   "XDELE %u %u\r\n", 1, count);
43855682Smarkm		sent_xdele++;
43955682Smarkm	    }
44055682Smarkm	    else if(state == DELE)
44155682Smarkm		out_len = snprintf (out_buf, sizeof(out_buf),
44255682Smarkm				    "DELE %u\r\n", ++asked_deleted);
44355682Smarkm	    if (net_write (s, out_buf, out_len) != out_len)
44455682Smarkm		err (1, "write");
44555682Smarkm	    if (verbose > 1)
44655682Smarkm		write (STDERR_FILENO, out_buf, out_len);
44755682Smarkm	}
44855682Smarkm    }
44955682Smarkm    if (verbose)
45055682Smarkm	fprintf (stderr, "Done\n");
45155682Smarkm    if (!do_from)
45255682Smarkm	write_state_destroy (&write_state);
45355682Smarkm    return 0;
45455682Smarkm}
45555682Smarkm
45655682Smarkm#ifdef KRB5
45755682Smarkmstatic int
45855682Smarkmdo_v5 (const char *host,
45955682Smarkm       int port,
46055682Smarkm       const char *user,
46155682Smarkm       const char *filename,
46255682Smarkm       const char *header_str,
46355682Smarkm       int leavep,
46455682Smarkm       int verbose,
46555682Smarkm       int forkp)
46655682Smarkm{
46755682Smarkm    krb5_error_code ret;
46855682Smarkm    krb5_auth_context auth_context = NULL;
46955682Smarkm    krb5_principal server;
47055682Smarkm    int s;
47155682Smarkm
47255682Smarkm    s = do_connect (host, port, 1);
47355682Smarkm    if (s < 0)
47455682Smarkm	return 1;
47555682Smarkm
47655682Smarkm    ret = krb5_sname_to_principal (context,
47755682Smarkm				   host,
47855682Smarkm				   "pop",
47955682Smarkm				   KRB5_NT_SRV_HST,
48055682Smarkm				   &server);
48155682Smarkm    if (ret) {
48255682Smarkm	warnx ("krb5_sname_to_principal: %s",
48355682Smarkm	       krb5_get_err_text (context, ret));
48455682Smarkm	return 1;
48555682Smarkm    }
48655682Smarkm
48755682Smarkm    ret = krb5_sendauth (context,
48855682Smarkm			 &auth_context,
48955682Smarkm			 &s,
49055682Smarkm			 "KPOPV1.0",
49155682Smarkm			 NULL,
49255682Smarkm			 server,
49355682Smarkm			 0,
49455682Smarkm			 NULL,
49555682Smarkm			 NULL,
49655682Smarkm			 NULL,
49755682Smarkm			 NULL,
49855682Smarkm			 NULL,
49955682Smarkm			 NULL);
50055682Smarkm    krb5_free_principal (context, server);
50155682Smarkm    if (ret) {
50255682Smarkm	warnx ("krb5_sendauth: %s",
50355682Smarkm	       krb5_get_err_text (context, ret));
50455682Smarkm	return 1;
50555682Smarkm    }
50655682Smarkm    return doit (s, host, user, filename, header_str, leavep, verbose, forkp);
50755682Smarkm}
50855682Smarkm#endif
50955682Smarkm
51055682Smarkm#ifdef KRB4
51155682Smarkmstatic int
51255682Smarkmdo_v4 (const char *host,
51355682Smarkm       int port,
51455682Smarkm       const char *user,
51555682Smarkm       const char *filename,
51655682Smarkm       const char *header_str,
51755682Smarkm       int leavep,
51855682Smarkm       int verbose,
51955682Smarkm       int forkp)
52055682Smarkm{
52155682Smarkm    KTEXT_ST ticket;
52255682Smarkm    MSG_DAT msg_data;
52355682Smarkm    CREDENTIALS cred;
52455682Smarkm    des_key_schedule sched;
52555682Smarkm    int s;
52655682Smarkm    int ret;
52755682Smarkm
52855682Smarkm    s = do_connect (host, port, 1);
52955682Smarkm    if (s < 0)
53055682Smarkm	return 1;
53155682Smarkm    ret = krb_sendauth(0,
53255682Smarkm		       s,
53355682Smarkm		       &ticket,
53455682Smarkm		       "pop",
53555682Smarkm		       (char *)host,
53655682Smarkm		       krb_realmofhost(host),
53755682Smarkm		       getpid(),
53855682Smarkm		       &msg_data,
53955682Smarkm		       &cred,
54055682Smarkm		       sched,
54155682Smarkm		       NULL,
54255682Smarkm		       NULL,
54355682Smarkm		       "KPOPV0.1");
54455682Smarkm    if(ret) {
54555682Smarkm	warnx("krb_sendauth: %s", krb_get_err_text(ret));
54655682Smarkm	return 1;
54755682Smarkm    }
54855682Smarkm    return doit (s, host, user, filename, header_str, leavep, verbose, forkp);
54955682Smarkm}
55055682Smarkm#endif /* KRB4 */
55155682Smarkm
55255682Smarkm#ifdef HESIOD
55355682Smarkm
55455682Smarkm#ifdef HESIOD_INTERFACES
55555682Smarkm
55655682Smarkmstatic char *
55755682Smarkmhesiod_get_pobox (const char **user)
55855682Smarkm{
55955682Smarkm    void *context;
56055682Smarkm    struct hesiod_postoffice *hpo;
56155682Smarkm    char *ret = NULL;
56255682Smarkm
56355682Smarkm    if(hesiod_init (&context) != 0)
56455682Smarkm	err (1, "hesiod_init");
56555682Smarkm
56655682Smarkm    hpo = hesiod_getmailhost (context, *user);
56755682Smarkm    if (hpo == NULL) {
56855682Smarkm	warn ("hesiod_getmailhost %s", *user);
56955682Smarkm    } else {
57055682Smarkm	if (strcasecmp(hpo->hesiod_po_type, "pop") != 0)
57155682Smarkm	    errx (1, "Unsupported po type %s", hpo->hesiod_po_type);
57255682Smarkm
57355682Smarkm	ret = strdup(hpo->hesiod_po_host);
57455682Smarkm	if(ret == NULL)
57555682Smarkm	    errx (1, "strdup: out of memory");
57655682Smarkm	*user = strdup(hpo->hesiod_po_name);
57755682Smarkm	if (*user == NULL)
57855682Smarkm	    errx (1, "strdup: out of memory");
57955682Smarkm	hesiod_free_postoffice (context, hpo);
58055682Smarkm    }
58155682Smarkm    hesiod_end (context);
58255682Smarkm    return ret;
58355682Smarkm}
58455682Smarkm
58555682Smarkm#else /* !HESIOD_INTERFACES */
58655682Smarkm
58755682Smarkmstatic char *
58855682Smarkmhesiod_get_pobox (const char **user)
58955682Smarkm{
59055682Smarkm    char *ret = NULL;
59155682Smarkm    struct hes_postoffice *hpo;
59255682Smarkm
59355682Smarkm    hpo = hes_getmailhost (*user);
59455682Smarkm    if (hpo == NULL) {
59555682Smarkm	warn ("hes_getmailhost %s", *user);
59655682Smarkm    } else {
59755682Smarkm	if (strcasecmp(hpo->po_type, "pop") != 0)
59855682Smarkm	    errx (1, "Unsupported po type %s", hpo->po_type);
59955682Smarkm
60055682Smarkm	ret = strdup(hpo->po_host);
60155682Smarkm	if(ret == NULL)
60255682Smarkm	    errx (1, "strdup: out of memory");
60355682Smarkm	*user = strdup(hpo->po_name);
60455682Smarkm	if (*user == NULL)
60555682Smarkm	    errx (1, "strdup: out of memory");
60655682Smarkm    }
60755682Smarkm    return ret;
60855682Smarkm}
60955682Smarkm
61055682Smarkm#endif /* HESIOD_INTERFACES */
61155682Smarkm
61255682Smarkm#endif /* HESIOD */
61355682Smarkm
61455682Smarkmstatic char *
61555682Smarkmget_pobox (const char **user)
61655682Smarkm{
61755682Smarkm    char *ret = NULL;
61855682Smarkm
61955682Smarkm#ifdef HESIOD
62055682Smarkm    ret = hesiod_get_pobox (user);
62155682Smarkm#endif
62255682Smarkm
62355682Smarkm    if (ret == NULL)
62455682Smarkm	ret = getenv("MAILHOST");
62555682Smarkm    if (ret == NULL)
62655682Smarkm	errx (1, "MAILHOST not set");
62755682Smarkm    return ret;
62855682Smarkm}
62955682Smarkm
63055682Smarkmstatic void
63155682Smarkmparse_pobox (char *a0, const char **host, const char **user)
63255682Smarkm{
63355682Smarkm    const char *h, *u;
63455682Smarkm    char *p;
63555682Smarkm    int po = 0;
63655682Smarkm
63755682Smarkm    if (a0 == NULL) {
63855682Smarkm
63955682Smarkm	*user = getenv ("USERNAME");
64055682Smarkm	if (*user == NULL) {
64155682Smarkm	    struct passwd *pwd = getpwuid (getuid ());
64255682Smarkm
64355682Smarkm	    if (pwd == NULL)
64455682Smarkm		errx (1, "Who are you?");
64555682Smarkm	    *user = strdup (pwd->pw_name);
64655682Smarkm	    if (*user == NULL)
64755682Smarkm		errx (1, "strdup: out of memory");
64855682Smarkm	}
64955682Smarkm	*host = get_pobox (user);
65055682Smarkm	return;
65155682Smarkm    }
65255682Smarkm
65355682Smarkm    /* if the specification starts with po:, remember this information */
65455682Smarkm    if(strncmp(a0, "po:", 3) == 0) {
65555682Smarkm	a0 += 3;
65655682Smarkm	po++;
65755682Smarkm    }
65855682Smarkm    /* if there is an `@', the hostname is after it, otherwise at the
65955682Smarkm       beginning of the string */
66055682Smarkm    p = strchr(a0, '@');
66155682Smarkm    if(p != NULL) {
66255682Smarkm	*p++ = '\0';
66355682Smarkm	h = p;
66455682Smarkm    } else {
66555682Smarkm	h = a0;
66655682Smarkm    }
66755682Smarkm    /* if there is a `:', the username comes before it, otherwise at
66855682Smarkm       the beginning of the string */
66955682Smarkm    p = strchr(a0, ':');
67055682Smarkm    if(p != NULL) {
67155682Smarkm	*p++ = '\0';
67255682Smarkm	u = p;
67355682Smarkm    } else {
67455682Smarkm	u = a0;
67555682Smarkm    }
67655682Smarkm    if(h == u) {
67755682Smarkm	/* some inconsistent compatibility with various mailers */
67855682Smarkm	if(po) {
67955682Smarkm	    h = get_pobox (&u);
68055682Smarkm	} else {
68155682Smarkm	    u = get_default_username ();
68255682Smarkm	    if (u == NULL)
68355682Smarkm		errx (1, "Who are you?");
68455682Smarkm	}
68555682Smarkm    }
68655682Smarkm    *host = h;
68755682Smarkm    *user = u;
68855682Smarkm}
68955682Smarkm
69055682Smarkmint
69155682Smarkmmain(int argc, char **argv)
69255682Smarkm{
69355682Smarkm    int port = 0;
69455682Smarkm    int optind = 0;
69555682Smarkm    int ret = 1;
69655682Smarkm    const char *host, *user, *filename = NULL;
69755682Smarkm    char *pobox = NULL;
69855682Smarkm
69955682Smarkm    set_progname (argv[0]);
70055682Smarkm
70155682Smarkm#ifdef KRB5
70255682Smarkm    krb5_init_context (&context);
70355682Smarkm#endif
70455682Smarkm
70555682Smarkm    if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv,
70655682Smarkm		&optind))
70755682Smarkm	usage (1);
70855682Smarkm
70955682Smarkm    argc -= optind;
71055682Smarkm    argv += optind;
71155682Smarkm
71255682Smarkm#if defined(KRB4) && defined(KRB5)
71355682Smarkm    if(use_v4 == -1 && use_v5 == 1)
71455682Smarkm	use_v4 = 0;
71555682Smarkm    if(use_v5 == -1 && use_v4 == 1)
71655682Smarkm	use_v5 = 0;
71755682Smarkm#endif
71855682Smarkm
71955682Smarkm    if (do_help)
72055682Smarkm	usage (0);
72155682Smarkm
72255682Smarkm    if (do_version) {
72355682Smarkm	print_version(NULL);
72455682Smarkm	return 0;
72555682Smarkm    }
72655682Smarkm
72755682Smarkm    if (do_from && header_str == NULL)
72855682Smarkm	header_str = "From:";
72955682Smarkm    else if (header_str != NULL)
73055682Smarkm	do_from = 1;
73155682Smarkm
73255682Smarkm    if (do_from) {
73355682Smarkm	if (argc == 0)
73455682Smarkm	    pobox = NULL;
73555682Smarkm	else if (argc == 1)
73655682Smarkm	    pobox = argv[0];
73755682Smarkm	else
73855682Smarkm	    usage (1);
73955682Smarkm    } else {
74055682Smarkm	if (argc == 1) {
74155682Smarkm	    filename = argv[0];
74255682Smarkm	    pobox    = NULL;
74355682Smarkm	} else if (argc == 2) {
74455682Smarkm	    filename = argv[1];
74555682Smarkm	    pobox    = argv[0];
74655682Smarkm	} else
74755682Smarkm	    usage (1);
74855682Smarkm    }
74955682Smarkm
75055682Smarkm    if (port_str) {
75155682Smarkm	struct servent *s = roken_getservbyname (port_str, "tcp");
75255682Smarkm
75355682Smarkm	if (s)
75455682Smarkm	    port = s->s_port;
75555682Smarkm	else {
75655682Smarkm	    char *ptr;
75755682Smarkm
75855682Smarkm	    port = strtol (port_str, &ptr, 10);
75955682Smarkm	    if (port == 0 && ptr == port_str)
76055682Smarkm		errx (1, "Bad port `%s'", port_str);
76155682Smarkm	    port = htons(port);
76255682Smarkm	}
76355682Smarkm    }
76455682Smarkm    if (port == 0) {
76555682Smarkm#ifdef KRB5
76655682Smarkm	port = krb5_getportbyname (context, "kpop", "tcp", 1109);
76755682Smarkm#elif defined(KRB4)
76855682Smarkm	port = k_getportbyname ("kpop", "tcp", htons(1109));
76955682Smarkm#else
77055682Smarkm#error must define KRB4 or KRB5
77155682Smarkm#endif
77255682Smarkm    }
77355682Smarkm
77455682Smarkm    parse_pobox (pobox, &host, &user);
77555682Smarkm
77655682Smarkm#ifdef KRB5
77755682Smarkm    if (ret && use_v5) {
77855682Smarkm	ret = do_v5 (host, port, user, filename, header_str,
77955682Smarkm		     do_leave, verbose_level, do_fork);
78055682Smarkm    }
78155682Smarkm#endif
78255682Smarkm
78355682Smarkm#ifdef KRB4
78455682Smarkm    if (ret && use_v4) {
78555682Smarkm	ret = do_v4 (host, port, user, filename, header_str,
78655682Smarkm		     do_leave, verbose_level, do_fork);
78755682Smarkm    }
78855682Smarkm#endif /* KRB4 */
78955682Smarkm    return ret;
79055682Smarkm}
791