comparison lib-src/pop.c @ 428:3ecd8885ac67 r21-2-22

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