push.c revision 142403
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"
35142403SnectarRCSID("$Id: push.c,v 1.47.2.1 2004/06/21 10:54:46 lha 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 },
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));
27155682Smarkm
27255682Smarkm    out_len = snprintf (out_buf, sizeof(out_buf),
27355682Smarkm			"USER %s\r\nPASS hej\r\nSTAT\r\n",
27455682Smarkm			user);
27590926Snectar    if (out_len < 0)
27690926Snectar	errx (1, "snprintf failed");
27755682Smarkm    if (net_write (s, out_buf, out_len) != out_len)
27855682Smarkm	err (1, "write");
27955682Smarkm    if (verbose > 1)
280120945Snectar	fprintf (stderr, "%s", out_buf);
28155682Smarkm
28255682Smarkm    if (!do_from)
28355682Smarkm	write_state_init (&write_state, out_fd);
28455682Smarkm
28555682Smarkm    while(state != QUIT) {
28655682Smarkm	fd_set readset, writeset;
28755682Smarkm
28855682Smarkm	FD_ZERO(&readset);
28955682Smarkm	FD_ZERO(&writeset);
29072445Sassar	if (s >= FD_SETSIZE)
29172445Sassar	    errx (1, "fd too large");
29255682Smarkm	FD_SET(s,&readset);
293120945Snectar
294120945Snectar	if (verbose > 1)
295120945Snectar	    fprintf (stderr, "state: %s count: %d asked_for: %d "
296120945Snectar		     "retrieved: %d asked_deleted: %d\n",
297120945Snectar		     pop_state_string[state],
298120945Snectar		     count, asked_for, retrieved, asked_deleted);
299120945Snectar
30055682Smarkm	if (((state == STAT || state == RETR || state == TOP)
30155682Smarkm	     && asked_for < count)
30255682Smarkm	    || (state == XDELE && !sent_xdele)
30355682Smarkm	    || (state == DELE && asked_deleted < count))
30455682Smarkm	    FD_SET(s,&writeset);
30555682Smarkm	ret = select (s + 1, &readset, &writeset, NULL, NULL);
30655682Smarkm	if (ret < 0) {
30755682Smarkm	    if (errno == EAGAIN)
30855682Smarkm		continue;
30955682Smarkm	    else
31055682Smarkm		err (1, "select");
31155682Smarkm	}
31255682Smarkm
31355682Smarkm	if (FD_ISSET(s, &readset)) {
31455682Smarkm	    char *beg, *p;
31555682Smarkm	    size_t rem;
31655682Smarkm	    int blank_line = 0;
31755682Smarkm
318142403Snectar	    if(in_len >= in_buf_size) {
319142403Snectar		char *tmp = erealloc(in_buf, in_buf_size + PUSH_BUFSIZ + 1);
320142403Snectar		in_ptr = tmp + (in_ptr - in_buf);
321142403Snectar		in_buf = tmp;
322142403Snectar		in_buf_size += PUSH_BUFSIZ;
323142403Snectar	    }
324142403Snectar
325142403Snectar	    ret = read (s, in_ptr, in_buf_size - in_len);
32655682Smarkm	    if (ret < 0)
32755682Smarkm		err (1, "read");
32855682Smarkm	    else if (ret == 0)
32955682Smarkm		errx (1, "EOF during read");
33055682Smarkm
33155682Smarkm	    in_len += ret;
33255682Smarkm	    in_ptr += ret;
33355682Smarkm	    *in_ptr = '\0';
33455682Smarkm
33555682Smarkm	    beg = in_buf;
33655682Smarkm	    rem = in_len;
33755682Smarkm	    while(rem > 1
33855682Smarkm		  && (p = strstr(beg, "\r\n")) != NULL) {
33955682Smarkm		if (state == TOP) {
34055682Smarkm		    char *copy = beg;
34155682Smarkm
34272445Sassar		    for (i = 0; i < numheaders; i++) {
34372445Sassar			size_t len;
34472445Sassar
34572445Sassar			len = min(p - copy + 1, strlen(headers[i]));
34672445Sassar			if (strncasecmp(copy, headers[i], len) == 0) {
34772445Sassar			    fprintf (stdout, "%.*s\n", (int)(p - copy), copy);
34872445Sassar			}
34955682Smarkm		    }
35055682Smarkm		    if (beg[0] == '.' && beg[1] == '\r' && beg[2] == '\n') {
35172445Sassar			if (numheaders > 1)
35272445Sassar			    fprintf (stdout, "\n");
35355682Smarkm			state = STAT;
35455682Smarkm			if (++retrieved == count) {
35555682Smarkm			    state = QUIT;
35655682Smarkm			    net_write (s, "QUIT\r\n", 6);
35755682Smarkm			    if (verbose > 1)
358120945Snectar				fprintf (stderr, "QUIT\r\n");
35955682Smarkm			}
36055682Smarkm		    }
36155682Smarkm		    rem -= p - beg + 2;
36255682Smarkm		    beg = p + 2;
36355682Smarkm		} else if (state == RETR) {
36455682Smarkm		    char *copy = beg;
36555682Smarkm		    if (beg[0] == '.') {
36655682Smarkm			if (beg[1] == '\r' && beg[2] == '\n') {
36755682Smarkm			    if(!blank_line)
36855682Smarkm				write_state_add(&write_state, "\n", 1);
36955682Smarkm			    state = STAT;
37055682Smarkm			    rem -= p - beg + 2;
37155682Smarkm			    beg = p + 2;
37255682Smarkm			    if (++retrieved == count) {
37355682Smarkm				write_state_flush (&write_state);
37455682Smarkm				if (fsync (out_fd) < 0)
37555682Smarkm				    err (1, "fsync");
37655682Smarkm				close(out_fd);
37755682Smarkm				if (leavep) {
37855682Smarkm				    state = QUIT;
37955682Smarkm				    net_write (s, "QUIT\r\n", 6);
38055682Smarkm				    if (verbose > 1)
381120945Snectar					fprintf (stderr, "QUIT\r\n");
38255682Smarkm				} else {
38355682Smarkm				    if (forkp) {
38455682Smarkm					pid_t pid;
38555682Smarkm
38655682Smarkm					pid = fork();
38755682Smarkm					if (pid < 0)
38855682Smarkm					    warn ("fork");
38955682Smarkm					else if(pid != 0) {
39055682Smarkm					    if(verbose)
39155682Smarkm						fprintf (stderr,
39255682Smarkm							 "(exiting)");
39355682Smarkm					    return 0;
39455682Smarkm					}
39555682Smarkm				    }
39655682Smarkm
39755682Smarkm				    state = XDELE;
39855682Smarkm				    if (verbose)
39955682Smarkm					fprintf (stderr, "deleting... ");
40055682Smarkm				}
40155682Smarkm			    }
40255682Smarkm			    continue;
40355682Smarkm			} else
40455682Smarkm			    ++copy;
40555682Smarkm		    }
40655682Smarkm		    *p = '\n';
40755682Smarkm		    if(blank_line &&
40855682Smarkm		       strncmp(copy, "From ", min(p - copy + 1, 5)) == 0)
40955682Smarkm			write_state_add(&write_state, ">", 1);
41055682Smarkm		    write_state_add(&write_state, copy, p - copy + 1);
41155682Smarkm		    blank_line = (*copy == '\n');
41255682Smarkm		    rem -= p - beg + 2;
41355682Smarkm		    beg = p + 2;
41455682Smarkm		} else if (rem >= 3 && strncmp (beg, "+OK", 3) == 0) {
41555682Smarkm		    if (state == STAT) {
41655682Smarkm			if (!do_from)
41755682Smarkm			    write_state_add(&write_state,
41855682Smarkm					    from_line, from_line_length);
41955682Smarkm			blank_line = 0;
42055682Smarkm			if (do_from)
42155682Smarkm			    state = TOP;
42255682Smarkm			else
42355682Smarkm			    state = RETR;
42455682Smarkm		    } else if (state == XDELE) {
42555682Smarkm			state = QUIT;
42655682Smarkm			net_write (s, "QUIT\r\n", 6);
42755682Smarkm			if (verbose > 1)
428120945Snectar			    fprintf (stderr, "QUIT\r\n");
42955682Smarkm			break;
43055682Smarkm		    } else if (state == DELE) {
43155682Smarkm			if (++deleted == count) {
43255682Smarkm			    state = QUIT;
43355682Smarkm			    net_write (s, "QUIT\r\n", 6);
43455682Smarkm			    if (verbose > 1)
435120945Snectar				fprintf (stderr, "QUIT\r\n");
43655682Smarkm			    break;
43755682Smarkm			}
43855682Smarkm		    } else if (++state == STAT) {
43955682Smarkm			if(sscanf (beg + 4, "%u %u", &count, &bytes) != 2)
44055682Smarkm			    errx(1, "Bad STAT-line: %.*s", (int)(p - beg), beg);
44155682Smarkm			if (verbose) {
44255682Smarkm			    fprintf (stderr, "%u message(s) (%u bytes). "
44355682Smarkm				     "fetching... ",
44455682Smarkm				     count, bytes);
44555682Smarkm			    if (do_from)
44655682Smarkm				fprintf (stderr, "\n");
44755682Smarkm			} else if (do_count) {
44855682Smarkm			    fprintf (stderr, "%u message(s) (%u bytes).\n",
44955682Smarkm				     count, bytes);
45055682Smarkm			}
45155682Smarkm			if (count == 0) {
45255682Smarkm			    state = QUIT;
45355682Smarkm			    net_write (s, "QUIT\r\n", 6);
45455682Smarkm			    if (verbose > 1)
455120945Snectar				fprintf (stderr, "QUIT\r\n");
45655682Smarkm			    break;
45755682Smarkm			}
45855682Smarkm		    }
45955682Smarkm
46055682Smarkm		    rem -= p - beg + 2;
46155682Smarkm		    beg = p + 2;
46255682Smarkm		} else {
46355682Smarkm		    if(state == XDELE) {
46455682Smarkm			state = DELE;
46555682Smarkm			rem -= p - beg + 2;
46655682Smarkm			beg = p + 2;
46755682Smarkm		    } else
46855682Smarkm			errx (1, "Bad response: %.*s", (int)(p - beg), beg);
46955682Smarkm		}
47055682Smarkm	    }
47155682Smarkm	    if (!do_from)
47255682Smarkm		write_state_flush (&write_state);
47355682Smarkm
47455682Smarkm	    memmove (in_buf, beg, rem);
47555682Smarkm	    in_len = rem;
47655682Smarkm	    in_ptr = in_buf + rem;
47755682Smarkm	}
47855682Smarkm	if (FD_ISSET(s, &writeset)) {
47955682Smarkm	    if ((state == STAT && !do_from) || state == RETR)
48055682Smarkm		out_len = snprintf (out_buf, sizeof(out_buf),
48155682Smarkm				    "RETR %u\r\n", ++asked_for);
48255682Smarkm	    else if ((state == STAT && do_from) || state == TOP)
48355682Smarkm		out_len = snprintf (out_buf, sizeof(out_buf),
48455682Smarkm				    "TOP %u 0\r\n", ++asked_for);
48555682Smarkm	    else if(state == XDELE) {
48655682Smarkm		out_len = snprintf(out_buf, sizeof(out_buf),
48755682Smarkm				   "XDELE %u %u\r\n", 1, count);
48855682Smarkm		sent_xdele++;
48955682Smarkm	    }
49055682Smarkm	    else if(state == DELE)
49155682Smarkm		out_len = snprintf (out_buf, sizeof(out_buf),
49255682Smarkm				    "DELE %u\r\n", ++asked_deleted);
49390926Snectar	    if (out_len < 0)
49490926Snectar		errx (1, "snprintf failed");
49555682Smarkm	    if (net_write (s, out_buf, out_len) != out_len)
49655682Smarkm		err (1, "write");
49755682Smarkm	    if (verbose > 1)
498120945Snectar		fprintf (stderr, "%s", out_buf);
49955682Smarkm	}
50055682Smarkm    }
50155682Smarkm    if (verbose)
50255682Smarkm	fprintf (stderr, "Done\n");
50372445Sassar    if (do_from) {
50472445Sassar	free (tmp);
50572445Sassar	free (headers);
50672445Sassar    } else {
50755682Smarkm	write_state_destroy (&write_state);
50872445Sassar    }
50955682Smarkm    return 0;
51055682Smarkm}
51155682Smarkm
51255682Smarkm#ifdef KRB5
51355682Smarkmstatic int
51455682Smarkmdo_v5 (const char *host,
51555682Smarkm       int port,
51655682Smarkm       const char *user,
51755682Smarkm       const char *filename,
51855682Smarkm       const char *header_str,
51955682Smarkm       int leavep,
52055682Smarkm       int verbose,
52155682Smarkm       int forkp)
52255682Smarkm{
52355682Smarkm    krb5_error_code ret;
52455682Smarkm    krb5_auth_context auth_context = NULL;
52555682Smarkm    krb5_principal server;
52655682Smarkm    int s;
52755682Smarkm
52855682Smarkm    s = do_connect (host, port, 1);
52955682Smarkm    if (s < 0)
53055682Smarkm	return 1;
53155682Smarkm
53255682Smarkm    ret = krb5_sname_to_principal (context,
53355682Smarkm				   host,
53455682Smarkm				   "pop",
53555682Smarkm				   KRB5_NT_SRV_HST,
53655682Smarkm				   &server);
53755682Smarkm    if (ret) {
53855682Smarkm	warnx ("krb5_sname_to_principal: %s",
53955682Smarkm	       krb5_get_err_text (context, ret));
54055682Smarkm	return 1;
54155682Smarkm    }
54255682Smarkm
54355682Smarkm    ret = krb5_sendauth (context,
54455682Smarkm			 &auth_context,
54555682Smarkm			 &s,
54655682Smarkm			 "KPOPV1.0",
54755682Smarkm			 NULL,
54855682Smarkm			 server,
54955682Smarkm			 0,
55055682Smarkm			 NULL,
55155682Smarkm			 NULL,
55255682Smarkm			 NULL,
55355682Smarkm			 NULL,
55455682Smarkm			 NULL,
55555682Smarkm			 NULL);
55655682Smarkm    krb5_free_principal (context, server);
55755682Smarkm    if (ret) {
55855682Smarkm	warnx ("krb5_sendauth: %s",
55955682Smarkm	       krb5_get_err_text (context, ret));
56055682Smarkm	return 1;
56155682Smarkm    }
56255682Smarkm    return doit (s, host, user, filename, header_str, leavep, verbose, forkp);
56355682Smarkm}
56455682Smarkm#endif
56555682Smarkm
56655682Smarkm#ifdef KRB4
56755682Smarkmstatic int
56855682Smarkmdo_v4 (const char *host,
56955682Smarkm       int port,
57055682Smarkm       const char *user,
57155682Smarkm       const char *filename,
57255682Smarkm       const char *header_str,
57355682Smarkm       int leavep,
57455682Smarkm       int verbose,
57555682Smarkm       int forkp)
57655682Smarkm{
57755682Smarkm    KTEXT_ST ticket;
57855682Smarkm    MSG_DAT msg_data;
57955682Smarkm    CREDENTIALS cred;
58055682Smarkm    des_key_schedule sched;
58155682Smarkm    int s;
58255682Smarkm    int ret;
58355682Smarkm
58455682Smarkm    s = do_connect (host, port, 1);
58555682Smarkm    if (s < 0)
58655682Smarkm	return 1;
58755682Smarkm    ret = krb_sendauth(0,
58855682Smarkm		       s,
58955682Smarkm		       &ticket,
59055682Smarkm		       "pop",
59155682Smarkm		       (char *)host,
59255682Smarkm		       krb_realmofhost(host),
59355682Smarkm		       getpid(),
59455682Smarkm		       &msg_data,
59555682Smarkm		       &cred,
59655682Smarkm		       sched,
59755682Smarkm		       NULL,
59855682Smarkm		       NULL,
59955682Smarkm		       "KPOPV0.1");
60055682Smarkm    if(ret) {
60155682Smarkm	warnx("krb_sendauth: %s", krb_get_err_text(ret));
60255682Smarkm	return 1;
60355682Smarkm    }
60455682Smarkm    return doit (s, host, user, filename, header_str, leavep, verbose, forkp);
60555682Smarkm}
60655682Smarkm#endif /* KRB4 */
60755682Smarkm
60855682Smarkm#ifdef HESIOD
60955682Smarkm
61055682Smarkm#ifdef HESIOD_INTERFACES
61155682Smarkm
61255682Smarkmstatic char *
61355682Smarkmhesiod_get_pobox (const char **user)
61455682Smarkm{
61555682Smarkm    void *context;
61655682Smarkm    struct hesiod_postoffice *hpo;
61755682Smarkm    char *ret = NULL;
61855682Smarkm
61955682Smarkm    if(hesiod_init (&context) != 0)
62055682Smarkm	err (1, "hesiod_init");
62155682Smarkm
62255682Smarkm    hpo = hesiod_getmailhost (context, *user);
62355682Smarkm    if (hpo == NULL) {
62455682Smarkm	warn ("hesiod_getmailhost %s", *user);
62555682Smarkm    } else {
62655682Smarkm	if (strcasecmp(hpo->hesiod_po_type, "pop") != 0)
62755682Smarkm	    errx (1, "Unsupported po type %s", hpo->hesiod_po_type);
62855682Smarkm
62972445Sassar	ret = estrdup(hpo->hesiod_po_host);
63072445Sassar	*user = estrdup(hpo->hesiod_po_name);
63155682Smarkm	hesiod_free_postoffice (context, hpo);
63255682Smarkm    }
63355682Smarkm    hesiod_end (context);
63455682Smarkm    return ret;
63555682Smarkm}
63655682Smarkm
63755682Smarkm#else /* !HESIOD_INTERFACES */
63855682Smarkm
63955682Smarkmstatic char *
64055682Smarkmhesiod_get_pobox (const char **user)
64155682Smarkm{
64255682Smarkm    char *ret = NULL;
64355682Smarkm    struct hes_postoffice *hpo;
64455682Smarkm
64555682Smarkm    hpo = hes_getmailhost (*user);
64655682Smarkm    if (hpo == NULL) {
64755682Smarkm	warn ("hes_getmailhost %s", *user);
64855682Smarkm    } else {
64955682Smarkm	if (strcasecmp(hpo->po_type, "pop") != 0)
65055682Smarkm	    errx (1, "Unsupported po type %s", hpo->po_type);
65155682Smarkm
65272445Sassar	ret = estrdup(hpo->po_host);
65372445Sassar	*user = estrdup(hpo->po_name);
65455682Smarkm    }
65555682Smarkm    return ret;
65655682Smarkm}
65755682Smarkm
65855682Smarkm#endif /* HESIOD_INTERFACES */
65955682Smarkm
66055682Smarkm#endif /* HESIOD */
66155682Smarkm
66255682Smarkmstatic char *
66355682Smarkmget_pobox (const char **user)
66455682Smarkm{
66555682Smarkm    char *ret = NULL;
66655682Smarkm
66755682Smarkm#ifdef HESIOD
66855682Smarkm    ret = hesiod_get_pobox (user);
66955682Smarkm#endif
67055682Smarkm
67155682Smarkm    if (ret == NULL)
67255682Smarkm	ret = getenv("MAILHOST");
67355682Smarkm    if (ret == NULL)
67455682Smarkm	errx (1, "MAILHOST not set");
67555682Smarkm    return ret;
67655682Smarkm}
67755682Smarkm
67855682Smarkmstatic void
67955682Smarkmparse_pobox (char *a0, const char **host, const char **user)
68055682Smarkm{
68155682Smarkm    const char *h, *u;
68255682Smarkm    char *p;
68355682Smarkm    int po = 0;
68455682Smarkm
68555682Smarkm    if (a0 == NULL) {
68655682Smarkm
68755682Smarkm	*user = getenv ("USERNAME");
68855682Smarkm	if (*user == NULL) {
68955682Smarkm	    struct passwd *pwd = getpwuid (getuid ());
69055682Smarkm
69155682Smarkm	    if (pwd == NULL)
69255682Smarkm		errx (1, "Who are you?");
69372445Sassar	    *user = estrdup (pwd->pw_name);
69455682Smarkm	}
69555682Smarkm	*host = get_pobox (user);
69655682Smarkm	return;
69755682Smarkm    }
69855682Smarkm
69955682Smarkm    /* if the specification starts with po:, remember this information */
70055682Smarkm    if(strncmp(a0, "po:", 3) == 0) {
70155682Smarkm	a0 += 3;
70255682Smarkm	po++;
70355682Smarkm    }
70455682Smarkm    /* if there is an `@', the hostname is after it, otherwise at the
70555682Smarkm       beginning of the string */
70655682Smarkm    p = strchr(a0, '@');
70755682Smarkm    if(p != NULL) {
70855682Smarkm	*p++ = '\0';
70955682Smarkm	h = p;
71055682Smarkm    } else {
71155682Smarkm	h = a0;
71255682Smarkm    }
71355682Smarkm    /* if there is a `:', the username comes before it, otherwise at
71455682Smarkm       the beginning of the string */
71555682Smarkm    p = strchr(a0, ':');
71655682Smarkm    if(p != NULL) {
71755682Smarkm	*p++ = '\0';
71855682Smarkm	u = p;
71955682Smarkm    } else {
72055682Smarkm	u = a0;
72155682Smarkm    }
72255682Smarkm    if(h == u) {
72355682Smarkm	/* some inconsistent compatibility with various mailers */
72455682Smarkm	if(po) {
72555682Smarkm	    h = get_pobox (&u);
72655682Smarkm	} else {
72755682Smarkm	    u = get_default_username ();
72855682Smarkm	    if (u == NULL)
72955682Smarkm		errx (1, "Who are you?");
73055682Smarkm	}
73155682Smarkm    }
73255682Smarkm    *host = h;
73355682Smarkm    *user = u;
73455682Smarkm}
73555682Smarkm
73655682Smarkmint
73755682Smarkmmain(int argc, char **argv)
73855682Smarkm{
73955682Smarkm    int port = 0;
74055682Smarkm    int optind = 0;
74155682Smarkm    int ret = 1;
74255682Smarkm    const char *host, *user, *filename = NULL;
74355682Smarkm    char *pobox = NULL;
74455682Smarkm
74578527Sassar    setprogname (argv[0]);
74655682Smarkm
74755682Smarkm#ifdef KRB5
74872445Sassar    {
74972445Sassar	krb5_error_code ret;
75072445Sassar
75172445Sassar	ret = krb5_init_context (&context);
75272445Sassar	if (ret)
75372445Sassar	    errx (1, "krb5_init_context failed: %d", ret);
75472445Sassar    }
75555682Smarkm#endif
75655682Smarkm
75755682Smarkm    if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv,
75855682Smarkm		&optind))
75955682Smarkm	usage (1);
76055682Smarkm
76155682Smarkm    argc -= optind;
76255682Smarkm    argv += optind;
76355682Smarkm
76455682Smarkm#if defined(KRB4) && defined(KRB5)
76555682Smarkm    if(use_v4 == -1 && use_v5 == 1)
76655682Smarkm	use_v4 = 0;
76755682Smarkm    if(use_v5 == -1 && use_v4 == 1)
76855682Smarkm	use_v5 = 0;
76955682Smarkm#endif
77055682Smarkm
77155682Smarkm    if (do_help)
77255682Smarkm	usage (0);
77355682Smarkm
77455682Smarkm    if (do_version) {
77555682Smarkm	print_version(NULL);
77655682Smarkm	return 0;
77755682Smarkm    }
77855682Smarkm
77955682Smarkm    if (do_from && header_str == NULL)
78055682Smarkm	header_str = "From:";
78155682Smarkm    else if (header_str != NULL)
78255682Smarkm	do_from = 1;
78355682Smarkm
78455682Smarkm    if (do_from) {
78555682Smarkm	if (argc == 0)
78655682Smarkm	    pobox = NULL;
78755682Smarkm	else if (argc == 1)
78855682Smarkm	    pobox = argv[0];
78955682Smarkm	else
79055682Smarkm	    usage (1);
79155682Smarkm    } else {
79255682Smarkm	if (argc == 1) {
79355682Smarkm	    filename = argv[0];
79455682Smarkm	    pobox    = NULL;
79555682Smarkm	} else if (argc == 2) {
79655682Smarkm	    filename = argv[1];
79755682Smarkm	    pobox    = argv[0];
79855682Smarkm	} else
79955682Smarkm	    usage (1);
80055682Smarkm    }
80155682Smarkm
80255682Smarkm    if (port_str) {
80355682Smarkm	struct servent *s = roken_getservbyname (port_str, "tcp");
80455682Smarkm
80555682Smarkm	if (s)
80655682Smarkm	    port = s->s_port;
80755682Smarkm	else {
80855682Smarkm	    char *ptr;
80955682Smarkm
81055682Smarkm	    port = strtol (port_str, &ptr, 10);
81155682Smarkm	    if (port == 0 && ptr == port_str)
81255682Smarkm		errx (1, "Bad port `%s'", port_str);
81355682Smarkm	    port = htons(port);
81455682Smarkm	}
81555682Smarkm    }
81655682Smarkm    if (port == 0) {
81755682Smarkm#ifdef KRB5
81855682Smarkm	port = krb5_getportbyname (context, "kpop", "tcp", 1109);
81955682Smarkm#elif defined(KRB4)
82055682Smarkm	port = k_getportbyname ("kpop", "tcp", htons(1109));
82155682Smarkm#else
82255682Smarkm#error must define KRB4 or KRB5
82355682Smarkm#endif
82455682Smarkm    }
82555682Smarkm
82655682Smarkm    parse_pobox (pobox, &host, &user);
82755682Smarkm
82855682Smarkm#ifdef KRB5
82955682Smarkm    if (ret && use_v5) {
83055682Smarkm	ret = do_v5 (host, port, user, filename, header_str,
83155682Smarkm		     do_leave, verbose_level, do_fork);
83255682Smarkm    }
83355682Smarkm#endif
83455682Smarkm
83555682Smarkm#ifdef KRB4
83655682Smarkm    if (ret && use_v4) {
83755682Smarkm	ret = do_v4 (host, port, user, filename, header_str,
83855682Smarkm		     do_leave, verbose_level, do_fork);
83955682Smarkm    }
84055682Smarkm#endif /* KRB4 */
84155682Smarkm    return ret;
84255682Smarkm}
843