1/* pop.c: client routines for talking to a POP3-protocol post-office server
2   Copyright (C) 1991, 1993, 1996, 1997, 1999, 2001, 2002, 2003, 2004,
3                 2005, 2006, 2007  Free Software Foundation, Inc.
4   Written by Jonathan Kamens, jik@security.ov.com.
5
6This file is part of GNU Emacs.
7
8GNU Emacs is free software; you can redistribute it and/or modify
9it under the terms of the GNU General Public License as published by
10the Free Software Foundation; either version 2, or (at your option)
11any later version.
12
13GNU Emacs is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16GNU General Public License for more details.
17
18You should have received a copy of the GNU General Public License
19along with GNU Emacs; see the file COPYING.  If not, write to
20the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21Boston, MA 02110-1301, USA.  */
22
23#ifdef HAVE_CONFIG_H
24#define NO_SHORTNAMES	/* Tell config not to load remap.h */
25#include <config.h>
26#else
27#define MAIL_USE_POP
28#endif
29
30#ifdef MAIL_USE_POP
31
32#include <sys/types.h>
33#ifdef WINDOWSNT
34#include "ntlib.h"
35#include <winsock.h>
36#undef SOCKET_ERROR
37#define RECV(s,buf,len,flags) recv(s,buf,len,flags)
38#define SEND(s,buf,len,flags) send(s,buf,len,flags)
39#define CLOSESOCKET(s) closesocket(s)
40#else
41#include <netinet/in.h>
42#include <sys/socket.h>
43#define RECV(s,buf,len,flags) read(s,buf,len)
44#define SEND(s,buf,len,flags) write(s,buf,len)
45#define CLOSESOCKET(s) close(s)
46#endif
47#include <pop.h>
48
49#ifdef sun
50#include <malloc.h>
51#endif /* sun */
52
53#ifdef HESIOD
54#include <hesiod.h>
55/*
56 * It really shouldn't be necessary to put this declaration here, but
57 * the version of hesiod.h that Athena has installed in release 7.2
58 * doesn't declare this function; I don't know if the 7.3 version of
59 * hesiod.h does.
60 */
61extern struct servent *hes_getservbyname (/* char *, char * */);
62#endif
63
64#include <pwd.h>
65#include <netdb.h>
66#include <errno.h>
67#include <stdio.h>
68#ifdef STDC_HEADERS
69#include <string.h>
70#define index strchr
71#endif
72#ifdef HAVE_UNISTD_H
73#include <unistd.h>
74#endif
75
76#ifdef KERBEROS
77# ifdef HAVE_KRB5_H
78#  include <krb5.h>
79# endif
80# ifdef HAVE_KRB_H
81#  include <krb.h>
82# else
83#  ifdef HAVE_KERBEROSIV_KRB_H
84#   include <kerberosIV/krb.h>
85#  else
86#   ifdef HAVE_KERBEROS_KRB_H
87#    include <kerberos/krb.h>
88#   endif
89#  endif
90# endif
91# ifdef HAVE_COM_ERR_H
92#  include <com_err.h>
93# endif
94#endif /* KERBEROS */
95
96#ifdef KERBEROS
97#ifndef KERBEROS5
98extern int krb_sendauth (/* long, int, KTEXT, char *, char *, char *,
99			    u_long, MSG_DAT *, CREDENTIALS *, Key_schedule,
100			    struct sockaddr_in *, struct sockaddr_in *,
101			    char * */);
102extern char *krb_realmofhost (/* char * */);
103#endif /* ! KERBEROS5 */
104#endif /* KERBEROS */
105
106#ifndef WINDOWSNT
107#if !defined(HAVE_H_ERRNO) || !defined(HAVE_CONFIG_H)
108extern int h_errno;
109#endif
110#endif
111
112#ifndef __P
113# ifdef __STDC__
114#  define __P(a) a
115# else
116#  define __P(a) ()
117# endif /* __STDC__ */
118#endif /* ! __P */
119
120static int socket_connection __P((char *, int));
121static int pop_getline __P((popserver, char **));
122static int sendline __P((popserver, char *));
123static int fullwrite __P((int, char *, int));
124static int getok __P((popserver));
125#if 0
126static int gettermination __P((popserver));
127#endif
128static void pop_trash __P((popserver));
129static char *find_crlf __P((char *, int));
130
131#define ERROR_MAX 160		/* a pretty arbitrary size, but needs
132				   to be bigger than the original
133				   value of 80 */
134#define POP_PORT 110
135#define KPOP_PORT 1109
136#define POP_SERVICE "pop3"	/* we don't want the POP2 port! */
137#ifdef KERBEROS
138#define KPOP_SERVICE "kpop"	/* never used: look for 20060515 to see why */
139#endif
140
141char pop_error[ERROR_MAX];
142int pop_debug = 0;
143
144#ifndef min
145#define min(a,b) (((a) < (b)) ? (a) : (b))
146#endif
147
148/*
149 * Function: pop_open (char *host, char *username, char *password,
150 * 		       int flags)
151 *
152 * Purpose: Establishes a connection with a post-office server, and
153 * 	completes the authorization portion of the session.
154 *
155 * Arguments:
156 * 	host	The server host with which the connection should be
157 * 		established.  Optional.  If omitted, internal
158 * 		heuristics will be used to determine the server host,
159 * 		if possible.
160 * 	username
161 * 		The username of the mail-drop to access.  Optional.
162 * 		If omitted, internal heuristics will be used to
163 * 		determine the username, if possible.
164 * 	password
165 * 		The password to use for authorization.  If omitted,
166 * 		internal heuristics will be used to determine the
167 * 		password, if possible.
168 * 	flags	A bit mask containing flags controlling certain
169 * 		functions of the routine.  Valid flags are defined in
170 * 		the file pop.h
171 *
172 * Return value: Upon successful establishment of a connection, a
173 * 	non-null popserver will be returned.  Otherwise, null will be
174 * 	returned, and the string variable pop_error will contain an
175 * 	explanation of the error.
176 */
177popserver
178pop_open (host, username, password, flags)
179     char *host;
180     char *username;
181     char *password;
182     int flags;
183{
184  int sock;
185  popserver server;
186
187  /* Determine the user name */
188  if (! username)
189    {
190      username = getenv ("USER");
191      if (! (username && *username))
192	{
193	  username = getlogin ();
194	  if (! (username && *username))
195	    {
196	      struct passwd *passwd;
197	      passwd = getpwuid (getuid ());
198	      if (passwd && passwd->pw_name && *passwd->pw_name)
199		{
200		  username = passwd->pw_name;
201		}
202	      else
203		{
204		  strcpy (pop_error, "Could not determine username");
205		  return (0);
206		}
207	    }
208	}
209    }
210
211  /*
212   *  Determine the mail host.
213   */
214
215  if (! host)
216    {
217      host = getenv ("MAILHOST");
218    }
219
220#ifdef HESIOD
221  if ((! host) && (! (flags & POP_NO_HESIOD)))
222    {
223      struct hes_postoffice *office;
224      office = hes_getmailhost (username);
225      if (office && office->po_type && (! strcmp (office->po_type, "POP"))
226	  && office->po_name && *office->po_name && office->po_host
227	  && *office->po_host)
228	{
229	  host = office->po_host;
230	  username = office->po_name;
231	}
232    }
233#endif
234
235#ifdef MAILHOST
236  if (! host)
237    {
238      host = MAILHOST;
239    }
240#endif
241
242  if (! host)
243    {
244      strcpy (pop_error, "Could not determine POP server");
245      return (0);
246    }
247
248  /* Determine the password */
249#ifdef KERBEROS
250#define DONT_NEED_PASSWORD (! (flags & POP_NO_KERBEROS))
251#else
252#define DONT_NEED_PASSWORD 0
253#endif
254
255  if ((! password) && (! DONT_NEED_PASSWORD))
256    {
257      if (! (flags & POP_NO_GETPASS))
258	{
259	  password = getpass ("Enter POP password:");
260	}
261      if (! password)
262	{
263	  strcpy (pop_error, "Could not determine POP password");
264	  return (0);
265	}
266    }
267  if (password)			/* always true, detected 20060515 */
268    flags |= POP_NO_KERBEROS;
269  else
270    password = username;	/* dead code, detected 20060515 */
271  /** "kpop" service is  never used: look for 20060515 to see why **/
272
273  sock = socket_connection (host, flags);
274  if (sock == -1)
275    return (0);
276
277  server = (popserver) malloc (sizeof (struct _popserver));
278  if (! server)
279    {
280      strcpy (pop_error, "Out of memory in pop_open");
281      return (0);
282    }
283  server->buffer = (char *) malloc (GETLINE_MIN);
284  if (! server->buffer)
285    {
286      strcpy (pop_error, "Out of memory in pop_open");
287      free ((char *) server);
288      return (0);
289    }
290
291  server->file = sock;
292  server->data = 0;
293  server->buffer_index = 0;
294  server->buffer_size = GETLINE_MIN;
295  server->in_multi = 0;
296  server->trash_started = 0;
297
298  if (getok (server))
299    return (0);
300
301  /*
302   * I really shouldn't use the pop_error variable like this, but....
303   */
304  if (strlen (username) > ERROR_MAX - 6)
305    {
306      pop_close (server);
307      strcpy (pop_error,
308	      "Username too long; recompile pop.c with larger ERROR_MAX");
309      return (0);
310    }
311  sprintf (pop_error, "USER %s", username);
312
313  if (sendline (server, pop_error) || getok (server))
314    {
315      return (0);
316    }
317
318  if (strlen (password) > ERROR_MAX - 6)
319    {
320      pop_close (server);
321      strcpy (pop_error,
322	      "Password too long; recompile pop.c with larger ERROR_MAX");
323      return (0);
324    }
325  sprintf (pop_error, "PASS %s", password);
326
327  if (sendline (server, pop_error) || getok (server))
328    {
329      return (0);
330    }
331
332  return (server);
333}
334
335/*
336 * Function: pop_stat
337 *
338 * Purpose: Issue the STAT command to the server and return (in the
339 * 	value parameters) the number of messages in the maildrop and
340 * 	the total size of the maildrop.
341 *
342 * Return value: 0 on success, or non-zero with an error in pop_error
343 * 	in failure.
344 *
345 * Side effects: On failure, may make further operations on the
346 * 	connection impossible.
347 */
348int
349pop_stat (server, count, size)
350     popserver server;
351     int *count;
352     int *size;
353{
354  char *fromserver;
355
356  if (server->in_multi)
357    {
358      strcpy (pop_error, "In multi-line query in pop_stat");
359      return (-1);
360    }
361
362  if (sendline (server, "STAT") || (pop_getline (server, &fromserver) < 0))
363    return (-1);
364
365  if (strncmp (fromserver, "+OK ", 4))
366    {
367      if (0 == strncmp (fromserver, "-ERR", 4))
368	{
369	  strncpy (pop_error, fromserver, ERROR_MAX);
370	}
371      else
372	{
373	  strcpy (pop_error,
374		  "Unexpected response from POP server in pop_stat");
375	  pop_trash (server);
376	}
377      return (-1);
378    }
379
380  *count = atoi (&fromserver[4]);
381
382  fromserver = index (&fromserver[4], ' ');
383  if (! fromserver)
384    {
385      strcpy (pop_error,
386	      "Badly formatted response from server in pop_stat");
387      pop_trash (server);
388      return (-1);
389    }
390
391  *size = atoi (fromserver + 1);
392
393  return (0);
394}
395
396/*
397 * Function: pop_list
398 *
399 * Purpose: Performs the POP "list" command and returns (in value
400 * 	parameters) two malloc'd zero-terminated arrays -- one of
401 * 	message IDs, and a parallel one of sizes.
402 *
403 * Arguments:
404 * 	server	The pop connection to talk to.
405 * 	message	The number of the one message about which to get
406 * 		information, or 0 to get information about all
407 * 		messages.
408 *
409 * Return value: 0 on success, non-zero with error in pop_error on
410 * 	failure.
411 *
412 * Side effects: On failure, may make further operations on the
413 * 	connection impossible.
414 */
415int
416pop_list (server, message, IDs, sizes)
417     popserver server;
418     int message;
419     int **IDs;
420     int **sizes;
421{
422  int how_many, i;
423  char *fromserver;
424
425  if (server->in_multi)
426    {
427      strcpy (pop_error, "In multi-line query in pop_list");
428      return (-1);
429    }
430
431  if (message)
432    how_many = 1;
433  else
434    {
435      int count, size;
436      if (pop_stat (server, &count, &size))
437	return (-1);
438      how_many = count;
439    }
440
441  *IDs = (int *) malloc ((how_many + 1) * sizeof (int));
442  *sizes = (int *) malloc ((how_many + 1) * sizeof (int));
443  if (! (*IDs && *sizes))
444    {
445      strcpy (pop_error, "Out of memory in pop_list");
446      return (-1);
447    }
448
449  if (message)
450    {
451      sprintf (pop_error, "LIST %d", message);
452      if (sendline (server, pop_error))
453	{
454	  free ((char *) *IDs);
455	  free ((char *) *sizes);
456	  return (-1);
457	}
458      if (pop_getline (server, &fromserver) < 0)
459	{
460	  free ((char *) *IDs);
461	  free ((char *) *sizes);
462	  return (-1);
463	}
464      if (strncmp (fromserver, "+OK ", 4))
465	{
466	  if (! strncmp (fromserver, "-ERR", 4))
467	    strncpy (pop_error, fromserver, ERROR_MAX);
468	  else
469	    {
470	      strcpy (pop_error,
471		      "Unexpected response from server in pop_list");
472	      pop_trash (server);
473	    }
474	  free ((char *) *IDs);
475	  free ((char *) *sizes);
476	  return (-1);
477	}
478      (*IDs)[0] = atoi (&fromserver[4]);
479      fromserver = index (&fromserver[4], ' ');
480      if (! fromserver)
481	{
482	  strcpy (pop_error,
483		  "Badly formatted response from server in pop_list");
484	  pop_trash (server);
485	  free ((char *) *IDs);
486	  free ((char *) *sizes);
487	  return (-1);
488	}
489      (*sizes)[0] = atoi (fromserver);
490      (*IDs)[1] = (*sizes)[1] = 0;
491      return (0);
492    }
493  else
494    {
495      if (pop_multi_first (server, "LIST", &fromserver))
496	{
497	  free ((char *) *IDs);
498	  free ((char *) *sizes);
499	  return (-1);
500	}
501      for (i = 0; i < how_many; i++)
502	{
503	  if (pop_multi_next (server, &fromserver) <= 0)
504	    {
505	      free ((char *) *IDs);
506	      free ((char *) *sizes);
507	      return (-1);
508	    }
509	  (*IDs)[i] = atoi (fromserver);
510	  fromserver = index (fromserver, ' ');
511	  if (! fromserver)
512	    {
513	      strcpy (pop_error,
514		      "Badly formatted response from server in pop_list");
515	      free ((char *) *IDs);
516	      free ((char *) *sizes);
517	      pop_trash (server);
518	      return (-1);
519	    }
520	  (*sizes)[i] = atoi (fromserver);
521	}
522      if (pop_multi_next (server, &fromserver) < 0)
523	{
524	  free ((char *) *IDs);
525	  free ((char *) *sizes);
526	  return (-1);
527	}
528      else if (fromserver)
529	{
530	  strcpy (pop_error,
531		  "Too many response lines from server in pop_list");
532	  free ((char *) *IDs);
533	  free ((char *) *sizes);
534	  return (-1);
535	}
536      (*IDs)[i] = (*sizes)[i] = 0;
537      return (0);
538    }
539}
540
541/*
542 * Function: pop_retrieve
543 *
544 * Purpose: Retrieve a specified message from the maildrop.
545 *
546 * Arguments:
547 * 	server	The server to retrieve from.
548 * 	message	The message number to retrieve.
549 *	markfrom
550 * 		If true, then mark the string "From " at the beginning
551 * 		of lines with '>'.
552 *	msg_buf	Output parameter to which a buffer containing the
553 * 		message is assigned.
554 *
555 * Return value: The number of bytes in msg_buf, which may contain
556 * 	embedded nulls, not including its final null, or -1 on error
557 * 	with pop_error set.
558 *
559 * Side effects: May kill connection on error.
560 */
561int
562pop_retrieve (server, message, markfrom, msg_buf)
563     popserver server;
564     int message;
565     int markfrom;
566     char **msg_buf;
567{
568  int *IDs, *sizes, bufsize, fromcount = 0, cp = 0;
569  char *ptr, *fromserver;
570  int ret;
571
572  if (server->in_multi)
573    {
574      strcpy (pop_error, "In multi-line query in pop_retrieve");
575      return (-1);
576    }
577
578  if (pop_list (server, message, &IDs, &sizes))
579    return (-1);
580
581  if (pop_retrieve_first (server, message, &fromserver))
582    {
583      return (-1);
584    }
585
586  /*
587   * The "5" below is an arbitrary constant -- I assume that if
588   * there are "From" lines in the text to be marked, there
589   * probably won't be more than 5 of them.  If there are, I
590   * allocate more space for them below.
591   */
592  bufsize = sizes[0] + (markfrom ? 5 : 0);
593  ptr = (char *)malloc (bufsize);
594  free ((char *) IDs);
595  free ((char *) sizes);
596
597  if (! ptr)
598    {
599      strcpy (pop_error, "Out of memory in pop_retrieve");
600      pop_retrieve_flush (server);
601      return (-1);
602    }
603
604  while ((ret = pop_retrieve_next (server, &fromserver)) >= 0)
605    {
606      if (! fromserver)
607	{
608	  ptr[cp] = '\0';
609	  *msg_buf = ptr;
610	  return (cp);
611	}
612      if (markfrom && fromserver[0] == 'F' && fromserver[1] == 'r' &&
613	  fromserver[2] == 'o' && fromserver[3] == 'm' &&
614	  fromserver[4] == ' ')
615	{
616	  if (++fromcount == 5)
617	    {
618	      bufsize += 5;
619	      ptr = (char *)realloc (ptr, bufsize);
620	      if (! ptr)
621		{
622		  strcpy (pop_error, "Out of memory in pop_retrieve");
623		  pop_retrieve_flush (server);
624		  return (-1);
625		}
626	      fromcount = 0;
627	    }
628	  ptr[cp++] = '>';
629	}
630      bcopy (fromserver, &ptr[cp], ret);
631      cp += ret;
632      ptr[cp++] = '\n';
633    }
634
635  free (ptr);
636  return (-1);
637}
638
639int
640pop_retrieve_first (server, message, response)
641     popserver server;
642     int message;
643     char **response;
644{
645  sprintf (pop_error, "RETR %d", message);
646  return (pop_multi_first (server, pop_error, response));
647}
648
649/*
650  Returns a negative number on error, 0 to indicate that the data has
651  all been read (i.e., the server has returned a "." termination
652  line), or a positive number indicating the number of bytes in the
653  returned buffer (which is null-terminated and may contain embedded
654  nulls, but the returned bytecount doesn't include the final null).
655  */
656
657int
658pop_retrieve_next (server, line)
659     popserver server;
660     char **line;
661{
662  return (pop_multi_next (server, line));
663}
664
665int
666pop_retrieve_flush (server)
667     popserver server;
668{
669  return (pop_multi_flush (server));
670}
671
672int
673pop_top_first (server, message, lines, response)
674     popserver server;
675     int message, lines;
676     char **response;
677{
678  sprintf (pop_error, "TOP %d %d", message, lines);
679  return (pop_multi_first (server, pop_error, response));
680}
681
682/*
683  Returns a negative number on error, 0 to indicate that the data has
684  all been read (i.e., the server has returned a "." termination
685  line), or a positive number indicating the number of bytes in the
686  returned buffer (which is null-terminated and may contain embedded
687  nulls, but the returned bytecount doesn't include the final null).
688  */
689
690int
691pop_top_next (server, line)
692     popserver server;
693     char **line;
694{
695  return (pop_multi_next (server, line));
696}
697
698int
699pop_top_flush (server)
700     popserver server;
701{
702  return (pop_multi_flush (server));
703}
704
705int
706pop_multi_first (server, command, response)
707     popserver server;
708     char *command;
709     char **response;
710{
711  if (server->in_multi)
712    {
713      strcpy (pop_error,
714	      "Already in multi-line query in pop_multi_first");
715      return (-1);
716    }
717
718  if (sendline (server, command) || (pop_getline (server, response) < 0))
719    {
720      return (-1);
721    }
722
723  if (0 == strncmp (*response, "-ERR", 4))
724    {
725      strncpy (pop_error, *response, ERROR_MAX);
726      return (-1);
727    }
728  else if (0 == strncmp (*response, "+OK", 3))
729    {
730      for (*response += 3; **response == ' '; (*response)++) /* empty */;
731      server->in_multi = 1;
732      return (0);
733    }
734  else
735    {
736      strcpy (pop_error,
737	      "Unexpected response from server in pop_multi_first");
738      return (-1);
739    }
740}
741
742/*
743  Read the next line of data from SERVER and place a pointer to it
744  into LINE.  Return -1 on error, 0 if there are no more lines to read
745  (i.e., the server has returned a line containing only "."), or a
746  positive number indicating the number of bytes in the LINE buffer
747  (not including the final null).  The data in that buffer may contain
748  embedded nulls, but does not contain the final CRLF. When returning
749  0, LINE is set to null. */
750
751int
752pop_multi_next (server, line)
753     popserver server;
754     char **line;
755{
756  char *fromserver;
757  int ret;
758
759  if (! server->in_multi)
760    {
761      strcpy (pop_error, "Not in multi-line query in pop_multi_next");
762      return (-1);
763    }
764
765  if ((ret = pop_getline (server, &fromserver)) < 0)
766    {
767      return (-1);
768    }
769
770  if (fromserver[0] == '.')
771    {
772      if (! fromserver[1])
773	{
774	  *line = 0;
775	  server->in_multi = 0;
776	  return (0);
777	}
778      else
779	{
780	  *line = fromserver + 1;
781	  return (ret - 1);
782	}
783    }
784  else
785    {
786      *line = fromserver;
787      return (ret);
788    }
789}
790
791int
792pop_multi_flush (server)
793     popserver server;
794{
795  char *line;
796  int ret;
797
798  if (! server->in_multi)
799    {
800      return (0);
801    }
802
803  while ((ret = pop_multi_next (server, &line)))
804    {
805      if (ret < 0)
806	return (-1);
807    }
808
809  return (0);
810}
811
812/* Function: pop_delete
813 *
814 * Purpose: Delete a specified message.
815 *
816 * Arguments:
817 * 	server	Server from which to delete the message.
818 * 	message	Message to delete.
819 *
820 * Return value: 0 on success, non-zero with error in pop_error
821 * 	otherwise.
822 */
823int
824pop_delete (server, message)
825     popserver server;
826     int message;
827{
828  if (server->in_multi)
829    {
830      strcpy (pop_error, "In multi-line query in pop_delete");
831      return (-1);
832    }
833
834  sprintf (pop_error, "DELE %d", message);
835
836  if (sendline (server, pop_error) || getok (server))
837    return (-1);
838
839  return (0);
840}
841
842/*
843 * Function: pop_noop
844 *
845 * Purpose: Send a noop command to the server.
846 *
847 * Argument:
848 * 	server	The server to send to.
849 *
850 * Return value: 0 on success, non-zero with error in pop_error
851 * 	otherwise.
852 *
853 * Side effects: Closes connection on error.
854 */
855int
856pop_noop (server)
857     popserver server;
858{
859  if (server->in_multi)
860    {
861      strcpy (pop_error, "In multi-line query in pop_noop");
862      return (-1);
863    }
864
865  if (sendline (server, "NOOP") || getok (server))
866    return (-1);
867
868  return (0);
869}
870
871/*
872 * Function: pop_last
873 *
874 * Purpose: Find out the highest seen message from the server.
875 *
876 * Arguments:
877 * 	server	The server.
878 *
879 * Return value: If successful, the highest seen message, which is
880 * 	greater than or equal to 0.  Otherwise, a negative number with
881 * 	the error explained in pop_error.
882 *
883 * Side effects: Closes the connection on error.
884 */
885int
886pop_last (server)
887     popserver server;
888{
889  char *fromserver;
890
891  if (server->in_multi)
892    {
893      strcpy (pop_error, "In multi-line query in pop_last");
894      return (-1);
895    }
896
897  if (sendline (server, "LAST"))
898    return (-1);
899
900  if (pop_getline (server, &fromserver) < 0)
901    return (-1);
902
903  if (! strncmp (fromserver, "-ERR", 4))
904    {
905      strncpy (pop_error, fromserver, ERROR_MAX);
906      return (-1);
907    }
908  else if (strncmp (fromserver, "+OK ", 4))
909    {
910      strcpy (pop_error, "Unexpected response from server in pop_last");
911      pop_trash (server);
912      return (-1);
913    }
914  else
915    {
916      return (atoi (&fromserver[4]));
917    }
918}
919
920/*
921 * Function: pop_reset
922 *
923 * Purpose: Reset the server to its initial connect state
924 *
925 * Arguments:
926 * 	server	The server.
927 *
928 * Return value: 0 for success, non-0 with error in pop_error
929 * 	otherwise.
930 *
931 * Side effects: Closes the connection on error.
932 */
933int
934pop_reset (server)
935     popserver server;
936{
937  if (pop_retrieve_flush (server))
938    {
939      return (-1);
940    }
941
942  if (sendline (server, "RSET") || getok (server))
943    return (-1);
944
945  return (0);
946}
947
948/*
949 * Function: pop_quit
950 *
951 * Purpose: Quit the connection to the server,
952 *
953 * Arguments:
954 * 	server	The server to quit.
955 *
956 * Return value: 0 for success, non-zero otherwise with error in
957 * 	pop_error.
958 *
959 * Side Effects: The popserver passed in is unusable after this
960 * 	function is called, even if an error occurs.
961 */
962int
963pop_quit (server)
964     popserver server;
965{
966  int ret = 0;
967
968  if (server->file >= 0)
969    {
970      if (pop_retrieve_flush (server))
971	{
972	  ret = -1;
973	}
974
975      if (sendline (server, "QUIT") || getok (server))
976	{
977	  ret = -1;
978	}
979
980      close (server->file);
981    }
982
983  if (server->buffer)
984    free (server->buffer);
985  free ((char *) server);
986
987  return (ret);
988}
989
990#ifdef WINDOWSNT
991static int have_winsock = 0;
992#endif
993
994/*
995 * Function: socket_connection
996 *
997 * Purpose: Opens the network connection with the mail host, without
998 * 	doing any sort of I/O with it or anything.
999 *
1000 * Arguments:
1001 * 	host	The host to which to connect.
1002 *	flags	Option flags.
1003 *
1004 * Return value: A file descriptor indicating the connection, or -1
1005 * 	indicating failure, in which case an error has been copied
1006 * 	into pop_error.
1007 */
1008static int
1009socket_connection (host, flags)
1010     char *host;
1011     int flags;
1012{
1013  struct hostent *hostent;
1014  struct servent *servent;
1015  struct sockaddr_in addr;
1016  char found_port = 0;
1017  char *service;
1018  int sock;
1019#ifdef KERBEROS
1020#ifdef KERBEROS5
1021  krb5_error_code rem;
1022  krb5_context kcontext = 0;
1023  krb5_auth_context auth_context = 0;
1024  krb5_ccache ccdef;
1025  krb5_principal client, server;
1026  krb5_error *err_ret;
1027  register char *cp;
1028#else
1029  KTEXT ticket;
1030  MSG_DAT msg_data;
1031  CREDENTIALS cred;
1032  Key_schedule schedule;
1033  int rem;
1034  char *realhost;
1035#endif /* KERBEROS5 */
1036#endif /* KERBEROS */
1037
1038  int try_count = 0;
1039
1040#ifdef WINDOWSNT
1041  {
1042    WSADATA winsockData;
1043    if (WSAStartup (0x101, &winsockData) == 0)
1044      have_winsock = 1;
1045  }
1046#endif
1047
1048  bzero ((char *) &addr, sizeof (addr));
1049  addr.sin_family = AF_INET;
1050
1051  /** "kpop" service is  never used: look for 20060515 to see why **/
1052#ifdef KERBEROS
1053  service = (flags & POP_NO_KERBEROS) ? POP_SERVICE : KPOP_SERVICE;
1054#else
1055  service = POP_SERVICE;
1056#endif
1057
1058#ifdef HESIOD
1059  if (! (flags & POP_NO_HESIOD))
1060    {
1061      servent = hes_getservbyname (service, "tcp");
1062      if (servent)
1063	{
1064	  addr.sin_port = servent->s_port;
1065	  found_port = 1;
1066	}
1067    }
1068#endif
1069  if (! found_port)
1070    {
1071      servent = getservbyname (service, "tcp");
1072      if (servent)
1073	{
1074	  addr.sin_port = servent->s_port;
1075	}
1076      else
1077	{
1078  /** "kpop" service is  never used: look for 20060515 to see why **/
1079#ifdef KERBEROS
1080	  addr.sin_port = htons ((flags & POP_NO_KERBEROS) ?
1081				POP_PORT : KPOP_PORT);
1082#else
1083	  addr.sin_port = htons (POP_PORT);
1084#endif
1085	}
1086    }
1087
1088#define POP_SOCKET_ERROR "Could not create socket for POP connection: "
1089
1090  sock = socket (PF_INET, SOCK_STREAM, 0);
1091  if (sock < 0)
1092    {
1093      strcpy (pop_error, POP_SOCKET_ERROR);
1094      strncat (pop_error, strerror (errno),
1095	       ERROR_MAX - sizeof (POP_SOCKET_ERROR));
1096      return (-1);
1097
1098    }
1099
1100  do
1101    {
1102      hostent = gethostbyname (host);
1103      try_count++;
1104      if ((! hostent) && ((h_errno != TRY_AGAIN) || (try_count == 5)))
1105	{
1106	  strcpy (pop_error, "Could not determine POP server's address");
1107	  return (-1);
1108	}
1109    } while (! hostent);
1110
1111  while (*hostent->h_addr_list)
1112    {
1113      bcopy (*hostent->h_addr_list, (char *) &addr.sin_addr,
1114	     hostent->h_length);
1115      if (! connect (sock, (struct sockaddr *) &addr, sizeof (addr)))
1116	break;
1117      hostent->h_addr_list++;
1118    }
1119
1120#define CONNECT_ERROR "Could not connect to POP server: "
1121
1122  if (! *hostent->h_addr_list)
1123    {
1124      CLOSESOCKET (sock);
1125      strcpy (pop_error, CONNECT_ERROR);
1126      strncat (pop_error, strerror (errno),
1127	       ERROR_MAX - sizeof (CONNECT_ERROR));
1128      return (-1);
1129
1130    }
1131
1132#ifdef KERBEROS
1133#define KRB_ERROR "Kerberos error connecting to POP server: "
1134  if (! (flags & POP_NO_KERBEROS))
1135    {
1136#ifdef KERBEROS5
1137      if ((rem = krb5_init_context (&kcontext)))
1138	{
1139	krb5error:
1140	  if (auth_context)
1141	    krb5_auth_con_free (kcontext, auth_context);
1142	  if (kcontext)
1143	    krb5_free_context (kcontext);
1144	  strcpy (pop_error, KRB_ERROR);
1145	  strncat (pop_error, error_message (rem),
1146		   ERROR_MAX - sizeof(KRB_ERROR));
1147	  CLOSESOCKET (sock);
1148	  return (-1);
1149	}
1150
1151      if ((rem = krb5_auth_con_init (kcontext, &auth_context)))
1152	goto krb5error;
1153
1154      if (rem = krb5_cc_default (kcontext, &ccdef))
1155	goto krb5error;
1156
1157      if (rem = krb5_cc_get_principal (kcontext, ccdef, &client))
1158	goto krb5error;
1159
1160      for (cp = hostent->h_name; *cp; cp++)
1161	{
1162	  if (isupper (*cp))
1163	    {
1164	      *cp = tolower (*cp);
1165	    }
1166	}
1167
1168      if (rem = krb5_sname_to_principal (kcontext, hostent->h_name,
1169					 POP_SERVICE, FALSE, &server))
1170	goto krb5error;
1171
1172      rem = krb5_sendauth (kcontext, &auth_context,
1173			   (krb5_pointer) &sock, "KPOPV1.0", client, server,
1174			  AP_OPTS_MUTUAL_REQUIRED,
1175			  0,	/* no checksum */
1176			  0,	/* no creds, use ccache instead */
1177			  ccdef,
1178			  &err_ret,
1179			  0,	/* don't need subsession key */
1180			  0);	/* don't need reply */
1181      krb5_free_principal (kcontext, server);
1182      if (rem)
1183	{
1184	  if (err_ret && err_ret->text.length)
1185	    {
1186	      strcpy (pop_error, KRB_ERROR);
1187	      strncat (pop_error, error_message (rem),
1188		       ERROR_MAX - sizeof (KRB_ERROR));
1189	      strncat (pop_error, " [server says '",
1190		       ERROR_MAX - strlen (pop_error) - 1);
1191	      strncat (pop_error, err_ret->text.data,
1192		       min (ERROR_MAX - strlen (pop_error) - 1,
1193			    err_ret->text.length));
1194	      strncat (pop_error, "']",
1195		       ERROR_MAX - strlen (pop_error) - 1);
1196	    }
1197	  else
1198	    {
1199	      strcpy (pop_error, KRB_ERROR);
1200	      strncat (pop_error, error_message (rem),
1201		       ERROR_MAX - sizeof (KRB_ERROR));
1202	    }
1203	  if (err_ret)
1204	    krb5_free_error (kcontext, err_ret);
1205	  krb5_auth_con_free (kcontext, auth_context);
1206	  krb5_free_context (kcontext);
1207
1208	  CLOSESOCKET (sock);
1209	  return (-1);
1210	}
1211#else  /* ! KERBEROS5 */
1212      ticket = (KTEXT) malloc (sizeof (KTEXT_ST));
1213      realhost = strdup (hostent->h_name);
1214      rem = krb_sendauth (0L, sock, ticket, "pop", realhost,
1215			  (char *) krb_realmofhost (realhost),
1216			  (unsigned long) 0, &msg_data, &cred, schedule,
1217			  (struct sockaddr_in *) 0,
1218			  (struct sockaddr_in *) 0,
1219			  "KPOPV0.1");
1220      free ((char *) ticket);
1221      free (realhost);
1222      if (rem != KSUCCESS)
1223	{
1224	  strcpy (pop_error, KRB_ERROR);
1225	  strncat (pop_error, krb_err_txt[rem],
1226		   ERROR_MAX - sizeof (KRB_ERROR));
1227	  CLOSESOCKET (sock);
1228	  return (-1);
1229	}
1230#endif /* KERBEROS5 */
1231    }
1232#endif /* KERBEROS */
1233
1234  return (sock);
1235} /* socket_connection */
1236
1237/*
1238 * Function: pop_getline
1239 *
1240 * Purpose: Get a line of text from the connection and return a
1241 * 	pointer to it.  The carriage return and linefeed at the end of
1242 * 	the line are stripped, but periods at the beginnings of lines
1243 * 	are NOT dealt with in any special way.
1244 *
1245 * Arguments:
1246 * 	server	The server from which to get the line of text.
1247 *
1248 * Returns: The number of characters in the line, which is returned in
1249 * 	LINE, not including the final null.  A return value of 0
1250 * 	indicates a blank line.  A negative return value indicates an
1251 * 	error (in which case the contents of LINE are undefined.  In
1252 * 	case of error, an error message is copied into pop_error.
1253 *
1254 * Notes: The line returned is overwritten with each call to pop_getline.
1255 *
1256 * Side effects: Closes the connection on error.
1257 *
1258 * THE RETURNED LINE MAY CONTAIN EMBEDDED NULLS!
1259 */
1260static int
1261pop_getline (server, line)
1262     popserver server;
1263     char **line;
1264{
1265#define GETLINE_ERROR "Error reading from server: "
1266
1267  int ret;
1268  int search_offset = 0;
1269
1270  if (server->data)
1271    {
1272      char *cp = find_crlf (server->buffer + server->buffer_index,
1273			    server->data);
1274      if (cp)
1275	{
1276	  int found;
1277	  int data_used;
1278
1279	  found = server->buffer_index;
1280	  data_used = (cp + 2) - server->buffer - found;
1281
1282	  *cp = '\0';		/* terminate the string to be returned */
1283	  server->data -= data_used;
1284	  server->buffer_index += data_used;
1285
1286	  if (pop_debug)
1287	    /* Embedded nulls will truncate this output prematurely,
1288	       but that's OK because it's just for debugging anyway. */
1289	    fprintf (stderr, "<<< %s\n", server->buffer + found);
1290	  *line = server->buffer + found;
1291	  return (data_used - 2);
1292	}
1293      else
1294	{
1295	  bcopy (server->buffer + server->buffer_index,
1296		 server->buffer, server->data);
1297	  /* Record the fact that we've searched the data already in
1298             the buffer for a CRLF, so that when we search below, we
1299             don't have to search the same data twice.  There's a "-
1300             1" here to account for the fact that the last character
1301             of the data we have may be the CR of a CRLF pair, of
1302             which we haven't read the second half yet, so we may have
1303             to search it again when we read more data. */
1304	  search_offset = server->data - 1;
1305	  server->buffer_index = 0;
1306	}
1307    }
1308  else
1309    {
1310      server->buffer_index = 0;
1311    }
1312
1313  while (1)
1314    {
1315      /* There's a "- 1" here to leave room for the null that we put
1316         at the end of the read data below.  We put the null there so
1317         that find_crlf knows where to stop when we call it. */
1318      if (server->data == server->buffer_size - 1)
1319	{
1320	  server->buffer_size += GETLINE_INCR;
1321	  server->buffer = (char *)realloc (server->buffer, server->buffer_size);
1322	  if (! server->buffer)
1323	    {
1324	      strcpy (pop_error, "Out of memory in pop_getline");
1325	      pop_trash (server);
1326	      return (-1);
1327	    }
1328	}
1329      ret = RECV (server->file, server->buffer + server->data,
1330		  server->buffer_size - server->data - 1, 0);
1331      if (ret < 0)
1332	{
1333	  strcpy (pop_error, GETLINE_ERROR);
1334	  strncat (pop_error, strerror (errno),
1335		   ERROR_MAX - sizeof (GETLINE_ERROR));
1336	  pop_trash (server);
1337	  return (-1);
1338	}
1339      else if (ret == 0)
1340	{
1341	  strcpy (pop_error, "Unexpected EOF from server in pop_getline");
1342	  pop_trash (server);
1343	  return (-1);
1344	}
1345      else
1346	{
1347	  char *cp;
1348	  server->data += ret;
1349	  server->buffer[server->data] = '\0';
1350
1351	  cp = find_crlf (server->buffer + search_offset,
1352			  server->data - search_offset);
1353	  if (cp)
1354	    {
1355	      int data_used = (cp + 2) - server->buffer;
1356	      *cp = '\0';
1357	      server->data -= data_used;
1358	      server->buffer_index = data_used;
1359
1360	      if (pop_debug)
1361		fprintf (stderr, "<<< %s\n", server->buffer);
1362	      *line = server->buffer;
1363	      return (data_used - 2);
1364	    }
1365	  /* As above, the "- 1" here is to account for the fact that
1366	     we may have read a CR without its accompanying LF. */
1367	  search_offset += ret - 1;
1368	}
1369    }
1370
1371  /* NOTREACHED */
1372}
1373
1374/*
1375 * Function: sendline
1376 *
1377 * Purpose: Sends a line of text to the POP server.  The line of text
1378 * 	passed into this function should NOT have the carriage return
1379 * 	and linefeed on the end of it.  Periods at beginnings of lines
1380 * 	will NOT be treated specially by this function.
1381 *
1382 * Arguments:
1383 * 	server	The server to which to send the text.
1384 * 	line	The line of text to send.
1385 *
1386 * Return value: Upon successful completion, a value of 0 will be
1387 * 	returned.  Otherwise, a non-zero value will be returned, and
1388 * 	an error will be copied into pop_error.
1389 *
1390 * Side effects: Closes the connection on error.
1391 */
1392static int
1393sendline (server, line)
1394     popserver server;
1395     char *line;
1396{
1397#define SENDLINE_ERROR "Error writing to POP server: "
1398  int ret;
1399  char *buf;
1400
1401  /* Combine the string and the CR-LF into one buffer.  Otherwise, two
1402     reasonable network stack optimizations, Nagle's algorithm and
1403     delayed acks, combine to delay us a fraction of a second on every
1404     message we send.  (Movemail writes line without \r\n, client
1405     kernel sends packet, server kernel delays the ack to see if it
1406     can combine it with data, movemail writes \r\n, client kernel
1407     waits because it has unacked data already in its outgoing queue,
1408     client kernel eventually times out and sends.)
1409
1410     This can be something like 0.2s per command, which can add up
1411     over a few dozen messages, and is a big chunk of the time we
1412     spend fetching mail from a server close by.  */
1413  buf = alloca (strlen (line) + 3);
1414  strcpy (buf, line);
1415  strcat (buf, "\r\n");
1416  ret = fullwrite (server->file, buf, strlen (buf));
1417
1418  if (ret < 0)
1419    {
1420      pop_trash (server);
1421      strcpy (pop_error, SENDLINE_ERROR);
1422      strncat (pop_error, strerror (errno),
1423	       ERROR_MAX - sizeof (SENDLINE_ERROR));
1424      return (ret);
1425    }
1426
1427  if (pop_debug)
1428    fprintf (stderr, ">>> %s\n", line);
1429
1430  return (0);
1431}
1432
1433/*
1434 * Procedure: fullwrite
1435 *
1436 * Purpose: Just like write, but keeps trying until the entire string
1437 * 	has been written.
1438 *
1439 * Return value: Same as write.  Pop_error is not set.
1440 */
1441static int
1442fullwrite (fd, buf, nbytes)
1443     int fd;
1444     char *buf;
1445     int nbytes;
1446{
1447  char *cp;
1448  int ret = 0;
1449
1450  cp = buf;
1451  while (nbytes && ((ret = SEND (fd, cp, nbytes, 0)) > 0))
1452    {
1453      cp += ret;
1454      nbytes -= ret;
1455    }
1456
1457  return (ret);
1458}
1459
1460/*
1461 * Procedure getok
1462 *
1463 * Purpose: Reads a line from the server.  If the return indicator is
1464 * 	positive, return with a zero exit status.  If not, return with
1465 * 	a negative exit status.
1466 *
1467 * Arguments:
1468 * 	server	The server to read from.
1469 *
1470 * Returns: 0 for success, else for failure and puts error in pop_error.
1471 *
1472 * Side effects: On failure, may make the connection unusable.
1473 */
1474static int
1475getok (server)
1476     popserver server;
1477{
1478  char *fromline;
1479
1480  if (pop_getline (server, &fromline) < 0)
1481    {
1482      return (-1);
1483    }
1484
1485  if (! strncmp (fromline, "+OK", 3))
1486    return (0);
1487  else if (! strncmp (fromline, "-ERR", 4))
1488    {
1489      strncpy (pop_error, fromline, ERROR_MAX);
1490      pop_error[ERROR_MAX-1] = '\0';
1491      return (-1);
1492    }
1493  else
1494    {
1495      strcpy (pop_error,
1496	      "Unexpected response from server; expecting +OK or -ERR");
1497      pop_trash (server);
1498      return (-1);
1499    }
1500}
1501
1502#if 0
1503/*
1504 * Function: gettermination
1505 *
1506 * Purpose: Gets the next line and verifies that it is a termination
1507 * 	line (nothing but a dot).
1508 *
1509 * Return value: 0 on success, non-zero with pop_error set on error.
1510 *
1511 * Side effects: Closes the connection on error.
1512 */
1513static int
1514gettermination (server)
1515     popserver server;
1516{
1517  char *fromserver;
1518
1519  if (pop_getline (server, &fromserver) < 0)
1520    return (-1);
1521
1522  if (strcmp (fromserver, "."))
1523    {
1524      strcpy (pop_error,
1525	      "Unexpected response from server in gettermination");
1526      pop_trash (server);
1527      return (-1);
1528    }
1529
1530  return (0);
1531}
1532#endif
1533
1534/*
1535 * Function pop_close
1536 *
1537 * Purpose: Close a pop connection, sending a "RSET" command to try to
1538 * 	preserve any changes that were made and a "QUIT" command to
1539 * 	try to get the server to quit, but ignoring any responses that
1540 * 	are received.
1541 *
1542 * Side effects: The server is unusable after this function returns.
1543 * 	Changes made to the maildrop since the session was started (or
1544 * 	since the last pop_reset) may be lost.
1545 */
1546void
1547pop_close (server)
1548     popserver server;
1549{
1550  pop_trash (server);
1551  free ((char *) server);
1552
1553  return;
1554}
1555
1556/*
1557 * Function: pop_trash
1558 *
1559 * Purpose: Like pop_close or pop_quit, but doesn't deallocate the
1560 * 	memory associated with the server.  It is legal to call
1561 * 	pop_close or pop_quit after this function has been called.
1562 */
1563static void
1564pop_trash (server)
1565     popserver server;
1566{
1567  if (server->file >= 0)
1568    {
1569      /* avoid recursion; sendline can call pop_trash */
1570      if (server->trash_started)
1571	return;
1572      server->trash_started = 1;
1573
1574      sendline (server, "RSET");
1575      sendline (server, "QUIT");
1576
1577      CLOSESOCKET (server->file);
1578      server->file = -1;
1579      if (server->buffer)
1580	{
1581	  free (server->buffer);
1582	  server->buffer = 0;
1583	}
1584    }
1585
1586#ifdef WINDOWSNT
1587  if (have_winsock)
1588    WSACleanup ();
1589#endif
1590}
1591
1592/* Return a pointer to the first CRLF in IN_STRING, which can contain
1593   embedded nulls and has LEN characters in it not including the final
1594   null, or 0 if it does not contain one.  */
1595
1596static char *
1597find_crlf (in_string, len)
1598     char *in_string;
1599     int len;
1600{
1601  while (len--)
1602    {
1603      if (*in_string == '\r')
1604	{
1605	  if (*++in_string == '\n')
1606	    return (in_string - 1);
1607	}
1608      else
1609	in_string++;
1610    }
1611  return (0);
1612}
1613
1614#endif /* MAIL_USE_POP */
1615
1616/* arch-tag: ceb37041-b7ad-49a8-a63d-286618b8367d
1617   (do not change this comment) */
1618