#include <stdio.h>
#include <sys/types.h>
#include <pwd.h>
#include <strings.h>
#ifdef HESIOD
#include <hesiod.h>
#endif
#include <ctype.h>

#define NOTOK (-1)
#define OK 0
#define DONE 1

#define MAX_HEADER_LINES 512

FILE 	*sfi, *sfo;
char 	Errmsg[80];
char	*optarg, *malloc(), *getlogin(), *strdup(), *strndup();
int	optind;
struct	passwd *getpwuid();
uid_t	getuid();

int	popmail_debug, verbose, nodelete;
char	*progname, *user, *host;

char	*headers[MAX_HEADER_LINES], *mail_from = NULL;
int	num_headers;

main(argc, argv)
	int	argc;
	char	**argv;
{
	int	retval;

	PRS(argc,argv);
	retval = getmail(user, host);
	exit(retval);
}

PRS(argc,argv)
	int	argc;
	char	**argv;
{
	register struct passwd *pp;
	int	c;
	extern char	*getenv();
#ifdef HESIOD
	struct hes_postoffice *p;
#endif HESIOD

	progname = argv[0];
	verbose = popmail_debug = nodelete = 0;
	host = user = NULL;
	
	optind = 1;
	while ((c = getopt(argc,argv,"nvdu:h:")) != EOF)
		switch(c) {
		case 'v':
			verbose++;
			break;
		case 'n':
			nodelete++;
			break;
		case 'd':
			popmail_debug++;
			break;
		case 'h':
			host = optarg;
			break;
		case 'u':
			user = optarg;
			break;
		case '?':
			lusage();
		}
	if (optind < argc)
		lusage();
	if (!user) {
		user = strdup(getlogin());
		if (!user || !*user) {
			pp = getpwuid((int) getuid());
			if (pp == NULL) {
				fprintf(stderr, "Who are you?\n");
				exit(1);
			}
			user = pp->pw_name;
		}
	}
	
	if (!host)
		host = getenv("MAILHOST");
#ifdef HESIOD
	if (!host) {
		p = hes_getmailhost(user);
		if (p && !strcmp(p->po_type, "POP"))
			host = p->po_host;
	}
#endif HESIOD
	if (!host) {
		fprintf(stderr, "%s: can't find post office server", progname);
		exit(1);
	}
}

lusage()
{
	printf("Usage: %s [-v] [-n] [-h host] [-u user]\n", progname);
	exit(1);
}


getmail(user, host)
	char	*user,*host;
{
	int nmsgs, nbytes;
	char response[128];
	register int i;
	int	header_scan();

	if (pop_init(host) == NOTOK) {
		error(Errmsg);
		return(1);
	}

	if ((getline(response, sizeof response, sfi) != OK) ||
	    (*response != '+')){
		error(response);
		return(1);
	}

#ifdef KPOP
	if (pop_command("USER %s", user) == NOTOK || 
	    pop_command("PASS %s", user) == NOTOK)
#else !KPOP
	if (pop_command("USER %s", user) == NOTOK || 
	    pop_command("RPOP %s", user) == NOTOK)
#endif KPOP
	{
		error(Errmsg);
		(void) pop_command("QUIT");
		return(1);
	}

	if (pop_stat(&nmsgs, &nbytes) == NOTOK) {
		error(Errmsg);
		(void) pop_command("QUIT");
		return(1);
	}
	if (verbose)
		printf("You have %d messages (%d bytes) on %s:\n",
		       nmsgs, nbytes, host);
	
	for (i = 1; i <= nmsgs; i++) {
		if (get_message(i) == NOTOK) {
			error(Errmsg);
			(void) pop_command("QUIT");
			return(1);
		}
        }

	if (!nodelete) {
		for (i = 1; i <= nmsgs; i++) {
			if (pop_command("DELE %d", i) == NOTOK) {
				error(Errmsg);
				pop_command("QUIT");
				return(1);
			}
		}
	}
	
	(void) pop_command("QUIT");
	return(0);
}

header_scan(line)
	char	*line;
{
	char	*keyword, *parse_from_field();
	register int	i;
	
	headers[num_headers++] = strdup(line);
	if (isspace(*line))
		return;
	for (i=0;line[i] && line[i]!=':';i++) ;
	keyword = strndup(line, i);
	MakeLowerCase(keyword);
	if (!strcmp(keyword,"from")) {
		mail_from = parse_from_field(line + i + 1);
	}
	free(keyword);
}

