pop.c 39.9 KB
Newer Older
Richard M. Stallman's avatar
Richard M. Stallman committed
1
/* pop.c: client routines for talking to a POP3-protocol post-office server
2

3
Copyright (C) 1991, 1993, 1996-1997, 1999, 2001-2020 Free Software
4
Foundation, Inc.
5 6

Author: Jonathan Kamens <jik@security.ov.com>
Richard M. Stallman's avatar
Richard M. Stallman committed
7 8 9

This file is part of GNU Emacs.

10
GNU Emacs is free software: you can redistribute it and/or modify
Richard M. Stallman's avatar
Richard M. Stallman committed
11
it under the terms of the GNU General Public License as published by
12 13
the Free Software Foundation, either version 3 of the License, or (at
your option) any later version.
Richard M. Stallman's avatar
Richard M. Stallman committed
14 15 16 17 18 19 20

GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
21
along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
22

Richard M. Stallman's avatar
Richard M. Stallman committed
23

Pavel Janík's avatar
Pavel Janík committed
24
#include <config.h>
25

26 27
#ifdef MAIL_USE_POP

Richard M. Stallman's avatar
Richard M. Stallman committed
28
#include <sys/types.h>
29 30
#ifdef WINDOWSNT
#include "ntlib.h"
31 32
#undef _WIN32_WINNT
#define _WIN32_WINNT 0x0501	/* for getaddrinfo stuff */
33
#if defined __MINGW32_VERSION && __MINGW32_VERSION >= 5000002L
34 35 36 37 38
# include <windows.h>
#else
# include <winsock2.h>
#endif
# include <ws2tcpip.h>
39 40 41 42 43 44 45
#undef getaddrinfo
#define getaddrinfo  sys_getaddrinfo
#undef freeaddrinfo
#define freeaddrinfo sys_freeaddrinfo
int sys_getaddrinfo (const char * node, const char * service,
		     const struct addrinfo * hints, struct addrinfo ** res);
void sys_freeaddrinfo (struct addrinfo * ai);
46
#undef SOCKET_ERROR
Juanma Barranquero's avatar
Juanma Barranquero committed
47 48 49
#define RECV(s,buf,len,flags) recv (s,buf,len,flags)
#define SEND(s,buf,len,flags) send (s,buf,len,flags)
#define CLOSESOCKET(s) closesocket (s)
50
#else
Richard M. Stallman's avatar
Richard M. Stallman committed
51 52
#include <netinet/in.h>
#include <sys/socket.h>
Juanma Barranquero's avatar
Juanma Barranquero committed
53 54 55
#define RECV(s,buf,len,flags) read (s,buf,len)
#define SEND(s,buf,len,flags) write (s,buf,len)
#define CLOSESOCKET(s) close (s)
56
#endif
Richard M. Stallman's avatar
Richard M. Stallman committed
57
#include <pop.h>
58

Richard M. Stallman's avatar
Richard M. Stallman committed
59 60 61 62 63 64 65 66 67 68
#ifdef HESIOD
#include <hesiod.h>
/*
 * It really shouldn't be necessary to put this declaration here, but
 * the version of hesiod.h that Athena has installed in release 7.2
 * doesn't declare this function; I don't know if the 7.3 version of
 * hesiod.h does.
 */
extern struct servent *hes_getservbyname (/* char *, char * */);
#endif
69

70
#include <alloca.h>
Richard M. Stallman's avatar
Richard M. Stallman committed
71 72 73 74
#include <pwd.h>
#include <netdb.h>
#include <errno.h>
#include <stdio.h>
75
#include <stdlib.h>
76
#include <string.h>
77
#include <unistd.h>
78

Richard M. Stallman's avatar
Richard M. Stallman committed
79
#ifdef KERBEROS
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
# ifdef HAVE_KRB5_H
#  include <krb5.h>
# endif
# ifdef HAVE_KRB_H
#  include <krb.h>
# else
#  ifdef HAVE_KERBEROSIV_KRB_H
#   include <kerberosIV/krb.h>
#  else
#   ifdef HAVE_KERBEROS_KRB_H
#    include <kerberos/krb.h>
#   endif
#  endif
# endif
# ifdef HAVE_COM_ERR_H
#  include <com_err.h>
# endif
97
#endif /* KERBEROS */
Richard M. Stallman's avatar
Richard M. Stallman committed
98

99
#include <c-ctype.h>
Paul Eggert's avatar
Paul Eggert committed
100 101
#include <min-max.h>

102
#ifdef KERBEROS
103
#ifndef KERBEROS5
Richard M. Stallman's avatar
Richard M. Stallman committed
104
extern int krb_sendauth (/* long, int, KTEXT, char *, char *, char *,
105 106 107
			    u_long, MSG_DAT *, CREDENTIALS *, Key_schedule,
			    struct sockaddr_in *, struct sockaddr_in *,
			    char * */);
Richard M. Stallman's avatar
Richard M. Stallman committed
108
extern char *krb_realmofhost (/* char * */);
109
#endif /* ! KERBEROS5 */
110
#endif /* KERBEROS */
Richard M. Stallman's avatar
Richard M. Stallman committed
111

112 113
static int socket_connection (char *, int);
static int pop_getline (popserver, char **);
114
static int sendline (popserver, const char *);
115 116
static int fullwrite (int, char *, int);
static int getok (popserver);
Richard M. Stallman's avatar
Richard M. Stallman committed
117
#if 0
118
static int gettermination (popserver);
Richard M. Stallman's avatar
Richard M. Stallman committed
119
#endif
120 121
static void pop_trash (popserver);
static char *find_crlf (char *, int);
122

123
#define ERROR_MAX 160		/* a pretty arbitrary size, but needs
Dave Love's avatar
Dave Love committed
124 125
				   to be bigger than the original
				   value of 80 */
Richard M. Stallman's avatar
Richard M. Stallman committed
126
#define POP_PORT 110
127
#define POP_SERVICE "pop3"	/* we don't want the POP2 port! */
Richard M. Stallman's avatar
Richard M. Stallman committed
128
#ifdef KERBEROS
129
#define KPOP_PORT 1109
130
#define KPOP_SERVICE "kpop"	/* never used: look for 20060515 to see why */
Richard M. Stallman's avatar
Richard M. Stallman committed
131 132 133
#endif

char pop_error[ERROR_MAX];
134
bool pop_debug = false;
Richard M. Stallman's avatar
Richard M. Stallman committed
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165

/*
 * Function: pop_open (char *host, char *username, char *password,
 * 		       int flags)
 *
 * Purpose: Establishes a connection with a post-office server, and
 * 	completes the authorization portion of the session.
 *
 * Arguments:
 * 	host	The server host with which the connection should be
 * 		established.  Optional.  If omitted, internal
 * 		heuristics will be used to determine the server host,
 * 		if possible.
 * 	username
 * 		The username of the mail-drop to access.  Optional.
 * 		If omitted, internal heuristics will be used to
 * 		determine the username, if possible.
 * 	password
 * 		The password to use for authorization.  If omitted,
 * 		internal heuristics will be used to determine the
 * 		password, if possible.
 * 	flags	A bit mask containing flags controlling certain
 * 		functions of the routine.  Valid flags are defined in
 * 		the file pop.h
 *
 * Return value: Upon successful establishment of a connection, a
 * 	non-null popserver will be returned.  Otherwise, null will be
 * 	returned, and the string variable pop_error will contain an
 * 	explanation of the error.
 */
