/*
 * pop.c: client routines for talking to a POP3-protocol post-office
 * server
 *
 * Jonathan Kamens
 * August 13, 1991
 */

#ifdef POP3_SUPPORT

#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include "pop.h"

extern int errno;

#ifdef KERBEROS
#include <krb.h>
#include <des.h>
#ifdef sun
#include <malloc.h>
#else /* !sun */
extern char *
malloc( /* unsigned */ );
extern void 
free( /* char * */ );
#endif /* sun */
#endif /* KERBEROS */

#ifdef HESIOD
#include <hesiod.h>
/*
 * It really shouldn't be necessary to put this declaration here, but
 * the version of hesiod.h that Athena has installed in release 7.2
 * doesn't declare this function; I don't know if the 7.3 version of
 * hesiod.h does.
 */
extern struct servent *
hes_getservbyname( /* char *, char * */ );
#endif /* HESIOD */

#include <pwd.h>
#include <string.h>
#include <strings.h>

extern char *
strstr( /* char *, char * */ );

#include <netdb.h>
#include <errno.h>
#include <stdio.h>

extern char *sys_errlist[];
#define strerror(eno)	sys_errlist[eno]

extern char *
getenv( /* char * */ );
extern char *
getlogin( /* void */ );
extern char *
getpass( /* char * */ );
extern int 
getuid( /* void */ );
extern void 
bzero( /* char *, int */ ), bcopy( /* char *, char *, int */ );
extern int 
socket( /* int, int, int */ );
extern int 
connect( /* int, struct sockaddr *, int */ );
extern int 
close( /* int */ );
extern int 
read( /* int, char *, int */ ), write( /* int, char *, int */ );
extern int 
atoi( /* char * */ );

#if !(defined(vax) && defined(__GNUC__))
extern int 
fprintf( /* FILE *, char *, ... */ );
#endif /* !(vax && __GNUC__) */

#ifdef KERBEROS
extern int 
krb_sendauth( /* long, int, KTEXT, char *, char *, char *, u_long, MSG_DAT **,
		CREDENTIALS *, Key_schedule, struct sockaddr_in *,
		struct sockaddr_in *, char * */ );
extern char *
krb_realmofhost( /* char * */ );
#endif /* KERBEROS */

extern int h_errno;

static int socket_connection();
static char *getline();
static int sendline();
static int fullwrite();
static int getok();
static int gettermination();

#define ERROR_MAX 80	/* a pretty arbitrary size */
#define POP_PORT 110
#define KPOP_PORT 1109
#define POP_SERVICE "pop"
#define KPOP_SERVICE "kpop"

char pop_error[ERROR_MAX];
int pop_debug = 0;

/*
 * Function: pop_open(char *host, char *username, char *password,
 * 		      int flags)
 *
 * Purpose: Establishes a connection with a post-office server, and
 * 	completes the authorization portion of the session.
 *
 * Arguments:
 * 	host	The server host with which the connection should be
 * 		established.  Optional.  If omitted, internal
 * 		heuristics will be used to determine the server host,
 * 		if possible.
 * 	username
 * 		The username of the mail-drop to access.  Optional.
 * 		If omitted, internal heuristics will be used to
 * 		determine the username, if possible.
 * 	password
 * 		The password to use for authorization.  If omitted,
 * 		internal heuristics will be used to determine the
 * 		password, if possible.
 * 	flags	A bit mask containing flags controlling certain
 * 		functions of the routine.  Valid flags are defined in
 * 		the file pop.h
 *
 * Return value: Upon successful establishment of a connection, a
 * 	non-null PopServer will be returned.  Otherwise, null will be
 * 	returned, and the string variable pop_error will contain an
 * 	explanation of the error.
 */
