示例#1
0
文件: imap4.c 项目: aberg001/plan9
//
// dial and handshake with the imap server
//
static char*
imap4dial(Imap *imap)
{
    char *err, *port;
    int sfd;
    TLSconn conn;

    if(imap->fd >= 0) {
        imap4cmd(imap, "noop");
        if(isokay(imap4resp(imap)))
            return nil;
        close(imap->fd);
        imap->fd = -1;
    }

    if(imap->mustssl)
        port = "imaps";
    else
        port = "imap";

    if((imap->fd = dial(netmkaddr(imap->host, "net", port), 0, 0, 0)) < 0)
        return imaperrstr(imap->host, port);

    if(imap->mustssl) {
        sfd = starttls(imap, &conn);
        if (sfd < 0) {
            free(conn.cert);
            return imaperrstr(imap->host, port);
        }
        if(imap->debug) {
            char fn[128];
            int fd;

            snprint(fn, sizeof fn, "%s/ctl", conn.dir);
            fd = open(fn, ORDWR);
            if(fd < 0)
                fprint(2, "opening ctl: %r\n");
            if(fprint(fd, "debug") < 0)
                fprint(2, "writing ctl: %r\n");
            close(fd);
        }
    }
    Binit(&imap->bin, imap->fd, OREAD);
    Binit(&imap->bout, imap->fd, OWRITE);

    if(err = imap4login(imap)) {
        close(imap->fd);
        return err;
    }

    return nil;
}
示例#2
0
static int cmd_STARTTLS(void)
{
  if (!respond(&resp_start))
    return 0;

  if (!starttls())
    return 0;

  tls_available = 0;
  session_setnum("tls_state", 1);

  /* Remove UCSPITLS from environment to indicate it no longer available. */
  unsetenv("UCSPITLS");
  return 1;
}
示例#3
0
int do_imap_command(const char *tag)
{
	struct	imaptoken *curtoken=nexttoken();
	char authservice[40];

#if SMAP
	if (strcmp(tag, "\\SMAP1") == 0)
	{
		const char *p=getenv("SMAP_CAPABILITY");

		if (p && *p)
			putenv("PROTOCOL=SMAP1");
		else
			return -1;
	}
#endif

	courier_authdebug_login( 1, "command=%s", curtoken->tokenbuf );

	if (strcmp(curtoken->tokenbuf, "LOGOUT") == 0)
	{
		if (nexttoken()->tokentype != IT_EOL)   return (-1);
		writes("* BYE Courier-IMAP server shutting down\r\n");
		cmdsuccess(tag, "LOGOUT completed\r\n");
		writeflush();
		fprintf(stderr, "INFO: LOGOUT, ip=[%s], rcvd=%lu, sent=%lu\n",
			getenv("TCPREMOTEIP"), bytes_received_count, bytes_sent_count);
		exit(0);
	}
	if (strcmp(curtoken->tokenbuf, "NOOP") == 0)
	{
		if (nexttoken()->tokentype != IT_EOL)	return (-1);
		cmdsuccess(tag, "NOOP completed\r\n");
		return (0);
	}
	if (strcmp(curtoken->tokenbuf, "CAPABILITY") == 0)
	{
		if (nexttoken()->tokentype != IT_EOL)	return (-1);

		writes("* CAPABILITY ");
		imapcapability();
		writes("\r\n");
		cmdsuccess(tag, "CAPABILITY completed\r\n");
		return (0);
	}

	if (strcmp(curtoken->tokenbuf, "STARTTLS") == 0)
	{
		if (!have_starttls())	return (-1);
		if (starttls(tag))		return (-2);
		putenv("IMAP_STARTTLS=NO");
		putenv("IMAP_TLS_REQUIRED=0");
		putenv("IMAP_TLS=1");

		return (0);
	}

	if (strcmp(curtoken->tokenbuf, "LOGIN") == 0)
	{
	struct imaptoken *tok=nexttoken_nouc();
	char	*userid;
	char	*passwd;
	const char *p;
	int	rc;

		if (have_starttls() && tlsrequired())	/* Not yet */
		{
			cmdfail(tag, "STARTTLS required\r\n");
			return (0);
		}

		switch (tok->tokentype)	{
		case IT_ATOM:
		case IT_NUMBER:
		case IT_QUOTED_STRING:
			break;
		default:
			return (-1);
		}

		userid=strdup(tok->tokenbuf);
		if (!userid)
			write_error_exit(0);
		tok=nexttoken_nouc_okbracket();
		switch (tok->tokentype)	{
		case IT_ATOM:
		case IT_NUMBER:
		case IT_QUOTED_STRING:
			break;
		default:
			free(userid);
			return (-1);
		}

		passwd=my_strdup(tok->tokenbuf);

		if (nexttoken()->tokentype != IT_EOL)
		{
			free(userid);
			free(passwd);
			return (-1);
		}

		strcat(strcpy(authservice, "AUTHSERVICE"),
		       getenv("TCPLOCALPORT"));

		p=getenv(authservice);

		if (!p || !*p)
			p="imap";

		rc=auth_login(p, userid, passwd, login_callback, (void *)tag);
		courier_safe_printf("INFO: LOGIN FAILED, user=%s, ip=[%s]",
				  userid, getenv("TCPREMOTEIP"));
		free(userid);
		free(passwd);
		if (rc > 0)
		{
			perror("ERR: authentication error");
			writes("* BYE Temporary problem, please try again later\r\n");
			writeflush();
			exit(1);
		}
		sleep(5);
		cmdfail(tag, "Login failed.\r\n");
		return (0);
	}

	if (strcmp(curtoken->tokenbuf, "AUTHENTICATE") == 0)
	{
	char	method[32];
	int	rc;

		if (have_starttls() && tlsrequired())	/* Not yet */
		{
			cmdfail(tag, "STARTTLS required\r\n");
			return (0);
		}
		rc=authenticate(tag, method, sizeof(method));
		courier_safe_printf("INFO: LOGIN FAILED, method=%s, ip=[%s]",
				  method, getenv("TCPREMOTEIP"));
		if (rc > 0)
		{
			perror("ERR: authentication error");
			writes("* BYE Temporary problem, please try again later\r\n");
			writeflush();
			exit(1);
		}
		sleep(5);
		cmdfail(tag, "Login failed.\r\n");
		writeflush();
		return (-2);
	}

	return (-1);
}
示例#4
0
int main(int argc, char **argv)
{
char	*user=0;
char	*p;
char	buf[BUFSIZ];
int	c;
const	char *ip=getenv("TCPREMOTEIP");
char authservice[40];
char *q ;

#ifdef HAVE_SETVBUF_IOLBF
	setvbuf(stderr, NULL, _IOLBF, BUFSIZ);
#endif

	if (!ip || !*ip)
	{
		ip="127.0.0.1";
	}

	if (argc != 3)
	{
		printf("-ERR pop3login requires exactly two arguments.\r\n");
		fflush(stdout);
		exit(1);
	}

	pop3d=argv[1];
	defaultmaildir=argv[2];

	courier_authdebug_login_init();

	fprintf(stderr, "DEBUG: Connection, ip=[%s]\n", ip);
	printf("+OK Hello there.\r\n");

	fflush(stdout);
	fflush(stderr);
	alarm(60);
	while (fgets(buf, sizeof(buf), stdin))
	{
		c=1;
		for (p=buf; *p; p++)
		{
			if (*p == '\n')
				break;

			if (*p == ' ' || *p == '\t')	c=0;
			if (c)
				*p=toupper((int)(unsigned char)*p);
		}

		if (*p)
			*p=0;
		else while ((c=getchar()) != EOF && c != '\n')
			;
		p=strtok(buf, " \t\r");
		if (p)
		{
			courier_authdebug_login( 1, "command=%s", p );

			if ( strcmp(p, "QUIT") == 0)
			{
				fprintf(stderr, "INFO: LOGOUT, ip=[%s]\n",
					ip);
				fflush(stderr);
				printf("+OK Better luck next time.\r\n");
				fflush(stdout);
				break;
			}

			if ( strcmp(p, "USER") == 0)
			{
				if (tls_required())
				{
					printf("-ERR TLS required to log in.\r\n");
					fflush(stdout);
					continue;
				}

				p=strtok(0, "\r\n");
				if (p)
				{
					if (user)	free(user);
					if ((user=malloc(strlen(p)+1)) == 0)
					{
						printf("-ERR Server out of memory, aborting connection.\r\n");
						fflush(stdout);
						perror("malloc");
						exit(1);
					}
					strcpy(user, p);
					printf("+OK Password required.\r\n");
					fflush(stdout);
					continue;
				}
			} else if (strcmp(p, "CAPA") == 0)
			{
				pop3dcapa();
				continue;
			} else if (strcmp(p, "STLS") == 0)
			{
				if (!have_starttls())
				{
					printf("-ERR TLS support not available.\r\n");
					fflush(stdout);
					continue;
				}
				starttls();
				fflush(stdout);
				continue;
			} else if (strcmp(p, "AUTH") == 0)
			{
				char *authtype, *authdata;
				char	*method=strtok(0, " \t\r");

				if (tls_required())
				{
					printf("-ERR TLS required to log in.\r\n");
					fflush(stdout);
					continue;
				}

				if (method)
				{
					char *initreply=strtok(0, " \t\r");
					int	rc;
					char *p;

					for (p=method; *p; p++)
						*p=toupper(*p);

					if (initreply &&
					    strcmp(initreply, "=") == 0)
						initreply="";

					rc=auth_sasl_ex(method, initreply,
							pop3_externalauth(),
							authresp,
							NULL,
							&authtype,
							&authdata);

					if (rc == 0)
					{
						strcat(strcpy(authservice, "AUTHSERVICE"),getenv("TCPLOCALPORT"));
						q=getenv(authservice);
						if (!q || !*q)
							q="pop3";

						rc=auth_generic(q,
							     authtype,
							     authdata,
							     login_callback,
							     NULL);
						free(authtype);
						free(authdata);
					}

					courier_safe_printf("INFO: LOGIN "
						"FAILED, method=%s, ip=[%s]",
						method, ip);
					if (rc == AUTHSASL_ABORTED)
					    printf("-ERR Authentication aborted.\r\n");
					else if (rc > 0)
					{
					    perror("ERR: authentication error");
					    printf("-ERR Temporary problem, please try again later\r\n");
					    fflush(stdout);
					    exit(1);
					}					
					else
					{
					    sleep(5);
					    printf("-ERR Authentication failed.\r\n");
					}

					fflush(stdout);
					continue;
				}
			} else if (strcmp(p, "PASS") == 0)
			{
				int rc;

				p=strtok(0, "\r\n");

				if (!user || p == 0)
				{
					printf("-ERR USER/PASS required.\r\n");
					fflush(stdout);
					continue;
				}

				strcat(strcpy(authservice, "AUTHSERVICE"),getenv("TCPLOCALPORT"));
				q=getenv(authservice);
				if (!q || !*q)
					q="pop3";

				rc=auth_login(q, user, p, login_callback, NULL);
				courier_safe_printf("INFO: LOGIN "
					"FAILED, user=%s, ip=[%s]",
					user, ip);
				if (rc > 0)
				{
					perror("ERR: authentication error");
					printf("-ERR Temporary problem, please try again later\r\n");
					fflush(stdout);
					exit(1);
				}
				sleep(5);
				printf("-ERR Login failed.\r\n");
				fflush(stdout);
				continue;
			}
		}
		printf("-ERR Invalid command.\r\n");
		fflush(stdout);
	}
	fprintf(stderr, "DEBUG: Disconnected, ip=[%s]\n", ip);
	exit(0);
	return (0);
}
示例#5
0
文件: gsasl.c 项目: cktan/toolchain
int
main (int argc, char *argv[])
{
  Gsasl *ctx = NULL;
  int res;
  char *in;
  char *connect_hostname = NULL;
  char *connect_service = NULL;
#ifdef HAVE_LIBGNUTLS
  gnutls_anon_client_credentials anoncred;
  gnutls_certificate_credentials x509cred;
#endif

  set_program_name (argv[0]);
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  /* This is necessary for modern MinGW compilers that provide working
     getaddrinfo function, which results in gnulib not detecting that
     it is broken.  The proper fix is for gnulib to wrap the
     getaddrinfo call and initialize Windows sockets in the
     wrapper.  */
  (void) gl_sockets_startup (SOCKETS_1_1);

  if (cmdline_parser (argc, argv, &args_info) != 0)
    return EXIT_FAILURE;

  if (args_info.version_given)
    {
      const char *p = PACKAGE_NAME;
      if (strcmp (gsasl_check_version (NULL), PACKAGE_VERSION) != 0)
	p = PACKAGE_STRING;
      version_etc (stdout, "gsasl", p, gsasl_check_version (NULL),
		   "Simon Josefsson", (char *) NULL);
      return EXIT_SUCCESS;
    }

  if (args_info.help_given)
    usage (EXIT_SUCCESS);

  if (!(args_info.client_flag || args_info.client_given) &&
      !args_info.server_given &&
      !args_info.client_mechanisms_flag && !args_info.server_mechanisms_flag)
    {
      error (0, 0, _("missing argument"));
      usage (EXIT_FAILURE);
    }

  if ((args_info.x509_cert_file_arg && !args_info.x509_key_file_arg) ||
      (!args_info.x509_cert_file_arg && args_info.x509_key_file_arg))
    error (EXIT_FAILURE, 0,
	   _("need both --x509-cert-file and --x509-key-file"));

  if (args_info.starttls_flag && args_info.no_starttls_flag)
    error (EXIT_FAILURE, 0,
	   _("cannot use both --starttls and --no-starttls"));

  if (args_info.smtp_flag && args_info.imap_flag)
    error (EXIT_FAILURE, 0, _("cannot use both --smtp and --imap"));

  if (!args_info.connect_given && args_info.inputs_num == 0 &&
      !args_info.client_given && !args_info.server_given &&
      !args_info.client_mechanisms_flag && !args_info.server_mechanisms_flag)
    {
      cmdline_parser_print_help ();
      emit_bug_reporting_address ();
      return EXIT_SUCCESS;
    }

  if (args_info.connect_given)
    {
      if (strrchr (args_info.connect_arg, ':'))
	{
	  connect_hostname = xstrdup (args_info.connect_arg);
	  *strrchr (connect_hostname, ':') = '\0';
	  connect_service =
	    xstrdup (strrchr (args_info.connect_arg, ':') + 1);
	}
      else
	{
	  connect_hostname = xstrdup (args_info.connect_arg);
	  if (args_info.smtp_flag)
	    connect_service = xstrdup ("smtp");
	  else
	    connect_service = xstrdup ("imap");
	}
    }
  else if (args_info.inputs_num > 0)
    {
      connect_hostname = args_info.inputs[0];
      if (args_info.inputs_num > 1)
	connect_service = args_info.inputs[1];
      else if (args_info.smtp_flag)
	connect_service = xstrdup ("smtp");
      else
	connect_service = xstrdup ("imap");
    }

  if (connect_service && !args_info.smtp_flag && !args_info.imap_flag)
    {
      if (strcmp (connect_service, "25") == 0 ||
	  strcmp (connect_service, "smtp") == 0)
	args_info.smtp_flag = 1;
      else
	args_info.imap_flag = 1;
    }

  if (args_info.imap_flag && !args_info.service_given)
    args_info.service_arg = xstrdup ("imap");

  if (args_info.smtp_flag && !args_info.service_given)
    args_info.service_arg = xstrdup ("smtp");

  if (args_info.imap_flag || args_info.smtp_flag)
    args_info.no_client_first_flag = 1;

  if (connect_hostname && !args_info.hostname_arg)
    args_info.hostname_arg = xstrdup (connect_hostname);

  if (!isatty (STDOUT_FILENO))
    setvbuf (stdout, NULL, _IOLBF, BUFSIZ);

  res = gsasl_init (&ctx);
  if (res != GSASL_OK)
    error (EXIT_FAILURE, 0, _("initialization failure: %s"),
	   gsasl_strerror (res));

  gsasl_callback_set (ctx, callback);

  if (args_info.client_mechanisms_flag || args_info.server_mechanisms_flag)
    {
      char *mechs;

      if (args_info.client_mechanisms_flag)
	res = gsasl_client_mechlist (ctx, &mechs);
      else
	res = gsasl_server_mechlist (ctx, &mechs);

      if (res != GSASL_OK)
	error (EXIT_FAILURE, 0, _("error listing mechanisms: %s"),
	       gsasl_strerror (res));

      if (!args_info.quiet_given)
	{
	  if (args_info.client_mechanisms_flag)
	    fprintf (stderr,
		     _("This client supports the following mechanisms:\n"));
	  else
	    fprintf (stderr,
		     _("This server supports the following mechanisms:\n"));
	}

      fprintf (stdout, "%s\n", mechs);

      free (mechs);

      return EXIT_SUCCESS;
    }

  if (args_info.connect_given || args_info.inputs_num > 0)
    {
      struct addrinfo hints;
      struct addrinfo *ai0, *ai;

      memset (&hints, 0, sizeof (hints));
      hints.ai_flags = AI_CANONNAME;
      hints.ai_socktype = SOCK_STREAM;
      res = getaddrinfo (connect_hostname, connect_service, &hints, &ai0);
      if (res != 0)
	error (EXIT_FAILURE, 0, "%s: %s", connect_hostname,
	       gai_strerror (res));

      for (ai = ai0; ai; ai = ai->ai_next)
	{
	  fprintf (stderr, "Trying %s...\n", quote (ai->ai_canonname ?
						    ai->ai_canonname :
						    connect_hostname));

	  sockfd = socket (ai->ai_family, ai->ai_socktype, ai->ai_protocol);
	  if (sockfd < 0)
	    {
	      error (0, errno, "socket");
	      continue;
	    }

	  if (connect (sockfd, ai->ai_addr, ai->ai_addrlen) < 0)
	    {
	      int save_errno = errno;
	      close (sockfd);
	      sockfd = -1;
	      error (0, save_errno, "connect");
	      continue;
	    }
	  break;
	}

      if (sockfd < 0)
	error (EXIT_FAILURE, errno, "socket");

      freeaddrinfo (ai);
    }

  if (!greeting ())
    return 1;

#ifdef HAVE_LIBGNUTLS
  if (sockfd && !args_info.no_starttls_flag &&
      (args_info.starttls_flag || has_starttls ()))
    {
      res = gnutls_global_init ();
      if (res < 0)
	error (EXIT_FAILURE, 0, _("GnuTLS global initialization failed: %s"),
	       gnutls_strerror (res));

      res = gnutls_init (&session, GNUTLS_CLIENT);
      if (res < 0)
	error (EXIT_FAILURE, 0, _("GnuTLS initialization failed: %s"),
	       gnutls_strerror (res));

      res = gnutls_set_default_priority (session);
      if (res < 0)
	error (EXIT_FAILURE, 0, _("setting GnuTLS defaults failed: %s"),
	       gnutls_strerror (res));

      res = gnutls_anon_allocate_client_credentials (&anoncred);
      if (res < 0)
	error (EXIT_FAILURE, 0,
	       _("allocating anonymous GnuTLS credential: %s"),
	       gnutls_strerror (res));

      res = gnutls_credentials_set (session, GNUTLS_CRD_ANON, anoncred);
      if (res < 0)
	error (EXIT_FAILURE, 0, _("setting anonymous GnuTLS credential: %s"),
	       gnutls_strerror (res));

      res = gnutls_certificate_allocate_credentials (&x509cred);
      if (res < 0)
	error (EXIT_FAILURE, 0, _("allocating X.509 GnuTLS credential: %s"),
	       gnutls_strerror (res));

      if (args_info.x509_cert_file_arg && args_info.x509_key_file_arg)
	res = gnutls_certificate_set_x509_key_file
	  (x509cred, args_info.x509_cert_file_arg,
	   args_info.x509_key_file_arg, GNUTLS_X509_FMT_PEM);
      if (res != GNUTLS_E_SUCCESS)
	error (EXIT_FAILURE, 0, _("loading X.509 GnuTLS credential: %s"),
	       gnutls_strerror (res));

      if (args_info.x509_ca_file_arg)
	{
	  res = gnutls_certificate_set_x509_trust_file
	    (x509cred, args_info.x509_ca_file_arg, GNUTLS_X509_FMT_PEM);
	  if (res < 0)
	    error (EXIT_FAILURE, 0, _("no X.509 CAs found: %s"),
		   gnutls_strerror (res));
	  if (res == 0)
	    error (EXIT_FAILURE, 0, _("no X.509 CAs found"));
	}

      res = gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE,
				    x509cred);
      if (res < 0)
	error (EXIT_FAILURE, 0, _("setting X.509 GnuTLS credential: %s"),
	       gnutls_strerror (res));

      if (args_info.priority_arg)
	{
	  const char *err_pos;

	  res = gnutls_priority_set_direct (session, args_info.priority_arg,
					    &err_pos);
	  if (res < 0)
	    error (EXIT_FAILURE, 0,
		   _("setting GnuTLS cipher priority (%s): %s\n"),
		   gnutls_strerror (res), err_pos);
	}

      gnutls_transport_set_ptr (session, (gnutls_transport_ptr)
				(unsigned long) sockfd);

      if (!starttls ())
	return 1;

      res = gnutls_handshake (session);
      if (res < 0)
	error (EXIT_FAILURE, 0, _("GnuTLS handshake failed: %s"),
	       gnutls_strerror (res));

      if (args_info.x509_ca_file_arg)
	{
	  unsigned int status;

	  res = gnutls_certificate_verify_peers2 (session, &status);
	  if (res < 0)
	    error (EXIT_FAILURE, 0, _("verifying peer certificate: %s"),
		   gnutls_strerror (res));

	  if (status & GNUTLS_CERT_INVALID)
	    error (EXIT_FAILURE, 0, _("server certificate is not trusted"));

	  if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
	    error (EXIT_FAILURE, 0,
		   _("server certificate hasn't got a known issuer"));

	  if (status & GNUTLS_CERT_REVOKED)
	    error (EXIT_FAILURE, 0, _("server certificate has been revoked"));

	  if (status != 0)
	    error (EXIT_FAILURE, 0,
		   _("could not verify server certificate (rc=%d)"), status);
	}

#if HAVE_GNUTLS_SESSION_CHANNEL_BINDING
      if (!args_info.no_cb_flag)
	{
	  gnutls_datum cb;

	  res = gnutls_session_channel_binding (session,
						GNUTLS_CB_TLS_UNIQUE, &cb);
	  if (res != GNUTLS_E_SUCCESS)
	    error (EXIT_FAILURE, 0, _("getting channel binding failed: %s"),
		   gnutls_strerror (res));

	  res = gsasl_base64_to ((char *) cb.data, cb.size,
				 &b64cbtlsunique, NULL);
	  if (res != GSASL_OK)
	    error (EXIT_FAILURE, 0, "%s", gsasl_strerror (res));
	}
#endif

      using_tls = true;
    }