popserver
166
pop_open (char *host, char *username, char *password, int flags)
Richard M. Stallman's avatar
Richard M. Stallman committed
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
{
  int sock;
  popserver server;

  /* Determine the user name */
  if (! username)
    {
      username = getenv ("USER");
      if (! (username && *username))
	{
	  username = getlogin ();
	  if (! (username && *username))
	    {
	      struct passwd *passwd;
	      passwd = getpwuid (getuid ());
	      if (passwd && passwd->pw_name && *passwd->pw_name)
		{
		  username = passwd->pw_name;
		}
	      else
		{
		  strcpy (pop_error, "Could not determine username");
		  return (0);
		}
	    }
	}
    }

  /*
   *  Determine the mail host.
   */

  if (! host)
    {
      host = getenv ("MAILHOST");
    }

#ifdef HESIOD
  if ((! host) && (! (flags & POP_NO_HESIOD)))
    {
      struct hes_postoffice *office;
      office = hes_getmailhost (username);
      if (office && office->po_type && (! strcmp (office->po_type, "POP"))
	  && office->po_name && *office->po_name && office->po_host
	  && *office->po_host)
	{
	  host = office->po_host;
	  username = office->po_name;
	}
    }
#endif

#ifdef MAILHOST
  if (! host)
    {
      host = MAILHOST;
    }
#endif

  if (! host)
    {
      strcpy (pop_error, "Could not determine POP server");
      return (0);
    }

  /* Determine the password */
233 234
#ifdef KERBEROS
#define DONT_NEED_PASSWORD (! (flags & POP_NO_KERBEROS))
Richard M. Stallman's avatar
Richard M. Stallman committed
235
#else
236
#define DONT_NEED_PASSWORD 0
Richard M. Stallman's avatar
Richard M. Stallman committed
237
#endif
238

Richard M. Stallman's avatar
Richard M. Stallman committed
239 240 241 242 243 244 245 246 247 248 249 250
  if ((! password) && (! DONT_NEED_PASSWORD))
    {
      if (! (flags & POP_NO_GETPASS))
	{
	  password = getpass ("Enter POP password:");
	}
      if (! password)
	{
	  strcpy (pop_error, "Could not determine POP password");
	  return (0);
	}
    }
251
  if (password)			/* always true, detected 20060515 */
252
    flags |= POP_NO_KERBEROS;
Richard M. Stallman's avatar
Richard M. Stallman committed
253
  else
254 255
    password = username;	/* dead code, detected 20060515 */
  /** "kpop" service is  never used: look for 20060515 to see why **/
Richard M. Stallman's avatar
Richard M. Stallman committed
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273

  sock = socket_connection (host, flags);
  if (sock == -1)
    return (0);

  server = (popserver) malloc (sizeof (struct _popserver));
  if (! server)
    {
      strcpy (pop_error, "Out of memory in pop_open");
      return (0);
    }
  server->buffer = (char *) malloc (GETLINE_MIN);
  if (! server->buffer)
    {
      strcpy (pop_error, "Out of memory in pop_open");
      free ((char *) server);
      return (0);
    }
274

Richard M. Stallman's avatar
Richard M. Stallman committed
275 276 277 278
  server->file = sock;
  server->data = 0;
  server->buffer_index = 0;
  server->buffer_size = GETLINE_MIN;
279 280
  server->in_multi = false;
  server->trash_started = false;
Richard M. Stallman's avatar
Richard M. Stallman committed
281 282 283 284 285 286 287

  if (getok (server))
    return (0);

  /*
   * I really shouldn't use the pop_error variable like this, but....
   */
288
  if (strnlen (username, ERROR_MAX - 6 + 1) == ERROR_MAX - 6 + 1)
Richard M. Stallman's avatar
Richard M. Stallman committed
289 290 291 292 293 294 295 296 297 298 299 300 301
    {
      pop_close (server);
      strcpy (pop_error,
	      "Username too long; recompile pop.c with larger ERROR_MAX");
      return (0);
    }
  sprintf (pop_error, "USER %s", username);

  if (sendline (server, pop_error) || getok (server))
    {
      return (0);
    }

302
  if (strnlen (password, ERROR_MAX - 6 + 1) == ERROR_MAX - 6 + 1)
Richard M. Stallman's avatar
Richard M. Stallman committed
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
    {
      pop_close (server);
      strcpy (pop_error,
	      "Password too long; recompile pop.c with larger ERROR_MAX");
      return (0);
    }
  sprintf (pop_error, "PASS %s", password);

  if (sendline (server, pop_error) || getok (server))
    {
      return (0);
    }

  return (server);
}

/*
 * Function: pop_stat
 *
 * Purpose: Issue the STAT command to the server and return (in the
 * 	value parameters) the number of messages in the maildrop and
 * 	the total size of the maildrop.
 *
 * Return value: 0 on success, or non-zero with an error in pop_error
 * 	in failure.
 *
 * Side effects: On failure, may make further operations on the
 * 	connection impossible.
 */
int
333
pop_stat (popserver server, int *count, int *size)
Richard M. Stallman's avatar
Richard M. Stallman committed
334 335
{
  char *fromserver;
336
  char *end_ptr;
Richard M. Stallman's avatar
Richard M. Stallman committed
337 338 339 340 341 342

  if (server->in_multi)
    {
      strcpy (pop_error, "In multi-line query in pop_stat");
      return (-1);
    }
343

344
  if (sendline (server, "STAT") || (pop_getline (server, &fromserver) < 0))
Richard M. Stallman's avatar
Richard M. Stallman committed
345 346 347 348 349
    return (-1);

  if (strncmp (fromserver, "+OK ", 4))
    {
      if (0 == strncmp (fromserver, "-ERR", 4))
350
	snprintf (pop_error, ERROR_MAX, "%s", fromserver);
Richard M. Stallman's avatar
Richard M. Stallman committed
351 352 353 354 355 356 357 358 359
      else
	{
	  strcpy (pop_error,
		  "Unexpected response from POP server in pop_stat");
	  pop_trash (server);
	}
      return (-1);
    }

360 361 362
  errno = 0;
  *count = strtol (&fromserver[4], &end_ptr, 10);
  /* Check validity of string-to-integer conversion. */
363
  if (fromserver + 4 == end_ptr || *end_ptr != ' ' || errno)
364 365 366 367 368
    {
      strcpy (pop_error, "Unexpected response from POP server in pop_stat");
      pop_trash (server);
      return (-1);
    }
369

370
  fromserver = end_ptr;
Richard M. Stallman's avatar
Richard M. Stallman committed
371

372 373
  errno = 0;
  *size = strtol (fromserver + 1, &end_ptr, 10);
374
  if (fromserver + 1 == end_ptr || errno)
375 376 377 378 379
    {
      strcpy (pop_error, "Unexpected response from POP server in pop_stat");
      pop_trash (server);
      return (-1);
    }
Richard M. Stallman's avatar
Richard M. Stallman committed
380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403

  return (0);
}

/*
 * Function: pop_list
 *
 * Purpose: Performs the POP "list" command and returns (in value
 * 	parameters) two malloc'd zero-terminated arrays -- one of
 * 	message IDs, and a parallel one of sizes.
 *
 * Arguments:
 * 	server	The pop connection to talk to.
 * 	message	The number of the one message about which to get
 * 		information, or 0 to get information about all
 * 		messages.
 *
 * Return value: 0 on success, non-zero with error in pop_error on
 * 	failure.
 *
 * Side effects: On failure, may make further operations on the
 * 	connection impossible.
 */
