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