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