int
404
pop_list (popserver server, int message, int **IDs, int **sizes)
Richard M. Stallman's avatar
Richard M. Stallman committed
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441
{
  int how_many, i;
  char *fromserver;

  if (server->in_multi)
    {
      strcpy (pop_error, "In multi-line query in pop_list");
      return (-1);
    }

  if (message)
    how_many = 1;
  else
    {
      int count, size;
      if (pop_stat (server, &count, &size))
	return (-1);
      how_many = count;
    }

  *IDs = (int *) malloc ((how_many + 1) * sizeof (int));
  *sizes = (int *) malloc ((how_many + 1) * sizeof (int));
  if (! (*IDs && *sizes))
    {
      strcpy (pop_error, "Out of memory in pop_list");
      return (-1);
    }

  if (message)
    {
      sprintf (pop_error, "LIST %d", message);
      if (sendline (server, pop_error))
	{
	  free ((char *) *IDs);
	  free ((char *) *sizes);
	  return (-1);
	}
442
      if (pop_getline (server, &fromserver) < 0)
Richard M. Stallman's avatar
Richard M. Stallman committed
443 444 445 446 447 448 449 450
	{
	  free ((char *) *IDs);
	  free ((char *) *sizes);
	  return (-1);
	}
      if (strncmp (fromserver, "+OK ", 4))
	{
	  if (! strncmp (fromserver, "-ERR", 4))
451
	    snprintf (pop_error, ERROR_MAX, "%s", fromserver);
Richard M. Stallman's avatar
Richard M. Stallman committed
452 453 454 455 456 457 458 459 460 461 462
	  else
	    {
	      strcpy (pop_error,
		      "Unexpected response from server in pop_list");
	      pop_trash (server);
	    }
	  free ((char *) *IDs);
	  free ((char *) *sizes);
	  return (-1);
	}
      (*IDs)[0] = atoi (&fromserver[4]);
463
      fromserver = strchr (&fromserver[4], ' ');
Richard M. Stallman's avatar
Richard M. Stallman committed
464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486
      if (! fromserver)
	{
	  strcpy (pop_error,
		  "Badly formatted response from server in pop_list");
	  pop_trash (server);
	  free ((char *) *IDs);
	  free ((char *) *sizes);
	  return (-1);
	}
      (*sizes)[0] = atoi (fromserver);
      (*IDs)[1] = (*sizes)[1] = 0;
      return (0);
    }
  else
    {
      if (pop_multi_first (server, "LIST", &fromserver))
	{
	  free ((char *) *IDs);
	  free ((char *) *sizes);
	  return (-1);
	}
      for (i = 0; i < how_many; i++)
	{
487
	  if (pop_multi_next (server, &fromserver) <= 0)
Richard M. Stallman's avatar
Richard M. Stallman committed
488 489 490 491 492 493
	    {
	      free ((char *) *IDs);
	      free ((char *) *sizes);
	      return (-1);
	    }
	  (*IDs)[i] = atoi (fromserver);
494
	  fromserver = strchr (fromserver, ' ');
Richard M. Stallman's avatar
Richard M. Stallman committed
495 496 497 498 499 500 501 502 503 504 505
	  if (! fromserver)
	    {
	      strcpy (pop_error,
		      "Badly formatted response from server in pop_list");
	      free ((char *) *IDs);
	      free ((char *) *sizes);
	      pop_trash (server);
	      return (-1);
	    }
	  (*sizes)[i] = atoi (fromserver);
	}
506
      if (pop_multi_next (server, &fromserver) < 0)
Richard M. Stallman's avatar
Richard M. Stallman committed
507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535
	{
	  free ((char *) *IDs);
	  free ((char *) *sizes);
	  return (-1);
	}
      else if (fromserver)
	{
	  strcpy (pop_error,
		  "Too many response lines from server in pop_list");
	  free ((char *) *IDs);
	  free ((char *) *sizes);
	  return (-1);
	}
      (*IDs)[i] = (*sizes)[i] = 0;
      return (0);
    }
}

/*
 * Function: pop_retrieve
 *
 * Purpose: Retrieve a specified message from the maildrop.
 *
 * Arguments:
 * 	server	The server to retrieve from.
 * 	message	The message number to retrieve.
 *	markfrom
 * 		If true, then mark the string "From " at the beginning
 * 		of lines with '>'.
536 537
 *	msg_buf	Output parameter to which a buffer containing the
 * 		message is assigned.
538
 *
539 540 541
 * Return value: The number of bytes in msg_buf, which may contain
 * 	embedded nulls, not including its final null, or -1 on error
 * 	with pop_error set.
Richard M. Stallman's avatar
Richard M. Stallman committed
542 543 544
 *
 * Side effects: May kill connection on error.
 */
545
int
546
pop_retrieve (popserver server, int message, int markfrom, char **msg_buf)
Richard M. Stallman's avatar
Richard M. Stallman committed
547 548 549 550 551 552 553 554
{
  int *IDs, *sizes, bufsize, fromcount = 0, cp = 0;
  char *ptr, *fromserver;
  int ret;

  if (server->in_multi)
    {
      strcpy (pop_error, "In multi-line query in pop_retrieve");
555
      return (-1);
Richard M. Stallman's avatar
Richard M. Stallman committed
556 557 558
    }

  if (pop_list (server, message, &IDs, &sizes))
559
    return (-1);
Richard M. Stallman's avatar
Richard M. Stallman committed
560 561 562

  if (pop_retrieve_first (server, message, &fromserver))
    {
563
      return (-1);
Richard M. Stallman's avatar
Richard M. Stallman committed
564 565 566 567 568 569 570 571 572
    }

  /*
   * The "5" below is an arbitrary constant -- I assume that if
   * there are "From" lines in the text to be marked, there
   * probably won't be more than 5 of them.  If there are, I
   * allocate more space for them below.
   */
  bufsize = sizes[0] + (markfrom ? 5 : 0);
573
  ptr = (char *)malloc (bufsize);
Richard M. Stallman's avatar
Richard M. Stallman committed
574 575 576 577 578 579 580
  free ((char *) IDs);
  free ((char *) sizes);

  if (! ptr)
    {
      strcpy (pop_error, "Out of memory in pop_retrieve");
      pop_retrieve_flush (server);
581
      return (-1);
Richard M. Stallman's avatar
Richard M. Stallman committed
582 583
    }

584
  while ((ret = pop_retrieve_next (server, &fromserver)) >= 0)
Richard M. Stallman's avatar
Richard M. Stallman committed
585 586 587 588
    {
      if (! fromserver)
	{
	  ptr[cp] = '\0';
589 590
	  *msg_buf = ptr;
	  return (cp);
Richard M. Stallman's avatar
Richard M. Stallman committed
591 592 593 594 595 596 597 598
	}
      if (markfrom && fromserver[0] == 'F' && fromserver[1] == 'r' &&
	  fromserver[2] == 'o' && fromserver[3] == 'm' &&
	  fromserver[4] == ' ')
	{
	  if (++fromcount == 5)
	    {
	      bufsize += 5;
599
	      ptr = (char *)realloc (ptr, bufsize);
Richard M. Stallman's avatar
Richard M. Stallman committed
600 601 602 603
	      if (! ptr)
		{
		  strcpy (pop_error, "Out of memory in pop_retrieve");
		  pop_retrieve_flush (server);
604
		  return (-1);
Richard M. Stallman's avatar
Richard M. Stallman committed
605 606 607 608 609
		}
	      fromcount = 0;
	    }
	  ptr[cp++] = '>';
	}
610
      memcpy (&ptr[cp], fromserver, ret);
611
      cp += ret;
Richard M. Stallman's avatar
Richard M. Stallman committed
612 613 614
      ptr[cp++] = '\n';
    }

615 616
  free (ptr);
  return (-1);
617
}
Richard M. Stallman's avatar
Richard M. Stallman committed
618 619

int
620
pop_retrieve_first (popserver server, int message, char **response)
Richard M. Stallman's avatar
Richard M. Stallman committed
621 622 623 624 625
{
  sprintf (pop_error, "RETR %d", message);
  return (pop_multi_first (server, pop_error, response));
}

626 627 628 629 630 631 632 633
/*
  Returns a negative number on error, 0 to indicate that the data has
  all been read (i.e., the server has returned a "." termination
  line), or a positive number indicating the number of bytes in the
  returned buffer (which is null-terminated and may contain embedded
  nulls, but the returned bytecount doesn't include the final null).
  */

Richard M. Stallman's avatar
Richard M. Stallman committed
634
int
635
pop_retrieve_next (popserver server, char **line)
Richard M. Stallman's avatar
Richard M. Stallman committed
636 637 638 639 640
{
  return (pop_multi_next (server, line));
}

int
641
pop_retrieve_flush (popserver server)
Richard M. Stallman's avatar
Richard M. Stallman committed
642 643 644 645 646
{
  return (pop_multi_flush (server));
}

int
647
pop_top_first (popserver server, int message, int lines, char **response)
Richard M. Stallman's avatar
Richard M. Stallman committed
648 649 650 651 652
{
  sprintf (pop_error, "TOP %d %d", message, lines);
  return (pop_multi_first (server, pop_error, response));
}