PopServer 
pop_open(host, username, password, flags)
char *host, *username, *password;
int flags;
{
    int sock;
    PopServer server;

    /* Determine the user name */
    if (!username) {
	username = getenv("USER");
	if (!(username && *username)) {
	    username = getlogin();
	    if (!(username && *username)) {
		struct passwd *passwd;

		passwd = getpwuid(getuid());
		if (passwd && passwd->pw_name && *passwd->pw_name) {
		    username = passwd->pw_name;
		} else {
		    strcpy(pop_error, "Could not determine username");
		    return (0);
		}
	    }
	}
    }
    /* Determine the mail host */
#ifdef HESIOD
    if ((!host) && (!(flags & POP_NO_HESIOD))) {
	struct hes_postoffice *office;

	office = hes_getmailhost(username);
	if (office && office->po_type && (!strcmp(office->po_type, "POP"))
		&& office->po_name && *office->po_name && office->po_host
		&& *office->po_host) {
	    host = office->po_host;
	    username = office->po_name;
	}
    }
#endif
    if (!host) {
	host = getenv("MAILHOST");
	if (!host) {
	    strcpy(pop_error, "Could not determine POP server");
	    return (0);
	}
    }
    /* Determine the password */
#ifdef KERBEROS
#define DONT_NEED_PASSWORD (! (flags & POP_NO_KERBEROS))
#else
#define DONT_NEED_PASSWORD 0
#endif

    /* Modified to return password if possible -- Bart */
    if ((!password || !*password) && (!DONT_NEED_PASSWORD)) {
	if (!(flags & POP_NO_GETPASS)) {
	    char *p = getpass("Enter POP password:");
	    if (p && password)
		(void) strcpy(password, p);
	    password = p;
	}
	if (!password) {
	    strcpy(pop_error, "Could not determine POP password");
	    return (0);
	}
    }
    if (password)
	flags |= POP_NO_KERBEROS;
    else
	password = username;

    sock = socket_connection(host, flags);
    if (sock == -1)
	return (0);

    server = (PopServer) malloc(sizeof(struct _PopServer));
    if (!server) {
	strcpy(pop_error, "Out of memory in pop_open");
	return (0);
    }
    server->file = sock;
    server->data = 0;
    server->dp = server->buffer;

    if (getok(server))
	return (0);

    /*
     * I really shouldn't use the pop_error variable like this, but.... 
     */
    if (strlen(username) > ERROR_MAX - 6) {
	pop_close(server);
	strcpy(pop_error,
		"Username too long; recompile pop.c with larger ERROR_MAX");
	return (0);
    }
    sprintf(pop_error, "USER %s", username);

    if (sendline(server, pop_error) || getok(server)) {
	return (0);
    }
    if (strlen(password) > ERROR_MAX - 6) {
	pop_close(server);
	strcpy(pop_error,
		"Password too long; recompile pop.c with larger ERROR_MAX");
	return (0);
    }
    sprintf(pop_error, "PASS %s", password);

    if (sendline(server, pop_error) || getok(server)) {
	return (0);
    }
    return (server);
}

/*
 * Function: pop_stat
 *
 * Purpose: Issue the STAT command to the server and return (in the
 * 	value parameters) the number of messages in the maildrop and
 * 	the total size of the maildrop.
 *
 * Return value: 0 on success, or non-zero with an error in pop_error
 * 	in failure.
 *
 * Side effects: Closes the connection on failure.
 */
int 
pop_stat(server, count, size)
PopServer server;
int *count, *size;
{
    char *fromserver;

    if (sendline(server, "STAT") || (!(fromserver = getline(server))))
	return (-1);

    if (strncmp(fromserver, "+OK ", 4)) {
	strcpy(pop_error, "Unexpected response from POP server in pop_stat");
	pop_close();
	return (-1);
    }
    *count = atoi(&fromserver[4]);

    fromserver = index(&fromserver[4], ' ');
    if (!fromserver) {
	strcpy(pop_error,
		"Badly formatted response from server in pop_stat");
	pop_close(server);
	return (-1);
    }
    *size = atoi(fromserver + 1);

    return (0);
}

/*
 * Function: pop_list
 *
 * Purpose: Performs the POP "list" command and returns (in value
 * 	parameters) two malloc'd zero-terminated arrays -- one of
 * 	message IDs, and a parallel one of sizes.
 *
 * Arguments:
 * 	server	The pop connection to talk to.
 * 	message	The number of the one message about which to get
 * 		information, or 0 to get information about all
 * 		messages.
 *
 * Return value: 0 on success, non-zero with error in pop_error on
 * 	failure.
 *
 * Side effects: Closes the connection on error.
 */
