155682Smarkm/*
2142403Snectar * Copyright (c) 1997-2004 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"
35178825SdfrRCSID("$Id: push.c 14850 2005-04-19 18:00:17Z lha $");
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 },
7572445Sassar    { "headers", 0,  arg_string,	&header_str,	"Headers 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,
9072445Sassar		    "[[{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;
10172445Sassar    int s = -1;
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
140120945Snectarstatic char *pop_state_string[] = {
141120945Snectar    "INIT", "GREET", "USER", "PASS", "STAT", "RETR", "TOP",
142120945Snectar    "DELE", "XDELE", "QUIT"
143120945Snectar};
144120945Snectar
14555682Smarkm#define PUSH_BUFSIZ 65536
14655682Smarkm
14755682Smarkm#define STEP 16
14855682Smarkm
14955682Smarkmstruct write_state {
15055682Smarkm    struct iovec *iovecs;
15155682Smarkm    size_t niovecs, maxiovecs, allociovecs;
15255682Smarkm    int fd;
15355682Smarkm};
15455682Smarkm
15555682Smarkmstatic void
15655682Smarkmwrite_state_init (struct write_state *w, int fd)
15755682Smarkm{
15855682Smarkm#ifdef UIO_MAXIOV
15955682Smarkm    w->maxiovecs = UIO_MAXIOV;
16055682Smarkm#else
16155682Smarkm    w->maxiovecs = 16;
16255682Smarkm#endif
16355682Smarkm    w->allociovecs = min(STEP, w->maxiovecs);
16455682Smarkm    w->niovecs = 0;
16572445Sassar    w->iovecs = emalloc(w->allociovecs * sizeof(*w->iovecs));
16655682Smarkm    w->fd = fd;
16755682Smarkm}
16855682Smarkm
16955682Smarkmstatic void
17055682Smarkmwrite_state_add (struct write_state *w, void *v, size_t len)
17155682Smarkm{
17255682Smarkm    if(w->niovecs == w->allociovecs) {
17355682Smarkm	if(w->niovecs == w->maxiovecs) {
17455682Smarkm	    if(writev (w->fd, w->iovecs, w->niovecs) < 0)
17555682Smarkm		err(1, "writev");
17655682Smarkm	    w->niovecs = 0;
17755682Smarkm	} else {
17855682Smarkm	    w->allociovecs = min(w->allociovecs + STEP, w->maxiovecs);
17972445Sassar	    w->iovecs = erealloc (w->iovecs,
18072445Sassar				  w->allociovecs * sizeof(*w->iovecs));
18155682Smarkm	}
18255682Smarkm    }
18355682Smarkm    w->iovecs[w->niovecs].iov_base = v;
18455682Smarkm    w->iovecs[w->niovecs].iov_len  = len;
18555682Smarkm    ++w->niovecs;
18655682Smarkm}
18755682Smarkm
18855682Smarkmstatic void
18955682Smarkmwrite_state_flush (struct write_state *w)
19055682Smarkm{
19155682Smarkm    if (w->niovecs) {
19255682Smarkm	if (writev (w->fd, w->iovecs, w->niovecs) < 0)
19355682Smarkm	    err (1, "writev");
19455682Smarkm	w->niovecs = 0;
19555682Smarkm    }
19655682Smarkm}
19755682Smarkm
19855682Smarkmstatic void
19955682Smarkmwrite_state_destroy (struct write_state *w)
20055682Smarkm{
20155682Smarkm    free (w->iovecs);
20255682Smarkm}
20355682Smarkm
20455682Smarkmstatic int
20555682Smarkmdoit(int s,
20655682Smarkm     const char *host,
20755682Smarkm     const char *user,
20855682Smarkm     const char *outfilename,
20955682Smarkm     const char *header_str,
21055682Smarkm     int leavep,
21155682Smarkm     int verbose,
21255682Smarkm     int forkp)
21355682Smarkm{
21455682Smarkm    int ret;
21555682Smarkm    char out_buf[PUSH_BUFSIZ];
21690926Snectar    int out_len = 0;
217142403Snectar    char *in_buf;
218142403Snectar    size_t in_buf_size;
21955682Smarkm    size_t in_len = 0;
220142403Snectar    char *in_ptr;
22155682Smarkm    pop_state state = INIT;
22255682Smarkm    unsigned count, bytes;
22355682Smarkm    unsigned asked_for = 0, retrieved = 0, asked_deleted = 0, deleted = 0;
22455682Smarkm    unsigned sent_xdele = 0;
22555682Smarkm    int out_fd;
22655682Smarkm    char from_line[128];
22755682Smarkm    size_t from_line_length;
22855682Smarkm    time_t now;
22955682Smarkm    struct write_state write_state;
23072445Sassar    int numheaders = 1;
23172445Sassar    char **headers = NULL;
23272445Sassar    int i;
23372445Sassar    char *tmp = NULL;
23455682Smarkm
235142403Snectar    in_buf = emalloc(PUSH_BUFSIZ + 1);
236142403Snectar    in_ptr = in_buf;
237142403Snectar    in_buf_size = PUSH_BUFSIZ;
238142403Snectar
23955682Smarkm    if (do_from) {
24072445Sassar	char *tmp2;
24172445Sassar
24272445Sassar	tmp2 = tmp = estrdup(header_str);
24372445Sassar
24455682Smarkm	out_fd = -1;
24555682Smarkm	if (verbose)
24655682Smarkm	    fprintf (stderr, "%s@%s\n", user, host);
24772445Sassar	while (*tmp != '\0') {
24872445Sassar	    tmp = strchr(tmp, ',');
24972445Sassar	    if (tmp == NULL)
25072445Sassar		break;
25172445Sassar	    tmp++;
25272445Sassar	    numheaders++;
25372445Sassar	}
25472445Sassar
25572445Sassar	headers = emalloc(sizeof(char *) * (numheaders + 1));
25672445Sassar	for (i = 0; i < numheaders; i++) {
25772445Sassar	    headers[i] = strtok_r(tmp2, ",", &tmp2);
25872445Sassar	}
25972445Sassar	headers[numheaders] = NULL;
26055682Smarkm    } else {
26155682Smarkm	out_fd = open(outfilename, O_WRONLY | O_APPEND | O_CREAT, 0666);
26255682Smarkm	if (out_fd < 0)
26355682Smarkm	    err (1, "open %s", outfilename);
26455682Smarkm	if (verbose)
26555682Smarkm	    fprintf (stderr, "%s@%s -> %s\n", user, host, outfilename);
26655682Smarkm    }
26755682Smarkm
26855682Smarkm    now = time(NULL);
26955682Smarkm    from_line_length = snprintf (from_line, sizeof(from_line),
27055682Smarkm				 "From %s %s", "push", ctime(&now));
271178825Sdfr    if (from_line_length < 0 || from_line_length > sizeof(from_line))
272178825Sdfr	errx (1, "snprintf failed");
27355682Smarkm
27455682Smarkm    out_len = snprintf (out_buf, sizeof(out_buf),
27555682Smarkm			"USER %s\r\nPASS hej\r\nSTAT\r\n",
27655682Smarkm			user);
277178825Sdfr    if (out_len < 0 || out_len > sizeof(out_buf))
27890926Snectar	errx (1, "snprintf failed");
27955682Smarkm    if (net_write (s, out_buf, out_len) != out_len)
28055682Smarkm	err (1, "write");
28155682Smarkm    if (verbose > 1)
282120945Snectar	fprintf (stderr, "%s", out_buf);
28355682Smarkm
28455682Smarkm    if (!do_from)
28555682Smarkm	write_state_init (&write_state, out_fd);
28655682Smarkm
28755682Smarkm    while(state != QUIT) {
28855682Smarkm	fd_set readset, writeset;
28955682Smarkm
29055682Smarkm	FD_ZERO(&readset);
29155682Smarkm	FD_ZERO(&writeset);
29272445Sassar	if (s >= FD_SETSIZE)
29372445Sassar	    errx (1, "fd too large");
29455682Smarkm	FD_SET(s,&readset);
295120945Snectar
296120945Snectar	if (verbose > 1)
297120945Snectar	    fprintf (stderr, "state: %s count: %d asked_for: %d "
298120945Snectar		     "retrieved: %d asked_deleted: %d\n",
299120945Snectar		     pop_state_string[state],
300120945Snectar		     count, asked_for, retrieved, asked_deleted);
301120945Snectar
30255682Smarkm	if (((state == STAT || state == RETR || state == TOP)
30355682Smarkm	     && asked_for < count)
30455682Smarkm	    || (state == XDELE && !sent_xdele)
30555682Smarkm	    || (state == DELE && asked_deleted < count))
30655682Smarkm	    FD_SET(s,&writeset);
30755682Smarkm	ret = select (s + 1, &readset, &writeset, NULL, NULL);
30855682Smarkm	if (ret < 0) {
30955682Smarkm	    if (errno == EAGAIN)
31055682Smarkm		continue;
31155682Smarkm	    else
31255682Smarkm		err (1, "select");
31355682Smarkm	}
31455682Smarkm
31555682Smarkm	if (FD_ISSET(s, &readset)) {
31655682Smarkm	    char *beg, *p;
31755682Smarkm	    size_t rem;
31855682Smarkm	    int blank_line = 0;
31955682Smarkm
320142403Snectar	    if(in_len >= in_buf_size) {
321142403Snectar		char *tmp = erealloc(in_buf, in_buf_size + PUSH_BUFSIZ + 1);
322142403Snectar		in_ptr = tmp + (in_ptr - in_buf);
323142403Snectar		in_buf = tmp;
324142403Snectar		in_buf_size += PUSH_BUFSIZ;
325142403Snectar	    }
326142403Snectar
327142403Snectar	    ret = read (s, in_ptr, in_buf_size - in_len);
32855682Smarkm	    if (ret < 0)
32955682Smarkm		err (1, "read");
33055682Smarkm	    else if (ret == 0)
33155682Smarkm		errx (1, "EOF during read");
33255682Smarkm
33355682Smarkm	    in_len += ret;
33455682Smarkm	    in_ptr += ret;
33555682Smarkm	    *in_ptr = '\0';
33655682Smarkm
33755682Smarkm	    beg = in_buf;
33855682Smarkm	    rem = in_len;
33955682Smarkm	    while(rem > 1
34055682Smarkm		  && (p = strstr(beg, "\r\n")) != NULL) {
34155682Smarkm		if (state == TOP) {
34255682Smarkm		    char *copy = beg;
34355682Smarkm
34472445Sassar		    for (i = 0; i < numheaders; i++) {
34572445Sassar			size_t len;
34672445Sassar
34772445Sassar			len = min(p - copy + 1, strlen(headers[i]));
34872445Sassar			if (strncasecmp(copy, headers[i], len) == 0) {
34972445Sassar			    fprintf (stdout, "%.*s\n", (int)(p - copy), copy);
35072445Sassar			}
35155682Smarkm		    }
35255682Smarkm		    if (beg[0] == '.' && beg[1] == '\r' && beg[2] == '\n') {
35372445Sassar			if (numheaders > 1)
35472445Sassar			    fprintf (stdout, "\n");
35555682Smarkm			state = STAT;
35655682Smarkm			if (++retrieved == count) {
35755682Smarkm			    state = QUIT;
35855682Smarkm			    net_write (s, "QUIT\r\n", 6);
35955682Smarkm			    if (verbose > 1)
360120945Snectar				fprintf (stderr, "QUIT\r\n");
36155682Smarkm			}
36255682Smarkm		    }
36355682Smarkm		    rem -= p - beg + 2;
36455682Smarkm		    beg = p + 2;
36555682Smarkm		} else if (state == RETR) {
36655682Smarkm		    char *copy = beg;
36755682Smarkm		    if (beg[0] == '.') {
36855682Smarkm			if (beg[1] == '\r' && beg[2] == '\n') {
36955682Smarkm			    if(!blank_line)
37055682Smarkm				write_state_add(&write_state, "\n", 1);
37155682Smarkm			    state = STAT;
37255682Smarkm			    rem -= p - beg + 2;
37355682Smarkm			    beg = p + 2;
37455682Smarkm			    if (++retrieved == count) {
37555682Smarkm				write_state_flush (&write_state);
37655682Smarkm				if (fsync (out_fd) < 0)
37755682Smarkm				    err (1, "fsync");
37855682Smarkm				close(out_fd);
37955682Smarkm				if (leavep) {
38055682Smarkm				    state = QUIT;
38155682Smarkm				    net_write (s, "QUIT\r\n", 6);
38255682Smarkm				    if (verbose > 1)
383120945Snectar					fprintf (stderr, "QUIT\r\n");
38455682Smarkm				} else {
38555682Smarkm				    if (forkp) {
38655682Smarkm					pid_t pid;
38755682Smarkm
38855682Smarkm					pid = fork();
38955682Smarkm					if (pid < 0)
39055682Smarkm					    warn ("fork");
39155682Smarkm					else if(pid != 0) {
39255682Smarkm					    if(verbose)
39355682Smarkm						fprintf (stderr,
39455682Smarkm							 "(exiting)");
39555682Smarkm					    return 0;
39655682Smarkm					}
39755682Smarkm				    }
39855682Smarkm
39955682Smarkm				    state = XDELE;
40055682Smarkm				    if (verbose)
40155682Smarkm					fprintf (stderr, "deleting... ");
40255682Smarkm				}
40355682Smarkm			    }
40455682Smarkm			    continue;
40555682Smarkm			} else
40655682Smarkm			    ++copy;
40755682Smarkm		    }
40855682Smarkm		    *p = '\n';
40955682Smarkm		    if(blank_line &&
41055682Smarkm		       strncmp(copy, "From ", min(p - copy + 1, 5)) == 0)
41155682Smarkm			write_state_add(&write_state, ">", 1);
41255682Smarkm		    write_state_add(&write_state, copy, p - copy + 1);
41355682Smarkm		    blank_line = (*copy == '\n');
41455682Smarkm		    rem -= p - beg + 2;
41555682Smarkm		    beg = p + 2;
41655682Smarkm		} else if (rem >= 3 && strncmp (beg, "+OK", 3) == 0) {
41755682Smarkm		    if (state == STAT) {
41855682Smarkm			if (!do_from)
41955682Smarkm			    write_state_add(&write_state,
42055682Smarkm					    from_line, from_line_length);
42155682Smarkm			blank_line = 0;
42255682Smarkm			if (do_from)
42355682Smarkm			    state = TOP;
42455682Smarkm			else
42555682Smarkm			    state = RETR;
42655682Smarkm		    } else if (state == XDELE) {
42755682Smarkm			state = QUIT;
42855682Smarkm			net_write (s, "QUIT\r\n", 6);
42955682Smarkm			if (verbose > 1)
430120945Snectar			    fprintf (stderr, "QUIT\r\n");
43155682Smarkm			break;
43255682Smarkm		    } else if (state == DELE) {
43355682Smarkm			if (++deleted == count) {
43455682Smarkm			    state = QUIT;
43555682Smarkm			    net_write (s, "QUIT\r\n", 6);
43655682Smarkm			    if (verbose > 1)
437120945Snectar				fprintf (stderr, "QUIT\r\n");
43855682Smarkm			    break;
43955682Smarkm			}
44055682Smarkm		    } else if (++state == STAT) {
44155682Smarkm			if(sscanf (beg + 4, "%u %u", &count, &bytes) != 2)
44255682Smarkm			    errx(1, "Bad STAT-line: %.*s", (int)(p - beg), beg);
44355682Smarkm			if (verbose) {
44455682Smarkm			    fprintf (stderr, "%u message(s) (%u bytes). "
44555682Smarkm				     "fetching... ",
44655682Smarkm				     count, bytes);
44755682Smarkm			    if (do_from)
44855682Smarkm				fprintf (stderr, "\n");
44955682Smarkm			} else if (do_count) {
45055682Smarkm			    fprintf (stderr, "%u message(s) (%u bytes).\n",
45155682Smarkm				     count, bytes);
45255682Smarkm			}
45355682Smarkm			if (count == 0) {
45455682Smarkm			    state = QUIT;
45555682Smarkm			    net_write (s, "QUIT\r\n", 6);
45655682Smarkm			    if (verbose > 1)
457120945Snectar				fprintf (stderr, "QUIT\r\n");
45855682Smarkm			    break;
45955682Smarkm			}
46055682Smarkm		    }
46155682Smarkm
46255682Smarkm		    rem -= p - beg + 2;
46355682Smarkm		    beg = p + 2;
46455682Smarkm		} else {
46555682Smarkm		    if(state == XDELE) {
46655682Smarkm			state = DELE;
46755682Smarkm			rem -= p - beg + 2;
46855682Smarkm			beg = p + 2;
46955682Smarkm		    } else
47055682Smarkm			errx (1, "Bad response: %.*s", (int)(p - beg), beg);
47155682Smarkm		}
47255682Smarkm	    }
47355682Smarkm	    if (!do_from)
47455682Smarkm		write_state_flush (&write_state);
47555682Smarkm
47655682Smarkm	    memmove (in_buf, beg, rem);
47755682Smarkm	    in_len = rem;
47855682Smarkm	    in_ptr = in_buf + rem;
47955682Smarkm	}
48055682Smarkm	if (FD_ISSET(s, &writeset)) {
48155682Smarkm	    if ((state == STAT && !do_from) || state == RETR)
48255682Smarkm		out_len = snprintf (out_buf, sizeof(out_buf),
48355682Smarkm				    "RETR %u\r\n", ++asked_for);
48455682Smarkm	    else if ((state == STAT && do_from) || state == TOP)
48555682Smarkm		out_len = snprintf (out_buf, sizeof(out_buf),
48655682Smarkm				    "TOP %u 0\r\n", ++asked_for);
48755682Smarkm	    else if(state == XDELE) {
48855682Smarkm		out_len = snprintf(out_buf, sizeof(out_buf),
48955682Smarkm				   "XDELE %u %u\r\n", 1, count);
49055682Smarkm		sent_xdele++;
49155682Smarkm	    }
49255682Smarkm	    else if(state == DELE)
49355682Smarkm		out_len = snprintf (out_buf, sizeof(out_buf),
49455682Smarkm				    "DELE %u\r\n", ++asked_deleted);
495178825Sdfr	    if (out_len < 0 || out_len > sizeof(out_buf))
49690926Snectar		errx (1, "snprintf failed");
49755682Smarkm	    if (net_write (s, out_buf, out_len) != out_len)
49855682Smarkm		err (1, "write");
49955682Smarkm	    if (verbose > 1)
500120945Snectar		fprintf (stderr, "%s", out_buf);
50155682Smarkm	}
50255682Smarkm    }
50355682Smarkm    if (verbose)
50455682Smarkm	fprintf (stderr, "Done\n");
50572445Sassar    if (do_from) {
50672445Sassar	free (tmp);
50772445Sassar	free (headers);
50872445Sassar    } else {
50955682Smarkm	write_state_destroy (&write_state);
51072445Sassar    }
51155682Smarkm    return 0;
51255682Smarkm}
51355682Smarkm
51455682Smarkm#ifdef KRB5
51555682Smarkmstatic int
51655682Smarkmdo_v5 (const char *host,
51755682Smarkm       int port,
51855682Smarkm       const char *user,
51955682Smarkm       const char *filename,
52055682Smarkm       const char *header_str,
52155682Smarkm       int leavep,
52255682Smarkm       int verbose,
52355682Smarkm       int forkp)
52455682Smarkm{
52555682Smarkm    krb5_error_code ret;
52655682Smarkm    krb5_auth_context auth_context = NULL;
52755682Smarkm    krb5_principal server;
52855682Smarkm    int s;
52955682Smarkm
53055682Smarkm    s = do_connect (host, port, 1);
53155682Smarkm    if (s < 0)
53255682Smarkm	return 1;
53355682Smarkm
53455682Smarkm    ret = krb5_sname_to_principal (context,
53555682Smarkm				   host,
53655682Smarkm				   "pop",
53755682Smarkm				   KRB5_NT_SRV_HST,
53855682Smarkm				   &server);
53955682Smarkm    if (ret) {
54055682Smarkm	warnx ("krb5_sname_to_principal: %s",
54155682Smarkm	       krb5_get_err_text (context, ret));
54255682Smarkm	return 1;
54355682Smarkm    }
54455682Smarkm
54555682Smarkm    ret = krb5_sendauth (context,
54655682Smarkm			 &auth_context,
54755682Smarkm			 &s,
54855682Smarkm			 "KPOPV1.0",
54955682Smarkm			 NULL,
55055682Smarkm			 server,
55155682Smarkm			 0,
55255682Smarkm			 NULL,
55355682Smarkm			 NULL,
55455682Smarkm			 NULL,
55555682Smarkm			 NULL,
55655682Smarkm			 NULL,
55755682Smarkm			 NULL);
55855682Smarkm    krb5_free_principal (context, server);
55955682Smarkm    if (ret) {
56055682Smarkm	warnx ("krb5_sendauth: %s",
56155682Smarkm	       krb5_get_err_text (context, ret));
56255682Smarkm	return 1;
56355682Smarkm    }
56455682Smarkm    return doit (s, host, user, filename, header_str, leavep, verbose, forkp);
56555682Smarkm}
56655682Smarkm#endif
56755682Smarkm
56855682Smarkm#ifdef KRB4
56955682Smarkmstatic int
57055682Smarkmdo_v4 (const char *host,
57155682Smarkm       int port,
57255682Smarkm       const char *user,
57355682Smarkm       const char *filename,
57455682Smarkm       const char *header_str,
57555682Smarkm       int leavep,
57655682Smarkm       int verbose,
57755682Smarkm       int forkp)
57855682Smarkm{
57955682Smarkm    KTEXT_ST ticket;
58055682Smarkm    MSG_DAT msg_data;
58155682Smarkm    CREDENTIALS cred;
58255682Smarkm    des_key_schedule sched;
58355682Smarkm    int s;
58455682Smarkm    int ret;
58555682Smarkm
58655682Smarkm    s = do_connect (host, port, 1);
58755682Smarkm    if (s < 0)
58855682Smarkm	return 1;
58955682Smarkm    ret = krb_sendauth(0,
59055682Smarkm		       s,
59155682Smarkm		       &ticket,
59255682Smarkm		       "pop",
59355682Smarkm		       (char *)host,
59455682Smarkm		       krb_realmofhost(host),
59555682Smarkm		       getpid(),
59655682Smarkm		       &msg_data,
59755682Smarkm		       &cred,
59855682Smarkm		       sched,
59955682Smarkm		       NULL,
60055682Smarkm		       NULL,
60155682Smarkm		       "KPOPV0.1");
60255682Smarkm    if(ret) {
60355682Smarkm	warnx("krb_sendauth: %s", krb_get_err_text(ret));
60455682Smarkm	return 1;
60555682Smarkm    }
60655682Smarkm    return doit (s, host, user, filename, header_str, leavep, verbose, forkp);
60755682Smarkm}
60855682Smarkm#endif /* KRB4 */
60955682Smarkm
61055682Smarkm#ifdef HESIOD
61155682Smarkm
61255682Smarkm#ifdef HESIOD_INTERFACES
61355682Smarkm
61455682Smarkmstatic char *
61555682Smarkmhesiod_get_pobox (const char **user)
61655682Smarkm{
61755682Smarkm    void *context;
61855682Smarkm    struct hesiod_postoffice *hpo;
61955682Smarkm    char *ret = NULL;
62055682Smarkm
62155682Smarkm    if(hesiod_init (&context) != 0)
62255682Smarkm	err (1, "hesiod_init");
62355682Smarkm
62455682Smarkm    hpo = hesiod_getmailhost (context, *user);
62555682Smarkm    if (hpo == NULL) {
62655682Smarkm	warn ("hesiod_getmailhost %s", *user);
62755682Smarkm    } else {
62855682Smarkm	if (strcasecmp(hpo->hesiod_po_type, "pop") != 0)
62955682Smarkm	    errx (1, "Unsupported po type %s", hpo->hesiod_po_type);
63055682Smarkm
63172445Sassar	ret = estrdup(hpo->hesiod_po_host);
63272445Sassar	*user = estrdup(hpo->hesiod_po_name);
63355682Smarkm	hesiod_free_postoffice (context, hpo);
63455682Smarkm    }
63555682Smarkm    hesiod_end (context);
63655682Smarkm    return ret;
63755682Smarkm}
63855682Smarkm
63955682Smarkm#else /* !HESIOD_INTERFACES */
64055682Smarkm
64155682Smarkmstatic char *
64255682Smarkmhesiod_get_pobox (const char **user)
64355682Smarkm{
64455682Smarkm    char *ret = NULL;
64555682Smarkm    struct hes_postoffice *hpo;
64655682Smarkm
64755682Smarkm    hpo = hes_getmailhost (*user);
64855682Smarkm    if (hpo == NULL) {
64955682Smarkm	warn ("hes_getmailhost %s", *user);
65055682Smarkm    } else {
65155682Smarkm	if (strcasecmp(hpo->po_type, "pop") != 0)
65255682Smarkm	    errx (1, "Unsupported po type %s", hpo->po_type);
65355682Smarkm
65472445Sassar	ret = estrdup(hpo->po_host);
65572445Sassar	*user = estrdup(hpo->po_name);
65655682Smarkm    }
65755682Smarkm    return ret;
65855682Smarkm}
65955682Smarkm
66055682Smarkm#endif /* HESIOD_INTERFACES */
66155682Smarkm
66255682Smarkm#endif /* HESIOD */
66355682Smarkm
66455682Smarkmstatic char *
66555682Smarkmget_pobox (const char **user)
66655682Smarkm{
66755682Smarkm    char *ret = NULL;
66855682Smarkm
66955682Smarkm#ifdef HESIOD
67055682Smarkm    ret = hesiod_get_pobox (user);
67155682Smarkm#endif
67255682Smarkm
67355682Smarkm    if (ret == NULL)
67455682Smarkm	ret = getenv("MAILHOST");
67555682Smarkm    if (ret == NULL)
67655682Smarkm	errx (1, "MAILHOST not set");
67755682Smarkm    return ret;
67855682Smarkm}
67955682Smarkm
68055682Smarkmstatic void
68155682Smarkmparse_pobox (char *a0, const char **host, const char **user)
68255682Smarkm{
68355682Smarkm    const char *h, *u;
68455682Smarkm    char *p;
68555682Smarkm    int po = 0;
68655682Smarkm
68755682Smarkm    if (a0 == NULL) {
68855682Smarkm
68955682Smarkm	*user = getenv ("USERNAME");
69055682Smarkm	if (*user == NULL) {
69155682Smarkm	    struct passwd *pwd = getpwuid (getuid ());
69255682Smarkm
69355682Smarkm	    if (pwd == NULL)
69455682Smarkm		errx (1, "Who are you?");
69572445Sassar	    *user = estrdup (pwd->pw_name);
69655682Smarkm	}
69755682Smarkm	*host = get_pobox (user);
69855682Smarkm	return;
69955682Smarkm    }
70055682Smarkm
70155682Smarkm    /* if the specification starts with po:, remember this information */
70255682Smarkm    if(strncmp(a0, "po:", 3) == 0) {
70355682Smarkm	a0 += 3;
70455682Smarkm	po++;
70555682Smarkm    }
70655682Smarkm    /* if there is an `@', the hostname is after it, otherwise at the
70755682Smarkm       beginning of the string */
70855682Smarkm    p = strchr(a0, '@');
70955682Smarkm    if(p != NULL) {
71055682Smarkm	*p++ = '\0';
71155682Smarkm	h = p;
71255682Smarkm    } else {
71355682Smarkm	h = a0;
71455682Smarkm    }
71555682Smarkm    /* if there is a `:', the username comes before it, otherwise at
71655682Smarkm       the beginning of the string */
71755682Smarkm    p = strchr(a0, ':');
71855682Smarkm    if(p != NULL) {
71955682Smarkm	*p++ = '\0';
72055682Smarkm	u = p;
72155682Smarkm    } else {
72255682Smarkm	u = a0;
72355682Smarkm    }
72455682Smarkm    if(h == u) {
72555682Smarkm	/* some inconsistent compatibility with various mailers */
72655682Smarkm	if(po) {
72755682Smarkm	    h = get_pobox (&u);
72855682Smarkm	} else {
72955682Smarkm	    u = get_default_username ();
73055682Smarkm	    if (u == NULL)
73155682Smarkm		errx (1, "Who are you?");
73255682Smarkm	}
73355682Smarkm    }
73455682Smarkm    *host = h;
73555682Smarkm    *user = u;
73655682Smarkm}
73755682Smarkm
73855682Smarkmint
73955682Smarkmmain(int argc, char **argv)
74055682Smarkm{
74155682Smarkm    int port = 0;
74255682Smarkm    int optind = 0;
74355682Smarkm    int ret = 1;
74455682Smarkm    const char *host, *user, *filename = NULL;
74555682Smarkm    char *pobox = NULL;
74655682Smarkm
74778527Sassar    setprogname (argv[0]);
74855682Smarkm
74955682Smarkm#ifdef KRB5
75072445Sassar    {
75172445Sassar	krb5_error_code ret;
75272445Sassar
75372445Sassar	ret = krb5_init_context (&context);
75472445Sassar	if (ret)
75572445Sassar	    errx (1, "krb5_init_context failed: %d", ret);
75672445Sassar    }
75755682Smarkm#endif
75855682Smarkm
75955682Smarkm    if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv,
76055682Smarkm		&optind))
76155682Smarkm	usage (1);
76255682Smarkm
76355682Smarkm    argc -= optind;
76455682Smarkm    argv += optind;
76555682Smarkm
76655682Smarkm#if defined(KRB4) && defined(KRB5)
76755682Smarkm    if(use_v4 == -1 && use_v5 == 1)
76855682Smarkm	use_v4 = 0;
76955682Smarkm    if(use_v5 == -1 && use_v4 == 1)
77055682Smarkm	use_v5 = 0;
77155682Smarkm#endif
77255682Smarkm
77355682Smarkm    if (do_help)
77455682Smarkm	usage (0);
77555682Smarkm
77655682Smarkm    if (do_version) {
77755682Smarkm	print_version(NULL);
77855682Smarkm	return 0;
77955682Smarkm    }
78055682Smarkm
78155682Smarkm    if (do_from && header_str == NULL)
78255682Smarkm	header_str = "From:";
78355682Smarkm    else if (header_str != NULL)
78455682Smarkm	do_from = 1;
78555682Smarkm
78655682Smarkm    if (do_from) {
78755682Smarkm	if (argc == 0)
78855682Smarkm	    pobox = NULL;
78955682Smarkm	else if (argc == 1)
79055682Smarkm	    pobox = argv[0];
79155682Smarkm	else
79255682Smarkm	    usage (1);
79355682Smarkm    } else {
79455682Smarkm	if (argc == 1) {
79555682Smarkm	    filename = argv[0];
79655682Smarkm	    pobox    = NULL;
79755682Smarkm	} else if (argc == 2) {
79855682Smarkm	    filename = argv[1];
79955682Smarkm	    pobox    = argv[0];
80055682Smarkm	} else
80155682Smarkm	    usage (1);
80255682Smarkm    }
80355682Smarkm
80455682Smarkm    if (port_str) {
80555682Smarkm	struct servent *s = roken_getservbyname (port_str, "tcp");
80655682Smarkm
80755682Smarkm	if (s)
80855682Smarkm	    port = s->s_port;
80955682Smarkm	else {
81055682Smarkm	    char *ptr;
81155682Smarkm
81255682Smarkm	    port = strtol (port_str, &ptr, 10);
81355682Smarkm	    if (port == 0 && ptr == port_str)
81455682Smarkm		errx (1, "Bad port `%s'", port_str);
81555682Smarkm	    port = htons(port);
81655682Smarkm	}
81755682Smarkm    }
81855682Smarkm    if (port == 0) {
81955682Smarkm#ifdef KRB5
82055682Smarkm	port = krb5_getportbyname (context, "kpop", "tcp", 1109);
82155682Smarkm#elif defined(KRB4)
82255682Smarkm	port = k_getportbyname ("kpop", "tcp", htons(1109));
82355682Smarkm#else
82455682Smarkm#error must define KRB4 or KRB5
82555682Smarkm#endif
82655682Smarkm    }
82755682Smarkm
82855682Smarkm    parse_pobox (pobox, &host, &user);
82955682Smarkm
83055682Smarkm#ifdef KRB5
83155682Smarkm    if (ret && use_v5) {
83255682Smarkm	ret = do_v5 (host, port, user, filename, header_str,
83355682Smarkm		     do_leave, verbose_level, do_fork);
83455682Smarkm    }
83555682Smarkm#endif
83655682Smarkm
83755682Smarkm#ifdef KRB4
83855682Smarkm    if (ret && use_v4) {
83955682Smarkm	ret = do_v4 (host, port, user, filename, header_str,
84055682Smarkm		     do_leave, verbose_level, do_fork);
84155682Smarkm    }
84255682Smarkm#endif /* KRB4 */
84355682Smarkm    return ret;
84455682Smarkm}
845