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