653 654 655 656 657 658 659 660
/*
  Returns a negative number on error, 0 to indicate that the data has
  all been read (i.e., the server has returned a "." termination
  line), or a positive number indicating the number of bytes in the
  returned buffer (which is null-terminated and may contain embedded
  nulls, but the returned bytecount doesn't include the final null).
  */

Richard M. Stallman's avatar
Richard M. Stallman committed
661
int
662
pop_top_next (popserver server, char **line)
Richard M. Stallman's avatar
Richard M. Stallman committed
663 664 665 666 667
{
  return (pop_multi_next (server, line));
}

int
668
pop_top_flush (popserver server)
Richard M. Stallman's avatar
Richard M. Stallman committed
669 670 671 672 673
{
  return (pop_multi_flush (server));
}

int
674
pop_multi_first (popserver server, const char *command, char **response)
Richard M. Stallman's avatar
Richard M. Stallman committed
675 676 677 678 679 680 681 682
{
  if (server->in_multi)
    {
      strcpy (pop_error,
	      "Already in multi-line query in pop_multi_first");
      return (-1);
    }

683
  if (sendline (server, command) || (pop_getline (server, response) < 0))
Richard M. Stallman's avatar
Richard M. Stallman committed
684 685 686 687 688 689
    {
      return (-1);
    }

  if (0 == strncmp (*response, "-ERR", 4))
    {
690
      snprintf (pop_error, ERROR_MAX, "%s", *response);
Richard M. Stallman's avatar
Richard M. Stallman committed
691 692 693 694 695
      return (-1);
    }
  else if (0 == strncmp (*response, "+OK", 3))
    {
      for (*response += 3; **response == ' '; (*response)++) /* empty */;
696
      server->in_multi = true;
Richard M. Stallman's avatar
Richard M. Stallman committed
697 698 699 700 701 702 703 704 705 706
      return (0);
    }
  else
    {
      strcpy (pop_error,
	      "Unexpected response from server in pop_multi_first");
      return (-1);
    }
}

707 708 709 710 711 712 713 714 715
/*
  Read the next line of data from SERVER and place a pointer to it
  into LINE.  Return -1 on error, 0 if there are no more lines to read
  (i.e., the server has returned a line containing only "."), or a
  positive number indicating the number of bytes in the LINE buffer
  (not including the final null).  The data in that buffer may contain
  embedded nulls, but does not contain the final CRLF. When returning
  0, LINE is set to null. */

Richard M. Stallman's avatar
Richard M. Stallman committed
716
int
717
pop_multi_next (popserver server, char **line)
Richard M. Stallman's avatar
Richard M. Stallman committed
718 719
{
  char *fromserver;
720
  int ret;
Richard M. Stallman's avatar
Richard M. Stallman committed
721 722 723 724 725 726 727

  if (! server->in_multi)
    {
      strcpy (pop_error, "Not in multi-line query in pop_multi_next");
      return (-1);
    }

728 729
  ret = pop_getline (server, &fromserver);
  if (ret < 0)
Richard M. Stallman's avatar
Richard M. Stallman committed
730 731 732 733 734 735 736 737 738
    {
      return (-1);
    }

  if (fromserver[0] == '.')
    {
      if (! fromserver[1])
	{
	  *line = 0;
739
	  server->in_multi = false;
Richard M. Stallman's avatar
Richard M. Stallman committed
740 741 742 743 744
	  return (0);
	}
      else
	{
	  *line = fromserver + 1;
745
	  return (ret - 1);
Richard M. Stallman's avatar
Richard M. Stallman committed
746 747 748 749 750
	}
    }
  else
    {
      *line = fromserver;
751
      return (ret);
Richard M. Stallman's avatar
Richard M. Stallman committed
752 753 754 755
    }
}

int
756
pop_multi_flush (popserver server)
Richard M. Stallman's avatar
Richard M. Stallman committed
757 758
{
  char *line;
759
  int ret;
Richard M. Stallman's avatar
Richard M. Stallman committed
760 761 762 763 764 765

  if (! server->in_multi)
    {
      return (0);
    }

766
  while ((ret = pop_multi_next (server, &line)))
Richard M. Stallman's avatar
Richard M. Stallman committed
767
    {
768 769
      if (ret < 0)
	return (-1);
Richard M. Stallman's avatar
Richard M. Stallman committed
770 771
    }

772
  return (0);
Richard M. Stallman's avatar
Richard M. Stallman committed
773 774 775 776 777 778 779 780 781 782 783 784 785 786
}

/* Function: pop_delete
 *
 * Purpose: Delete a specified message.
 *
 * Arguments:
 * 	server	Server from which to delete the message.
 * 	message	Message to delete.
 *
 * Return value: 0 on success, non-zero with error in pop_error
 * 	otherwise.
 */
int
787
pop_delete (popserver server, int message)
Richard M. Stallman's avatar
Richard M. Stallman committed
788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816
{
  if (server->in_multi)
    {
      strcpy (pop_error, "In multi-line query in pop_delete");
      return (-1);
    }

  sprintf (pop_error, "DELE %d", message);

  if (sendline (server, pop_error) || getok (server))
    return (-1);

  return (0);
}

/*
 * Function: pop_noop
 *
 * Purpose: Send a noop command to the server.
 *
 * Argument:
 * 	server	The server to send to.
 *
 * Return value: 0 on success, non-zero with error in pop_error
 * 	otherwise.
 *
 * Side effects: Closes connection on error.
 */
int
817
pop_noop (popserver server)
Richard M. Stallman's avatar
Richard M. Stallman committed
818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845
{
  if (server->in_multi)
    {
      strcpy (pop_error, "In multi-line query in pop_noop");
      return (-1);
    }

  if (sendline (server, "NOOP") || getok (server))
    return (-1);

  return (0);
}

/*
 * Function: pop_last
 *
 * Purpose: Find out the highest seen message from the server.
 *
 * Arguments:
 * 	server	The server.
 *
 * Return value: If successful, the highest seen message, which is
 * 	greater than or equal to 0.  Otherwise, a negative number with
 * 	the error explained in pop_error.
 *
 * Side effects: Closes the connection on error.
 */
int
846
pop_last (popserver server)
Richard M. Stallman's avatar
Richard M. Stallman committed
847 848
{
  char *fromserver;
849

Richard M. Stallman's avatar
Richard M. Stallman committed
850 851 852 853 854 855 856 857 858
  if (server->in_multi)
    {
      strcpy (pop_error, "In multi-line query in pop_last");
      return (-1);
    }

  if (sendline (server, "LAST"))
    return (-1);

859
  if (pop_getline (server, &fromserver) < 0)
Richard M. Stallman's avatar
Richard M. Stallman committed
860 861 862 863
    return (-1);

  if (! strncmp (fromserver, "-ERR", 4))
    {
864
      snprintf (pop_error, ERROR_MAX, "%s", fromserver);
Richard M. Stallman's avatar
Richard M. Stallman committed
865 866 867 868 869 870 871 872 873 874
      return (-1);
    }
  else if (strncmp (fromserver, "+OK ", 4))
    {
      strcpy (pop_error, "Unexpected response from server in pop_last");
      pop_trash (server);
      return (-1);
    }
  else
    {
875 876 877 878
      char *end_ptr;
      int count;
      errno = 0;
      count = strtol (&fromserver[4], &end_ptr, 10);
879
      if (fromserver + 4 == end_ptr || errno)
880 881 882 883 884 885
	{
	  strcpy (pop_error, "Unexpected response from server in pop_last");
	  pop_trash (server);
	  return (-1);
	}
      return count;
Richard M. Stallman's avatar
Richard M. Stallman committed
886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902
    }
}

/*
 * Function: pop_reset
 *
 * Purpose: Reset the server to its initial connect state
 *
 * Arguments:
 * 	server	The server.
 *
 * Return value: 0 for success, non-0 with error in pop_error
 * 	otherwise.
 *
 * Side effects: Closes the connection on error.
 */
