X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/9c0f2dac9b9e8672dedfbe23b6e4eb1340169861..ef53d75e82b52e5948807e3af892fb3f3e8f98cc:/lib-src/pop.c diff --git a/lib-src/pop.c b/lib-src/pop.c index 9f5b5476f5..1c2cc95ac6 100644 --- a/lib-src/pop.c +++ b/lib-src/pop.c @@ -1,5 +1,5 @@ /* pop.c: client routines for talking to a POP3-protocol post-office server - Copyright (c) 1991,1993 Free Software Foundation, Inc. + Copyright (c) 1991, 1993, 1996, 1997, 1999 Free Software Foundation, Inc. Written by Jonathan Kamens, jik@security.ov.com. This file is part of GNU Emacs. @@ -16,22 +16,39 @@ 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. */ +the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#ifdef HAVE_CONFIG_H +#define NO_SHORTNAMES /* Tell config not to load remap.h */ +#include <../src/config.h> +#else +#define MAIL_USE_POP +#endif #ifdef MAIL_USE_POP #include +#ifdef WINDOWSNT +#include "ntlib.h" +#include +#undef SOCKET_ERROR +#define RECV(s,buf,len,flags) recv(s,buf,len,flags) +#define SEND(s,buf,len,flags) send(s,buf,len,flags) +#define CLOSESOCKET(s) closesocket(s) +#else #include #include +#define RECV(s,buf,len,flags) read(s,buf,len) +#define SEND(s,buf,len,flags) write(s,buf,len) +#define CLOSESOCKET(s) close(s) +#endif #include + #ifdef sun #include -#else -extern char *malloc (/* unsigned */); -extern char *realloc (/* char *, unsigned */); -extern void free (/* char * */); -#endif -#endif +#endif /* sun */ + #ifdef HESIOD #include /* @@ -42,60 +59,94 @@ extern void free (/* char * */); */ extern struct servent *hes_getservbyname (/* char *, char * */); #endif + #include -#include -#include #include #include #include +#ifdef STDC_HEADERS +#include +#define index strchr +#endif +#ifdef HAVE_UNISTD_H +#include +#endif + #ifdef KERBEROS -#ifndef KRB5 -#include -#include -#else /* KRB5 */ -#include -#include -#include -#endif /* KRB5 */ - -extern char *getenv (/* char * */); -extern char *getlogin (/* void */); -extern char *getpass (/* char * */); -extern char *strerror (/* int */); +# ifdef HAVE_KRB5_H +# include +# endif +# ifdef HAVE_DES_H +# include +# else +# ifdef HAVE_KERBEROSIV_DES_H +# include +# else +# ifdef HAVE_KERBEROS_DES_H +# include +# endif +# endif +# endif +# ifdef HAVE_KRB_H +# include +# else +# ifdef HAVE_KERBEROSIV_KRB_H +# include +# else +# ifdef HAVE_KERBEROS_KRB_H +# include +# endif +# endif +# endif +# ifdef HAVE_COM_ERR_H +# include +# endif +#endif /* KERBEROS */ #ifdef KERBEROS +#ifndef KERBEROS5 extern int krb_sendauth (/* long, int, KTEXT, char *, char *, char *, - u_long, MSG_DAT *, CREDENTIALS *, Key_schedule, - struct sockaddr_in *, struct sockaddr_in *, - char * */); + u_long, MSG_DAT *, CREDENTIALS *, Key_schedule, + struct sockaddr_in *, struct sockaddr_in *, + char * */); extern char *krb_realmofhost (/* char * */); -#endif +#endif /* ! KERBEROS5 */ +#endif /* KERBEROS */ -#ifndef HAVE_H_ERRNO +#ifndef WINDOWSNT +#if !defined(HAVE_H_ERRNO) || !defined(HAVE_CONFIG_H) extern int h_errno; #endif +#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 */); +#ifndef _P +# ifdef __STDC__ +# define _P(a) a +# else +# define _P(a) () +# endif /* __STDC__ */ +#endif /* ! __P */ + +static int socket_connection _P((char *, int)); +static int pop_getline _P((popserver, char **)); +static int sendline _P((popserver, char *)); +static int fullwrite _P((int, char *, int)); +static int getok _P((popserver)); #if 0 -static int gettermination (/* popserver */); +static int gettermination _P((popserver)); #endif -static void pop_trash (/* popserver */); +static void pop_trash _P((popserver)); +static char *find_crlf _P((char *, int)); -#define ERROR_MAX 80 /* a pretty arbitrary size */ +#define ERROR_MAX 160 /* a pretty arbitrary size, but needs + to be bigger than the original + value of 80 */ #define POP_PORT 110 #define KPOP_PORT 1109 -#define POP_SERVICE "pop" +#define POP_SERVICE "pop3" /* we don't want the POP2 port! */ #ifdef KERBEROS -#ifdef KRB5 -#define KPOP_SERVICE "k5pop"; -#else #define KPOP_SERVICE "kpop" #endif -#endif char pop_error[ERROR_MAX]; int pop_debug = 0; @@ -251,6 +302,7 @@ pop_open (host, username, password, flags) server->buffer_index = 0; server->buffer_size = GETLINE_MIN; server->in_multi = 0; + server->trash_started = 0; if (getok (server)) return (0); @@ -316,7 +368,7 @@ pop_stat (server, count, size) return (-1); } - if (sendline (server, "STAT") || (! (fromserver = getline (server)))) + if (sendline (server, "STAT") || (pop_getline (server, &fromserver) < 0)) return (-1); if (strncmp (fromserver, "+OK ", 4)) @@ -412,7 +464,7 @@ pop_list (server, message, IDs, sizes) free ((char *) *sizes); return (-1); } - if (! (fromserver = getline (server))) + if (pop_getline (server, &fromserver) < 0) { free ((char *) *IDs); free ((char *) *sizes); @@ -457,7 +509,7 @@ pop_list (server, message, IDs, sizes) } for (i = 0; i < how_many; i++) { - if (pop_multi_next (server, &fromserver)) + if (pop_multi_next (server, &fromserver) <= 0) { free ((char *) *IDs); free ((char *) *sizes); @@ -476,7 +528,7 @@ pop_list (server, message, IDs, sizes) } (*sizes)[i] = atoi (fromserver); } - if (pop_multi_next (server, &fromserver)) + if (pop_multi_next (server, &fromserver) < 0) { free ((char *) *IDs); free ((char *) *sizes); @@ -506,17 +558,21 @@ pop_list (server, message, IDs, sizes) * markfrom * If true, then mark the string "From " at the beginning * of lines with '>'. + * msg_buf Output parameter to which a buffer containing the + * message is assigned. * - * Return value: A string pointing to the message, if successful, or - * null with pop_error set if not. + * Return value: The number of bytes in msg_buf, which may contain + * embedded nulls, not including its final null, or -1 on error + * with pop_error set. * * Side effects: May kill connection on error. */ -char * -pop_retrieve (server, message, markfrom) +int +pop_retrieve (server, message, markfrom, msg_buf) popserver server; int message; int markfrom; + char **msg_buf; { int *IDs, *sizes, bufsize, fromcount = 0, cp = 0; char *ptr, *fromserver; @@ -525,15 +581,15 @@ pop_retrieve (server, message, markfrom) if (server->in_multi) { strcpy (pop_error, "In multi-line query in pop_retrieve"); - return (0); + return (-1); } if (pop_list (server, message, &IDs, &sizes)) - return (0); + return (-1); if (pop_retrieve_first (server, message, &fromserver)) { - return (0); + return (-1); } /* @@ -543,7 +599,7 @@ pop_retrieve (server, message, markfrom) * allocate more space for them below. */ bufsize = sizes[0] + (markfrom ? 5 : 0); - ptr = malloc (bufsize); + ptr = (char *)malloc (bufsize); free ((char *) IDs); free ((char *) sizes); @@ -551,17 +607,16 @@ pop_retrieve (server, message, markfrom) { strcpy (pop_error, "Out of memory in pop_retrieve"); pop_retrieve_flush (server); - return (0); + return (-1); } - while (! (ret = pop_retrieve_next (server, &fromserver))) + while ((ret = pop_retrieve_next (server, &fromserver)) >= 0) { - int linesize; - if (! fromserver) { ptr[cp] = '\0'; - return (ptr); + *msg_buf = ptr; + return (cp); } if (markfrom && fromserver[0] == 'F' && fromserver[1] == 'r' && fromserver[2] == 'o' && fromserver[3] == 'm' && @@ -570,28 +625,24 @@ pop_retrieve (server, message, markfrom) if (++fromcount == 5) { bufsize += 5; - ptr = realloc (ptr, bufsize); + ptr = (char *)realloc (ptr, bufsize); if (! ptr) { strcpy (pop_error, "Out of memory in pop_retrieve"); pop_retrieve_flush (server); - return (0); + return (-1); } fromcount = 0; } ptr[cp++] = '>'; } - linesize = strlen (fromserver); - bcopy (fromserver, &ptr[cp], linesize); - cp += linesize; + bcopy (fromserver, &ptr[cp], ret); + cp += ret; ptr[cp++] = '\n'; } - if (ret) - { - free (ptr); - return (0); - } + free (ptr); + return (-1); } int @@ -604,6 +655,14 @@ pop_retrieve_first (server, message, response) return (pop_multi_first (server, pop_error, response)); } +/* + Returns a negative number on error, 0 to indicate that the data has + all been read (i.e., the server has returned a "." termination + line), or a positive number indicating the number of bytes in the + returned buffer (which is null-terminated and may contain embedded + nulls, but the returned bytecount doesn't include the final null). + */ + int pop_retrieve_next (server, line) popserver server; @@ -629,6 +688,14 @@ pop_top_first (server, message, lines, response) return (pop_multi_first (server, pop_error, response)); } +/* + Returns a negative number on error, 0 to indicate that the data has + all been read (i.e., the server has returned a "." termination + line), or a positive number indicating the number of bytes in the + returned buffer (which is null-terminated and may contain embedded + nulls, but the returned bytecount doesn't include the final null). + */ + int pop_top_next (server, line) popserver server; @@ -657,7 +724,7 @@ pop_multi_first (server, command, response) return (-1); } - if (sendline (server, command) || (! (*response = getline (server)))) + if (sendline (server, command) || (pop_getline (server, response) < 0)) { return (-1); } @@ -681,12 +748,22 @@ pop_multi_first (server, command, response) } } +/* + Read the next line of data from SERVER and place a pointer to it + into LINE. Return -1 on error, 0 if there are no more lines to read + (i.e., the server has returned a line containing only "."), or a + positive number indicating the number of bytes in the LINE buffer + (not including the final null). The data in that buffer may contain + embedded nulls, but does not contain the final CRLF. When returning + 0, LINE is set to null. */ + int pop_multi_next (server, line) popserver server; char **line; { char *fromserver; + int ret; if (! server->in_multi) { @@ -694,8 +771,7 @@ pop_multi_next (server, line) return (-1); } - fromserver = getline (server); - if (! fromserver) + if ((ret = pop_getline (server, &fromserver)) < 0) { return (-1); } @@ -711,13 +787,13 @@ pop_multi_next (server, line) else { *line = fromserver + 1; - return (0); + return (ret - 1); } } else { *line = fromserver; - return (0); + return (ret); } } @@ -726,21 +802,20 @@ pop_multi_flush (server) popserver server; { char *line; + int ret; if (! server->in_multi) { return (0); } - while (! pop_multi_next (server, &line)) + while ((ret = pop_multi_next (server, &line))) { - if (! line) - { - return (0); - } + if (ret < 0) + return (-1); } - return (-1); + return (0); } /* Function: pop_delete @@ -831,7 +906,7 @@ pop_last (server) if (sendline (server, "LAST")) return (-1); - if (! (fromserver = getline (server))) + if (pop_getline (server, &fromserver) < 0) return (-1); if (! strncmp (fromserver, "-ERR", 4)) @@ -890,7 +965,7 @@ pop_reset (server) * Return value: 0 for success, non-zero otherwise with error in * pop_error. * - * Side Effects: The popserver passed in is unuseable after this + * Side Effects: The popserver passed in is unusable after this * function is called, even if an error occurs. */ int @@ -921,6 +996,10 @@ pop_quit (server) return (ret); } +#ifdef WINDOWSNT +static int have_winsock = 0; +#endif + /* * Function: socket_connection * @@ -947,8 +1026,10 @@ socket_connection (host, flags) char *service; int sock; #ifdef KERBEROS -#ifdef KRB5 +#ifdef KERBEROS5 krb5_error_code rem; + krb5_context kcontext = 0; + krb5_auth_context auth_context = 0; krb5_ccache ccdef; krb5_principal client, server; krb5_error *err_ret; @@ -959,11 +1040,20 @@ socket_connection (host, flags) CREDENTIALS cred; Key_schedule schedule; int rem; -#endif /* KRB5 */ + char *realhost; +#endif /* KERBEROS5 */ #endif /* KERBEROS */ int try_count = 0; +#ifdef WINDOWSNT + { + WSADATA winsockData; + if (WSAStartup (0x101, &winsockData) == 0) + have_winsock = 1; + } +#endif + do { hostent = gethostbyname (host); @@ -1013,14 +1103,14 @@ socket_connection (host, flags) } } -#define SOCKET_ERROR "Could not create socket for POP connection: " +#define POP_SOCKET_ERROR "Could not create socket for POP connection: " sock = socket (PF_INET, SOCK_STREAM, 0); if (sock < 0) { - strcpy (pop_error, SOCKET_ERROR); + strcpy (pop_error, POP_SOCKET_ERROR); strncat (pop_error, strerror (errno), - ERROR_MAX - sizeof (SOCKET_ERROR)); + ERROR_MAX - sizeof (POP_SOCKET_ERROR)); return (-1); } @@ -1038,7 +1128,7 @@ socket_connection (host, flags) if (! *hostent->h_addr_list) { - (void) close (sock); + CLOSESOCKET (sock); strcpy (pop_error, CONNECT_ERROR); strncat (pop_error, strerror (errno), ERROR_MAX - sizeof (CONNECT_ERROR)); @@ -1050,23 +1140,29 @@ socket_connection (host, flags) #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)) +#ifdef KERBEROS5 + if ((rem = krb5_init_context (&kcontext))) { krb5error: + if (auth_context) + krb5_auth_con_free (kcontext, auth_context); + if (kcontext) + krb5_free_context (kcontext); strcpy (pop_error, KRB_ERROR); strncat (pop_error, error_message (rem), ERROR_MAX - sizeof(KRB_ERROR)); - (void) close (sock); + CLOSESOCKET (sock); return (-1); } - if (rem = krb5_cc_get_principal (ccdef, &client)) - { - goto krb5error; - } + if ((rem = krb5_auth_con_init (kcontext, &auth_context))) + goto krb5error; + + if (rem = krb5_cc_default (kcontext, &ccdef)) + goto krb5error; + + if (rem = krb5_cc_get_principal (kcontext, ccdef, &client)) + goto krb5error; for (cp = hostent->h_name; *cp; cp++) { @@ -1076,22 +1172,20 @@ socket_connection (host, flags) } } - if (rem = krb5_sname_to_principal (hostent->h_name, POP_SERVICE, - FALSE, &server)) - { - goto krb5error; - } + if (rem = krb5_sname_to_principal (kcontext, hostent->h_name, + POP_SERVICE, FALSE, &server)) + goto krb5error; - rem = krb5_sendauth ((krb5_pointer) &sock, "KPOPV1.0", client, server, + rem = krb5_sendauth (kcontext, &auth_context, + (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 subsession key */ 0); /* don't need reply */ - krb5_free_principal (server); + krb5_free_principal (kcontext, server); if (rem) { if (err_ret && err_ret->text.length) @@ -1114,29 +1208,33 @@ socket_connection (host, flags) ERROR_MAX - sizeof (KRB_ERROR)); } if (err_ret) - krb5_free_error (err_ret); + krb5_free_error (kcontext, err_ret); + krb5_auth_con_free (kcontext, auth_context); + krb5_free_context (kcontext); - (void) close (sock); + CLOSESOCKET (sock); return (-1); } -#else /* ! KRB5 */ +#else /* ! KERBEROS5 */ ticket = (KTEXT) malloc (sizeof (KTEXT_ST)); - rem = krb_sendauth (0L, sock, ticket, "pop", hostent->h_name, - (char *) krb_realmofhost (hostent->h_name), + realhost = strdup (hostent->h_name); + rem = krb_sendauth (0L, sock, ticket, "pop", realhost, + (char *) krb_realmofhost (realhost), (unsigned long) 0, &msg_data, &cred, schedule, (struct sockaddr_in *) 0, (struct sockaddr_in *) 0, "KPOPV0.1"); free ((char *) ticket); + free (realhost); if (rem != KSUCCESS) { strcpy (pop_error, KRB_ERROR); strncat (pop_error, krb_err_txt[rem], ERROR_MAX - sizeof (KRB_ERROR)); - (void) close (sock); + CLOSESOCKET (sock); return (-1); } -#endif /* KRB5 */ +#endif /* KERBEROS5 */ } #endif /* KERBEROS */ @@ -1144,7 +1242,7 @@ socket_connection (host, flags) } /* socket_connection */ /* - * Function: getline + * Function: pop_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 @@ -1154,24 +1252,32 @@ socket_connection (host, flags) * 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. + * Returns: The number of characters in the line, which is returned in + * LINE, not including the final null. A return value of 0 + * indicates a blank line. A negative return value indicates an + * error (in which case the contents of LINE are undefined. In + * case of error, an error message is copied into pop_error. * - * Notes: The line returned is overwritten with each call to getline. + * Notes: The line returned is overwritten with each call to pop_getline. * * Side effects: Closes the connection on error. + * + * THE RETURNED LINE MAY CONTAIN EMBEDDED NULLS! */ -static char * -getline (server) +static int +pop_getline (server, line) popserver server; + char **line; { #define GETLINE_ERROR "Error reading from server: " int ret; - + int search_offset = 0; + if (server->data) { - char *cp = strstr (server->buffer + server->buffer_index, "\r\n"); + char *cp = find_crlf (server->buffer + server->buffer_index, + server->data); if (cp) { int found; @@ -1185,13 +1291,24 @@ getline (server) server->buffer_index += data_used; if (pop_debug) + /* Embedded nulls will truncate this output prematurely, + but that's OK because it's just for debugging anyway. */ fprintf (stderr, "<<< %s\n", server->buffer + found); - return (server->buffer + found); + *line = server->buffer + found; + return (data_used - 2); } else { bcopy (server->buffer + server->buffer_index, server->buffer, server->data); + /* Record the fact that we've searched the data already in + the buffer for a CRLF, so that when we search below, we + don't have to search the same data twice. There's a "- + 1" here to account for the fact that the last character + of the data we have may be the CR of a CRLF pair, of + which we haven't read the second half yet, so we may have + to search it again when we read more data. */ + search_offset = server->data - 1; server->buffer_index = 0; } } @@ -1202,38 +1319,44 @@ getline (server) while (1) { - if (server->data == server->buffer_size) + /* There's a "- 1" here to leave room for the null that we put + at the end of the read data below. We put the null there so + that find_crlf knows where to stop when we call it. */ + if (server->data == server->buffer_size - 1) { server->buffer_size += GETLINE_INCR; - server->buffer = realloc (server->buffer, server->buffer_size); + server->buffer = (char *)realloc (server->buffer, server->buffer_size); if (! server->buffer) { - strcpy (pop_error, "Out of memory in getline"); + strcpy (pop_error, "Out of memory in pop_getline"); pop_trash (server); - return (0); + return (-1); } } - ret = read (server->file, server->buffer + server->data, - server->buffer_size - server->data); + ret = RECV (server->file, server->buffer + server->data, + server->buffer_size - server->data - 1, 0); if (ret < 0) { strcpy (pop_error, GETLINE_ERROR); strncat (pop_error, strerror (errno), ERROR_MAX - sizeof (GETLINE_ERROR)); pop_trash (server); - return (0); + return (-1); } else if (ret == 0) { - strcpy (pop_error, "Unexpected EOF from server in getline"); + strcpy (pop_error, "Unexpected EOF from server in pop_getline"); pop_trash (server); - return (0); + return (-1); } else { - char *cp = strstr (server->buffer, "\r\n"); + char *cp; server->data += ret; + server->buffer[server->data] = '\0'; + cp = find_crlf (server->buffer + search_offset, + server->data - search_offset); if (cp) { int data_used = (cp + 2) - server->buffer; @@ -1243,8 +1366,12 @@ getline (server) if (pop_debug) fprintf (stderr, "<<< %s\n", server->buffer); - return (server->buffer); + *line = server->buffer; + return (data_used - 2); } + /* As above, the "- 1" here is to account for the fact that + we may have read a CR without its accompanying LF. */ + search_offset += ret - 1; } } @@ -1313,10 +1440,10 @@ fullwrite (fd, buf, nbytes) int nbytes; { char *cp; - int ret; + int ret = 0; cp = buf; - while ((ret = write (fd, cp, nbytes)) > 0) + while (nbytes && ((ret = SEND (fd, cp, nbytes, 0)) > 0)) { cp += ret; nbytes -= ret; @@ -1337,7 +1464,7 @@ fullwrite (fd, buf, nbytes) * * Returns: 0 for success, else for failure and puts error in pop_error. * - * Side effects: On failure, may make the connection unuseable. + * Side effects: On failure, may make the connection unusable. */ static int getok (server) @@ -1345,7 +1472,7 @@ getok (server) { char *fromline; - if (! (fromline = getline (server))) + if (pop_getline (server, &fromline) < 0) { return (-1); } @@ -1384,8 +1511,7 @@ gettermination (server) { char *fromserver; - fromserver = getline (server); - if (! fromserver) + if (pop_getline (server, &fromserver) < 0) return (-1); if (strcmp (fromserver, ".")) @@ -1408,7 +1534,7 @@ gettermination (server) * try to get the server to quit, but ignoring any responses that * are received. * - * Side effects: The server is unuseable after this function returns. + * Side effects: The server is unusable after this function returns. * Changes made to the maildrop since the session was started (or * since the last pop_reset) may be lost. */ @@ -1435,10 +1561,15 @@ pop_trash (server) { if (server->file >= 0) { + /* avoid recursion; sendline can call pop_trash */ + if (server->trash_started) + return; + server->trash_started = 1; + sendline (server, "RSET"); sendline (server, "QUIT"); - close (server->file); + CLOSESOCKET (server->file); server->file = -1; if (server->buffer) { @@ -1446,25 +1577,33 @@ pop_trash (server) server->buffer = 0; } } + +#ifdef WINDOWSNT + if (have_winsock) + WSACleanup (); +#endif } -/* Search in STRING for an occurrence of SUBSTRING - and return a pointer to that occurrence. - Return 0 if SUBSTRING does not occur in STRING. */ +/* Return a pointer to the first CRLF in IN_STRING, which can contain + embedded nulls and has LEN characters in it not including the final + null, or 0 if it does not contain one. */ static char * -my_strstr (string, substring) - char *string, *substring; +find_crlf (in_string, len) + char *in_string; + int len; { - char *p = string; - - while (*p) + while (len--) { - if (!strcmp (p, substring)) - return p; - p++; + if (*in_string == '\r') + { + if (*++in_string == '\n') + return (in_string - 1); + } + else + in_string++; } - return 0; + return (0); } #endif /* MAIL_USE_POP */