Undo this previous change:
[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));
60b0a379 142static int pop_getline _P((popserver, char **));
d89d0243
KH
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 163
0ea95737
KH
164#ifdef GSSAPI
165# ifdef HAVE_GSSAPI_H
166# include <gssapi.h>
167# else
168# include <gssapi/gssapi.h>
169# endif
170#define GSSAPI_SERVICE "pop"
171static int pop_auth (/* popserver server, char *user,
172 char *host, int flags */);
173static void gen_gss_error (/* char *msg, OM_uint32 major, OM_uint32 minor */);
174struct _pop_gssapi
175{
176 int gss_flags; /* encryption? integrity protection? */
177 OM_uint32 max_size; /* max size we can send the server */
178 gss_ctx_id_t gss_context; /* the security context */
179};
180#define GSSAPI_NOPROT 0x01
181#define GSSAPI_INTEGRITY 0x02
182#define GSSAPI_PRIVACY 0x04
183#define GSSAPI_NEEDWRAP (GSSAPI_INTEGRITY|GSSAPI_PRIVACY)
184#define GSSAPI_PROTECTION (GSSAPI_NOPROT|GSSAPI_INTEGRITY|GSSAPI_PRIVACY)
185#define GSSAPI_RCVBUF 1024
186#define GSSAPI_SVC_TYPE {10, "\052\206\110\206\367\022\001\002\001\004"}
187#define Gssapi(data) ((struct _pop_gssapi *) (data))
188
189static int b64_decode (/* char *enc, gss_buffer_t dec */);
190static int b64_encode (/* gss_buffer_t dec, char **enc */);
191#define B64_SUCCESS 0
192#define B64_BADPARAM 1
193#define B64_BADCHAR 2
194#define B64_BADPAD 3
195#define B64_BADLEN 4
196#define B64_NOMEM 5
197static char *b64_error[] =
198{
199 "Success",
200 "Bad parameters",
201 "Bad characters in encoding",
202 "Bad padding in encoding",
203 "Bad length",
204 "Out of memory"
205};
206
207/*
208 * This function is only needed if you are using the GSSAPI protection
209 * mechanisms; it keeps trying until it has read the requested number
210 * bytes from the passed-in fd.
211 */
212static int fullread (/* int fd, char *buf, int nbytes */);
213#endif /* GSSAPI */
214
9c0f2dac
RS
215char pop_error[ERROR_MAX];
216int pop_debug = 0;
217
218#ifndef min
219#define min(a,b) (((a) < (b)) ? (a) : (b))
220#endif
221
222/*
223 * Function: pop_open (char *host, char *username, char *password,
224 * int flags)
225 *
226 * Purpose: Establishes a connection with a post-office server, and
227 * completes the authorization portion of the session.
228 *
229 * Arguments:
230 * host The server host with which the connection should be
231 * established. Optional. If omitted, internal
232 * heuristics will be used to determine the server host,
233 * if possible.
234 * username
235 * The username of the mail-drop to access. Optional.
236 * If omitted, internal heuristics will be used to
237 * determine the username, if possible.
238 * password
239 * The password to use for authorization. If omitted,
240 * internal heuristics will be used to determine the
241 * password, if possible.
242 * flags A bit mask containing flags controlling certain
243 * functions of the routine. Valid flags are defined in
244 * the file pop.h
245 *
246 * Return value: Upon successful establishment of a connection, a
247 * non-null popserver will be returned. Otherwise, null will be
248 * returned, and the string variable pop_error will contain an
249 * explanation of the error.
250 */
251popserver
252pop_open (host, username, password, flags)
253 char *host;
254 char *username;
255 char *password;
256 int flags;
257{
258 int sock;
259 popserver server;
260
261 /* Determine the user name */
262 if (! username)
263 {
264 username = getenv ("USER");
265 if (! (username && *username))
266 {
267 username = getlogin ();
268 if (! (username && *username))
269 {
270 struct passwd *passwd;
271 passwd = getpwuid (getuid ());
272 if (passwd && passwd->pw_name && *passwd->pw_name)
273 {
274 username = passwd->pw_name;
275 }
276 else
277 {
278 strcpy (pop_error, "Could not determine username");
279 return (0);
280 }
281 }
282 }
283 }
284
285 /*
286 * Determine the mail host.
287 */
288
289 if (! host)
290 {
291 host = getenv ("MAILHOST");
292 }
293
294#ifdef HESIOD
295 if ((! host) && (! (flags & POP_NO_HESIOD)))
296 {
297 struct hes_postoffice *office;
298 office = hes_getmailhost (username);
299 if (office && office->po_type && (! strcmp (office->po_type, "POP"))
300 && office->po_name && *office->po_name && office->po_host
301 && *office->po_host)
302 {
303 host = office->po_host;
304 username = office->po_name;
305 }
306 }
307#endif
308
309#ifdef MAILHOST
310 if (! host)
311 {
312 host = MAILHOST;
313 }
314#endif
315
316 if (! host)
317 {
318 strcpy (pop_error, "Could not determine POP server");
319 return (0);
320 }
321
322 /* Determine the password */
0ea95737
KH
323#if defined(KERBEROS) || defined(GSSAPI)
324# ifdef KERBEROS
325# define NO_KERBEROS POP_NO_KERBEROS
326# else
327# define NO_KERBEROS 0
328# endif /* KERBEROS */
329
330# ifdef GSSAPI
331# define NO_GSSAPI POP_NO_GSSAPI
332# else
333# define NO_GSSAPI 0
334# endif /* GSSAPI */
335
336# define DONT_NEED_PASSWORD (! (flags & (NO_KERBEROS | NO_GSSAPI)))
9c0f2dac 337#else
0ea95737 338# define DONT_NEED_PASSWORD 0
9c0f2dac
RS
339#endif
340
341 if ((! password) && (! DONT_NEED_PASSWORD))
342 {
343 if (! (flags & POP_NO_GETPASS))
344 {
345 password = getpass ("Enter POP password:");
346 }
347 if (! password)
348 {
349 strcpy (pop_error, "Could not determine POP password");
350 return (0);
351 }
352 }
353 if (password)
0ea95737 354 flags |= POP_NO_KERBEROS | (!(flags & POP_NO_NOPROT) ? POP_NO_GSSAPI : 0);
9c0f2dac
RS
355 else
356 password = username;
357
358 sock = socket_connection (host, flags);
359 if (sock == -1)
360 return (0);
361
362 server = (popserver) malloc (sizeof (struct _popserver));
363 if (! server)
364 {
365 strcpy (pop_error, "Out of memory in pop_open");
366 return (0);
367 }
368 server->buffer = (char *) malloc (GETLINE_MIN);
369 if (! server->buffer)
370 {
371 strcpy (pop_error, "Out of memory in pop_open");
372 free ((char *) server);
373 return (0);
374 }
375
376 server->file = sock;
377 server->data = 0;
378 server->buffer_index = 0;
379 server->buffer_size = GETLINE_MIN;
380 server->in_multi = 0;
c2668a61 381 server->trash_started = 0;
0ea95737 382 server->extra = 0;
9c0f2dac
RS
383
384 if (getok (server))
385 return (0);
386
0ea95737
KH
387#ifdef GSSAPI
388 /*
389 * unless forbidden to use GSSAPI, try the GSSAPI AUTH mechanism..first.
390 */
391 pop_error[0] = '\0'; /* so we can detect errors later... */
392 if (! (flags & POP_NO_GSSAPI))
393 {
394 int ret;
395
396 ret = pop_auth (server, username, host, flags);
397 if (ret == 0)
398 {
399 return (server);
400 }
401 else if (ret == -2)
402 {
403 pop_close (server);
404 return (0);
405 }
406 }
407#endif /* GSSAPI */
408 /*
409 * POP_NO_NOPROT is used in the case that we want protection; if
410 * the authentication negotiation failed, then we want to fail now.
411 */
412 if ((flags & POP_NO_NOPROT))
413 {
414 pop_close (server);
415#ifdef GSSAPI
416 if (pop_error[0] == '\0')
417#endif
418 strcpy (pop_error, "Unable to provide protection");
419 return (0);
420 }
421
9c0f2dac
RS
422 /*
423 * I really shouldn't use the pop_error variable like this, but....
424 */
425 if (strlen (username) > ERROR_MAX - 6)
426 {
427 pop_close (server);
428 strcpy (pop_error,
429 "Username too long; recompile pop.c with larger ERROR_MAX");
430 return (0);
431 }
432 sprintf (pop_error, "USER %s", username);
433
434 if (sendline (server, pop_error) || getok (server))
435 {
436 return (0);
437 }
438
439 if (strlen (password) > ERROR_MAX - 6)
440 {
441 pop_close (server);
442 strcpy (pop_error,
443 "Password too long; recompile pop.c with larger ERROR_MAX");
444 return (0);
445 }
446 sprintf (pop_error, "PASS %s", password);
447
448 if (sendline (server, pop_error) || getok (server))
449 {
450 return (0);
451 }
452
453 return (server);
454}
455
456/*
457 * Function: pop_stat
458 *
459 * Purpose: Issue the STAT command to the server and return (in the
460 * value parameters) the number of messages in the maildrop and
461 * the total size of the maildrop.
462 *
463 * Return value: 0 on success, or non-zero with an error in pop_error
464 * in failure.
465 *
466 * Side effects: On failure, may make further operations on the
467 * connection impossible.
468 */
469int
470pop_stat (server, count, size)
471 popserver server;
472 int *count;
473 int *size;
474{
475 char *fromserver;
476
477 if (server->in_multi)
478 {
479 strcpy (pop_error, "In multi-line query in pop_stat");
480 return (-1);
481 }
482
60b0a379 483 if (sendline (server, "STAT") || (pop_getline (server, &fromserver) < 0))
9c0f2dac
RS
484 return (-1);
485
486 if (strncmp (fromserver, "+OK ", 4))
487 {
488 if (0 == strncmp (fromserver, "-ERR", 4))
489 {
490 strncpy (pop_error, fromserver, ERROR_MAX);
491 }
492 else
493 {
494 strcpy (pop_error,
495 "Unexpected response from POP server in pop_stat");
496 pop_trash (server);
497 }
498 return (-1);
499 }
500
501 *count = atoi (&fromserver[4]);
502
503 fromserver = index (&fromserver[4], ' ');
504 if (! fromserver)
505 {
506 strcpy (pop_error,
507 "Badly formatted response from server in pop_stat");
508 pop_trash (server);
509 return (-1);
510 }
511
512 *size = atoi (fromserver + 1);
513
514 return (0);
515}
516
517/*
518 * Function: pop_list
519 *
520 * Purpose: Performs the POP "list" command and returns (in value
521 * parameters) two malloc'd zero-terminated arrays -- one of
522 * message IDs, and a parallel one of sizes.
523 *
524 * Arguments:
525 * server The pop connection to talk to.
526 * message The number of the one message about which to get
527 * information, or 0 to get information about all
528 * messages.
529 *
530 * Return value: 0 on success, non-zero with error in pop_error on
531 * failure.
532 *
533 * Side effects: On failure, may make further operations on the
534 * connection impossible.
535 */
536int
537pop_list (server, message, IDs, sizes)
538 popserver server;
539 int message;
540 int **IDs;
541 int **sizes;
542{
543 int how_many, i;
544 char *fromserver;
545
546 if (server->in_multi)
547 {
548 strcpy (pop_error, "In multi-line query in pop_list");
549 return (-1);
550 }
551
552 if (message)
553 how_many = 1;
554 else
555 {
556 int count, size;
557 if (pop_stat (server, &count, &size))
558 return (-1);
559 how_many = count;
560 }
561
562 *IDs = (int *) malloc ((how_many + 1) * sizeof (int));
563 *sizes = (int *) malloc ((how_many + 1) * sizeof (int));
564 if (! (*IDs && *sizes))
565 {
566 strcpy (pop_error, "Out of memory in pop_list");
567 return (-1);
568 }
569
570 if (message)
571 {
572 sprintf (pop_error, "LIST %d", message);
573 if (sendline (server, pop_error))
574 {
575 free ((char *) *IDs);
576 free ((char *) *sizes);
577 return (-1);
578 }
60b0a379 579 if (pop_getline (server, &fromserver) < 0)
9c0f2dac
RS
580 {
581 free ((char *) *IDs);
582 free ((char *) *sizes);
583 return (-1);
584 }
585 if (strncmp (fromserver, "+OK ", 4))
586 {
587 if (! strncmp (fromserver, "-ERR", 4))
588 strncpy (pop_error, fromserver, ERROR_MAX);
589 else
590 {
591 strcpy (pop_error,
592 "Unexpected response from server in pop_list");
593 pop_trash (server);
594 }
595 free ((char *) *IDs);
596 free ((char *) *sizes);
597 return (-1);
598 }
599 (*IDs)[0] = atoi (&fromserver[4]);
600 fromserver = index (&fromserver[4], ' ');
601 if (! fromserver)
602 {
603 strcpy (pop_error,
604 "Badly formatted response from server in pop_list");
605 pop_trash (server);
606 free ((char *) *IDs);
607 free ((char *) *sizes);
608 return (-1);
609 }
610 (*sizes)[0] = atoi (fromserver);
611 (*IDs)[1] = (*sizes)[1] = 0;
612 return (0);
613 }
614 else
615 {
616 if (pop_multi_first (server, "LIST", &fromserver))
617 {
618 free ((char *) *IDs);
619 free ((char *) *sizes);
620 return (-1);
621 }
622 for (i = 0; i < how_many; i++)
623 {
d89d0243 624 if (pop_multi_next (server, &fromserver) <= 0)
9c0f2dac
RS
625 {
626 free ((char *) *IDs);
627 free ((char *) *sizes);
628 return (-1);
629 }
630 (*IDs)[i] = atoi (fromserver);
631 fromserver = index (fromserver, ' ');
632 if (! fromserver)
633 {
634 strcpy (pop_error,
635 "Badly formatted response from server in pop_list");
636 free ((char *) *IDs);
637 free ((char *) *sizes);
638 pop_trash (server);
639 return (-1);
640 }
641 (*sizes)[i] = atoi (fromserver);
642 }
d89d0243 643 if (pop_multi_next (server, &fromserver) < 0)
9c0f2dac
RS
644 {
645 free ((char *) *IDs);
646 free ((char *) *sizes);
647 return (-1);
648 }
649 else if (fromserver)
650 {
651 strcpy (pop_error,
652 "Too many response lines from server in pop_list");
653 free ((char *) *IDs);
654 free ((char *) *sizes);
655 return (-1);
656 }
657 (*IDs)[i] = (*sizes)[i] = 0;
658 return (0);
659 }
660}
661
662/*
663 * Function: pop_retrieve
664 *
665 * Purpose: Retrieve a specified message from the maildrop.
666 *
667 * Arguments:
668 * server The server to retrieve from.
669 * message The message number to retrieve.
670 * markfrom
671 * If true, then mark the string "From " at the beginning
672 * of lines with '>'.
d89d0243
KH
673 * msg_buf Output parameter to which a buffer containing the
674 * message is assigned.
9c0f2dac 675 *
d89d0243
KH
676 * Return value: The number of bytes in msg_buf, which may contain
677 * embedded nulls, not including its final null, or -1 on error
678 * with pop_error set.
9c0f2dac
RS
679 *
680 * Side effects: May kill connection on error.
681 */
d89d0243
KH
682int
683pop_retrieve (server, message, markfrom, msg_buf)
9c0f2dac
RS
684 popserver server;
685 int message;
686 int markfrom;
d89d0243 687 char **msg_buf;
9c0f2dac
RS
688{
689 int *IDs, *sizes, bufsize, fromcount = 0, cp = 0;
690 char *ptr, *fromserver;
691 int ret;
692
693 if (server->in_multi)
694 {
695 strcpy (pop_error, "In multi-line query in pop_retrieve");
d89d0243 696 return (-1);
9c0f2dac
RS
697 }
698
699 if (pop_list (server, message, &IDs, &sizes))
d89d0243 700 return (-1);
9c0f2dac
RS
701
702 if (pop_retrieve_first (server, message, &fromserver))
703 {
d89d0243 704 return (-1);
9c0f2dac
RS
705 }
706
707 /*
708 * The "5" below is an arbitrary constant -- I assume that if
709 * there are "From" lines in the text to be marked, there
710 * probably won't be more than 5 of them. If there are, I
711 * allocate more space for them below.
712 */
713 bufsize = sizes[0] + (markfrom ? 5 : 0);
7d665945 714 ptr = (char *)malloc (bufsize);
9c0f2dac
RS
715 free ((char *) IDs);
716 free ((char *) sizes);
717
718 if (! ptr)
719 {
720 strcpy (pop_error, "Out of memory in pop_retrieve");
721 pop_retrieve_flush (server);
d89d0243 722 return (-1);
9c0f2dac
RS
723 }
724
d89d0243 725 while ((ret = pop_retrieve_next (server, &fromserver)) >= 0)
9c0f2dac 726 {
9c0f2dac
RS
727 if (! fromserver)
728 {
729 ptr[cp] = '\0';
d89d0243
KH
730 *msg_buf = ptr;
731 return (cp);
9c0f2dac
RS
732 }
733 if (markfrom && fromserver[0] == 'F' && fromserver[1] == 'r' &&
734 fromserver[2] == 'o' && fromserver[3] == 'm' &&
735 fromserver[4] == ' ')
736 {
737 if (++fromcount == 5)
738 {
739 bufsize += 5;
7d665945 740 ptr = (char *)realloc (ptr, bufsize);
9c0f2dac
RS
741 if (! ptr)
742 {
743 strcpy (pop_error, "Out of memory in pop_retrieve");
744 pop_retrieve_flush (server);
d89d0243 745 return (-1);
9c0f2dac
RS
746 }
747 fromcount = 0;
748 }
749 ptr[cp++] = '>';
750 }
d89d0243
KH
751 bcopy (fromserver, &ptr[cp], ret);
752 cp += ret;
9c0f2dac
RS
753 ptr[cp++] = '\n';
754 }
755
d89d0243
KH
756 free (ptr);
757 return (-1);
9c0f2dac
RS
758}
759
760int
761pop_retrieve_first (server, message, response)
762 popserver server;
763 int message;
764 char **response;
765{
766 sprintf (pop_error, "RETR %d", message);
767 return (pop_multi_first (server, pop_error, response));
768}
769
d89d0243
KH
770/*
771 Returns a negative number on error, 0 to indicate that the data has
772 all been read (i.e., the server has returned a "." termination
773 line), or a positive number indicating the number of bytes in the
774 returned buffer (which is null-terminated and may contain embedded
775 nulls, but the returned bytecount doesn't include the final null).
776 */
777
9c0f2dac
RS
778int
779pop_retrieve_next (server, line)
780 popserver server;
781 char **line;
782{
783 return (pop_multi_next (server, line));
784}
785
786int
787pop_retrieve_flush (server)
788 popserver server;
789{
790 return (pop_multi_flush (server));
791}
792
793int
794pop_top_first (server, message, lines, response)
795 popserver server;
796 int message, lines;
797 char **response;
798{
799 sprintf (pop_error, "TOP %d %d", message, lines);
800 return (pop_multi_first (server, pop_error, response));
801}
802
d89d0243
KH
803/*
804 Returns a negative number on error, 0 to indicate that the data has
805 all been read (i.e., the server has returned a "." termination
806 line), or a positive number indicating the number of bytes in the
807 returned buffer (which is null-terminated and may contain embedded
808 nulls, but the returned bytecount doesn't include the final null).
809 */
810
9c0f2dac
RS
811int
812pop_top_next (server, line)
813 popserver server;
814 char **line;
815{
816 return (pop_multi_next (server, line));
817}
818
819int
820pop_top_flush (server)
821 popserver server;
822{
823 return (pop_multi_flush (server));
824}
825
826int
827pop_multi_first (server, command, response)
828 popserver server;
829 char *command;
830 char **response;
831{
832 if (server->in_multi)
833 {
834 strcpy (pop_error,
835 "Already in multi-line query in pop_multi_first");
836 return (-1);
837 }
838
60b0a379 839 if (sendline (server, command) || (pop_getline (server, response) < 0))
9c0f2dac
RS
840 {
841 return (-1);
842 }
843
844 if (0 == strncmp (*response, "-ERR", 4))
845 {
846 strncpy (pop_error, *response, ERROR_MAX);
847 return (-1);
848 }
849 else if (0 == strncmp (*response, "+OK", 3))
850 {
851 for (*response += 3; **response == ' '; (*response)++) /* empty */;
852 server->in_multi = 1;
853 return (0);
854 }
855 else
856 {
857 strcpy (pop_error,
858 "Unexpected response from server in pop_multi_first");
859 return (-1);
860 }
861}
862
d89d0243
KH
863/*
864 Read the next line of data from SERVER and place a pointer to it
865 into LINE. Return -1 on error, 0 if there are no more lines to read
866 (i.e., the server has returned a line containing only "."), or a
867 positive number indicating the number of bytes in the LINE buffer
868 (not including the final null). The data in that buffer may contain
869 embedded nulls, but does not contain the final CRLF. When returning
870 0, LINE is set to null. */
871
9c0f2dac
RS
872int
873pop_multi_next (server, line)
874 popserver server;
875 char **line;
876{
877 char *fromserver;
d89d0243 878 int ret;
9c0f2dac
RS
879
880 if (! server->in_multi)
881 {
882 strcpy (pop_error, "Not in multi-line query in pop_multi_next");
883 return (-1);
884 }
885
60b0a379 886 if ((ret = pop_getline (server, &fromserver)) < 0)
9c0f2dac
RS
887 {
888 return (-1);
889 }
890
891 if (fromserver[0] == '.')
892 {
893 if (! fromserver[1])
894 {
895 *line = 0;
896 server->in_multi = 0;
897 return (0);
898 }
899 else
900 {
901 *line = fromserver + 1;
d89d0243 902 return (ret - 1);
9c0f2dac
RS
903 }
904 }
905 else
906 {
907 *line = fromserver;
d89d0243 908 return (ret);
9c0f2dac
RS
909 }
910}
911
912int
913pop_multi_flush (server)
914 popserver server;
915{
916 char *line;
d89d0243 917 int ret;
9c0f2dac
RS
918
919 if (! server->in_multi)
920 {
921 return (0);
922 }
923
d89d0243 924 while ((ret = pop_multi_next (server, &line)))
9c0f2dac 925 {
d89d0243
KH
926 if (ret < 0)
927 return (-1);
9c0f2dac
RS
928 }
929
d89d0243 930 return (0);
9c0f2dac
RS
931}
932
933/* Function: pop_delete
934 *
935 * Purpose: Delete a specified message.
936 *
937 * Arguments:
938 * server Server from which to delete the message.
939 * message Message to delete.
940 *
941 * Return value: 0 on success, non-zero with error in pop_error
942 * otherwise.
943 */
944int
945pop_delete (server, message)
946 popserver server;
947 int message;
948{
949 if (server->in_multi)
950 {
951 strcpy (pop_error, "In multi-line query in pop_delete");
952 return (-1);
953 }
954
955 sprintf (pop_error, "DELE %d", message);
956
957 if (sendline (server, pop_error) || getok (server))
958 return (-1);
959
960 return (0);
961}
962
963/*
964 * Function: pop_noop
965 *
966 * Purpose: Send a noop command to the server.
967 *
968 * Argument:
969 * server The server to send to.
970 *
971 * Return value: 0 on success, non-zero with error in pop_error
972 * otherwise.
973 *
974 * Side effects: Closes connection on error.
975 */
976int
977pop_noop (server)
978 popserver server;
979{
980 if (server->in_multi)
981 {
982 strcpy (pop_error, "In multi-line query in pop_noop");
983 return (-1);
984 }
985
986 if (sendline (server, "NOOP") || getok (server))
987 return (-1);
988
989 return (0);
990}
991
992/*
993 * Function: pop_last
994 *
995 * Purpose: Find out the highest seen message from the server.
996 *
997 * Arguments:
998 * server The server.
999 *
1000 * Return value: If successful, the highest seen message, which is
1001 * greater than or equal to 0. Otherwise, a negative number with
1002 * the error explained in pop_error.
1003 *
1004 * Side effects: Closes the connection on error.
1005 */
1006int
1007pop_last (server)
1008 popserver server;
1009{
1010 char *fromserver;
1011
1012 if (server->in_multi)
1013 {
1014 strcpy (pop_error, "In multi-line query in pop_last");
1015 return (-1);
1016 }
1017
1018 if (sendline (server, "LAST"))
1019 return (-1);
1020
60b0a379 1021 if (pop_getline (server, &fromserver) < 0)
9c0f2dac
RS
1022 return (-1);
1023
1024 if (! strncmp (fromserver, "-ERR", 4))
1025 {
1026 strncpy (pop_error, fromserver, ERROR_MAX);
1027 return (-1);
1028 }
1029 else if (strncmp (fromserver, "+OK ", 4))
1030 {
1031 strcpy (pop_error, "Unexpected response from server in pop_last");
1032 pop_trash (server);
1033 return (-1);
1034 }
1035 else
1036 {
1037 return (atoi (&fromserver[4]));
1038 }
1039}
1040
1041/*
1042 * Function: pop_reset
1043 *
1044 * Purpose: Reset the server to its initial connect state
1045 *
1046 * Arguments:
1047 * server The server.
1048 *
1049 * Return value: 0 for success, non-0 with error in pop_error
1050 * otherwise.
1051 *
1052 * Side effects: Closes the connection on error.
1053 */
1054int
1055pop_reset (server)
1056 popserver server;
1057{
1058 if (pop_retrieve_flush (server))
1059 {
1060 return (-1);
1061 }
1062
1063 if (sendline (server, "RSET") || getok (server))
1064 return (-1);
1065
1066 return (0);
1067}
1068
1069/*
1070 * Function: pop_quit
1071 *
1072 * Purpose: Quit the connection to the server,
1073 *
1074 * Arguments:
1075 * server The server to quit.
1076 *
1077 * Return value: 0 for success, non-zero otherwise with error in
1078 * pop_error.
1079 *
8e6208c5 1080 * Side Effects: The popserver passed in is unusable after this
9c0f2dac
RS
1081 * function is called, even if an error occurs.
1082 */
1083int
1084pop_quit (server)
1085 popserver server;
1086{
1087 int ret = 0;
1088
1089 if (server->file >= 0)
1090 {
1091 if (pop_retrieve_flush (server))
1092 {
1093 ret = -1;
1094 }
1095
1096 if (sendline (server, "QUIT") || getok (server))
1097 {
1098 ret = -1;
1099 }
1100
1101 close (server->file);
1102 }
1103
1104 if (server->buffer)
1105 free (server->buffer);
0ea95737
KH
1106#ifdef GSSAPI
1107 if (server->extra)
1108 {
1109 OM_uint32 minor;
1110
1111 if (Gssapi (server->extra)->gss_context != GSS_C_NO_CONTEXT)
1112 gss_delete_sec_context (&minor, &(Gssapi (server->extra)->gss_context),
1113 GSS_C_NO_BUFFER);
1114 free ((char *) server->extra);
1115 }
1116#endif /* GSSAPI */
9c0f2dac
RS
1117 free ((char *) server);
1118
1119 return (ret);
1120}
1121
c2668a61
RS
1122#ifdef WINDOWSNT
1123static int have_winsock = 0;
1124#endif
1125
9c0f2dac
RS
1126/*
1127 * Function: socket_connection
1128 *
1129 * Purpose: Opens the network connection with the mail host, without
1130 * doing any sort of I/O with it or anything.
1131 *
1132 * Arguments:
1133 * host The host to which to connect.
1134 * flags Option flags.
1135 *
1136 * Return value: A file descriptor indicating the connection, or -1
1137 * indicating failure, in which case an error has been copied
1138 * into pop_error.
1139 */
1140static int
1141socket_connection (host, flags)
1142 char *host;
1143 int flags;
1144{
1145 struct hostent *hostent;
1146 struct servent *servent;
1147 struct sockaddr_in addr;
1148 char found_port = 0;
1149 char *service;
1150 int sock;
1151#ifdef KERBEROS
a27738b6 1152#ifdef KERBEROS5
9c0f2dac 1153 krb5_error_code rem;
a27738b6
RS
1154 krb5_context kcontext = 0;
1155 krb5_auth_context auth_context = 0;
9c0f2dac
RS
1156 krb5_ccache ccdef;
1157 krb5_principal client, server;
1158 krb5_error *err_ret;
1159 register char *cp;
1160#else
1161 KTEXT ticket;
1162 MSG_DAT msg_data;
1163 CREDENTIALS cred;
1164 Key_schedule schedule;
1165 int rem;
51a66525 1166 char *realhost;
a27738b6 1167#endif /* KERBEROS5 */
9c0f2dac
RS
1168#endif /* KERBEROS */
1169
1170 int try_count = 0;
1171
c2668a61
RS
1172#ifdef WINDOWSNT
1173 {
1174 WSADATA winsockData;
1175 if (WSAStartup (0x101, &winsockData) == 0)
1176 have_winsock = 1;
1177 }
1178#endif
1179
9c0f2dac
RS
1180 do
1181 {
1182 hostent = gethostbyname (host);
1183 try_count++;
1184 if ((! hostent) && ((h_errno != TRY_AGAIN) || (try_count == 5)))
1185 {
1186 strcpy (pop_error, "Could not determine POP server's address");
1187 return (-1);
1188 }
1189 } while (! hostent);
1190
1191 bzero ((char *) &addr, sizeof (addr));
1192 addr.sin_family = AF_INET;
1193
1194#ifdef KERBEROS
1195 service = (flags & POP_NO_KERBEROS) ? POP_SERVICE : KPOP_SERVICE;
1196#else
1197 service = POP_SERVICE;
1198#endif
1199
1200#ifdef HESIOD
1201 if (! (flags & POP_NO_HESIOD))
1202 {
1203 servent = hes_getservbyname (service, "tcp");
1204 if (servent)
1205 {
1206 addr.sin_port = servent->s_port;
1207 found_port = 1;
1208 }
1209 }
1210#endif
1211 if (! found_port)
1212 {
1213 servent = getservbyname (service, "tcp");
1214 if (servent)
1215 {
1216 addr.sin_port = servent->s_port;
1217 }
1218 else
1219 {
1220#ifdef KERBEROS
1221 addr.sin_port = htons ((flags & POP_NO_KERBEROS) ?
1222 POP_PORT : KPOP_PORT);
1223#else
1224 addr.sin_port = htons (POP_PORT);
1225#endif
1226 }
1227 }
1228
a27738b6 1229#define POP_SOCKET_ERROR "Could not create socket for POP connection: "
9c0f2dac
RS
1230
1231 sock = socket (PF_INET, SOCK_STREAM, 0);
1232 if (sock < 0)
1233 {
a27738b6 1234 strcpy (pop_error, POP_SOCKET_ERROR);
9c0f2dac 1235 strncat (pop_error, strerror (errno),
a27738b6 1236 ERROR_MAX - sizeof (POP_SOCKET_ERROR));
9c0f2dac
RS
1237 return (-1);
1238
1239 }
1240
1241 while (*hostent->h_addr_list)
1242 {
1243 bcopy (*hostent->h_addr_list, (char *) &addr.sin_addr,
1244 hostent->h_length);
1245 if (! connect (sock, (struct sockaddr *) &addr, sizeof (addr)))
1246 break;
1247 hostent->h_addr_list++;
1248 }
1249
1250#define CONNECT_ERROR "Could not connect to POP server: "
1251
1252 if (! *hostent->h_addr_list)
1253 {
de5c39cf 1254 CLOSESOCKET (sock);
9c0f2dac
RS
1255 strcpy (pop_error, CONNECT_ERROR);
1256 strncat (pop_error, strerror (errno),
1257 ERROR_MAX - sizeof (CONNECT_ERROR));
1258 return (-1);
1259
1260 }
1261
1262#ifdef KERBEROS
1263#define KRB_ERROR "Kerberos error connecting to POP server: "
1264 if (! (flags & POP_NO_KERBEROS))
1265 {
a27738b6
RS
1266#ifdef KERBEROS5
1267 if ((rem = krb5_init_context (&kcontext)))
9c0f2dac
RS
1268 {
1269 krb5error:
a27738b6
RS
1270 if (auth_context)
1271 krb5_auth_con_free (kcontext, auth_context);
1272 if (kcontext)
1273 krb5_free_context (kcontext);
9c0f2dac
RS
1274 strcpy (pop_error, KRB_ERROR);
1275 strncat (pop_error, error_message (rem),
1276 ERROR_MAX - sizeof(KRB_ERROR));
de5c39cf 1277 CLOSESOCKET (sock);
9c0f2dac
RS
1278 return (-1);
1279 }
1280
a27738b6
RS
1281 if ((rem = krb5_auth_con_init (kcontext, &auth_context)))
1282 goto krb5error;
1283
1284 if (rem = krb5_cc_default (kcontext, &ccdef))
1285 goto krb5error;
1286
1287 if (rem = krb5_cc_get_principal (kcontext, ccdef, &client))
1288 goto krb5error;
9c0f2dac
RS
1289
1290 for (cp = hostent->h_name; *cp; cp++)
1291 {
1292 if (isupper (*cp))
1293 {
1294 *cp = tolower (*cp);
1295 }
1296 }
1297
a27738b6
RS
1298 if (rem = krb5_sname_to_principal (kcontext, hostent->h_name,
1299 POP_SERVICE, FALSE, &server))
1300 goto krb5error;
9c0f2dac 1301
a27738b6
RS
1302 rem = krb5_sendauth (kcontext, &auth_context,
1303 (krb5_pointer) &sock, "KPOPV1.0", client, server,
9c0f2dac
RS
1304 AP_OPTS_MUTUAL_REQUIRED,
1305 0, /* no checksum */
1306 0, /* no creds, use ccache instead */
1307 ccdef,
9c0f2dac 1308 &err_ret,
a27738b6 1309 0, /* don't need subsession key */
9c0f2dac 1310 0); /* don't need reply */
a27738b6 1311 krb5_free_principal (kcontext, server);
9c0f2dac
RS
1312 if (rem)
1313 {
1314 if (err_ret && err_ret->text.length)
1315 {
1316 strcpy (pop_error, KRB_ERROR);
1317 strncat (pop_error, error_message (rem),
1318 ERROR_MAX - sizeof (KRB_ERROR));
1319 strncat (pop_error, " [server says '",
1320 ERROR_MAX - strlen (pop_error) - 1);
1321 strncat (pop_error, err_ret->text.data,
1322 min (ERROR_MAX - strlen (pop_error) - 1,
1323 err_ret->text.length));
1324 strncat (pop_error, "']",
1325 ERROR_MAX - strlen (pop_error) - 1);
1326 }
1327 else
1328 {
1329 strcpy (pop_error, KRB_ERROR);
1330 strncat (pop_error, error_message (rem),
1331 ERROR_MAX - sizeof (KRB_ERROR));
1332 }
1333 if (err_ret)
a27738b6
RS
1334 krb5_free_error (kcontext, err_ret);
1335 krb5_auth_con_free (kcontext, auth_context);
1336 krb5_free_context (kcontext);
9c0f2dac 1337
de5c39cf 1338 CLOSESOCKET (sock);
9c0f2dac
RS
1339 return (-1);
1340 }
a27738b6 1341#else /* ! KERBEROS5 */
9c0f2dac 1342 ticket = (KTEXT) malloc (sizeof (KTEXT_ST));
51a66525
CH
1343 realhost = strdup (hostent->h_name);
1344 rem = krb_sendauth (0L, sock, ticket, "pop", realhost,
1345 (char *) krb_realmofhost (realhost),
9c0f2dac
RS
1346 (unsigned long) 0, &msg_data, &cred, schedule,
1347 (struct sockaddr_in *) 0,
1348 (struct sockaddr_in *) 0,
1349 "KPOPV0.1");
1350 free ((char *) ticket);
6a7eabe0 1351 free (realhost);
9c0f2dac
RS
1352 if (rem != KSUCCESS)
1353 {
1354 strcpy (pop_error, KRB_ERROR);
1355 strncat (pop_error, krb_err_txt[rem],
1356 ERROR_MAX - sizeof (KRB_ERROR));
de5c39cf 1357 CLOSESOCKET (sock);
9c0f2dac
RS
1358 return (-1);
1359 }
a27738b6 1360#endif /* KERBEROS5 */
9c0f2dac
RS
1361 }
1362#endif /* KERBEROS */
1363
1364 return (sock);
1365} /* socket_connection */
1366
1367/*
60b0a379 1368 * Function: pop_getline
9c0f2dac
RS
1369 *
1370 * Purpose: Get a line of text from the connection and return a
1371 * pointer to it. The carriage return and linefeed at the end of
1372 * the line are stripped, but periods at the beginnings of lines
1373 * are NOT dealt with in any special way.
1374 *
1375 * Arguments:
1376 * server The server from which to get the line of text.
1377 *
d89d0243
KH
1378 * Returns: The number of characters in the line, which is returned in
1379 * LINE, not including the final null. A return value of 0
1380 * indicates a blank line. A negative return value indicates an
1381 * error (in which case the contents of LINE are undefined. In
1382 * case of error, an error message is copied into pop_error.
9c0f2dac 1383 *
60b0a379 1384 * Notes: The line returned is overwritten with each call to pop_getline.
9c0f2dac
RS
1385 *
1386 * Side effects: Closes the connection on error.
d89d0243
KH
1387 *
1388 * THE RETURNED LINE MAY CONTAIN EMBEDDED NULLS!
9c0f2dac 1389 */
d89d0243 1390static int
60b0a379 1391pop_getline (server, line)
9c0f2dac 1392 popserver server;
d89d0243 1393 char **line;
9c0f2dac
RS
1394{
1395#define GETLINE_ERROR "Error reading from server: "
1396
1397 int ret;
b6606ad8
RS
1398 int search_offset = 0;
1399
9c0f2dac
RS
1400 if (server->data)
1401 {
d89d0243
KH
1402 char *cp = find_crlf (server->buffer + server->buffer_index,
1403 server->data);
9c0f2dac
RS
1404 if (cp)
1405 {
1406 int found;
1407 int data_used;
1408
1409 found = server->buffer_index;
1410 data_used = (cp + 2) - server->buffer - found;
1411
1412 *cp = '\0'; /* terminate the string to be returned */
1413 server->data -= data_used;
1414 server->buffer_index += data_used;
1415
1416 if (pop_debug)
d89d0243
KH
1417 /* Embedded nulls will truncate this output prematurely,
1418 but that's OK because it's just for debugging anyway. */
9c0f2dac 1419 fprintf (stderr, "<<< %s\n", server->buffer + found);
d89d0243
KH
1420 *line = server->buffer + found;
1421 return (data_used - 2);
9c0f2dac
RS
1422 }
1423 else
1424 {
1425 bcopy (server->buffer + server->buffer_index,
1426 server->buffer, server->data);
b6606ad8
RS
1427 /* Record the fact that we've searched the data already in
1428 the buffer for a CRLF, so that when we search below, we
1429 don't have to search the same data twice. There's a "-
1430 1" here to account for the fact that the last character
1431 of the data we have may be the CR of a CRLF pair, of
1432 which we haven't read the second half yet, so we may have
1433 to search it again when we read more data. */
1434 search_offset = server->data - 1;
9c0f2dac
RS
1435 server->buffer_index = 0;
1436 }
1437 }
1438 else
1439 {
1440 server->buffer_index = 0;
1441 }
1442
1443 while (1)
1444 {
0ea95737
KH
1445#ifdef GSSAPI
1446 /*
1447 * We might be playing with a protected connection. If we are, then
1448 * we need to first read a chunk of ciphertext from the server,
1449 * unwrap it, and stuff it into the buffer.
1450 */
1451 if (server->extra &&
1452 ((Gssapi (server->extra)->gss_flags) & GSSAPI_NEEDWRAP))
9c0f2dac 1453 {
0ea95737
KH
1454 char rcvbuf[GSSAPI_RCVBUF];
1455 OM_uint32 major, minor, length;
1456 gss_buffer_desc in_tok, out_tok;
1457 struct _pop_gssapi *gss_data = Gssapi (server->extra);
1458
1459 ret = fullread (server->file, (char *) &length, sizeof (length));
1460
1461 if (ret == sizeof (length))
9c0f2dac 1462 {
0ea95737
KH
1463 in_tok.length = ntohl (length);
1464
1465 if (in_tok.length <= GSSAPI_RCVBUF)
1466 {
1467 ret = fullread (server->file, rcvbuf, in_tok.length);
1468
1469 if (ret == in_tok.length)
1470 {
1471 in_tok.value = (void *) rcvbuf;
1472
1473 major = gss_unwrap (&minor, gss_data->gss_context,
1474 &in_tok, &out_tok, 0, 0);
1475
1476 if (major != GSS_S_COMPLETE)
1477 {
1478 pop_trash (server);
1479 gen_gss_error ("unwrapping", major, minor);
1480 return (-1);
1481 }
1482
1483 while (server->data + out_tok.length >=
1484 server->buffer_size - 1)
1485 server->buffer_size += GETLINE_INCR;
1486
1487 server->buffer = (char *)realloc (server->buffer,
1488 server->buffer_size);
1489
1490 if (! server->buffer)
1491 {
1492 gss_release_buffer (&minor, &out_tok);
1493 pop_trash (server);
1494 strcpy (pop_error, "Out of memory in pop_getline");
1495 return (-1);
1496 }
1497
1498 bcopy (out_tok.value, server->buffer + server->data,
1499 out_tok.length);
1500
1501 ret = out_tok.length;
1502
1503 gss_release_buffer (&minor, &out_tok);
1504 }
1505 else
1506 ret = 0; /* force detection of unexpected EOF */
1507 }
1508 else
1509 {
1510 pop_trash (server);
1511 strcpy (pop_error, "Token from server too long in pop_getline");
1512 return (-1);
1513 }
9c0f2dac 1514 }
0ea95737
KH
1515 else
1516 ret = 0; /* force detection of unexpected EOF */
9c0f2dac 1517 }
0ea95737
KH
1518 else
1519 {
1520#endif /* GSSAPI */
1521 /* There's a "- 1" here to leave room for the null that we put
1522 at the end of the read data below. We put the null there so
1523 that find_crlf knows where to stop when we call it. */
1524 if (server->data == server->buffer_size - 1)
1525 {
1526 server->buffer_size += GETLINE_INCR;
1527 server->buffer = (char *)realloc (server->buffer,
1528 server->buffer_size);
1529 if (! server->buffer)
1530 {
1531 strcpy (pop_error, "Out of memory in pop_getline");
1532 pop_trash (server);
1533 return (-1);
1534 }
1535 }
1536 ret = RECV (server->file, server->buffer + server->data,
1537 server->buffer_size - server->data - 1, 0);
1538#ifdef GSSAPI
1539 }
1540#endif /* GSSAPI */
9c0f2dac
RS
1541 if (ret < 0)
1542 {
1543 strcpy (pop_error, GETLINE_ERROR);
1544 strncat (pop_error, strerror (errno),
1545 ERROR_MAX - sizeof (GETLINE_ERROR));
1546 pop_trash (server);
d89d0243 1547 return (-1);
9c0f2dac
RS
1548 }
1549 else if (ret == 0)
1550 {
60b0a379 1551 strcpy (pop_error, "Unexpected EOF from server in pop_getline");
9c0f2dac 1552 pop_trash (server);
d89d0243 1553 return (-1);
9c0f2dac
RS
1554 }
1555 else
1556 {
7a481e50 1557 char *cp;
9c0f2dac 1558 server->data += ret;
7a481e50 1559 server->buffer[server->data] = '\0';
9c0f2dac 1560
d89d0243
KH
1561 cp = find_crlf (server->buffer + search_offset,
1562 server->data - search_offset);
9c0f2dac
RS
1563 if (cp)
1564 {
1565 int data_used = (cp + 2) - server->buffer;
1566 *cp = '\0';
1567 server->data -= data_used;
1568 server->buffer_index = data_used;
1569
1570 if (pop_debug)
1571 fprintf (stderr, "<<< %s\n", server->buffer);
d89d0243
KH
1572 *line = server->buffer;
1573 return (data_used - 2);
9c0f2dac 1574 }
a8f44d07
RS
1575 /* As above, the "- 1" here is to account for the fact that
1576 we may have read a CR without its accompanying LF. */
1577 search_offset += ret - 1;
9c0f2dac
RS
1578 }
1579 }
1580
1581 /* NOTREACHED */
1582}
1583
0ea95737
KH
1584#ifdef GSSAPI
1585/*
1586 * Function: fullread
1587 *
1588 * Purpose: Just like read, but keeps trying until the specified number
1589 * number of bytes has been read into the buffer. This function is
1590 * only needed if you are using the GSSAPI protection mechanisms.
1591 *
1592 * Return value: Same as read. Pop_error is not set.
1593 */
1594static int
1595fullread (fd, buf, nbytes)
1596 int fd;
1597 char *buf;
1598 int nbytes;
1599{
1600 char *cp;
1601 int ret;
1602
1603 cp = buf;
1604
1605 while (nbytes > 0 && (ret = RECV (fd, cp, nbytes, 0)) > 0)
1606 {
1607 cp += ret;
1608 nbytes -= ret;
1609 }
1610
1611 return (ret);
1612}
1613#endif /* GSSAPI */
1614
9c0f2dac
RS
1615/*
1616 * Function: sendline
1617 *
1618 * Purpose: Sends a line of text to the POP server. The line of text
1619 * passed into this function should NOT have the carriage return
1620 * and linefeed on the end of it. Periods at beginnings of lines
1621 * will NOT be treated specially by this function.
1622 *
1623 * Arguments:
1624 * server The server to which to send the text.
1625 * line The line of text to send.
1626 *
1627 * Return value: Upon successful completion, a value of 0 will be
1628 * returned. Otherwise, a non-zero value will be returned, and
1629 * an error will be copied into pop_error.
1630 *
1631 * Side effects: Closes the connection on error.
1632 */
1633static int
1634sendline (server, line)
1635 popserver server;
1636 char *line;
1637{
1638#define SENDLINE_ERROR "Error writing to POP server: "
1639 int ret;
1640
0ea95737
KH
1641#ifdef GSSAPI
1642 /*
1643 * We might be playing with a protected connection. If we are, then we
1644 * need to build our full plaintext, parse it into chunks small enough
1645 * for the server to swallow, wrap each one, and send it over the net as
1646 * specified by the RFC.
1647 */
1648 if (server->extra && ((Gssapi (server->extra)->gss_flags) & GSSAPI_NEEDWRAP))
1649 {
1650 char *sendbuf, *ptr;
1651 OM_uint32 major, minor, length;
1652 gss_buffer_desc in_tok, out_tok;
1653 int len = 0, tot_len;
1654 struct _pop_gssapi *gss_data = Gssapi (server->extra);
1655
1656 sendbuf = malloc (strlen (line) + 3);
1657
1658 if (! sendbuf)
1659 {
1660 pop_trash (server);
1661 strcpy (pop_error, "Out of memory in sendline");
1662 return (-1);
1663 }
1664
1665 tot_len = sprintf (sendbuf, "%s\r\n", line);
1666
1667 for (ptr = sendbuf; tot_len > 0; tot_len -= len, ptr += len)
1668 {
1669 len = ((tot_len > gss_data->max_size) ?
1670 gss_data->max_size : tot_len);
1671
1672 in_tok.value = (void *) ptr;
1673 in_tok.length = len;
1674
1675 major = gss_wrap (&minor, gss_data->gss_context,
1676 (gss_data->gss_flags & GSSAPI_PRIVACY) ? 1 : 0,
1677 GSS_C_QOP_DEFAULT, &in_tok, 0, &out_tok);
1678
1679 if (major != GSS_S_COMPLETE)
1680 {
1681 free (sendbuf);
1682 pop_trash (server);
1683 gen_gss_error ("wrapping", major, minor);
1684 return (-1);
1685 }
1686
1687 /*
1688 * "Once the protection mechanism is in effect, the stream of
1689 * command and response octets is processed into buffers of
1690 * ciphertext. Each buffer is transferred over the connection
1691 * as a stream of octets prepended with a four octet field in
1692 * network byte order that represents the length of the
1693 * following data." - RFC 1734, section 2
1694 */
1695 length = htonl (out_tok.length);
1696 ret = fullwrite (server->file, (char *) &length, sizeof (length));
1697 if (ret == sizeof (length))
1698 {
1699 ret = fullwrite (server->file, (char *) out_tok.value,
1700 out_tok.length);
1701 }
1702
1703 gss_release_buffer (&minor, &out_tok);
1704
1705 if (ret < 0)
1706 break;
1707 }
1708
1709 free (sendbuf);
9c0f2dac 1710 }
0ea95737
KH
1711 else
1712 {
1713#endif /* GSSAPI */
1714 ret = fullwrite (server->file, line, strlen (line));
1715 if (ret >= 0)
1716 { /* 0 indicates that a blank line was written */
1717 ret = fullwrite (server->file, "\r\n", 2);
1718 }
1719#ifdef GSSAPI
1720 }
1721#endif /* GSSAPI */
9c0f2dac
RS
1722
1723 if (ret < 0)
1724 {
1725 pop_trash (server);
1726 strcpy (pop_error, SENDLINE_ERROR);
1727 strncat (pop_error, strerror (errno),
1728 ERROR_MAX - sizeof (SENDLINE_ERROR));
1729 return (ret);
1730 }
1731
1732 if (pop_debug)
1733 fprintf (stderr, ">>> %s\n", line);
1734
1735 return (0);
1736}
1737
1738/*
1739 * Procedure: fullwrite
1740 *
1741 * Purpose: Just like write, but keeps trying until the entire string
1742 * has been written.
1743 *
1744 * Return value: Same as write. Pop_error is not set.
1745 */
1746static int
1747fullwrite (fd, buf, nbytes)
1748 int fd;
1749 char *buf;
1750 int nbytes;
1751{
1752 char *cp;
9d3f4cee 1753 int ret = 0;
9c0f2dac
RS
1754
1755 cp = buf;
9d3f4cee 1756 while (nbytes && ((ret = SEND (fd, cp, nbytes, 0)) > 0))
9c0f2dac
RS
1757 {
1758 cp += ret;
1759 nbytes -= ret;
1760 }
1761
1762 return (ret);
1763}
1764
1765/*
1766 * Procedure getok
1767 *
1768 * Purpose: Reads a line from the server. If the return indicator is
1769 * positive, return with a zero exit status. If not, return with
1770 * a negative exit status.
1771 *
1772 * Arguments:
1773 * server The server to read from.
1774 *
1775 * Returns: 0 for success, else for failure and puts error in pop_error.
1776 *
8e6208c5 1777 * Side effects: On failure, may make the connection unusable.
9c0f2dac
RS
1778 */
1779static int
1780getok (server)
1781 popserver server;
1782{
1783 char *fromline;
1784
60b0a379 1785 if (pop_getline (server, &fromline) < 0)
9c0f2dac
RS
1786 {
1787 return (-1);
1788 }
1789
1790 if (! strncmp (fromline, "+OK", 3))
1791 return (0);
1792 else if (! strncmp (fromline, "-ERR", 4))
1793 {
1794 strncpy (pop_error, fromline, ERROR_MAX);
1795 pop_error[ERROR_MAX-1] = '\0';
1796 return (-1);
1797 }
1798 else
1799 {
1800 strcpy (pop_error,
1801 "Unexpected response from server; expecting +OK or -ERR");
1802 pop_trash (server);
1803 return (-1);
1804 }
1805}
1806
1807#if 0
1808/*
1809 * Function: gettermination
1810 *
1811 * Purpose: Gets the next line and verifies that it is a termination
1812 * line (nothing but a dot).
1813 *
1814 * Return value: 0 on success, non-zero with pop_error set on error.
1815 *
1816 * Side effects: Closes the connection on error.
1817 */
1818static int
1819gettermination (server)
1820 popserver server;
1821{
1822 char *fromserver;
1823
60b0a379 1824 if (pop_getline (server, &fromserver) < 0)
9c0f2dac
RS
1825 return (-1);
1826
1827 if (strcmp (fromserver, "."))
1828 {
1829 strcpy (pop_error,
1830 "Unexpected response from server in gettermination");
1831 pop_trash (server);
1832 return (-1);
1833 }
1834
1835 return (0);
1836}
1837#endif
1838
1839/*
1840 * Function pop_close
1841 *
1842 * Purpose: Close a pop connection, sending a "RSET" command to try to
1843 * preserve any changes that were made and a "QUIT" command to
1844 * try to get the server to quit, but ignoring any responses that
1845 * are received.
1846 *
8e6208c5 1847 * Side effects: The server is unusable after this function returns.
9c0f2dac
RS
1848 * Changes made to the maildrop since the session was started (or
1849 * since the last pop_reset) may be lost.
1850 */
1851void
1852pop_close (server)
1853 popserver server;
1854{
1855 pop_trash (server);
1856 free ((char *) server);
1857
1858 return;
1859}
1860
1861/*
1862 * Function: pop_trash
1863 *
1864 * Purpose: Like pop_close or pop_quit, but doesn't deallocate the
1865 * memory associated with the server. It is legal to call
1866 * pop_close or pop_quit after this function has been called.
1867 */
1868static void
1869pop_trash (server)
1870 popserver server;
1871{
1872 if (server->file >= 0)
1873 {
c2668a61
RS
1874 /* avoid recursion; sendline can call pop_trash */
1875 if (server->trash_started)
1876 return;
1877 server->trash_started = 1;
de5c39cf 1878
9c0f2dac
RS
1879 sendline (server, "RSET");
1880 sendline (server, "QUIT");
1881
de5c39cf 1882 CLOSESOCKET (server->file);
9c0f2dac
RS
1883 server->file = -1;
1884 if (server->buffer)
1885 {
1886 free (server->buffer);
1887 server->buffer = 0;
1888 }
0ea95737
KH
1889#ifdef GSSAPI
1890 if (server->extra)
1891 {
1892 OM_uint32 minor;
1893
1894 if (Gssapi (server->extra)->gss_context != GSS_C_NO_CONTEXT)
1895 gss_delete_sec_context (&minor,
1896 &(Gssapi (server->extra)->gss_context),
1897 GSS_C_NO_BUFFER);
1898 free ((char *) server->extra);
1899 server->extra = 0;
1900 }
1901#endif /* GSSAPI */
9c0f2dac 1902 }
c2668a61
RS
1903
1904#ifdef WINDOWSNT
1905 if (have_winsock)
1906 WSACleanup ();
1907#endif
9c0f2dac
RS
1908}
1909
0ea95737
KH
1910#ifdef GSSAPI
1911/*
1912 * Function: pop_auth
1913 *
1914 * Purpose: To perform a GSSAPI authentication handshake with a POP server.
1915 * If the negotiation is successful, it will return 0; otherwise, it
1916 * will fill in pop_error with the error message and return either -1,
1917 * indicating a potentially recoverable error, or -2, indicating an
1918 * unrecoverable error.
1919 *
1920 * Side effects: The server may choose to close the connection if the
1921 * handshake fails. The connection will be trashed if the error is
1922 * unrecoverable.
1923 */
1924static int
1925pop_auth (server, username, host, flags)
1926 popserver server;
1927 char *username, *host;
1928 int flags;
1929{
1930 int gss_flags, ret;
1931 char *fromserver;
1932 OM_uint32 max_size, t_flags;
1933 gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT;
1934 gss_buffer_desc in_tok, out_tok;
1935 gss_name_t svc_name;
1936 OM_uint32 major, minor, t_minor;
1937
1938 /* calculate usable protection mechanisms */
1939 gss_flags = (GSSAPI_PROTECTION &
1940 ~(((flags & POP_NO_NOPROT) ? GSSAPI_NOPROT : 0) |
1941 ((flags & POP_NO_INTEG) ? GSSAPI_INTEGRITY : 0) |
1942 ((flags & POP_NO_ENCRYPT) ? GSSAPI_PRIVACY : 0)));
1943
1944 if (gss_flags == 0)
1945 {
1946 strcpy (pop_error, "Unable to provide selected protection level");
1947 return (-1);
1948 }
1949
1950 /* import service name of pop server */
1951 in_tok.value = (void *) malloc (strlen (host) + sizeof (GSSAPI_SERVICE) + 2);
1952
1953 if (! in_tok.value)
1954 {
1955 strcpy (pop_error, "Out of memory in pop_auth");
1956 return (-1);
1957 }
1958
1959 sprintf ((char *) in_tok.value, "%s@%s", GSSAPI_SERVICE, host);
1960 in_tok.length = strlen ((char *) in_tok.value);
1961
1962 {
1963 gss_OID_desc svc_name_oid = GSSAPI_SVC_TYPE;
1964
1965 major = gss_import_name (&minor, &in_tok, &svc_name_oid, &svc_name);
1966 }
1967
1968 free ((char *) in_tok.value);
1969
1970 if (major != GSS_S_COMPLETE)
1971 {
1972 gen_gss_error ("parsing name", major, minor);
1973 return (-1);
1974 }
1975
1976 /* begin GSSAPI authentication handshake */
1977 if (sendline (server, "AUTH GSSAPI") || (pop_getline (server, &fromserver) < 0))
1978 {
1979 gss_release_name (&t_minor, &svc_name);
1980 return (-1);
1981 }
1982
1983 do
1984 {
1985 /* sanity-check server response */
1986 if (strncmp (fromserver, "+ ", 2))
1987 {
1988 gss_release_name (&t_minor, &svc_name);
1989 if (gss_context != GSS_C_NO_CONTEXT)
1990 gss_delete_sec_context (&t_minor, &gss_context, GSS_C_NO_BUFFER);
1991 if (0 == strncmp (fromserver, "-ERR", 4))
1992 {
1993 strncpy (pop_error, fromserver, ERROR_MAX);
1994 return (-1);
1995 }
1996 else
1997 {
1998 pop_trash (server);
1999 strcpy (pop_error,
2000 "Unexpected response from POP server in pop_auth");
2001 return (-2);
2002 }
2003 }
2004
2005 if (strlen (fromserver) > 2)
2006 {
2007 /* base 64 decode the response... */
2008 ret = b64_decode (fromserver + 2, &in_tok);
2009 if (ret != B64_SUCCESS)
2010 {
2011 gss_release_name (&t_minor, &svc_name);
2012 if (gss_context != GSS_C_NO_CONTEXT)
2013 gss_delete_sec_context (&t_minor, &gss_context,
2014 GSS_C_NO_BUFFER);
2015 sendline (server, "*");
2016 strcpy (pop_error, b64_error[ret]);
2017 return (-1);
2018 }
2019 }
2020 else
2021 {
2022 in_tok.length = 0;
2023 in_tok.value = 0;
2024 }
2025
2026 /* call init_sec_context */
2027 major = gss_init_sec_context (&minor, GSS_C_NO_CREDENTIAL, &gss_context,
2028 svc_name, GSS_C_NULL_OID,
2029 GSS_C_MUTUAL_FLAG, 0,
2030 GSS_C_NO_CHANNEL_BINDINGS,
2031 in_tok.length ? & in_tok : GSS_C_NO_BUFFER,
2032 0, &out_tok, 0, 0);
2033
2034 if (in_tok.length != 0)
2035 free ((char *) in_tok.value);
2036
2037 /* check for error */
2038 if (GSS_ERROR (major))
2039 {
2040 gss_release_name (&t_minor, &svc_name);
2041 if (gss_context != GSS_C_NO_CONTEXT)
2042 gss_delete_sec_context (&t_minor, &gss_context, GSS_C_NO_BUFFER);
2043 sendline (server, "*");
2044 gen_gss_error ("in init_sec_context", major, minor);
2045 return (-1);
2046 }
2047
2048 if (out_tok.length != 0)
2049 {
2050 /* base 64 encode output token, if any */
2051 ret = b64_encode (&out_tok, &fromserver);
2052
2053 gss_release_buffer (&t_minor, &out_tok);
2054
2055 if (ret != B64_SUCCESS)
2056 {
2057 gss_release_name (&t_minor, &svc_name);
2058 if (gss_context != GSS_C_NO_CONTEXT)
2059 gss_delete_sec_context (&t_minor, &gss_context,
2060 GSS_C_NO_BUFFER);
2061 sendline (server, "*");
2062 strcpy (pop_error, b64_error[ret]);
2063 return (-1);
2064 }
2065
2066 /* send output token... */
2067 ret = sendline (server, fromserver);
2068
2069 free (fromserver);
2070 }
2071 else
2072 /* empty output token... */
2073 ret = sendline (server, "");
2074
2075 /* get next token from server */
2076 if (ret || (pop_getline (server, &fromserver) < 0))
2077 {
2078 gss_release_name (&t_minor, &svc_name);
2079 if (gss_context != GSS_C_NO_CONTEXT)
2080 gss_delete_sec_context (&t_minor, &gss_context, GSS_C_NO_BUFFER);
2081 return (-1);
2082 }
2083 } while ((major & GSS_S_CONTINUE_NEEDED));
2084
2085 /* release name... */
2086 gss_release_name (&t_minor, &svc_name);
2087
2088 /* get final response from server */
2089 if (strncmp (fromserver, "+ ", 2))
2090 {
2091 gss_delete_sec_context (&t_minor, &gss_context, GSS_C_NO_BUFFER);
2092 if (0 == strncmp (fromserver, "-ERR", 4))
2093 {
2094 strncpy (pop_error, fromserver, ERROR_MAX);
2095 return (-1);
2096 }
2097 else
2098 {
2099 pop_trash (server);
2100 strcpy (pop_error,
2101 "Unexpected response from POP server in pop_auth");
2102 return (-2);
2103 }
2104 }
2105
2106 /* base 64 decode... */
2107 ret = b64_decode (fromserver + 2, &in_tok);
2108 if (ret != B64_SUCCESS)
2109 {
2110 gss_delete_sec_context (&t_minor, &gss_context, GSS_C_NO_BUFFER);
2111 sendline (server, "*");
2112 strcpy (pop_error, b64_error[ret]);
2113 return (-1);
2114 }
2115
2116 /* unwrap... */
2117 major = gss_unwrap (&minor, gss_context, &in_tok, &out_tok, 0, 0);
2118
2119 free ((char *) in_tok.value);
2120
2121 if (major != GSS_S_COMPLETE || out_tok.length != sizeof (t_flags))
2122 {
2123 if (out_tok.length != 0)
2124 gss_release_buffer (&t_minor, &out_tok);
2125 gss_delete_sec_context (&t_minor, &gss_context, GSS_C_NO_BUFFER);
2126 sendline (server, "*");
2127 gen_gss_error ("in gss_unwrap", major, minor);
2128 return (-1);
2129 }
2130
2131 /* get and check flags/size */
2132 bcopy ((void *) out_tok.value, (void *) &t_flags, sizeof (t_flags));
2133
2134 gss_release_buffer (&t_minor, &out_tok);
2135
2136 max_size = ntohl (t_flags);
2137
2138 t_flags = ((max_size & 0xFF000000) >> 24) & gss_flags;
2139 max_size &= 0x00FFFFFF;
2140
2141 if ((t_flags & GSSAPI_PRIVACY))
2142 gss_flags = GSSAPI_PRIVACY;
2143
2144 else if ((t_flags & GSSAPI_INTEGRITY))
2145 gss_flags = GSSAPI_INTEGRITY;
2146
2147 else if ((t_flags & GSSAPI_NOPROT))
2148 gss_flags = GSSAPI_NOPROT;
2149
2150 else
2151 {
2152 gss_delete_sec_context (&t_minor, &gss_context, GSS_C_NO_BUFFER);
2153 sendline (server, "*");
2154 strcpy (pop_error, "Server does not provide selected protection level");
2155 return (-1);
2156 }
2157
2158 if (max_size == 0)
2159 {
2160 gss_delete_sec_context (&t_minor, &gss_context, GSS_C_NO_BUFFER);
2161 sendline (server, "*");
2162 strcpy (pop_error, "Bad server max length");
2163 return (-1);
2164 }
2165
2166 if ((gss_flags & GSSAPI_NEEDWRAP))
2167 {
2168 major = gss_wrap_size_limit (&t_minor, gss_context,
2169 (gss_flags & GSSAPI_PRIVACY) ? 1 : 0,
2170 GSS_C_QOP_DEFAULT,
2171 (max_size < GSSAPI_RCVBUF) ? max_size :
2172 GSSAPI_RCVBUF, &max_size);
2173 if (major != GSS_S_COMPLETE)
2174 {
2175 gss_delete_sec_context (&t_minor, &gss_context, GSS_C_NO_BUFFER);
2176 sendline (server, "*");
2177 gen_gss_error ("getting max size", major, minor);
2178 return (-1);
2179 }
2180 }
2181
2182 /* generate return flags */
2183 {
2184 OM_uint32 tmp;
2185
2186 tmp = (((gss_flags << 24) & 0xFF000000) | (GSSAPI_RCVBUF & 0x00FFFFFF));
2187 t_flags = ntohl (tmp);
2188 }
2189
2190 in_tok.length = sizeof (t_flags) + strlen (username);
2191 in_tok.value = (void *) malloc (in_tok.length);
2192
2193 if (! in_tok.value)
2194 {
2195 gss_delete_sec_context (&t_minor, &gss_context, GSS_C_NO_BUFFER);
2196 sendline (server, "*");
2197 strcpy (pop_error, "Out of memory in pop_auth");
2198 return (-1);
2199 }
2200
2201 bcopy ((void *) &t_flags, in_tok.value, sizeof (t_flags));
2202 bcopy ((void *) username,
2203 (void *) (((char *) in_tok.value) + sizeof (t_flags)),
2204 in_tok.length - sizeof (t_flags));
2205
2206 /* wrap result */
2207 major = gss_wrap (&minor, gss_context, 0, GSS_C_QOP_DEFAULT,
2208 &in_tok, 0, &out_tok);
2209
2210 free ((char *) in_tok.value);
2211
2212 if (major != GSS_S_COMPLETE || out_tok.length == 0)
2213 {
2214 if (out_tok.length != 0)
2215 gss_release_buffer (&t_minor, &out_tok);
2216 gss_delete_sec_context (&t_minor, &gss_context, GSS_C_NO_BUFFER);
2217 sendline (server, "*");
2218 gen_gss_error ("in gss_wrap", major, minor);
2219 return (-1);
2220 }
2221
2222 /* base 64 encode... */
2223 ret = b64_encode (&out_tok, &fromserver);
2224
2225 gss_release_buffer (&t_minor, &out_tok);
2226
2227 if (ret != B64_SUCCESS)
2228 {
2229 gss_delete_sec_context (&t_minor, &gss_context, GSS_C_NO_BUFFER);
2230 sendline (server, "*");
2231 strcpy (pop_error, b64_error[ret]);
2232 return (-1);
2233 }
2234
2235 /* send to server */
2236 ret = sendline (server, fromserver);
2237
2238 free (fromserver);
2239
2240 /* see if the server likes me... */
2241 if (ret || getok (server))
2242 {
2243 gss_delete_sec_context (&t_minor, &gss_context, GSS_C_NO_BUFFER);
2244 return (-1);
2245 }
2246
2247 /* stash context */
2248 {
2249 struct _pop_gssapi *gss_data;
2250
2251 gss_data = (struct _pop_gssapi *) malloc (sizeof (struct _pop_gssapi));
2252
2253 if (! gss_data)
2254 {
2255 pop_trash (server);
2256 gss_delete_sec_context (&t_minor, &gss_context, GSS_C_NO_BUFFER);
2257 strcpy (pop_error, "Out of memory in pop_auth");
2258 return (-2);
2259 }
2260
2261 gss_data->gss_flags = gss_flags;
2262 gss_data->max_size = max_size;
2263 gss_data->gss_context = gss_context;
2264
2265 server->extra = gss_data;
2266 }
2267
2268 return (0);
2269}
2270
2271/*
2272 * Add as much error text to pop_error as will fit, but only put complete
2273 * messages
2274 */
2275static void
2276gen_gss_error (msg, major, minor)
2277 char *msg;
2278 OM_uint32 major, minor;
2279{
2280 char *p = pop_error, *t, *saved;
2281 int max = ERROR_MAX - 1; /* for \0 */
2282 OM_uint32 t_minor, msg_ctx = 0;
2283 gss_buffer_desc gss_msg;
2284
2285 while (*msg && max)
2286 {
2287 *p++ = *msg++;
2288 max--;
2289 }
2290
2291 if (max >= 2)
2292 {
2293 saved = p;
2294 *p++ = ':';
2295 *p++ = ' ';
2296 max -= 2;
2297 }
2298 else
2299 {
2300 *p = '\0';
2301 return;
2302 }
2303
2304 do
2305 {
2306 gss_display_status (&t_minor, major, GSS_C_GSS_CODE, GSS_C_NO_OID,
2307 &msg_ctx, &gss_msg);
2308 for (t = (char *) gss_msg.value; *t && max; max--)
2309 {
2310 *p++ = *t++;
2311 }
2312 gss_release_buffer (&t_minor, &gss_msg);
2313 if (max == 0)
2314 {
2315 *saved = '\0';
2316 return;
2317 }
2318 } while (msg_ctx);
2319
2320 saved = p;
2321
2322 do
2323 {
2324 gss_display_status (&t_minor, minor, GSS_C_MECH_CODE, GSS_C_NO_OID,
2325 &msg_ctx, &gss_msg);
2326 for (t = (char *) gss_msg.value; *t && max; max--)
2327 {
2328 *p++ = *t++;
2329 }
2330 gss_release_buffer (&t_minor, &gss_msg);
2331 if (max == 0)
2332 {
2333 *saved = '\0';
2334 return;
2335 }
2336 } while (msg_ctx);
2337
2338 *p = '\0';
2339 return;
2340}
2341
2342/*
2343 * table-based base64 decoding function; takes 4 characters from in and
2344 * writes from 1 to 3 bytes to out, storing the amount written in len
2345 */
2346static int
2347b64_d (in, out, len)
2348 char *in, *out;
2349 int *len;
2350{
2351 int decodearray[] =
2352 {
2353 0x3e, -1, -1, -1, 0x3f, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a,
2354 0x3b, 0x3c, 0x3d, -1, -1, -1, -1, -1, -1, -1, 0x00, 0x01,
2355 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
2356 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
2357 -1, -1, -1, -1, -1, -1, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
2358 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
2359 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33
2360 };
2361
2362 int d;
2363
2364 if (!in || !out || !len)
2365 return (B64_BADPARAM);
2366
2367 if (*in < '+' || *in > 'z')
2368 return (B64_BADCHAR);
2369
2370 d = decodearray[*(in++) - '+'];
2371 if (d == -1)
2372 return (B64_BADCHAR);
2373 *out = d << 2;
2374
2375 if (*in < '+' || *in > 'z')
2376 return (B64_BADCHAR);
2377
2378 d = decodearray[*(in++) - '+'];
2379 if (d == -1)
2380 return (B64_BADCHAR);
2381 *(out++) |= d >> 4;
2382 *out = (d & 15) << 4;
2383
2384 if (*in < '+' || *in > 'z')
2385 return (B64_BADCHAR);
2386 else if (*in == '=')
2387 if (*(in + 1) != '=')
2388 return (B64_BADPAD);
2389 else
2390 {
2391 *len = 1;
2392 return (B64_SUCCESS);
2393 }
2394
2395 d = decodearray[*(in++) - '+'];
2396 if (d == -1)
2397 return (B64_BADCHAR);
2398 *(out++) |= d >> 2;
2399 *out = (d & 3) << 6;
2400
2401 if (*in < '+' || *in > 'z')
2402 return (B64_BADCHAR);
2403 else if (*in == '=')
2404 {
2405 *len = 2;
2406 return (B64_SUCCESS);
2407 }
2408
2409 d = decodearray[*in - '+'];
2410 if (d == -1)
2411 return (B64_BADCHAR);
2412 *out |= d;
2413
2414 *len = 3;
2415 return (B64_SUCCESS);
2416}
2417
2418/*
2419 * simple base64 encoding function that takes from 0 to 3 bytes and
2420 * outputs 4 encoded characters, with appropriate padding
2421 */
2422static int
2423b64_e (in, out, len)
2424 unsigned char *in, *out;
2425 int len;
2426{
2427 unsigned char codearray[] =
2428 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2429
2430 if (!in || !out || len <= 0 || len > 3)
2431 return (B64_BADPARAM);
2432
2433 *(out++) = codearray[((*in) >> 2)];
2434
2435 if (--len == 0)
2436 {
2437 *(out++) = codearray[(((*in) & 3) << 4)];
2438 *(out++) = '=';
2439 *out = '=';
2440 return (B64_SUCCESS);
2441 }
2442
2443 *(out++) = codearray[(((*in) & 3) << 4) | ((*(in + 1)) >> 4)];
2444 in++;
2445
2446 if (--len == 0)
2447 {
2448 *(out++) = codearray[(((*in) & 15) << 2)];
2449 *out = '=';
2450 return (B64_SUCCESS);
2451 }
2452
2453 *(out++) = codearray[(((*in) & 15) << 2) | ((*(in + 1)) >> 6)];
2454 *out = codearray[((*(in + 1)) & 63)];
2455
2456 return (B64_SUCCESS);
2457}
2458
2459/*
2460 * given an input string, generate an output gss_buffer_t containing the
2461 * decoded data and correct length; works by repeatedly driving b64_d ()
2462 * over the input string
2463 */
2464static int
2465b64_decode (enc, dec)
2466 char *enc;
2467 gss_buffer_t dec;
2468{
2469 char *tmp;
2470 int inlen, outlen = 0, t_len, ret;
2471
2472 if (!enc || !dec)
2473 return (B64_BADPARAM);
2474
2475 dec->value = 0;
2476 dec->length = 0;
2477
2478 inlen = strlen (enc);
2479 if ((inlen % 4))
2480 return (B64_BADLEN);
2481
2482 dec->value = (void *) (tmp = (char *) malloc ((inlen / 4) * 3));
2483
2484 if (! tmp)
2485 return (B64_NOMEM);
2486
2487 for (; inlen; inlen -= 4)
2488 {
2489 ret = b64_d (enc, tmp, &t_len);
2490 if (ret != B64_SUCCESS)
2491 {
2492 free ((char *) dec->value);
2493 dec->value = 0;
2494 return (ret);
2495 }
2496 else if (t_len != 3)
2497 {
2498 dec->length = outlen + t_len;
2499 return (B64_SUCCESS);
2500 }
2501 else
2502 {
2503 enc += 4;
2504 tmp += t_len;
2505 outlen += t_len;
2506 }
2507 }
2508
2509 dec->length = outlen;
2510 return (B64_SUCCESS);
2511}
2512
2513/*
2514 * given a gss_buffer_t, generate an encoded string containing the data.
2515 * works by repeatedly driving b64_e () over the contents of the buffer_t
2516 */
2517static int
2518b64_encode (dec, enc)
2519 gss_buffer_t dec;
2520 char **enc;
2521{
2522 unsigned char *tmp, *in;
2523 int ret, len;
2524
2525 if (!dec || !enc)
2526 return (B64_BADPARAM);
2527
2528 in = (unsigned char *) dec->value;
2529 len = dec->length;
2530 *enc = (char *) (tmp = (unsigned char *) malloc (((len * 4) / 3) + 5));
2531
2532 if (! tmp)
2533 return (B64_NOMEM);
2534
2535 do
2536 {
2537 ret = b64_e (in, tmp, len >= 3 ? 3 : len);
2538 if (ret != B64_SUCCESS)
2539 {
2540 free (*enc);
2541 *enc = 0;
2542 return (ret);
2543 }
2544 else
2545 {
2546 in += 3;
2547 tmp += 4;
2548 }
2549 } while ((len -= 3) > 0);
2550
2551 *tmp = '\0';
2552
2553 return (B64_SUCCESS);
2554}
2555
2556#endif /* GSSAPI */
2557
d89d0243
KH
2558/* Return a pointer to the first CRLF in IN_STRING, which can contain
2559 embedded nulls and has LEN characters in it not including the final
2560 null, or 0 if it does not contain one. */
9c0f2dac
RS
2561
2562static char *
d89d0243 2563find_crlf (in_string, len)
8148baf9 2564 char *in_string;
d89d0243 2565 int len;
9c0f2dac 2566{
d89d0243 2567 while (len--)
9c0f2dac 2568 {
d89d0243 2569 if (*in_string == '\r')
8148baf9
RS
2570 {
2571 if (*++in_string == '\n')
2572 return (in_string - 1);
2573 }
2574 else
2575 in_string++;
9c0f2dac 2576 }
d89d0243 2577 return (0);
9c0f2dac
RS
2578}
2579
2580#endif /* MAIL_USE_POP */