int
903
pop_reset (popserver server)
Richard M. Stallman's avatar
Richard M. Stallman committed
904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926
{
  if (pop_retrieve_flush (server))
    {
      return (-1);
    }

  if (sendline (server, "RSET") || getok (server))
    return (-1);

  return (0);
}

/*
 * Function: pop_quit
 *
 * Purpose: Quit the connection to the server,
 *
 * Arguments:
 * 	server	The server to quit.
 *
 * Return value: 0 for success, non-zero otherwise with error in
 * 	pop_error.
 *
Karl Heuer's avatar
Karl Heuer committed
927
 * Side Effects: The popserver passed in is unusable after this
Richard M. Stallman's avatar
Richard M. Stallman committed
928 929 930
 * 	function is called, even if an error occurs.
 */
int
931
pop_quit (popserver server)
Richard M. Stallman's avatar
Richard M. Stallman committed
932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949
{
  int ret = 0;

  if (server->file >= 0)
    {
      if (pop_retrieve_flush (server))
	{
	  ret = -1;
	}

      if (sendline (server, "QUIT") || getok (server))
	{
	  ret = -1;
	}

      close (server->file);
    }

950
  free (server->buffer);
Richard M. Stallman's avatar
Richard M. Stallman committed
951 952 953 954 955
  free ((char *) server);

  return (ret);
}

956 957 958 959
#ifdef WINDOWSNT
static int have_winsock = 0;
#endif

Richard M. Stallman's avatar
Richard M. Stallman committed
960 961 962 963 964 965 966 967 968
/*
 * Function: socket_connection
 *
 * Purpose: Opens the network connection with the mail host, without
 * 	doing any sort of I/O with it or anything.
 *
 * Arguments:
 * 	host	The host to which to connect.
 *	flags	Option flags.
969
 *
Richard M. Stallman's avatar
Richard M. Stallman committed
970 971 972 973 974
 * Return value: A file descriptor indicating the connection, or -1
 * 	indicating failure, in which case an error has been copied
 * 	into pop_error.
 */
static int
975
socket_connection (char *host, int flags)
Richard M. Stallman's avatar
Richard M. Stallman committed
976
{
977 978 979
  struct addrinfo *res, *it;
  struct addrinfo hints;
  int ret;
Richard M. Stallman's avatar
Richard M. Stallman committed
980 981 982
  struct servent *servent;
  struct sockaddr_in addr;
  char found_port = 0;
983
  const char *service;
Richard M. Stallman's avatar
Richard M. Stallman committed
984
  int sock;
985
  char *realhost;
Richard M. Stallman's avatar
Richard M. Stallman committed
986
#ifdef KERBEROS
987
#ifdef KERBEROS5
Richard M. Stallman's avatar
Richard M. Stallman committed
988
  krb5_error_code rem;
989 990
  krb5_context kcontext = 0;
  krb5_auth_context auth_context = 0;
Richard M. Stallman's avatar
Richard M. Stallman committed
991 992 993 994 995 996 997 998 999 1000
  krb5_ccache ccdef;
  krb5_principal client, server;
  krb5_error *err_ret;
  register char *cp;
#else
  KTEXT ticket;
  MSG_DAT msg_data;
  CREDENTIALS cred;
  Key_schedule schedule;
  int rem;
1001
#endif /* KERBEROS5 */
Richard M. Stallman's avatar
Richard M. Stallman committed
1002 1003 1004
#endif /* KERBEROS */

  int try_count = 0;
1005
  int connect_ok;
Richard M. Stallman's avatar
Richard M. Stallman committed
1006

1007 1008 1009 1010 1011 1012 1013 1014
#ifdef WINDOWSNT
  {
    WSADATA winsockData;
    if (WSAStartup (0x101, &winsockData) == 0)
      have_winsock = 1;
  }
#endif

1015
  memset (&addr, 0, sizeof (addr));
Richard M. Stallman's avatar
Richard M. Stallman committed
1016 1017
  addr.sin_family = AF_INET;

1018
  /** "kpop" service is  never used: look for 20060515 to see why **/
Richard M. Stallman's avatar
Richard M. Stallman committed
1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044
#ifdef KERBEROS
  service = (flags & POP_NO_KERBEROS) ? POP_SERVICE : KPOP_SERVICE;
#else
  service = POP_SERVICE;
#endif

#ifdef HESIOD
  if (! (flags & POP_NO_HESIOD))
    {
      servent = hes_getservbyname (service, "tcp");
      if (servent)
	{
	  addr.sin_port = servent->s_port;
	  found_port = 1;
	}
    }
#endif
  if (! found_port)
    {
      servent = getservbyname (service, "tcp");
      if (servent)
	{
	  addr.sin_port = servent->s_port;
	}
      else
	{
1045
  /** "kpop" service is  never used: look for 20060515 to see why **/
Richard M. Stallman's avatar
Richard M. Stallman committed
1046 1047 1048 1049 1050 1051 1052 1053 1054
#ifdef KERBEROS
	  addr.sin_port = htons ((flags & POP_NO_KERBEROS) ?
				POP_PORT : KPOP_PORT);
#else
	  addr.sin_port = htons (POP_PORT);
#endif
	}
    }

1055
#define POP_SOCKET_ERROR "Could not create socket for POP connection: "
Richard M. Stallman's avatar
Richard M. Stallman committed
1056 1057 1058 1059

  sock = socket (PF_INET, SOCK_STREAM, 0);
  if (sock < 0)
    {
1060 1061
      snprintf (pop_error, ERROR_MAX, "%s%s",
		POP_SOCKET_ERROR, strerror (errno));
Richard M. Stallman's avatar
Richard M. Stallman committed
1062
      return (-1);
1063

Richard M. Stallman's avatar
Richard M. Stallman committed
1064 1065
    }

Juanma Barranquero's avatar
Juanma Barranquero committed
1066
  memset (&hints, 0, sizeof (hints));
1067
  hints.ai_socktype = SOCK_STREAM;
1068
  hints.ai_flags = AI_CANONNAME;
1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080
  hints.ai_family = AF_INET;
  do
    {
      ret = getaddrinfo (host, service, &hints, &res);
      try_count++;
      if (ret != 0 && (ret != EAI_AGAIN || try_count == 5))
	{
	  strcpy (pop_error, "Could not determine POP server's address");
	  return (-1);
	}
    } while (ret != 0);

1081 1082 1083 1084 1085 1086 1087 1088 1089 1090
  for (it = res; it; it = it->ai_next)
    if (it->ai_addrlen == sizeof addr)
      {
	struct sockaddr_in *in_a = (struct sockaddr_in *) it->ai_addr;
	addr.sin_addr = in_a->sin_addr;
	if (! connect (sock, (struct sockaddr *) &addr, sizeof addr))
	  break;
      }
  connect_ok = it != NULL;
  if (connect_ok)
1091
    {
1092 1093
      realhost = alloca (strlen (it->ai_canonname) + 1);
      strcpy (realhost, it->ai_canonname);
1094
    }
1095 1096
  freeaddrinfo (res);

Richard M. Stallman's avatar
Richard M. Stallman committed
1097
#define CONNECT_ERROR "Could not connect to POP server: "
1098

1099
  if (! connect_ok)
Richard M. Stallman's avatar
Richard M. Stallman committed
1100
    {
1101
      CLOSESOCKET (sock);
1102
      snprintf (pop_error, ERROR_MAX, "%s%s", CONNECT_ERROR, strerror (errno));
Richard M. Stallman's avatar
Richard M. Stallman committed
1103
      return (-1);
1104

Richard M. Stallman's avatar
Richard M. Stallman committed
1105 1106 1107
    }

#ifdef KERBEROS
1108

Richard M. Stallman's avatar
Richard M. Stallman committed
1109 1110 1111
#define KRB_ERROR "Kerberos error connecting to POP server: "
  if (! (flags & POP_NO_KERBEROS))
    {
1112
#ifdef KERBEROS5
1113 1114
      rem = krb5_init_context (&kcontext);
      if (rem)
Richard M. Stallman's avatar
Richard M. Stallman committed
1115 1116
	{
	krb5error:
1117 1118 1119 1120
	  if (auth_context)
	    krb5_auth_con_free (kcontext, auth_context);
	  if (kcontext)
	    krb5_free_context (kcontext);
1121 1122
	  snprintf (pop_error, ERROR_MAX, "%s%s",
		    KRB_ERROR, error_message (rem));
1123
	  CLOSESOCKET (sock);
Richard M. Stallman's avatar
Richard M. Stallman committed
1124 1125 1126
	  return (-1);
	}

1127 1128
      rem = krb5_auth_con_init (kcontext, &auth_context);
      if (rem)
1129
	goto krb5error;
1130

1131 1132
      rem = krb5_cc_default (kcontext, &ccdef);
      if (rem)
1133 1134
	goto krb5error;

1135 1136
      rem = krb5_cc_get_principal (kcontext, ccdef, &client);
      if (rem)
1137
	goto krb5error;
Richard M. Stallman's avatar
Richard M. Stallman committed
1138

1139
      for (cp = realhost; *cp; cp++)
1140
	*cp = c_tolower (*cp);
Richard M. Stallman's avatar
Richard M. Stallman committed
1141

1142 1143 1144
      rem = krb5_sname_to_principal (kcontext, realhost,
				     POP_SERVICE, FALSE, &server);
      if (rem)
1145
	goto krb5error;
Richard M. Stallman's avatar
Richard M. Stallman committed
1146

1147
      rem = krb5_sendauth (kcontext, &auth_context,
1148 1149
			   (krb5_pointer) &sock, (char *) "KPOPV1.0",
			   client, server,
Richard M. Stallman's avatar
Richard M. Stallman committed
1150 1151 1152 1153 1154
			  AP_OPTS_MUTUAL_REQUIRED,
			  0,	/* no checksum */
			  0,	/* no creds, use ccache instead */
			  ccdef,
			  &err_ret,
1155
			  0,	/* don't need subsession key */
Richard M. Stallman's avatar
Richard M. Stallman committed
1156
			  0);	/* don't need reply */
1157
      krb5_free_principal (kcontext, server);
Richard M. Stallman's avatar
Richard M. Stallman committed
1158 1159
      if (rem)
	{
1160 1161
	  int pop_error_len = snprintf (pop_error, ERROR_MAX, "%s%s",
					KRB_ERROR, error_message (rem));
1162
#if defined HAVE_KRB5_ERROR_TEXT
Richard M. Stallman's avatar
Richard M. Stallman committed
1163 1164
	  if (err_ret && err_ret->text.length)
	    {
1165 1166
	      int errlen = err_ret->text.length;
	      snprintf (pop_error + pop_error_len, ERROR_MAX - pop_error_len,
1167
			" [server says '%.*s']", errlen, err_ret->text.data);
Richard M. Stallman's avatar
Richard M. Stallman committed
1168
	    }
1169
#elif defined HAVE_KRB5_ERROR_E_TEXT
1170
	  if (err_ret && err_ret->e_text && **err_ret->e_text)
1171
	    snprintf (pop_error + pop_error_len, ERROR_MAX - pop_error_len,
1172
		      " [server says '%s']", *err_ret->e_text);
1173
#endif
Richard M. Stallman's avatar
Richard M. Stallman committed
1174
	  if (err_ret)
1175 1176 1177
	    krb5_free_error (kcontext, err_ret);
	  krb5_auth_con_free (kcontext, auth_context);
	  krb5_free_context (kcontext);
Richard M. Stallman's avatar
Richard M. Stallman committed
1178

1179
	  CLOSESOCKET (sock);
Richard M. Stallman's avatar
Richard M. Stallman committed
1180 1181
	  return (-1);
	}
1182
#else  /* ! KERBEROS5 */
Richard M. Stallman's avatar
Richard M. Stallman committed
1183
      ticket = (KTEXT) malloc (sizeof (KTEXT_ST));
1184 1185
      rem = krb_sendauth (0L, sock, ticket, "pop", realhost,
			  (char *) krb_realmofhost (realhost),
Richard M. Stallman's avatar
Richard M. Stallman committed
1186 1187 1188 1189 1190 1191 1192
			  (unsigned long) 0, &msg_data, &cred, schedule,
			  (struct sockaddr_in *) 0,
			  (struct sockaddr_in *) 0,
			  "KPOPV0.1");
      free ((char *) ticket);
      if (rem != KSUCCESS)
	{
1193
	  snprintf (pop_error, ERROR_MAX, "%s%s", KRB_ERROR, krb_err_txt[rem]);
1194
	  CLOSESOCKET (sock);
Richard M. Stallman's avatar
Richard M. Stallman committed
1195 1196
	  return (-1);
	}
1197
#endif /* KERBEROS5 */
Richard M. Stallman's avatar
Richard M. Stallman committed
1198 1199 1200 1201 1202 1203 1204
    }
#endif /* KERBEROS */

  return (sock);
} /* socket_connection */

