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