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