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