int 
pop_list(server, message, IDs, sizes)
PopServer server;
int message, **IDs, **sizes;
{
    int how_many, i;
    char *fromserver;

    if (message)
	how_many = 1;
    else {
	int count, size;

	if (pop_stat(server, &count, &size))
	    return (-1);
	how_many = count;
    }

    *IDs = (int *) malloc((how_many + 1) * sizeof(int));
    *sizes = (int *) malloc((how_many + 1) * sizeof(int));
    if (!(*IDs && *sizes)) {
	strcpy(pop_error, "Out of memory in pop_list");
	pop_close(server);
	return (-1);
    }
    if (message) {
	sprintf(pop_error, "LIST %d", message);
	if (sendline(server, pop_error)) {
	    free((char *) *IDs);
	    free((char *) *sizes);
	    return (-1);
	}
	if (!(fromserver = getline(server))) {
	    free((char *) *IDs);
	    free((char *) *sizes);
	    return (-1);
	}
	if (strncmp(fromserver, "+OK ", 4)) {
	    if (!strncmp(fromserver, "-ERR", 4))
		strncpy(pop_error, fromserver, ERROR_MAX);
	    else
		strcpy(pop_error,
			"Unexpected response from server in pop_list");
	    free((char *) *IDs);
	    free((char *) *sizes);
	    pop_close(server);
	    return (-1);
	}
	(*IDs)[0] = atoi(&fromserver[4]);
	fromserver = index(&fromserver[4], ' ');
	if (!fromserver) {
	    strcpy(pop_error,
		    "Badly formatted response from server in pop_list");
	    free((char *) *IDs);
	    free((char *) *sizes);
	    pop_close(server);
	    return (-1);
	}
	(*sizes)[0] = atoi(fromserver);
	(*IDs)[1] = (*sizes)[1] = 0;
	return (0);
    } else {
	if (sendline(server, "LIST") || getok(server)) {
	    free((char *) *IDs);
	    free((char *) *sizes);
	    return (-1);
	}
	for (i = 0; i < how_many; i++) {
	    if (!(fromserver = getline(server))) {
		free((char *) *IDs);
		free((char *) *sizes);
		return (-1);
	    }
	    (*IDs)[i] = atoi(fromserver);
	    fromserver = index(fromserver, ' ');
	    if (!fromserver) {
		strcpy(pop_error,
			"Badly formatted response from server in pop_list");
		free((char *) *IDs);
		free((char *) *sizes);
		pop_close(server);
		return (-1);
	    }
	    (*sizes)[i] = atoi(fromserver);
	}
	if (gettermination(server)) {
	    free((char *) *IDs);
	    free((char *) *sizes);
	    return (-1);
	}
	(*IDs)[i] = (*sizes)[i] = 0;
	return (0);
    }
}

/*
 * Function: pop_retrieve
 *
 * Purpose: Retrieve a specified message from the maildrop.
 *
 * Arguments:
 * 	server	The server to retrieve from.
 * 	message	The message number to retrieve.
 *
 * Return value: A string pointing to the message, if successful, or
 * 	null with pop_error set if not.
 *
 * Side effects: Closes the connection on error.
 */
char *
pop_retrieve(server, message)
PopServer server;
int message;
{
    int *IDs, *sizes;
    char *ptr, *cp;

    if (pop_list(server, message, &IDs, &sizes))
	return (0);

    sprintf(pop_error, "RETR %d", message);
    if (sendline(server, pop_error) || getok(server)) {
	return (0);
    }
    cp = ptr = (char *) malloc(sizes[0]+1);
    free((char *) IDs);
    free((char *) sizes);

    if (!ptr) {
	strcpy(pop_error, "Out of memory in pop_retrieve");
	pop_close(server);
	return (0);
    }
    *cp = '\0';

    while (1) {
	char *fromserver = getline(server);
	int size;

	if (!fromserver) {
	    free(ptr);
	    pop_close(server);
	    return (0);
	}
	if (fromserver[0] == '.') {
	    if (!fromserver[1]) {
		return (ptr);
	    }
	    fromserver++;
	}
	size = strlen(fromserver);
	bcopy(fromserver, cp, size + 1);
	cp += size;
	*cp++ = '\n';
	*cp = '\0';
    }
}

/* Function: pop_delete
 *
 * Purpose: Delete a specified message.
 *
 * Arguments:
 * 	server	Server from which to delete the message.
 * 	message	Message to delete.
 *
 * Return value: 0 on success, non-zero with error in pop_error
 * 	otherwise.
 */
