/* 
 * $Id: popmail.c,v 1.5 94/11/06 05:46:12 svalente Exp $
 * $Source: /afs/sipb.mit.edu/project/sipbsrc/src/pfrom/RCS/popmail.c,v $
 * $Author: svalente $
 *
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netdb.h>

#ifdef KPOP
#include <krb.h>
#endif
#ifdef HESIOD
#include <hesiod.h>
#endif

#include "popmail.h"

FILE *sfi;
FILE *sfo;
char Errmsg[80];
#ifdef KPOP
char *PrincipalHostname();
#endif
extern int popmail_debug;
extern int errno;

int pop_init(char *host)
{
    register struct hostent *hp;
    register struct servent *sp;
    struct sockaddr_in sin;
    register int s;
#ifdef KPOP
    KTEXT ticket = (KTEXT)NULL;
    int rem;
    long authopts;
    char *hostname;
#else
    int lport = IPPORT_RESERVED - 1;
#endif

    hp = gethostbyname(host);
    if (hp == NULL) {
	sprintf(Errmsg, "MAILHOST unknown: %s", host);
	return(NOTOK);
    }

#ifdef KPOP
    sp = getservbyname("kpop", "tcp");
    if (sp == 0) {
	strcpy(Errmsg, "tcp/kpop: unknown service");
	return(NOTOK);
    }
#else
    sp = getservbyname("pop", "tcp");
    if (sp == 0) {
	strcpy(Errmsg, "tcp/pop: unknown service");
	return(NOTOK);
    }
#endif

    sin.sin_family = hp->h_addrtype;
    memcpy(&sin.sin_addr, hp->h_addr, hp->h_length);
    sin.sin_port = sp->s_port;
#ifdef KPOP
    s = socket(AF_INET, SOCK_STREAM, 0);
#else
    s = rresvport(&lport);
#endif
    if (s < 0) {
	sprintf(Errmsg, "error creating socket: %s", strerror(errno));
	return(NOTOK);
    }

    if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
	sprintf(Errmsg, "error during connect: %s", strerror(errno));
	close(s);
	return(NOTOK);
    }
#ifdef KPOP
    ticket = (KTEXT) xmalloc( sizeof(KTEXT_ST) );
    rem=KSUCCESS;
    authopts = 0L;
    hostname = xstrdup (hp->h_name);
    rem = krb_sendauth(authopts, s, ticket, "pop", hostname,
		       (char *) krb_realmofhost(hostname),
		       0, (MSG_DAT *) 0, (CREDENTIALS *) 0,
		       (bit_64 *) 0, (struct sockaddr_in *)0,
		       (struct sockaddr_in *)0,"ZMAIL0.0");
    free (hostname);
    if (rem != KSUCCESS) {
	sprintf(Errmsg, "kerberos error: %s",krb_err_txt[rem]);
	close(s);
	return(NOTOK);
    }
#endif

    sfi = fdopen(s, "r");
    sfo = fdopen(s, "w");
    if (sfi == NULL || sfo == NULL) {
	sprintf(Errmsg, "error in fdopen\n");
	close(s);
	return(NOTOK);
    }

    return(OK);
}

int pop_command(char *fmt, ...)
{
    char buf[4096];
    va_list ap;

    va_start(ap, fmt);
    vsprintf(buf, fmt, ap);
    va_end(ap);

    if (popmail_debug) fprintf(stderr, "---> %s\n", buf);
    if (putline(buf, Errmsg, sfo) == NOTOK) return(NOTOK);

    if (getline(buf, sizeof(buf), sfi) != OK) {
	strcpy(Errmsg, buf);
	return(NOTOK);
    }

    if (popmail_debug) fprintf(stderr, "<--- %s\n", buf);
    if (*buf != '+') {
	strcpy(Errmsg, buf);
	return(NOTOK);
    }
    return(OK);
}

    
int pop_stat(int *nmsgs, int *nbytes)
{
    char buf[4096];

    if (popmail_debug) fprintf(stderr, "---> STAT\n");
    if (putline("STAT", Errmsg, sfo) == NOTOK) return(NOTOK);

    if (getline(buf, sizeof(buf), sfi) != OK) {
	strcpy(Errmsg, buf);
	return(NOTOK);
    }

    if (popmail_debug) fprintf(stderr, "<--- %s\n", buf);
    if (*buf != '+') {
	strcpy(Errmsg, buf);
	return(NOTOK);
    }
    sscanf(buf, "+OK %d %d", nmsgs, nbytes);
    return(OK);
}


int putline(char *buf, char *err, FILE *f)
{
    fprintf(f, "%s\r\n", buf);
    fflush(f);
    if (ferror(f)) {
	strcpy(err, "lost connection");
	return(NOTOK);
    }
    return(OK);
}

int getline(char *buf, register int n, FILE *f)
{
    register char *p;
    int c;

    c = 0;
    p = buf;
    while (--n > 0) {
	c = fgetc(f);
	if (c == EOF) break;
	*p++ = c;
	if (c == '\n') break;
    }

    if (ferror(f)) {
	strcpy(buf, "error on connection");
	return (NOTOK);
    }

    if (c == EOF && p == buf) {
	strcpy(buf, "connection closed by foreign host");
	return (DONE);
    }

    *p = '\0';
    if (*--p == '\n') *p = '\0';
    if (*--p == '\r') *p = '\0';
    return(OK);
}

int multiline(char *buf, register int n, FILE *f)
{
    if (getline(buf, n, f) != OK) return (NOTOK);
    if (*buf == '.') {
	if (*(buf+1) == '\0') {
	    return (DONE);
	} else {
	    strcpy(buf, buf+1);
	}
    }
    return(OK);
}

char *pop_get_mailhost (char *user)
{
#ifdef HESIOD
    struct hes_postoffice *p;
#endif
    char *host;

    host = getenv("MAILHOST");
#ifdef HESIOD
    if (!host) {
	p = hes_getmailhost(user);
	if (p && !strcmp(p->po_type, "POP"))
	    host = p->po_host;
    }
#endif
    return (host);
}

/*
 * Malloc that never fails
 */
void *xmalloc(int bytes)
{
	void *vp;

	vp = malloc(bytes);
	if (! vp && bytes) {
		perror("malloc");
		exit(1);
	}
	return(vp);
}

/*
 * Duplicate a string in malloc'ed memory
 */
char *xstrdup(const char *s)
{
	if (!s) return(NULL);
	return(strcpy(xmalloc(strlen(s)+1),s));
}
