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