int 
pop_delete(server, message)
PopServer server;
int message;
{
    sprintf(pop_error, "DELE %d", message);

    if (sendline(server, pop_error) || getok(server)) {
	pop_close(server);
	return (-1);
    }
    return (0);
}

/*
 * Function: pop_noop
 *
 * Purpose: Send a noop command to the server.
 *
 * Argument:
 * 	server	The server to send to.
 *
 * Return value: 0 on success, non-zero with error in pop_error
 * 	otherwise.
 *
 * Side effects: Closes connection on error.
 */
int 
pop_noop(server)
PopServer server;
{
    if (sendline(server, "NOOP") || getok(server))
	return (-1);

    return (0);
}

/*
 * Function: pop_last
 *
 * Purpose: Find out the highest seen message from the server.
 *
 * Arguments:
 * 	server	The server.
 *
 * Return value: If successful, the highest seen message, which is
 * 	greater than or equal to 0.  Otherwise, a negative number with
 * 	the error explained in pop_error.
 *
 * Side effects: Closes the connection on error.
 */
int 
pop_last(server)
PopServer server;
{
    char *fromserver;

    if (sendline(server, "LAST"))
	return (-1);

    if (!(fromserver = getline(server)))
	return (-1);

    if (!strncmp(fromserver, "-ERR", 4)) {
	strncpy(pop_error, fromserver, ERROR_MAX);
	pop_close(server);
	return (-1);
    } else if (strncmp(fromserver, "+OK", 3)) {
	strcpy(pop_error, "Unexpected response from server in pop_last");
	pop_close(server);
	return (-1);
    } else {
	return (atoi(&fromserver[4]));
    }
}

/*
 * Function: pop_reset
 *
 * Purpose: Reset the server to its initial connect state
 *
 * Arguments:
 * 	server	The server.
 *
 * Return value: 0 for success, non-0 with error in pop_error
 * 	otherwise.
 *
 * Side effects: Closes the connection on error.
 */
int 
pop_reset(server)
PopServer server;
{
    if (sendline(server, "RSET") || getok(server)) {
	pop_close(server);
	return (-1);
    }
    return (0);
}

/*
 * Function: pop_quit
 *
 * Purpose: Quit the connection to the server,
 *
 * Arguments:
 * 	server	The server to quit.
 *
 * Return value: 0 for success, non-zero otherwise with error in
 * 	pop_error.
 *
 * Side Effects: The PopServer passed in is unuseable after this
 * 	function is called, even if an error occurs.
 */
int 
pop_quit(server)
PopServer server;
{
    int ret = 0;

    if (sendline(server, "QUIT") || getok(server))
	ret = -1;

    close(server->file);
    free((char *) server);

    return (ret);
}

/*
 * Function: socket_connection
 *
 * Purpose: Opens the network connection with the mail host, without
 * 	doing any sort of I/O with it or anything.
 *
 * Arguments:
 * 	host	The host to which to connect.
 *	flags	Option flags.
 * 	
 * Return value: A file descriptor indicating the connection, or -1
 * 	indicating failure, in which case an error has been copied
 * 	into pop_error.
 */