char *parse_from_field(str)
	char	*str;
{
	register char	*p, *q;

	p = str;
	while (*p && isspace(*p))
		p++;
	/* Bypass initial comments */
	if (*p && (*p == '(')) {
		/* We have to be able to deal with nested comments */
		register int	paren = 1;
		p++;
		while (*p && paren) {
			if (*p == '(')
				paren++;
			if (*p == ')')
				paren--;
			if (*p == '\\')
				p++;
			p++;
		}
	}
	while (*p && isspace(*p))
		p++;
	if (!*p)
		return(NULL);
	if (q = index(p, '<')) {
		register char	*r, *ret;
		if (r = index(p, '>')) {
			q++;
			ret = strndup(q, r-q);
			if (q = index(ret, ' '))
				*q = '\0';
			return(ret);
		}
	}
	if (q = index(p, ' '))
		return(strndup(p, q-p));
	else
		return(strdup(p));
}


get_message(msgno)
	int	msgno;
{	
	char buf[4096];
	int	in_headers = 1;
	register int	i;
	char	command[256];
	FILE	*pf;

	(void) sprintf(buf, "RETR %d", msgno);
	if (popmail_debug) fprintf(stderr, "%s\n", buf);
	if (putline(buf, Errmsg, sfo) == NOTOK)
		return(NOTOK);
	if (getline(buf, sizeof buf, sfi) != OK) {
	(void) strcpy(Errmsg, buf);
		return(NOTOK);
	}
	num_headers = 0;
	mail_from = NULL;
	while (in_headers) {
		switch (multiline(buf, sizeof buf, sfi)) {
		case OK:
			if (!*buf)
				in_headers = 0;
			header_scan(buf);
			break;
		case DONE:
			return(OK);
		case NOTOK:
			return(NOTOK);
		}
	}
	if (mail_from) {
		sprintf(command, "/bin/mail -r '%s' -d %s", mail_from, user);
		free(mail_from);
	} else
		sprintf(command, "/bin/mail -r 'The unknown user' -d %s",
			user);
	if (popmail_debug)
		fprintf(stderr,"invoking: %s\n", command);
	if (!(pf = popen(command, "w"))) {
		perror("popen: /bin/mail");
		return(NOTOK);
	}
	for (i = 0; i < num_headers; i++) {
		fputs(headers[i], pf);
		putc('\n',pf);
		free(headers[i]);
	}
	putc('\n',pf);
	while (1) {
		switch (multiline(buf, sizeof buf, sfi)) {
		case OK:
			fputs(buf, pf);
			putc('\n', pf);
			break;
		case DONE:
			pclose(pf);
			return(OK);
		case NOTOK:
			pclose(pf);
			return(NOTOK);
		}
	}
}

/* Print error message.  `s1' is printf control string, `s2' is arg for it. */

/*VARARGS1*/
error (s1, s2)
     char *s1, *s2;
{
  printf ("pop: ");
  printf (s1, s2);
  printf ("\n");
}

char *re_comp();

int list_compare(s,list)
      char *s,**list;
{
      char *err;

      while (*list!=NULL) {
              err=re_comp(*list++);
              if (err) {
                      fprintf(stderr,"%s: %s - %s\n",progname,err,*(--list));
                      exit(1);
                      }
              if (re_exec(s))
                      return(1);
      }
      return(0);
}

MakeLowerCase(s)
      char *s;
{
      int i;
      for (i=0;s[i];i++)
              s[i]=isupper(s[i]) ? tolower(s[i]) : s[i];
}

/*
 * Duplicate a string in malloc'ed memory
 */
char *strdup(s)
      char    *s;
{
      register char   *cp;
      
      if (!s)
	      return(NULL);
      if (!(cp = malloc((unsigned) strlen(s)+1))) {
              printf("Out of memory!!!\n");
              abort();
      }
      return(strcpy(cp,s));
}

/*
 * Duplicate up to the first n characters of a string in malloc'ed memory
 */
char *strndup(s, n)
	char	*s;
	int	n;
{
	register char	*cp;
	register int	len;

	len = strlen(s);
	len = (len > n) ? n : len;
	
	if (!(cp = malloc(len+1))) {
		printf("Out of memory!!!\n");
		abort();
	}
	cp[len] = '\0';
	return(strncpy(cp,s,len));
}


	    
