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