static int 
socket_connection(host, flags)
char *host;
int flags;
{
    struct hostent *hostent;
    struct servent *servent;
    struct sockaddr_in addr;
    char found_port = 0;
    char *service;
    int sock;

#ifdef KERBEROS
    KTEXT ticket;
    MSG_DAT msg_data;
    CREDENTIALS cred;
    Key_schedule schedule;
    int rem;

#endif

    int try_count = 0;

    do {
	hostent = gethostbyname(host);
	try_count++;
	if ((!hostent) && ((h_errno != TRY_AGAIN) || (try_count == 5))) {
	    strcpy(pop_error, "Could not determine POP server's address");
	    return (-1);
	}
    } while (!hostent);

    bzero((char *) &addr, sizeof(addr));
    addr.sin_family = AF_INET;

#ifdef KERBEROS
    service = (flags & POP_NO_KERBEROS) ? POP_SERVICE : KPOP_SERVICE;
#else
    service = POP_SERVICE;
#endif

#ifdef HESIOD
    if (!(flags & POP_NO_HESIOD)) {
	servent = hes_getservbyname(service, "tcp");
	if (servent) {
	    addr.sin_port = servent->s_port;
	    found_port = 1;
	}
    }
#endif
    if (!found_port) {
	servent = getservbyname(service, "tcp");
	if (servent) {
	    addr.sin_port = servent->s_port;
	} else {
#ifdef KERBEROS
	    addr.sin_port = htons((flags & POP_NO_KERBEROS) ?
		    POP_PORT : KPOP_PORT);
#else
	    addr.sin_port = htons(POP_PORT);
#endif
	}
    }
#define SOCKET_ERROR "Could not create socket for POP connection: "

    sock = socket(PF_INET, SOCK_STREAM, 0);
    if (sock < 0) {
	strcpy(pop_error, SOCKET_ERROR);
	strncat(pop_error, strerror(errno),
		ERROR_MAX - sizeof(SOCKET_ERROR));
	return (-1);

    }
    while (*hostent->h_addr_list) {
	bcopy(*hostent->h_addr_list, (char *) &addr.sin_addr,
		hostent->h_length);
	if (!connect(sock, (struct sockaddr *) & addr, sizeof(addr)))
	    break;
	hostent->h_addr_list++;
    }

#define CONNECT_ERROR "Could not connect to POP server: "

    if (!*hostent->h_addr_list) {
	(void) close(sock);
	strcpy(pop_error, CONNECT_ERROR);
	strncat(pop_error, strerror(errno),
		ERROR_MAX - sizeof(CONNECT_ERROR));
	return (-1);

    }
#ifdef KERBEROS
    if (!(flags & POP_NO_KERBEROS)) {
	ticket = (KTEXT) malloc(sizeof(KTEXT_ST));
	rem = krb_sendauth(0L, sock, ticket, "pop", hostent->h_name,
		(char *) krb_realmofhost(hostent->h_name),
		(unsigned long) 0, &msg_data, &cred, schedule,
		(struct sockaddr_in *) 0,
		(struct sockaddr_in *) 0,
		"KPOPV0.1");
	free((char *) ticket);
	if (rem != KSUCCESS) {
#define KRB_ERROR "Kerberos error connecting to POP server: "
	    strcpy(pop_error, KRB_ERROR);
	    strncat(pop_error, krb_err_txt[rem],
		    ERROR_MAX - sizeof(KRB_ERROR));
	    (void) close(sock);
	    return (-1);
	}
    }
#endif	/* KERBEROS */

    return (sock);
}/* socket_connection */

/*
 * Function: getline
 *
 * Purpose: Get a line of text from the connection and return a
 * 	pointer to it.  The carriage return and linefeed at the end of
 * 	the line are stripped, but periods at the beginnings of lines
 * 	are NOT dealt with in any special way.
 *
 * Arguments:
 * 	server	The server from which to get the line of text.
 *
 * Returns: A non-null pointer if successful, or a null pointer on any
 * 	error, with an error message copied into pop_error.
 *
 * Notes: The line returned is overwritten with each call to getline.
 *
 * Side effects: Closes the connection on error.
 */
static char *
getline(server)
PopServer server;
{
#define GETLINE_ERROR "Error reading from server: "

    int ret;

    if (server->data) {
	char *cp = strstr(server->dp, "\r\n");

	if (cp) {
	    char *found;

	    *cp = '\0';
	    server->data -= (&cp[2] - server->dp);
	    found = server->dp;
	    server->dp = &cp[2];

	    if (pop_debug)
		fprintf(stderr, "<<< %s\n", found);
	    return (found);
	} else {
	    bcopy(server->dp, server->buffer, server->data);
	    server->dp = server->buffer;
	}
    } else {
	server->dp = server->buffer;
    }

    while (server->data < GETLINE_MAX) {
	ret = read(server->file, &server->buffer[server->data],
		GETLINE_MAX - server->data);
	if (ret < 0) {
	    strcpy(pop_error, GETLINE_ERROR);
	    strncat(pop_error, strerror(errno),
		    GETLINE_MAX - sizeof(GETLINE_ERROR));
	    pop_close(server);
	    return (0);
	} else if (ret == 0) {
	    strcpy(pop_error, "Unexpected EOF from server in getline");
	    pop_close(server);
	    return (0);
	} else {
	    char *cp = strstr(server->buffer, "\r\n");

	    server->data += ret;

	    if (cp) {
		*cp = '\0';
		server->data -= (&cp[2] - server->dp);
		server->dp = &cp[2];

		if (pop_debug)
		    fprintf(stderr, "<<< %s\n", server->buffer);
		return (server->buffer);
	    }
	}
    }

    strcpy(pop_error, "Line too long reading from server; compile pop.c with larger GETLINE_MAX");
    pop_close(server);
    return (0);
}