/*
1205
 * Function: pop_getline
Richard M. Stallman's avatar
Richard M. Stallman committed
1206 1207 1208 1209 1210 1211 1212 1213 1214
 *
 * Purpose: Get a line of text from the connection and return a
 * 	pointer to it.  The carriage return and linefeed at the end of
 * 	the line are stripped, but periods at the beginnings of lines
 * 	are NOT dealt with in any special way.
 *
 * Arguments:
 * 	server	The server from which to get the line of text.
 *
1215 1216 1217 1218 1219
 * Returns: The number of characters in the line, which is returned in
 * 	LINE, not including the final null.  A return value of 0
 * 	indicates a blank line.  A negative return value indicates an
 * 	error (in which case the contents of LINE are undefined.  In
 * 	case of error, an error message is copied into pop_error.
Richard M. Stallman's avatar
Richard M. Stallman committed
1220
 *
1221
 * Notes: The line returned is overwritten with each call to pop_getline.
Richard M. Stallman's avatar
Richard M. Stallman committed
1222 1223
 *
 * Side effects: Closes the connection on error.
1224 1225
 *
 * THE RETURNED LINE MAY CONTAIN EMBEDDED NULLS!
Richard M. Stallman's avatar
Richard M. Stallman committed
1226
 */
1227
static int
1228
pop_getline (popserver server, char **line)
Richard M. Stallman's avatar
Richard M. Stallman committed
1229 1230 1231 1232
{
#define GETLINE_ERROR "Error reading from server: "

  int ret;
1233 1234
  int search_offset = 0;

Richard M. Stallman's avatar
Richard M. Stallman committed
1235 1236
  if (server->data)
    {
1237 1238
      char *cp = find_crlf (server->buffer + server->buffer_index,
			    server->data);
Richard M. Stallman's avatar
Richard M. Stallman committed
1239 1240 1241 1242 1243 1244 1245
      if (cp)
	{
	  int found;
	  int data_used;

	  found = server->buffer_index;
	  data_used = (cp + 2) - server->buffer - found;
1246

Richard M. Stallman's avatar
Richard M. Stallman committed
1247 1248 1249 1250 1251
	  *cp = '\0';		/* terminate the string to be returned */
	  server->data -= data_used;
	  server->buffer_index += data_used;

	  if (pop_debug)
1252 1253
	    /* Embedded nulls will truncate this output prematurely,
	       but that's OK because it's just for debugging anyway. */
Richard M. Stallman's avatar
Richard M. Stallman committed
1254
	    fprintf (stderr, "<<< %s\n", server->buffer + found);
1255 1256
	  *line = server->buffer + found;
	  return (data_used - 2);
Richard M. Stallman's avatar
Richard M. Stallman committed
1257 1258 1259
	}
      else
	{
1260 1261
	  memmove (server->buffer, server->buffer + server->buffer_index,
		   server->data);
1262 1263 1264 1265 1266 1267 1268 1269
	  /* Record the fact that we've searched the data already in
             the buffer for a CRLF, so that when we search below, we
             don't have to search the same data twice.  There's a "-
             1" here to account for the fact that the last character
             of the data we have may be the CR of a CRLF pair, of
             which we haven't read the second half yet, so we may have
             to search it again when we read more data. */
	  search_offset = server->data - 1;
Richard M. Stallman's avatar
Richard M. Stallman committed
1270 1271 1272 1273 1274 1275 1276 1277
	  server->buffer_index = 0;
	}
    }
  else
    {
      server->buffer_index = 0;
    }

1278
  while (true)
Richard M. Stallman's avatar
Richard M. Stallman committed
1279
    {
1280 1281 1282 1283
      /* There's a "- 1" here to leave room for the null that we put
         at the end of the read data below.  We put the null there so
         that find_crlf knows where to stop when we call it. */
      if (server->data == server->buffer_size - 1)
Richard M. Stallman's avatar
Richard M. Stallman committed
1284
	{
1285 1286 1287
	  server->buffer_size += GETLINE_INCR;
	  server->buffer = (char *)realloc (server->buffer, server->buffer_size);
	  if (! server->buffer)
Richard M. Stallman's avatar
Richard M. Stallman committed
1288
	    {
1289 1290
	      strcpy (pop_error, "Out of memory in pop_getline");
	      pop_trash (server);
1291
	      break;
1292 1293
	    }
	}
1294 1295
      ret = RECV (server->file, server->buffer + server->data,
		  server->buffer_size - server->data - 1, 0);
Richard M. Stallman's avatar
Richard M. Stallman committed
1296 1297
      if (ret < 0)
	{
1298 1299
	  snprintf (pop_error, ERROR_MAX, "%s%s",
		    GETLINE_ERROR, strerror (errno));
Richard M. Stallman's avatar
Richard M. Stallman committed
1300
	  pop_trash (server);
1301
	  break;
Richard M. Stallman's avatar
Richard M. Stallman committed
1302 1303 1304
	}
      else if (ret == 0)
	{
1305
	  strcpy (pop_error, "Unexpected EOF from server in pop_getline");
Richard M. Stallman's avatar
Richard M. Stallman committed
1306
	  pop_trash (server);
1307
	  break;
Richard M. Stallman's avatar
Richard M. Stallman committed
1308 1309 1310
	}
      else
	{
1311
	  char *cp;
Richard M. Stallman's avatar
Richard M. Stallman committed
1312
	  server->data += ret;
1313
	  server->buffer[server->data] = '\0';
1314

1315 1316
	  cp = find_crlf (server->buffer + search_offset,
			  server->data - search_offset);
Richard M. Stallman's avatar
Richard M. Stallman committed
1317 1318 1319 1320 1321 1322 1323 1324 1325
	  if (cp)
	    {
	      int data_used = (cp + 2) - server->buffer;
	      *cp = '\0';
	      server->data -= data_used;
	      server->buffer_index = data_used;

	      if (pop_debug)
		fprintf (stderr, "<<< %s\n", server->buffer);
1326 1327
	      *line = server->buffer;
	      return (data_used - 2);
Richard M. Stallman's avatar
Richard M. Stallman committed
1328
	    }
1329 1330 1331
	  /* As above, the "- 1" here is to account for the fact that
	     we may have read a CR without its accompanying LF. */
	  search_offset += ret - 1;
Richard M. Stallman's avatar
Richard M. Stallman committed
1332 1333 1334
	}
    }

1335
  return -1;
Richard M. Stallman's avatar
Richard M. Stallman committed
1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356
}