#endif

  if (args_info.client_flag || args_info.client_given
      || args_info.server_given)
    {
      char *out;
      char *b64output;
      size_t output_len;
      size_t b64output_len;
      const char *mech;
      Gsasl_session *xctx = NULL;

      if (!select_mechanism (&in))
	return 1;

      mech = gsasl_client_suggest_mechanism (ctx, in);
      if (mech == NULL)
	{
	  fprintf (stderr, _("Cannot find mechanism...\n"));
	  return 0;
	}

      if (args_info.mechanism_arg)
	mech = args_info.mechanism_arg;

      if (!authenticate (mech))
	return 1;

      /* Authenticate using mechanism */

      if (args_info.server_flag)
	res = gsasl_server_start (ctx, mech, &xctx);
      else
	res = gsasl_client_start (ctx, mech, &xctx);
      if (res != GSASL_OK)
	error (EXIT_FAILURE, 0, _("mechanism unavailable: %s"),
	       gsasl_strerror (res));

      in = NULL;
      out = NULL;

      if (!args_info.server_flag && args_info.no_client_first_flag)
	{
	  res = GSASL_NEEDS_MORE;
	  goto no_client_first;
	}

      do
	{
	  int res2;

	  res = gsasl_step64 (xctx, in, &out);
	  if (res != GSASL_NEEDS_MORE && res != GSASL_OK)
	    break;

	  if (!step_send (out))
	    return 1;

	no_client_first:
	  if (!args_info.quiet_given &&
	      !args_info.imap_flag && !args_info.smtp_flag)
	    {
	      if (args_info.server_flag)
		fprintf (stderr, _("Enter base64 authentication data "
				   "from client (press RET if none):\n"));
	      else
		fprintf (stderr, _("Enter base64 authentication data "
				   "from server (press RET if none):\n"));
	    }

	  /* Return 1 on token, 2 on protocol success, 3 on protocol fail, 0 on
	     errors. */
	  res2 = step_recv (&in);
	  if (!res2)
	    return 1;
	  if (res2 == 3)
	    error (EXIT_FAILURE, 0, _("server error"));
	  if (res2 == 2)
	    break;
	}
      while (args_info.imap_flag || args_info.smtp_flag
	     || res == GSASL_NEEDS_MORE);

      if (res != GSASL_OK)
	error (EXIT_FAILURE, 0, _("mechanism error: %s"),
	       gsasl_strerror (res));

      if (!args_info.quiet_given)
	{
	  if (args_info.server_flag)
	    fprintf (stderr, _("Server authentication "
			       "finished (client trusted)...\n"));
	  else
	    fprintf (stderr, _("Client authentication "
			       "finished (server trusted)...\n"));
	  fflush (stderr);
	}

      /* Transfer application payload */
      if (args_info.application_data_flag)
	{
	  struct pollfd pfd[2];
	  char *sockbuf = NULL;
	  /* we read chunks of 1000 bytes at a time */
	  size_t sockpos = 0, sockalloc = 0, sockalloc1 = 1000;

	  /* Setup pollfd structs... */
	  pfd[0].fd = STDIN_FILENO;
	  pfd[0].events = POLLIN;
	  if (sockfd)
	    {
	      pfd[1].fd = sockfd;
	      pfd[1].events = POLLIN;
	    }

	  if (!args_info.quiet_given)
	    {
	      fprintf (stderr,
		       _("Enter application data (EOF to finish):\n"));
	      fflush (stderr);
	    }

	  while (1)
	    {
	      int rc;

	      pfd[0].revents = 0;
	      pfd[1].revents = 0;

	      rc = poll (pfd, sockfd ? 2 : 1, -1);
	      if (rc < 0 && errno == EINTR)
		continue;

	      /* Always check for errors */
	      if (rc < 0)
		error (EXIT_FAILURE, errno, "poll");

	      /* We got data to read from stdin.. */
	      if ((pfd[0].revents & (POLLIN | POLLERR)) == POLLIN)
		{
		  char *line = NULL;
		  size_t n;
		  ssize_t len;

		  len = getline (&line, &n, stdin);
		  if (len <= 0)
		    break;

		  if (args_info.imap_flag || args_info.smtp_flag)
		    {
		      if (len < 2 || strcmp (&line[len - 2], "\r\n") != 0)
			{
			  line = xrealloc (line, len + 2);
			  line[len - 1] = '\r';
			  line[len] = '\n';
			  line[len + 1] = '\0';
			  len++;
			}
		    }
		  else
		    {
		      len--;
		      line[len] = '\0';
		    }

		  res = gsasl_encode (xctx, line, len, &out, &output_len);
		  if (res != GSASL_OK)
		    break;

		  if (sockfd)
		    {
#ifdef HAVE_LIBGNUTLS
		      if (using_tls)
			len = gnutls_record_send (session, out, output_len);
		      else
#endif
			len = write (sockfd, out, output_len);
		      if (len != output_len)
			error (EXIT_FAILURE, errno, "write");
		    }
		  else if (!(strlen (line) == output_len &&
			     memcmp (line, out, output_len) == 0))
		    {
		      res = gsasl_base64_to (out, output_len,
					     &b64output, &b64output_len);
		      if (res != GSASL_OK)
			break;

		      if (!args_info.quiet_given)
			fprintf (stderr, _("Base64 encoded application "
					   "data to send:\n"));
		      fprintf (stdout, "%s\n", b64output);

		      free (b64output);
		    }

		  free (line);
		  free (out);
		}
	      /* If there was an error, quit.  */
	      else if (pfd[0].revents & (POLLERR | POLLHUP))
		{
		  error (0, 0, "poll stdin");
		  break;
		}

	      /* We got data to read from the socket.. */
	      if (sockfd && (pfd[1].revents & (POLLIN | POLLERR)) == POLLIN)
		{
		  ssize_t len;

		  if (sockalloc == sockpos)
		    sockbuf = x2realloc (sockbuf, &sockalloc1);
		  sockalloc = sockalloc1;

#ifdef HAVE_LIBGNUTLS
		  if (using_tls)
		    len = gnutls_record_recv (session, &sockbuf[sockpos],
					      sockalloc - sockpos);
		  else
#endif
		    len = recv (sockfd, &sockbuf[sockpos],
				sockalloc - sockpos, 0);
		  if (len <= 0)
		    break;

		  sockpos += len;

		  res = gsasl_decode (xctx, sockbuf, sockpos,
				      &out, &output_len);
		  if (res == GSASL_NEEDS_MORE)
		    {
#define MAX_INPUT_SIZE	0x100000
		      if (sockpos > MAX_INPUT_SIZE)
			error (EXIT_FAILURE, 0,
			       _("SASL record too large: %zu\n"), sockpos);
		      continue;
		    }
		  if (res != GSASL_OK)
		    break;

		  free (sockbuf);
		  sockbuf = NULL;
		  sockpos = 0;
		  sockalloc = 0;
		  sockalloc1 = 1000;

		  printf ("%.*s", (int) output_len, out);
		  free (out);
		}
	      /* If there was an error, quit.  */
	      else if (pfd[1].revents & (POLLERR | POLLHUP))
		{
		  error (0, 0, "poll socket");
		  break;
		}
	    }

	  if (res != GSASL_OK)
	    error (EXIT_FAILURE, 0, _("encoding error: %s"),
		   gsasl_strerror (res));
	}

      if (!args_info.quiet_given)
	fprintf (stderr, _("Session finished...\n"));

      if (!logout ())
	return 1;

      gsasl_finish (xctx);
    }

  if (sockfd)
    {
#ifdef HAVE_LIBGNUTLS
      if (using_tls)
	{
	  res = gnutls_bye (session, GNUTLS_SHUT_RDWR);
	  if (res < 0)
	    error (EXIT_FAILURE, 0,
		   _("terminating GnuTLS session failed: %s"),
		   gnutls_strerror (res));

	}
#endif
      shutdown (sockfd, SHUT_RDWR);
      close (sockfd);
    }

  gsasl_done (ctx);

#ifdef HAVE_LIBGNUTLS
  if (using_tls)
    {
      gnutls_deinit (session);
      gnutls_anon_free_client_credentials (anoncred);
      gnutls_certificate_free_credentials (x509cred);
      gnutls_global_deinit ();
    }
#endif

  return 0;
}