/*
 * Function: sendline
 *
 * Purpose: Sends a line of text to the POP server.  The line of text
 * 	passed into this function should NOT have the carriage return
 * 	and linefeed on the end of it.  Periods at beginnings of lines
 * 	will NOT be treated specially by this function.
 *
 * Arguments:
 * 	server	The server to which to send the text.
 * 	line	The line of text to send.
 *
 * Return value: Upon successful completion, a value of 0 will be
 * 	returned.  Otherwise, a non-zero value will be returned, and
 * 	an error will be copied into pop_error.
 *
 * Side effects: Closes the connection on error.
 */
static int 
sendline(server, line)
PopServer server;
char *line;
{
#define SENDLINE_ERROR "Error writing to POP server: "
    int ret;

    ret = fullwrite(server->file, line, strlen(line));
    if (ret >= 0) {	/* 0 indicates that a blank line was written */
	ret = fullwrite(server->file, "\r\n", 2);
    }
    if (ret < 0) {
	pop_close(server);
	strcpy(pop_error, SENDLINE_ERROR);
	strncat(pop_error, strerror(errno),
		ERROR_MAX - sizeof(SENDLINE_ERROR));
	return (ret);
    }
    if (pop_debug)
	fprintf(stderr, ">>> %s\n", line);

    return (0);
}

/*
 * Procedure: fullwrite
 *
 * Purpose: Just like write, but keeps trying until the entire string
 * 	has been written.
 *
 * Return value: Same as write.  Pop_error is not set.
 */
static int 
fullwrite(fd, buf, nbytes)
int fd;
char *buf;
int nbytes;
{
    char *cp;
    int ret;

    cp = buf;
    while ((ret = write(fd, cp, nbytes)) > 0) {
	cp += ret;
	nbytes -= ret;
    }

    return (ret);
}

/*
 * Procedure getok
 *
 * Purpose: Reads a line from the server.  If the return indicator is
 * 	positive, return with a zero exit status.  If not, return with
 * 	a negative exit status.
 *
 * Arguments:
 * 	server	The server to read from.
 * 
 * Returns: 0 for success, else for failure and puts error in pop_error.
 *
 * Side effects: Closes the connection on error.
 */
static int 
getok(server)
PopServer server;
{
    char *fromline;

    if (!(fromline = getline(server))) {
	return (-1);
    }
    if (!strncmp(fromline, "+OK", 3))
	return (0);
    else if (!strncmp(fromline, "-ERR", 4)) {
	strncpy(pop_error, fromline, ERROR_MAX);
	pop_error[ERROR_MAX - 1] = '\0';
	pop_close(server);
	return (-1);
    } else {
	strcpy(pop_error,
		"Unknown response from server; expecting +OK or -ERR");
	pop_close(server);
	return (-1);
    }
}

/*
 * Function: gettermination
 *
 * Purpose: Gets the next line and verifies that it is a termination
 * 	line (nothing but a dot).
 *
 * Return value: 0 on success, non-zero with pop_error set on error.
 *
 * Side effects: Closes the connection on error.
 */
static int 
gettermination(server)
PopServer server;
{
    char *fromserver;

    fromserver = getline(server);
    if (!fromserver)
	return (-1);

    if (strcmp(fromserver, ".")) {
	strcpy(pop_error,
		"Unexpected response from server in gettermination");
	pop_close(server);
	return (-1);
    }
    return (0);
}

/*
 * Function pop_close
 *
 * Purpose: Close a pop connection, sending a "RSET" command to try to
 * 	preserve any changes that were made and a "QUIT" command to
 * 	try to get the server to quit, but ignoring any responses that
 * 	are received.
 *
 * Side effects: The server is unuseable after this function returns.
 * 	Changes made to the maildrop since the session was started (or
 * 	since the last pop_reset) may be lost.
 */
void 
pop_close(server)
PopServer server;
{
    sendline(server, "RSET");
    sendline(server, "QUIT");

    close(server->file);
    free((char *) server);

    return;
}

#endif /* POP3_SUPPORT */
