diff options
author | Richard M. Stallman <rms@gnu.org> | 1994-09-27 19:49:25 +0000 |
---|---|---|
committer | Richard M. Stallman <rms@gnu.org> | 1994-09-27 19:49:25 +0000 |
commit | f9102adf4c783a9f4985b837b176f7f32ec9acb1 (patch) | |
tree | 83a6008625dddc008ec178a323839cf24a04d1af | |
parent | bc0fd9f22f02203b30fcaf953f39551683eda3a2 (diff) | |
download | emacs-f9102adf4c783a9f4985b837b176f7f32ec9acb1.tar.gz |
Initial revision
-rw-r--r-- | lib-src/pop.c | 1470 | ||||
-rw-r--r-- | lib-src/pop.h | 80 |
2 files changed, 1550 insertions, 0 deletions
diff --git a/lib-src/pop.c b/lib-src/pop.c new file mode 100644 index 00000000000..9f5b5476f50 --- /dev/null +++ b/lib-src/pop.c @@ -0,0 +1,1470 @@ +/* pop.c: client routines for talking to a POP3-protocol post-office server + Copyright (c) 1991,1993 Free Software Foundation, Inc. + Written by Jonathan Kamens, jik@security.ov.com. + +This file is part of GNU Emacs. + +GNU Emacs is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Emacs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifdef MAIL_USE_POP + +#include <sys/types.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <pop.h> +#ifdef sun +#include <malloc.h> +#else +extern char *malloc (/* unsigned */); +extern char *realloc (/* char *, unsigned */); +extern void free (/* char * */); +#endif +#endif +#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 +#include <pwd.h> +#include <string.h> +#include <strings.h> +#include <netdb.h> +#include <errno.h> +#include <stdio.h> +#ifdef KERBEROS +#ifndef KRB5 +#include <krb.h> +#include <des.h> +#else /* KRB5 */ +#include <krb5/krb5.h> +#include <krb5/ext-proto.h> +#include <ctype.h> +#endif /* KRB5 */ + +extern char *getenv (/* char * */); +extern char *getlogin (/* void */); +extern char *getpass (/* char * */); +extern char *strerror (/* int */); + +#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 + +#ifndef HAVE_H_ERRNO +extern int h_errno; +#endif + +static int socket_connection (/* char *, int */); +static char *getline (/* popserver */); +static int sendline (/* popserver, char * */); +static int fullwrite (/* int, char *, int */); +static int getok (/* popserver */); +#if 0 +static int gettermination (/* popserver */); +#endif +static void pop_trash (/* popserver */); + +#define ERROR_MAX 80 /* a pretty arbitrary size */ +#define POP_PORT 110 +#define KPOP_PORT 1109 +#define POP_SERVICE "pop" +#ifdef KERBEROS +#ifdef KRB5 +#define KPOP_SERVICE "k5pop"; +#else +#define KPOP_SERVICE "kpop" +#endif +#endif + +char pop_error[ERROR_MAX]; +int pop_debug = 0; + +#ifndef min +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +/* + * 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; + char *username; + char *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. + */ + + if (! host) + { + host = getenv ("MAILHOST"); + } + +#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 + +#ifdef MAILHOST + if (! host) + { + host = MAILHOST; + } +#endif + + 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 + + if ((! password) && (! DONT_NEED_PASSWORD)) + { + if (! (flags & POP_NO_GETPASS)) + { + password = getpass ("Enter POP password:"); + } + 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->buffer = (char *) malloc (GETLINE_MIN); + if (! server->buffer) + { + strcpy (pop_error, "Out of memory in pop_open"); + free ((char *) server); + return (0); + } + + server->file = sock; + server->data = 0; + server->buffer_index = 0; + server->buffer_size = GETLINE_MIN; + server->in_multi = 0; + + 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: On failure, may make further operations on the + * connection impossible. + */ +int +pop_stat (server, count, size) + popserver server; + int *count; + int *size; +{ + char *fromserver; + + if (server->in_multi) + { + strcpy (pop_error, "In multi-line query in pop_stat"); + return (-1); + } + + if (sendline (server, "STAT") || (! (fromserver = getline (server)))) + return (-1); + + if (strncmp (fromserver, "+OK ", 4)) + { + if (0 == strncmp (fromserver, "-ERR", 4)) + { + strncpy (pop_error, fromserver, ERROR_MAX); + } + else + { + strcpy (pop_error, + "Unexpected response from POP server in pop_stat"); + pop_trash (server); + } + return (-1); + } + + *count = atoi (&fromserver[4]); + + fromserver = index (&fromserver[4], ' '); + if (! fromserver) + { + strcpy (pop_error, + "Badly formatted response from server in pop_stat"); + pop_trash (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: On failure, may make further operations on the + * connection impossible. + */ +int +pop_list (server, message, IDs, sizes) + popserver server; + int message; + int **IDs; + int **sizes; +{ + int how_many, i; + char *fromserver; + + if (server->in_multi) + { + strcpy (pop_error, "In multi-line query in pop_list"); + return (-1); + } + + 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"); + 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"); + pop_trash (server); + } + free ((char *) *IDs); + free ((char *) *sizes); + return (-1); + } + (*IDs)[0] = atoi (&fromserver[4]); + fromserver = index (&fromserver[4], ' '); + if (! fromserver) + { + strcpy (pop_error, + "Badly formatted response from server in pop_list"); + pop_trash (server); + free ((char *) *IDs); + free ((char *) *sizes); + return (-1); + } + (*sizes)[0] = atoi (fromserver); + (*IDs)[1] = (*sizes)[1] = 0; + return (0); + } + else + { + if (pop_multi_first (server, "LIST", &fromserver)) + { + free ((char *) *IDs); + free ((char *) *sizes); + return (-1); + } + for (i = 0; i < how_many; i++) + { + if (pop_multi_next (server, &fromserver)) + { + 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_trash (server); + return (-1); + } + (*sizes)[i] = atoi (fromserver); + } + if (pop_multi_next (server, &fromserver)) + { + free ((char *) *IDs); + free ((char *) *sizes); + return (-1); + } + else if (fromserver) + { + strcpy (pop_error, + "Too many response lines from server in pop_list"); + 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. + * markfrom + * If true, then mark the string "From " at the beginning + * of lines with '>'. + * + * Return value: A string pointing to the message, if successful, or + * null with pop_error set if not. + * + * Side effects: May kill connection on error. + */ +char * +pop_retrieve (server, message, markfrom) + popserver server; + int message; + int markfrom; +{ + int *IDs, *sizes, bufsize, fromcount = 0, cp = 0; + char *ptr, *fromserver; + int ret; + + if (server->in_multi) + { + strcpy (pop_error, "In multi-line query in pop_retrieve"); + return (0); + } + + if (pop_list (server, message, &IDs, &sizes)) + return (0); + + if (pop_retrieve_first (server, message, &fromserver)) + { + return (0); + } + + /* + * The "5" below is an arbitrary constant -- I assume that if + * there are "From" lines in the text to be marked, there + * probably won't be more than 5 of them. If there are, I + * allocate more space for them below. + */ + bufsize = sizes[0] + (markfrom ? 5 : 0); + ptr = malloc (bufsize); + free ((char *) IDs); + free ((char *) sizes); + + if (! ptr) + { + strcpy (pop_error, "Out of memory in pop_retrieve"); + pop_retrieve_flush (server); + return (0); + } + + while (! (ret = pop_retrieve_next (server, &fromserver))) + { + int linesize; + + if (! fromserver) + { + ptr[cp] = '\0'; + return (ptr); + } + if (markfrom && fromserver[0] == 'F' && fromserver[1] == 'r' && + fromserver[2] == 'o' && fromserver[3] == 'm' && + fromserver[4] == ' ') + { + if (++fromcount == 5) + { + bufsize += 5; + ptr = realloc (ptr, bufsize); + if (! ptr) + { + strcpy (pop_error, "Out of memory in pop_retrieve"); + pop_retrieve_flush (server); + return (0); + } + fromcount = 0; + } + ptr[cp++] = '>'; + } + linesize = strlen (fromserver); + bcopy (fromserver, &ptr[cp], linesize); + cp += linesize; + ptr[cp++] = '\n'; + } + + if (ret) + { + free (ptr); + return (0); + } +} + +int +pop_retrieve_first (server, message, response) + popserver server; + int message; + char **response; +{ + sprintf (pop_error, "RETR %d", message); + return (pop_multi_first (server, pop_error, response)); +} + +int +pop_retrieve_next (server, line) + popserver server; + char **line; +{ + return (pop_multi_next (server, line)); +} + +int +pop_retrieve_flush (server) + popserver server; +{ + return (pop_multi_flush (server)); +} + +int +pop_top_first (server, message, lines, response) + popserver server; + int message, lines; + char **response; +{ + sprintf (pop_error, "TOP %d %d", message, lines); + return (pop_multi_first (server, pop_error, response)); +} + +int +pop_top_next (server, line) + popserver server; + char **line; +{ + return (pop_multi_next (server, line)); +} + +int +pop_top_flush (server) + popserver server; +{ + return (pop_multi_flush (server)); +} + +int +pop_multi_first (server, command, response) + popserver server; + char *command; + char **response; +{ + if (server->in_multi) + { + strcpy (pop_error, + "Already in multi-line query in pop_multi_first"); + return (-1); + } + + if (sendline (server, command) || (! (*response = getline (server)))) + { + return (-1); + } + + if (0 == strncmp (*response, "-ERR", 4)) + { + strncpy (pop_error, *response, ERROR_MAX); + return (-1); + } + else if (0 == strncmp (*response, "+OK", 3)) + { + for (*response += 3; **response == ' '; (*response)++) /* empty */; + server->in_multi = 1; + return (0); + } + else + { + strcpy (pop_error, + "Unexpected response from server in pop_multi_first"); + return (-1); + } +} + +int +pop_multi_next (server, line) + popserver server; + char **line; +{ + char *fromserver; + + if (! server->in_multi) + { + strcpy (pop_error, "Not in multi-line query in pop_multi_next"); + return (-1); + } + + fromserver = getline (server); + if (! fromserver) + { + return (-1); + } + + if (fromserver[0] == '.') + { + if (! fromserver[1]) + { + *line = 0; + server->in_multi = 0; + return (0); + } + else + { + *line = fromserver + 1; + return (0); + } + } + else + { + *line = fromserver; + return (0); + } +} + +int +pop_multi_flush (server) + popserver server; +{ + char *line; + + if (! server->in_multi) + { + return (0); + } + + while (! pop_multi_next (server, &line)) + { + if (! line) + { + return (0); + } + } + + return (-1); +} + +/* 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; +{ + if (server->in_multi) + { + strcpy (pop_error, "In multi-line query in pop_delete"); + return (-1); + } + + sprintf (pop_error, "DELE %d", message); + + if (sendline (server, pop_error) || getok (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 (server->in_multi) + { + strcpy (pop_error, "In multi-line query in pop_noop"); + return (-1); + } + + 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 (server->in_multi) + { + strcpy (pop_error, "In multi-line query in pop_last"); + return (-1); + } + + if (sendline (server, "LAST")) + return (-1); + + if (! (fromserver = getline (server))) + return (-1); + + if (! strncmp (fromserver, "-ERR", 4)) + { + strncpy (pop_error, fromserver, ERROR_MAX); + return (-1); + } + else if (strncmp (fromserver, "+OK ", 4)) + { + strcpy (pop_error, "Unexpected response from server in pop_last"); + pop_trash (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 (pop_retrieve_flush (server)) + { + return (-1); + } + + if (sendline (server, "RSET") || getok (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 (server->file >= 0) + { + if (pop_retrieve_flush (server)) + { + ret = -1; + } + + if (sendline (server, "QUIT") || getok (server)) + { + ret = -1; + } + + close (server->file); + } + + if (server->buffer) + free (server->buffer); + 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 +#ifdef KRB5 + krb5_error_code rem; + krb5_ccache ccdef; + krb5_principal client, server; + krb5_error *err_ret; + register char *cp; +#else + KTEXT ticket; + MSG_DAT msg_data; + CREDENTIALS cred; + Key_schedule schedule; + int rem; +#endif /* KRB5 */ +#endif /* KERBEROS */ + + 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 +#define KRB_ERROR "Kerberos error connecting to POP server: " + if (! (flags & POP_NO_KERBEROS)) + { +#ifdef KRB5 + krb5_init_ets (); + + if (rem = krb5_cc_default (&ccdef)) + { + krb5error: + strcpy (pop_error, KRB_ERROR); + strncat (pop_error, error_message (rem), + ERROR_MAX - sizeof(KRB_ERROR)); + (void) close (sock); + return (-1); + } + + if (rem = krb5_cc_get_principal (ccdef, &client)) + { + goto krb5error; + } + + for (cp = hostent->h_name; *cp; cp++) + { + if (isupper (*cp)) + { + *cp = tolower (*cp); + } + } + + if (rem = krb5_sname_to_principal (hostent->h_name, POP_SERVICE, + FALSE, &server)) + { + goto krb5error; + } + + rem = krb5_sendauth ((krb5_pointer) &sock, "KPOPV1.0", client, server, + AP_OPTS_MUTUAL_REQUIRED, + 0, /* no checksum */ + 0, /* no creds, use ccache instead */ + ccdef, + 0, /* don't need seq # */ + 0, /* don't need subsession key */ + &err_ret, + 0); /* don't need reply */ + krb5_free_principal (server); + if (rem) + { + if (err_ret && err_ret->text.length) + { + strcpy (pop_error, KRB_ERROR); + strncat (pop_error, error_message (rem), + ERROR_MAX - sizeof (KRB_ERROR)); + strncat (pop_error, " [server says '", + ERROR_MAX - strlen (pop_error) - 1); + strncat (pop_error, err_ret->text.data, + min (ERROR_MAX - strlen (pop_error) - 1, + err_ret->text.length)); + strncat (pop_error, "']", + ERROR_MAX - strlen (pop_error) - 1); + } + else + { + strcpy (pop_error, KRB_ERROR); + strncat (pop_error, error_message (rem), + ERROR_MAX - sizeof (KRB_ERROR)); + } + if (err_ret) + krb5_free_error (err_ret); + + (void) close (sock); + return (-1); + } +#else /* ! KRB5 */ + 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) + { + strcpy (pop_error, KRB_ERROR); + strncat (pop_error, krb_err_txt[rem], + ERROR_MAX - sizeof (KRB_ERROR)); + (void) close (sock); + return (-1); + } +#endif /* KRB5 */ + } +#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->buffer + server->buffer_index, "\r\n"); + if (cp) + { + int found; + int data_used; + + found = server->buffer_index; + data_used = (cp + 2) - server->buffer - found; + + *cp = '\0'; /* terminate the string to be returned */ + server->data -= data_used; + server->buffer_index += data_used; + + if (pop_debug) + fprintf (stderr, "<<< %s\n", server->buffer + found); + return (server->buffer + found); + } + else + { + bcopy (server->buffer + server->buffer_index, + server->buffer, server->data); + server->buffer_index = 0; + } + } + else + { + server->buffer_index = 0; + } + + while (1) + { + if (server->data == server->buffer_size) + { + server->buffer_size += GETLINE_INCR; + server->buffer = realloc (server->buffer, server->buffer_size); + if (! server->buffer) + { + strcpy (pop_error, "Out of memory in getline"); + pop_trash (server); + return (0); + } + } + ret = read (server->file, server->buffer + server->data, + server->buffer_size - server->data); + if (ret < 0) + { + strcpy (pop_error, GETLINE_ERROR); + strncat (pop_error, strerror (errno), + ERROR_MAX - sizeof (GETLINE_ERROR)); + pop_trash (server); + return (0); + } + else if (ret == 0) + { + strcpy (pop_error, "Unexpected EOF from server in getline"); + pop_trash (server); + return (0); + } + else + { + char *cp = strstr (server->buffer, "\r\n"); + server->data += ret; + + if (cp) + { + int data_used = (cp + 2) - server->buffer; + *cp = '\0'; + server->data -= data_used; + server->buffer_index = data_used; + + if (pop_debug) + fprintf (stderr, "<<< %s\n", server->buffer); + return (server->buffer); + } + } + } + + /* NOTREACHED */ +} + +/* + * 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_trash (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: On failure, may make the connection unuseable. + */ +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'; + return (-1); + } + else + { + strcpy (pop_error, + "Unexpected response from server; expecting +OK or -ERR"); + pop_trash (server); + return (-1); + } +} + +#if 0 +/* + * 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_trash (server); + return (-1); + } + + return (0); +} +#endif + +/* + * 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; +{ + pop_trash (server); + free ((char *) server); + + return; +} + +/* + * Function: pop_trash + * + * Purpose: Like pop_close or pop_quit, but doesn't deallocate the + * memory associated with the server. It is legal to call + * pop_close or pop_quit after this function has been called. + */ +static void +pop_trash (server) + popserver server; +{ + if (server->file >= 0) + { + sendline (server, "RSET"); + sendline (server, "QUIT"); + + close (server->file); + server->file = -1; + if (server->buffer) + { + free (server->buffer); + server->buffer = 0; + } + } +} + +/* Search in STRING for an occurrence of SUBSTRING + and return a pointer to that occurrence. + Return 0 if SUBSTRING does not occur in STRING. */ + +static char * +my_strstr (string, substring) + char *string, *substring; +{ + char *p = string; + + while (*p) + { + if (!strcmp (p, substring)) + return p; + p++; + } + return 0; +} + +#endif /* MAIL_USE_POP */ diff --git a/lib-src/pop.h b/lib-src/pop.h new file mode 100644 index 00000000000..6cdeb48aa0e --- /dev/null +++ b/lib-src/pop.h @@ -0,0 +1,80 @@ +/* pop.h: Header file for the "pop.c" client POP3 protocol. + Copyright (c) 1991,1993 Free Software Foundation, Inc. + Written by Jonathan Kamens, jik@security.ov.com. + +This file is part of GNU Emacs. + +GNU Emacs is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Emacs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <stdio.h> + +#define GETLINE_MIN 1024 /* the getline buffer starts out this */ + /* size */ +#define GETLINE_INCR 1024 /* the getline buffer is grown by this */ + /* size when it needs to grow */ + +extern char pop_error[]; +extern int pop_debug; + +struct _popserver +{ + int file, data; + char *buffer; + int buffer_size, buffer_index; + int in_multi; +}; + +typedef struct _popserver *popserver; + +/* + * Valid flags for the pop_open function. + */ + +#define POP_NO_KERBEROS (1<<0) +#define POP_NO_HESIOD (1<<1) +#define POP_NO_GETPASS (1<<2) + +#ifdef __STDC__ +#define _ARGS(a) a +#else +#define _ARGS(a) () +#endif + +extern popserver pop_open _ARGS((char *host, char *username, char *password, + int flags)); +extern int pop_stat _ARGS((popserver server, int *count, int *size)); +extern int pop_list _ARGS((popserver server, int message, int **IDs, + int **size)); +extern char *pop_retrieve _ARGS((popserver server, int message, int markfrom)); +extern int pop_retrieve_first _ARGS((popserver server, int message, + char **response)); +extern int pop_retrieve_next _ARGS((popserver server, char **line)); +extern int pop_retrieve_flush _ARGS((popserver server)); +extern int pop_top_first _ARGS((popserver server, int message, int lines, + char **response)); +extern int pop_top_next _ARGS((popserver server, char **line)); +extern int pop_top_flush _ARGS((popserver server)); +extern int pop_multi_first _ARGS((popserver server, char *command, + char **response)); +extern int pop_multi_next _ARGS((popserver server, char **line)); +extern int pop_multi_flush _ARGS((popserver server)); +extern int pop_delete _ARGS((popserver server, int message)); +extern int pop_noop _ARGS((popserver server)); +extern int pop_last _ARGS((popserver server)); +extern int pop_reset _ARGS((popserver server)); +extern int pop_quit _ARGS((popserver server)); +extern void pop_close _ARGS((popserver)); + +#undef _ARGS |