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