155682Smarkm/*
2233294Sstas * Copyright (c) 1997-2004 Kungliga Tekniska H��gskolan
3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden).
4233294Sstas * All rights reserved.
555682Smarkm *
6233294Sstas * Redistribution and use in source and binary forms, with or without
7233294Sstas * modification, are permitted provided that the following conditions
8233294Sstas * are met:
955682Smarkm *
10233294Sstas * 1. Redistributions of source code must retain the above copyright
11233294Sstas *    notice, this list of conditions and the following disclaimer.
1255682Smarkm *
13233294Sstas * 2. Redistributions in binary form must reproduce the above copyright
14233294Sstas *    notice, this list of conditions and the following disclaimer in the
15233294Sstas *    documentation and/or other materials provided with the distribution.
1655682Smarkm *
17233294Sstas * 3. Neither the name of the Institute nor the names of its contributors
18233294Sstas *    may be used to endorse or promote products derived from this software
19233294Sstas *    without specific prior written permission.
2055682Smarkm *
21233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24233294Sstas * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31233294Sstas * SUCH DAMAGE.
3255682Smarkm */
3355682Smarkm
3455682Smarkm#include "push_locl.h"
35233294SstasRCSID("$Id$");
3655682Smarkm
37233294Sstas#if defined(_AIX) && defined(STAT)
38233294Sstas/*
39233294Sstas * AIX defines STAT to 1 in sys/dir.h
40233294Sstas */
41233294Sstas#  undef STAT
4255682Smarkm#endif
4355682Smarkm
4455682Smarkm#ifdef KRB5
4555682Smarkmstatic int use_v5 = -1;
4655682Smarkmstatic krb5_context context;
4755682Smarkm#endif
4855682Smarkm
4955682Smarkmstatic char *port_str;
5055682Smarkmstatic int verbose_level;
5155682Smarkmstatic int do_fork;
5255682Smarkmstatic int do_leave;
5355682Smarkmstatic int do_version;
5455682Smarkmstatic int do_help;
5555682Smarkmstatic int do_from;
5655682Smarkmstatic int do_count;
5755682Smarkmstatic char *header_str;
5855682Smarkm
5955682Smarkmstruct getargs args[] = {
6055682Smarkm#ifdef KRB5
6155682Smarkm    { "krb5",	'5', arg_flag,		&use_v5,	"Use Kerberos V5",
6255682Smarkm      NULL },
6355682Smarkm#endif
6455682Smarkm    { "verbose",'v', arg_counter,	&verbose_level,	"Verbose",
6555682Smarkm      NULL },
6655682Smarkm    { "fork",	'f', arg_flag,		&do_fork,	"Fork deleting proc",
6755682Smarkm      NULL },
6855682Smarkm    { "leave",	'l', arg_flag,		&do_leave,	"Leave mail on server",
6955682Smarkm      NULL },
7055682Smarkm    { "port",	'p', arg_string,	&port_str,	"Use this port",
7155682Smarkm      "number-or-service" },
7255682Smarkm    { "from",	 0,  arg_flag,		&do_from,	"Behave like from",
7355682Smarkm      NULL },
7472445Sassar    { "headers", 0,  arg_string,	&header_str,	"Headers to print", NULL },
7555682Smarkm    { "count", 'c',  arg_flag,		&do_count,	"Print number of messages", NULL},
7655682Smarkm    { "version", 0,  arg_flag,		&do_version,	"Print version",
7755682Smarkm      NULL },
7855682Smarkm    { "help",	 0,  arg_flag,		&do_help,	NULL,
7955682Smarkm      NULL }
8055682Smarkm
8155682Smarkm};
8255682Smarkm
8355682Smarkmstatic void
8455682Smarkmusage (int ret)
8555682Smarkm{
8655682Smarkm    arg_printusage (args,
8755682Smarkm		    sizeof(args) / sizeof(args[0]),
8855682Smarkm		    NULL,
8972445Sassar		    "[[{po:username[@hostname] | hostname[:username]}] ...] "
9055682Smarkm		    "filename");
9155682Smarkm    exit (ret);
9255682Smarkm}
9355682Smarkm
9455682Smarkmstatic int
9555682Smarkmdo_connect (const char *hostname, int port, int nodelay)
9655682Smarkm{
9755682Smarkm    struct addrinfo *ai, *a;
9855682Smarkm    struct addrinfo hints;
9955682Smarkm    int error;
10072445Sassar    int s = -1;
10155682Smarkm    char portstr[NI_MAXSERV];
10255682Smarkm
10355682Smarkm    memset (&hints, 0, sizeof(hints));
10455682Smarkm    hints.ai_socktype = SOCK_STREAM;
10555682Smarkm    hints.ai_protocol = IPPROTO_TCP;
10655682Smarkm
10755682Smarkm    snprintf (portstr, sizeof(portstr), "%u", ntohs(port));
10855682Smarkm
10955682Smarkm    error = getaddrinfo (hostname, portstr, &hints, &ai);
11055682Smarkm    if (error)
11155682Smarkm	errx (1, "getaddrinfo(%s): %s", hostname, gai_strerror(error));
11255682Smarkm
11355682Smarkm    for (a = ai; a != NULL; a = a->ai_next) {
11455682Smarkm	s = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
11555682Smarkm	if (s < 0)
11655682Smarkm	    continue;
11755682Smarkm	if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
11855682Smarkm	    warn ("connect(%s)", hostname);
11955682Smarkm 	    close (s);
12055682Smarkm 	    continue;
12155682Smarkm	}
12255682Smarkm	break;
12355682Smarkm    }
12455682Smarkm    freeaddrinfo (ai);
12555682Smarkm    if (a == NULL) {
12655682Smarkm	warnx ("failed to contact %s", hostname);
12755682Smarkm	return -1;
12855682Smarkm    }
12955682Smarkm
13055682Smarkm    if(setsockopt(s, IPPROTO_TCP, TCP_NODELAY,
13155682Smarkm		  (void *)&nodelay, sizeof(nodelay)) < 0)
13255682Smarkm	err (1, "setsockopt TCP_NODELAY");
13355682Smarkm    return s;
13455682Smarkm}
13555682Smarkm
136233294Sstastypedef enum { INIT = 0, GREET, USER, PASS, STAT, RETR, TOP,
13755682Smarkm	       DELE, XDELE, QUIT} pop_state;
13855682Smarkm
139120945Snectarstatic char *pop_state_string[] = {
140120945Snectar    "INIT", "GREET", "USER", "PASS", "STAT", "RETR", "TOP",
141233294Sstas    "DELE", "XDELE", "QUIT"
142120945Snectar};
143120945Snectar
14455682Smarkm#define PUSH_BUFSIZ 65536
14555682Smarkm
14655682Smarkm#define STEP 16
14755682Smarkm
14855682Smarkmstruct write_state {
14955682Smarkm    struct iovec *iovecs;
15055682Smarkm    size_t niovecs, maxiovecs, allociovecs;
15155682Smarkm    int fd;
15255682Smarkm};
15355682Smarkm
15455682Smarkmstatic void
15555682Smarkmwrite_state_init (struct write_state *w, int fd)
15655682Smarkm{
15755682Smarkm#ifdef UIO_MAXIOV
15855682Smarkm    w->maxiovecs = UIO_MAXIOV;
15955682Smarkm#else
16055682Smarkm    w->maxiovecs = 16;
16155682Smarkm#endif
16255682Smarkm    w->allociovecs = min(STEP, w->maxiovecs);
16355682Smarkm    w->niovecs = 0;
16472445Sassar    w->iovecs = emalloc(w->allociovecs * sizeof(*w->iovecs));
16555682Smarkm    w->fd = fd;
16655682Smarkm}
16755682Smarkm
16855682Smarkmstatic void
16955682Smarkmwrite_state_add (struct write_state *w, void *v, size_t len)
17055682Smarkm{
171233294Sstas    if(w->niovecs == w->allociovecs) {
172233294Sstas	if(w->niovecs == w->maxiovecs) {
173233294Sstas	    if(writev (w->fd, w->iovecs, w->niovecs) < 0)
174233294Sstas		err(1, "writev");
175233294Sstas	    w->niovecs = 0;
176233294Sstas	} else {
177233294Sstas	    w->allociovecs = min(w->allociovecs + STEP, w->maxiovecs);
178233294Sstas	    w->iovecs = erealloc (w->iovecs,
179233294Sstas				  w->allociovecs * sizeof(*w->iovecs));
180233294Sstas	}
181233294Sstas    }
182233294Sstas    w->iovecs[w->niovecs].iov_base = v;
183233294Sstas    w->iovecs[w->niovecs].iov_len  = len;
184233294Sstas    ++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];
21590926Snectar    int out_len = 0;
216142403Snectar    char *in_buf;
217142403Snectar    size_t in_buf_size;
21855682Smarkm    size_t in_len = 0;
219142403Snectar    char *in_ptr;
22055682Smarkm    pop_state state = INIT;
221233294Sstas    unsigned count = 0, bytes;
22255682Smarkm    unsigned asked_for = 0, retrieved = 0, asked_deleted = 0, deleted = 0;
22355682Smarkm    unsigned sent_xdele = 0;
22455682Smarkm    int out_fd;
22555682Smarkm    char from_line[128];
22655682Smarkm    size_t from_line_length;
22755682Smarkm    time_t now;
22855682Smarkm    struct write_state write_state;
229233294Sstas    unsigned int numheaders = 1;
23072445Sassar    char **headers = NULL;
23172445Sassar    int i;
23272445Sassar    char *tmp = NULL;
23355682Smarkm
234142403Snectar    in_buf = emalloc(PUSH_BUFSIZ + 1);
235142403Snectar    in_ptr = in_buf;
236142403Snectar    in_buf_size = PUSH_BUFSIZ;
237142403Snectar
23855682Smarkm    if (do_from) {
23972445Sassar	char *tmp2;
24072445Sassar
24172445Sassar	tmp2 = tmp = estrdup(header_str);
24272445Sassar
24355682Smarkm	out_fd = -1;
24455682Smarkm	if (verbose)
24555682Smarkm	    fprintf (stderr, "%s@%s\n", user, host);
24672445Sassar	while (*tmp != '\0') {
24772445Sassar	    tmp = strchr(tmp, ',');
24872445Sassar	    if (tmp == NULL)
24972445Sassar		break;
25072445Sassar	    tmp++;
25172445Sassar	    numheaders++;
25272445Sassar	}
25372445Sassar
25472445Sassar	headers = emalloc(sizeof(char *) * (numheaders + 1));
25572445Sassar	for (i = 0; i < numheaders; i++) {
25672445Sassar	    headers[i] = strtok_r(tmp2, ",", &tmp2);
25772445Sassar	}
25872445Sassar	headers[numheaders] = NULL;
25955682Smarkm    } else {
26055682Smarkm	out_fd = open(outfilename, O_WRONLY | O_APPEND | O_CREAT, 0666);
26155682Smarkm	if (out_fd < 0)
26255682Smarkm	    err (1, "open %s", outfilename);
26355682Smarkm	if (verbose)
26455682Smarkm	    fprintf (stderr, "%s@%s -> %s\n", user, host, outfilename);
26555682Smarkm    }
26655682Smarkm
26755682Smarkm    now = time(NULL);
26855682Smarkm    from_line_length = snprintf (from_line, sizeof(from_line),
26955682Smarkm				 "From %s %s", "push", ctime(&now));
270178825Sdfr    if (from_line_length < 0 || from_line_length > sizeof(from_line))
271178825Sdfr	errx (1, "snprintf failed");
27255682Smarkm
27355682Smarkm    out_len = snprintf (out_buf, sizeof(out_buf),
27455682Smarkm			"USER %s\r\nPASS hej\r\nSTAT\r\n",
27555682Smarkm			user);
276178825Sdfr    if (out_len < 0 || out_len > sizeof(out_buf))
27790926Snectar	errx (1, "snprintf failed");
27855682Smarkm    if (net_write (s, out_buf, out_len) != out_len)
27955682Smarkm	err (1, "write");
28055682Smarkm    if (verbose > 1)
281120945Snectar	fprintf (stderr, "%s", out_buf);
28255682Smarkm
28355682Smarkm    if (!do_from)
28455682Smarkm	write_state_init (&write_state, out_fd);
28555682Smarkm
28655682Smarkm    while(state != QUIT) {
28755682Smarkm	fd_set readset, writeset;
28855682Smarkm
28955682Smarkm	FD_ZERO(&readset);
29055682Smarkm	FD_ZERO(&writeset);
29172445Sassar	if (s >= FD_SETSIZE)
29272445Sassar	    errx (1, "fd too large");
29355682Smarkm	FD_SET(s,&readset);
294120945Snectar
295120945Snectar	if (verbose > 1)
296120945Snectar	    fprintf (stderr, "state: %s count: %d asked_for: %d "
297120945Snectar		     "retrieved: %d asked_deleted: %d\n",
298233294Sstas		     pop_state_string[state],
299120945Snectar		     count, asked_for, retrieved, asked_deleted);
300120945Snectar
30155682Smarkm	if (((state == STAT || state == RETR || state == TOP)
30255682Smarkm	     && asked_for < count)
30355682Smarkm	    || (state == XDELE && !sent_xdele)
30455682Smarkm	    || (state == DELE && asked_deleted < count))
30555682Smarkm	    FD_SET(s,&writeset);
30655682Smarkm	ret = select (s + 1, &readset, &writeset, NULL, NULL);
30755682Smarkm	if (ret < 0) {
30855682Smarkm	    if (errno == EAGAIN)
30955682Smarkm		continue;
31055682Smarkm	    else
31155682Smarkm		err (1, "select");
31255682Smarkm	}
313233294Sstas
31455682Smarkm	if (FD_ISSET(s, &readset)) {
31555682Smarkm	    char *beg, *p;
31655682Smarkm	    size_t rem;
31755682Smarkm	    int blank_line = 0;
318233294Sstas
319142403Snectar	    if(in_len >= in_buf_size) {
320142403Snectar		char *tmp = erealloc(in_buf, in_buf_size + PUSH_BUFSIZ + 1);
321142403Snectar		in_ptr = tmp + (in_ptr - in_buf);
322142403Snectar		in_buf = tmp;
323142403Snectar		in_buf_size += PUSH_BUFSIZ;
324142403Snectar	    }
325142403Snectar
326142403Snectar	    ret = read (s, in_ptr, in_buf_size - in_len);
32755682Smarkm	    if (ret < 0)
32855682Smarkm		err (1, "read");
32955682Smarkm	    else if (ret == 0)
33055682Smarkm		errx (1, "EOF during read");
331233294Sstas
33255682Smarkm	    in_len += ret;
33355682Smarkm	    in_ptr += ret;
33455682Smarkm	    *in_ptr = '\0';
335233294Sstas
33655682Smarkm	    beg = in_buf;
33755682Smarkm	    rem = in_len;
33855682Smarkm	    while(rem > 1
33955682Smarkm		  && (p = strstr(beg, "\r\n")) != NULL) {
34055682Smarkm		if (state == TOP) {
34155682Smarkm		    char *copy = beg;
34255682Smarkm
34372445Sassar		    for (i = 0; i < numheaders; i++) {
34472445Sassar			size_t len;
34572445Sassar
34672445Sassar			len = min(p - copy + 1, strlen(headers[i]));
34772445Sassar			if (strncasecmp(copy, headers[i], len) == 0) {
34872445Sassar			    fprintf (stdout, "%.*s\n", (int)(p - copy), copy);
34972445Sassar			}
35055682Smarkm		    }
35155682Smarkm		    if (beg[0] == '.' && beg[1] == '\r' && beg[2] == '\n') {
35272445Sassar			if (numheaders > 1)
35372445Sassar			    fprintf (stdout, "\n");
35455682Smarkm			state = STAT;
35555682Smarkm			if (++retrieved == count) {
35655682Smarkm			    state = QUIT;
35755682Smarkm			    net_write (s, "QUIT\r\n", 6);
35855682Smarkm			    if (verbose > 1)
359120945Snectar				fprintf (stderr, "QUIT\r\n");
36055682Smarkm			}
36155682Smarkm		    }
36255682Smarkm		    rem -= p - beg + 2;
36355682Smarkm		    beg = p + 2;
36455682Smarkm		} else if (state == RETR) {
36555682Smarkm		    char *copy = beg;
36655682Smarkm		    if (beg[0] == '.') {
36755682Smarkm			if (beg[1] == '\r' && beg[2] == '\n') {
36855682Smarkm			    if(!blank_line)
36955682Smarkm				write_state_add(&write_state, "\n", 1);
37055682Smarkm			    state = STAT;
37155682Smarkm			    rem -= p - beg + 2;
37255682Smarkm			    beg = p + 2;
37355682Smarkm			    if (++retrieved == count) {
37455682Smarkm				write_state_flush (&write_state);
37555682Smarkm				if (fsync (out_fd) < 0)
37655682Smarkm				    err (1, "fsync");
37755682Smarkm				close(out_fd);
37855682Smarkm				if (leavep) {
37955682Smarkm				    state = QUIT;
38055682Smarkm				    net_write (s, "QUIT\r\n", 6);
38155682Smarkm				    if (verbose > 1)
382120945Snectar					fprintf (stderr, "QUIT\r\n");
38355682Smarkm				} else {
38455682Smarkm				    if (forkp) {
38555682Smarkm					pid_t pid;
38655682Smarkm
38755682Smarkm					pid = fork();
38855682Smarkm					if (pid < 0)
38955682Smarkm					    warn ("fork");
39055682Smarkm					else if(pid != 0) {
39155682Smarkm					    if(verbose)
39255682Smarkm						fprintf (stderr,
39355682Smarkm							 "(exiting)");
39455682Smarkm					    return 0;
39555682Smarkm					}
39655682Smarkm				    }
39755682Smarkm
39855682Smarkm				    state = XDELE;
39955682Smarkm				    if (verbose)
40055682Smarkm					fprintf (stderr, "deleting... ");
40155682Smarkm				}
40255682Smarkm			    }
40355682Smarkm			    continue;
40455682Smarkm			} else
40555682Smarkm			    ++copy;
40655682Smarkm		    }
40755682Smarkm		    *p = '\n';
408233294Sstas		    if(blank_line &&
40955682Smarkm		       strncmp(copy, "From ", min(p - copy + 1, 5)) == 0)
41055682Smarkm			write_state_add(&write_state, ">", 1);
41155682Smarkm		    write_state_add(&write_state, copy, p - copy + 1);
41255682Smarkm		    blank_line = (*copy == '\n');
41355682Smarkm		    rem -= p - beg + 2;
41455682Smarkm		    beg = p + 2;
41555682Smarkm		} else if (rem >= 3 && strncmp (beg, "+OK", 3) == 0) {
41655682Smarkm		    if (state == STAT) {
41755682Smarkm			if (!do_from)
41855682Smarkm			    write_state_add(&write_state,
41955682Smarkm					    from_line, from_line_length);
42055682Smarkm			blank_line = 0;
421233294Sstas			if (do_from)
42255682Smarkm			    state = TOP;
42355682Smarkm			else
42455682Smarkm			    state = RETR;
42555682Smarkm		    } else if (state == XDELE) {
42655682Smarkm			state = QUIT;
42755682Smarkm			net_write (s, "QUIT\r\n", 6);
42855682Smarkm			if (verbose > 1)
429120945Snectar			    fprintf (stderr, "QUIT\r\n");
43055682Smarkm			break;
43155682Smarkm		    } else if (state == DELE) {
43255682Smarkm			if (++deleted == count) {
43355682Smarkm			    state = QUIT;
43455682Smarkm			    net_write (s, "QUIT\r\n", 6);
43555682Smarkm			    if (verbose > 1)
436120945Snectar				fprintf (stderr, "QUIT\r\n");
43755682Smarkm			    break;
43855682Smarkm			}
43955682Smarkm		    } else if (++state == STAT) {
44055682Smarkm			if(sscanf (beg + 4, "%u %u", &count, &bytes) != 2)
44155682Smarkm			    errx(1, "Bad STAT-line: %.*s", (int)(p - beg), beg);
44255682Smarkm			if (verbose) {
44355682Smarkm			    fprintf (stderr, "%u message(s) (%u bytes). "
44455682Smarkm				     "fetching... ",
44555682Smarkm				     count, bytes);
44655682Smarkm			    if (do_from)
44755682Smarkm				fprintf (stderr, "\n");
44855682Smarkm			} else if (do_count) {
44955682Smarkm			    fprintf (stderr, "%u message(s) (%u bytes).\n",
45055682Smarkm				     count, bytes);
45155682Smarkm			}
45255682Smarkm			if (count == 0) {
45355682Smarkm			    state = QUIT;
45455682Smarkm			    net_write (s, "QUIT\r\n", 6);
45555682Smarkm			    if (verbose > 1)
456120945Snectar				fprintf (stderr, "QUIT\r\n");
45755682Smarkm			    break;
45855682Smarkm			}
45955682Smarkm		    }
46055682Smarkm
46155682Smarkm		    rem -= p - beg + 2;
46255682Smarkm		    beg = p + 2;
46355682Smarkm		} else {
46455682Smarkm		    if(state == XDELE) {
46555682Smarkm			state = DELE;
46655682Smarkm			rem -= p - beg + 2;
46755682Smarkm			beg = p + 2;
46855682Smarkm		    } else
46955682Smarkm			errx (1, "Bad response: %.*s", (int)(p - beg), beg);
47055682Smarkm		}
47155682Smarkm	    }
47255682Smarkm	    if (!do_from)
47355682Smarkm		write_state_flush (&write_state);
47455682Smarkm
47555682Smarkm	    memmove (in_buf, beg, rem);
47655682Smarkm	    in_len = rem;
47755682Smarkm	    in_ptr = in_buf + rem;
47855682Smarkm	}
47955682Smarkm	if (FD_ISSET(s, &writeset)) {
48055682Smarkm	    if ((state == STAT && !do_from) || state == RETR)
48155682Smarkm		out_len = snprintf (out_buf, sizeof(out_buf),
48255682Smarkm				    "RETR %u\r\n", ++asked_for);
48355682Smarkm	    else if ((state == STAT && do_from) || state == TOP)
48455682Smarkm		out_len = snprintf (out_buf, sizeof(out_buf),
48555682Smarkm				    "TOP %u 0\r\n", ++asked_for);
48655682Smarkm	    else if(state == XDELE) {
48755682Smarkm		out_len = snprintf(out_buf, sizeof(out_buf),
48855682Smarkm				   "XDELE %u %u\r\n", 1, count);
48955682Smarkm		sent_xdele++;
49055682Smarkm	    }
49155682Smarkm	    else if(state == DELE)
49255682Smarkm		out_len = snprintf (out_buf, sizeof(out_buf),
49355682Smarkm				    "DELE %u\r\n", ++asked_deleted);
494178825Sdfr	    if (out_len < 0 || out_len > sizeof(out_buf))
49590926Snectar		errx (1, "snprintf failed");
49655682Smarkm	    if (net_write (s, out_buf, out_len) != out_len)
49755682Smarkm		err (1, "write");
49855682Smarkm	    if (verbose > 1)
499120945Snectar		fprintf (stderr, "%s", out_buf);
50055682Smarkm	}
50155682Smarkm    }
50255682Smarkm    if (verbose)
50355682Smarkm	fprintf (stderr, "Done\n");
50472445Sassar    if (do_from) {
50572445Sassar	free (tmp);
50672445Sassar	free (headers);
50772445Sassar    } else {
50855682Smarkm	write_state_destroy (&write_state);
50972445Sassar    }
51055682Smarkm    return 0;
51155682Smarkm}
51255682Smarkm
51355682Smarkm#ifdef KRB5
51455682Smarkmstatic int
51555682Smarkmdo_v5 (const char *host,
51655682Smarkm       int port,
51755682Smarkm       const char *user,
51855682Smarkm       const char *filename,
51955682Smarkm       const char *header_str,
52055682Smarkm       int leavep,
52155682Smarkm       int verbose,
52255682Smarkm       int forkp)
52355682Smarkm{
52455682Smarkm    krb5_error_code ret;
52555682Smarkm    krb5_auth_context auth_context = NULL;
52655682Smarkm    krb5_principal server;
52755682Smarkm    int s;
52855682Smarkm
52955682Smarkm    s = do_connect (host, port, 1);
53055682Smarkm    if (s < 0)
53155682Smarkm	return 1;
53255682Smarkm
53355682Smarkm    ret = krb5_sname_to_principal (context,
53455682Smarkm				   host,
53555682Smarkm				   "pop",
53655682Smarkm				   KRB5_NT_SRV_HST,
53755682Smarkm				   &server);
53855682Smarkm    if (ret) {
53955682Smarkm	warnx ("krb5_sname_to_principal: %s",
54055682Smarkm	       krb5_get_err_text (context, ret));
54155682Smarkm	return 1;
54255682Smarkm    }
54355682Smarkm
54455682Smarkm    ret = krb5_sendauth (context,
54555682Smarkm			 &auth_context,
54655682Smarkm			 &s,
54755682Smarkm			 "KPOPV1.0",
54855682Smarkm			 NULL,
54955682Smarkm			 server,
55055682Smarkm			 0,
55155682Smarkm			 NULL,
55255682Smarkm			 NULL,
55355682Smarkm			 NULL,
55455682Smarkm			 NULL,
55555682Smarkm			 NULL,
55655682Smarkm			 NULL);
55755682Smarkm    krb5_free_principal (context, server);
55855682Smarkm    if (ret) {
55955682Smarkm	warnx ("krb5_sendauth: %s",
56055682Smarkm	       krb5_get_err_text (context, ret));
56155682Smarkm	return 1;
56255682Smarkm    }
56355682Smarkm    return doit (s, host, user, filename, header_str, leavep, verbose, forkp);
56455682Smarkm}
56555682Smarkm#endif
56655682Smarkm
56755682Smarkm#ifdef HESIOD
56855682Smarkm
56955682Smarkm#ifdef HESIOD_INTERFACES
57055682Smarkm
57155682Smarkmstatic char *
57255682Smarkmhesiod_get_pobox (const char **user)
57355682Smarkm{
57455682Smarkm    void *context;
57555682Smarkm    struct hesiod_postoffice *hpo;
57655682Smarkm    char *ret = NULL;
57755682Smarkm
57855682Smarkm    if(hesiod_init (&context) != 0)
57955682Smarkm	err (1, "hesiod_init");
58055682Smarkm
58155682Smarkm    hpo = hesiod_getmailhost (context, *user);
58255682Smarkm    if (hpo == NULL) {
58355682Smarkm	warn ("hesiod_getmailhost %s", *user);
58455682Smarkm    } else {
58555682Smarkm	if (strcasecmp(hpo->hesiod_po_type, "pop") != 0)
58655682Smarkm	    errx (1, "Unsupported po type %s", hpo->hesiod_po_type);
58755682Smarkm
58872445Sassar	ret = estrdup(hpo->hesiod_po_host);
58972445Sassar	*user = estrdup(hpo->hesiod_po_name);
59055682Smarkm	hesiod_free_postoffice (context, hpo);
59155682Smarkm    }
59255682Smarkm    hesiod_end (context);
59355682Smarkm    return ret;
59455682Smarkm}
59555682Smarkm
59655682Smarkm#else /* !HESIOD_INTERFACES */
59755682Smarkm
59855682Smarkmstatic char *
59955682Smarkmhesiod_get_pobox (const char **user)
60055682Smarkm{
60155682Smarkm    char *ret = NULL;
60255682Smarkm    struct hes_postoffice *hpo;
60355682Smarkm
60455682Smarkm    hpo = hes_getmailhost (*user);
60555682Smarkm    if (hpo == NULL) {
60655682Smarkm	warn ("hes_getmailhost %s", *user);
60755682Smarkm    } else {
60855682Smarkm	if (strcasecmp(hpo->po_type, "pop") != 0)
60955682Smarkm	    errx (1, "Unsupported po type %s", hpo->po_type);
61055682Smarkm
61172445Sassar	ret = estrdup(hpo->po_host);
61272445Sassar	*user = estrdup(hpo->po_name);
61355682Smarkm    }
61455682Smarkm    return ret;
61555682Smarkm}
61655682Smarkm
61755682Smarkm#endif /* HESIOD_INTERFACES */
61855682Smarkm
61955682Smarkm#endif /* HESIOD */
62055682Smarkm
62155682Smarkmstatic char *
62255682Smarkmget_pobox (const char **user)
62355682Smarkm{
62455682Smarkm    char *ret = NULL;
62555682Smarkm
62655682Smarkm#ifdef HESIOD
62755682Smarkm    ret = hesiod_get_pobox (user);
62855682Smarkm#endif
62955682Smarkm
63055682Smarkm    if (ret == NULL)
63155682Smarkm	ret = getenv("MAILHOST");
63255682Smarkm    if (ret == NULL)
63355682Smarkm	errx (1, "MAILHOST not set");
63455682Smarkm    return ret;
63555682Smarkm}
63655682Smarkm
63755682Smarkmstatic void
63855682Smarkmparse_pobox (char *a0, const char **host, const char **user)
63955682Smarkm{
64055682Smarkm    const char *h, *u;
64155682Smarkm    char *p;
64255682Smarkm    int po = 0;
64355682Smarkm
64455682Smarkm    if (a0 == NULL) {
64555682Smarkm
64655682Smarkm	*user = getenv ("USERNAME");
64755682Smarkm	if (*user == NULL) {
64855682Smarkm	    struct passwd *pwd = getpwuid (getuid ());
64955682Smarkm
65055682Smarkm	    if (pwd == NULL)
65155682Smarkm		errx (1, "Who are you?");
65272445Sassar	    *user = estrdup (pwd->pw_name);
65355682Smarkm	}
65455682Smarkm	*host = get_pobox (user);
65555682Smarkm	return;
65655682Smarkm    }
65755682Smarkm
65855682Smarkm    /* if the specification starts with po:, remember this information */
65955682Smarkm    if(strncmp(a0, "po:", 3) == 0) {
66055682Smarkm	a0 += 3;
66155682Smarkm	po++;
66255682Smarkm    }
66355682Smarkm    /* if there is an `@', the hostname is after it, otherwise at the
66455682Smarkm       beginning of the string */
66555682Smarkm    p = strchr(a0, '@');
66655682Smarkm    if(p != NULL) {
66755682Smarkm	*p++ = '\0';
66855682Smarkm	h = p;
66955682Smarkm    } else {
67055682Smarkm	h = a0;
67155682Smarkm    }
67255682Smarkm    /* if there is a `:', the username comes before it, otherwise at
67355682Smarkm       the beginning of the string */
67455682Smarkm    p = strchr(a0, ':');
67555682Smarkm    if(p != NULL) {
67655682Smarkm	*p++ = '\0';
67755682Smarkm	u = p;
67855682Smarkm    } else {
67955682Smarkm	u = a0;
68055682Smarkm    }
68155682Smarkm    if(h == u) {
68255682Smarkm	/* some inconsistent compatibility with various mailers */
68355682Smarkm	if(po) {
68455682Smarkm	    h = get_pobox (&u);
68555682Smarkm	} else {
68655682Smarkm	    u = get_default_username ();
68755682Smarkm	    if (u == NULL)
68855682Smarkm		errx (1, "Who are you?");
68955682Smarkm	}
69055682Smarkm    }
69155682Smarkm    *host = h;
69255682Smarkm    *user = u;
69355682Smarkm}
69455682Smarkm
69555682Smarkmint
69655682Smarkmmain(int argc, char **argv)
69755682Smarkm{
69855682Smarkm    int port = 0;
69955682Smarkm    int optind = 0;
70055682Smarkm    int ret = 1;
70155682Smarkm    const char *host, *user, *filename = NULL;
70255682Smarkm    char *pobox = NULL;
70355682Smarkm
70478527Sassar    setprogname (argv[0]);
70555682Smarkm
70655682Smarkm#ifdef KRB5
70772445Sassar    {
70872445Sassar	krb5_error_code ret;
70972445Sassar
71072445Sassar	ret = krb5_init_context (&context);
71172445Sassar	if (ret)
71272445Sassar	    errx (1, "krb5_init_context failed: %d", ret);
71372445Sassar    }
71455682Smarkm#endif
71555682Smarkm
71655682Smarkm    if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv,
71755682Smarkm		&optind))
71855682Smarkm	usage (1);
71955682Smarkm
72055682Smarkm    argc -= optind;
72155682Smarkm    argv += optind;
72255682Smarkm
72355682Smarkm    if (do_help)
72455682Smarkm	usage (0);
72555682Smarkm
72655682Smarkm    if (do_version) {
72755682Smarkm	print_version(NULL);
72855682Smarkm	return 0;
72955682Smarkm    }
730233294Sstas
73155682Smarkm    if (do_from && header_str == NULL)
73255682Smarkm	header_str = "From:";
73355682Smarkm    else if (header_str != NULL)
73455682Smarkm	do_from = 1;
73555682Smarkm
73655682Smarkm    if (do_from) {
73755682Smarkm	if (argc == 0)
73855682Smarkm	    pobox = NULL;
73955682Smarkm	else if (argc == 1)
74055682Smarkm	    pobox = argv[0];
74155682Smarkm	else
74255682Smarkm	    usage (1);
74355682Smarkm    } else {
74455682Smarkm	if (argc == 1) {
74555682Smarkm	    filename = argv[0];
74655682Smarkm	    pobox    = NULL;
74755682Smarkm	} else if (argc == 2) {
74855682Smarkm	    filename = argv[1];
74955682Smarkm	    pobox    = argv[0];
75055682Smarkm	} else
75155682Smarkm	    usage (1);
75255682Smarkm    }
75355682Smarkm
75455682Smarkm    if (port_str) {
75555682Smarkm	struct servent *s = roken_getservbyname (port_str, "tcp");
75655682Smarkm
75755682Smarkm	if (s)
75855682Smarkm	    port = s->s_port;
75955682Smarkm	else {
76055682Smarkm	    char *ptr;
76155682Smarkm
76255682Smarkm	    port = strtol (port_str, &ptr, 10);
76355682Smarkm	    if (port == 0 && ptr == port_str)
76455682Smarkm		errx (1, "Bad port `%s'", port_str);
76555682Smarkm	    port = htons(port);
76655682Smarkm	}
76755682Smarkm    }
76855682Smarkm    if (port == 0) {
76955682Smarkm#ifdef KRB5
77055682Smarkm	port = krb5_getportbyname (context, "kpop", "tcp", 1109);
77155682Smarkm#else
772233294Sstas#error must define KRB5
77355682Smarkm#endif
77455682Smarkm    }
77555682Smarkm
77655682Smarkm    parse_pobox (pobox, &host, &user);
77755682Smarkm
77855682Smarkm#ifdef KRB5
77955682Smarkm    if (ret && use_v5) {
78055682Smarkm	ret = do_v5 (host, port, user, filename, header_str,
78155682Smarkm		     do_leave, verbose_level, do_fork);
78255682Smarkm    }
78355682Smarkm#endif
78455682Smarkm    return ret;
78555682Smarkm}
786