/*
 * Function: sendline
 *
 * Purpose: Sends a line of text to the POP server.  The line of text
 * 	passed into this function should NOT have the carriage return
 * 	and linefeed on the end of it.  Periods at beginnings of lines
 * 	will NOT be treated specially by this function.
 *
 * Arguments:
 * 	server	The server to which to send the text.
 * 	line	The line of text to send.
 *
 * Return value: Upon successful completion, a value of 0 will be
 * 	returned.  Otherwise, a non-zero value will be returned, and
 * 	an error will be copied into pop_error.
 *
 * Side effects: Closes the connection on error.
 */
static int
1357
sendline (popserver server, const char *line)
Richard M. Stallman's avatar
Richard M. Stallman committed
1358 1359 1360
{
#define SENDLINE_ERROR "Error writing to POP server: "
  int ret;
1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375
  char *buf;

  /* Combine the string and the CR-LF into one buffer.  Otherwise, two
     reasonable network stack optimizations, Nagle's algorithm and
     delayed acks, combine to delay us a fraction of a second on every
     message we send.  (Movemail writes line without \r\n, client
     kernel sends packet, server kernel delays the ack to see if it
     can combine it with data, movemail writes \r\n, client kernel
     waits because it has unacked data already in its outgoing queue,
     client kernel eventually times out and sends.)

     This can be something like 0.2s per command, which can add up
     over a few dozen messages, and is a big chunk of the time we
     spend fetching mail from a server close by.  */
  buf = alloca (strlen (line) + 3);
Paul Eggert's avatar
Paul Eggert committed
1376
  strcpy (stpcpy (buf, line), "\r\n");
1377
  ret = fullwrite (server->file, buf, strlen (buf));
Richard M. Stallman's avatar
Richard M. Stallman committed
1378 1379 1380 1381

  if (ret < 0)
    {
      pop_trash (server);
1382
      snprintf (pop_error, ERROR_MAX, "%s%s", SENDLINE_ERROR, strerror (errno));
Richard M. Stallman's avatar
Richard M. Stallman committed
1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400
      return (ret);
    }

  if (pop_debug)
    fprintf (stderr, ">>> %s\n", line);

  return (0);
}

/*
 * Procedure: fullwrite
 *
 * Purpose: Just like write, but keeps trying until the entire string
 * 	has been written.
 *
 * Return value: Same as write.  Pop_error is not set.
 */
static int
1401
fullwrite (int fd, char *buf, int nbytes)
Richard M. Stallman's avatar
Richard M. Stallman committed
1402 1403
{
  char *cp;
1404
  int ret = 0;
Richard M. Stallman's avatar
Richard M. Stallman committed
1405 1406

  cp = buf;
1407
  while (nbytes && ((ret = SEND (fd, cp, nbytes, 0)) > 0))
Richard M. Stallman's avatar
Richard M. Stallman committed
1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424
    {
      cp += ret;
      nbytes -= ret;
    }

  return (ret);
}

/*
 * Procedure getok
 *
 * Purpose: Reads a line from the server.  If the return indicator is
 * 	positive, return with a zero exit status.  If not, return with
 * 	a negative exit status.
 *
 * Arguments:
 * 	server	The server to read from.
1425
 *
Richard M. Stallman's avatar
Richard M. Stallman committed
1426 1427
 * Returns: 0 for success, else for failure and puts error in pop_error.
 *
Karl Heuer's avatar
Karl Heuer committed
1428
 * Side effects: On failure, may make the connection unusable.
Richard M. Stallman's avatar
Richard M. Stallman committed
1429 1430
 */
static int
1431
getok (popserver server)
Richard M. Stallman's avatar
Richard M. Stallman committed
1432 1433 1434
{
  char *fromline;

1435
  if (pop_getline (server, &fromline) < 0)
Richard M. Stallman's avatar
Richard M. Stallman committed
1436 1437 1438 1439 1440 1441 1442 1443
    {
      return (-1);
    }

  if (! strncmp (fromline, "+OK", 3))
    return (0);
  else if (! strncmp (fromline, "-ERR", 4))
    {
1444
      snprintf (pop_error, ERROR_MAX, "%s", fromline);
Richard M. Stallman's avatar
Richard M. Stallman committed
1445 1446 1447 1448 1449 1450 1451 1452 1453
      return (-1);
    }
  else
    {
      strcpy (pop_error,
	      "Unexpected response from server; expecting +OK or -ERR");
      pop_trash (server);
      return (-1);
    }
1454
}
Richard M. Stallman's avatar
Richard M. Stallman committed
1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472

