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