#if 0
/*
 * Function: gettermination
 *
 * Purpose: Gets the next line and verifies that it is a termination
 * 	line (nothing but a dot).
 *
 * Return value: 0 on success, non-zero with pop_error set on error.
 *
 * Side effects: Closes the connection on error.
 */
static int
gettermination (server)
     popserver server;
{
  char *fromserver;

1473
  if (pop_getline (server, &fromserver) < 0)
Richard M. Stallman's avatar
Richard M. Stallman committed
1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495
    return (-1);

  if (strcmp (fromserver, "."))
    {
      strcpy (pop_error,
	      "Unexpected response from server in gettermination");
      pop_trash (server);
      return (-1);
    }

  return (0);
}
#endif

/*
 * Function pop_close
 *
 * Purpose: Close a pop connection, sending a "RSET" command to try to
 * 	preserve any changes that were made and a "QUIT" command to
 * 	try to get the server to quit, but ignoring any responses that
 * 	are received.
 *
Karl Heuer's avatar
Karl Heuer committed
1496
 * Side effects: The server is unusable after this function returns.
Richard M. Stallman's avatar
Richard M. Stallman committed
1497 1498 1499
 * 	Changes made to the maildrop since the session was started (or
 * 	since the last pop_reset) may be lost.
 */
1500
void
1501
pop_close (popserver server)
Richard M. Stallman's avatar
Richard M. Stallman committed
1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512
{
  pop_trash (server);
  free ((char *) server);

  return;
}

/*
 * Function: pop_trash
 *
 * Purpose: Like pop_close or pop_quit, but doesn't deallocate the
1513
 * 	memory associated with the server.  It is valid to call
Richard M. Stallman's avatar
Richard M. Stallman committed
1514 1515 1516
 * 	pop_close or pop_quit after this function has been called.
 */
static void
1517
pop_trash (popserver server)
Richard M. Stallman's avatar
Richard M. Stallman committed
1518 1519 1520
{
  if (server->file >= 0)
    {
1521 1522 1523
      /* avoid recursion; sendline can call pop_trash */
      if (server->trash_started)
	return;
1524
      server->trash_started = true;
1525

Richard M. Stallman's avatar
Richard M. Stallman committed
1526 1527 1528
      sendline (server, "RSET");
      sendline (server, "QUIT");

1529
      CLOSESOCKET (server->file);
Richard M. Stallman's avatar
Richard M. Stallman committed
1530 1531 1532 1533 1534 1535 1536
      server->file = -1;
      if (server->buffer)
	{
	  free (server->buffer);
	  server->buffer = 0;
	}
    }
1537 1538 1539 1540 1541

#ifdef WINDOWSNT
  if (have_winsock)
    WSACleanup ();
#endif
Richard M. Stallman's avatar
Richard M. Stallman committed
1542 1543
}

1544 1545 1546
/* Return a pointer to the first CRLF in IN_STRING, which can contain
   embedded nulls and has LEN characters in it not including the final
   null, or 0 if it does not contain one.  */
Richard M. Stallman's avatar
Richard M. Stallman committed
1547 1548

static char *
1549
find_crlf (char *in_string, int len)
Richard M. Stallman's avatar
Richard M. Stallman committed
1550
{
1551
  while (len--)
Richard M. Stallman's avatar
Richard M. Stallman committed
1552
    {
1553
      if (*in_string == '\r')
1554 1555 1556 1557 1558 1559
	{
	  if (*++in_string == '\n')
	    return (in_string - 1);
	}
      else
	in_string++;
Richard M. Stallman's avatar
Richard M. Stallman committed
1560
    }
1561
  return (0);
Richard M. Stallman's avatar
Richard M. Stallman committed
1562 1563
}

1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702
#ifdef WINDOWSNT
/* The following 2 functions are only available since XP, so we load
   them dynamically and provide fallbacks.  */

int (WINAPI *pfn_getaddrinfo) (const char *, const char *,
			       const struct addrinfo *, struct addrinfo **);
void (WINAPI *pfn_freeaddrinfo) (struct addrinfo *);

static int
load_ws2 (void)
{
  static int ws2_loaded = 0;

  if (!ws2_loaded)
    {
      HANDLE ws2_lib = LoadLibrary ("Ws2_32.dll");

      if (ws2_lib != NULL)
	{
	  ws2_loaded = 1;
	  pfn_getaddrinfo = (void *) GetProcAddress (ws2_lib, "getaddrinfo");
	  pfn_freeaddrinfo = (void *) GetProcAddress (ws2_lib, "freeaddrinfo");
	  /* Paranoia: these two functions should go together, so if
	     one is absent, we cannot use the other.  */
	  if (pfn_getaddrinfo == NULL)
	    pfn_freeaddrinfo = NULL;
	  else if (pfn_freeaddrinfo == NULL)
	    pfn_getaddrinfo = NULL;
	}
    }
  if (!ws2_loaded)
    {
      errno = ENETDOWN;
      return -1;
    }
  return 0;
}


int
sys_getaddrinfo (const char *node, const char *service,
		 const struct addrinfo *hints, struct addrinfo **res)
{
  int rc;

  if (load_ws2 () != 0)
    {
      errno = ENETDOWN;
      return WSANO_RECOVERY;
    }

  if (pfn_getaddrinfo)
    rc = pfn_getaddrinfo (node, service, hints, res);
  else
    {
      int port = 0;
      struct hostent *host_info;
      struct gai_storage {
	struct addrinfo addrinfo;
	struct sockaddr_in sockaddr_in;
      } *gai_storage;

      /* We don't support any flags besides AI_CANONNAME.  */
      if (hints && (hints->ai_flags & ~(AI_CANONNAME)) != 0)
	return WSAEINVAL;
      /* NODE cannot be NULL, since pop.c has fallbacks for that.  */
      if (!node)
	return WSAHOST_NOT_FOUND;

      if (service)
	{
	  const char *protocol =
	    (hints && hints->ai_socktype == SOCK_DGRAM) ? "udp" : "tcp";
	  struct servent *srv = getservbyname (service, protocol);

	  if (srv)
	    port = srv->s_port;
	  else
	    return WSAHOST_NOT_FOUND;
	}

      gai_storage = calloc (1, sizeof *gai_storage);
      gai_storage->sockaddr_in.sin_port = port;
      host_info = gethostbyname (node);
      if (host_info)
	{
	  memcpy (&gai_storage->sockaddr_in.sin_addr,
		  host_info->h_addr, host_info->h_length);
	  gai_storage->sockaddr_in.sin_family = host_info->h_addrtype;
	}
      else
	{
	  free (gai_storage);
	  return WSAHOST_NOT_FOUND;
	}

      gai_storage->addrinfo.ai_addr =
	(struct sockaddr *)&gai_storage->sockaddr_in;
      gai_storage->addrinfo.ai_addrlen = sizeof (gai_storage->sockaddr_in);
      if (hints && (hints->ai_flags & AI_CANONNAME) != 0)
	{
	  gai_storage->addrinfo.ai_canonname = strdup (host_info->h_name);
	  if (!gai_storage->addrinfo.ai_canonname)
	    {
	      free (gai_storage);
	      return WSA_NOT_ENOUGH_MEMORY;
	    }
	}
      gai_storage->addrinfo.ai_protocol = (hints) ? hints->ai_protocol : 0;
      gai_storage->addrinfo.ai_socktype = (hints) ? hints->ai_socktype : 0;
      gai_storage->addrinfo.ai_family = gai_storage->sockaddr_in.sin_family;
      gai_storage->addrinfo.ai_next = NULL;

      *res = &gai_storage->addrinfo;
      rc = 0;
    }

  return rc;
}

void
sys_freeaddrinfo (struct addrinfo *ai)
{
  if (load_ws2 () != 0)
    {
      errno = ENETDOWN;
      return;
    }

  if (pfn_freeaddrinfo)
    pfn_freeaddrinfo (ai);
  else
    {
      if (ai->ai_canonname)
	free (ai->ai_canonname);
      free (ai);
    }
}
#endif	/* WINDOWSNT */
Richard M. Stallman's avatar
Richard M. Stallman committed
1703
#endif /* MAIL_USE_POP */