Esempio n. 1
0
int
readln (char **out)
{
  if (sockfd)
    {
      size_t allocated = 0, used = 0;
      char *input = NULL;

      /* FIXME: Read larger chunks.  Problem: buffering too large reads? */

      do
	{
	  ssize_t nread;

	  if (used == allocated)
	    input = x2realloc (input, &allocated);

#ifdef HAVE_LIBGNUTLS
	  if (using_tls)
	    nread = gnutls_record_recv (session, &input[used], 1);
	  else
#endif
	    nread = recv (sockfd, &input[used], 1, 0);
	  if (nread <= 0)
	    return 0;

	  used += nread;
	}
      while (input[used - 1] != '\n');

      if (used == allocated)
	input = x2realloc (input, &allocated);

      input[used] = '\0';

      *out = input;

      printf ("%s", *out);
    }
  else
    {
      *out = readline ("");
      if (*out == NULL)
	return 0;
    }

  return 1;
}
Esempio n. 2
0
static char const *
xml_escape_string (struct escape_buf *buf, char const *str)
{
  size_t len = strlen (str);
  size_t max_expansion = sizeof "&quot;" - 1;
  char *p;

  if (buf->size <= max_expansion * len)
    {
      buf->size = max_expansion * len + 1;
      buf->ptr = x2realloc (buf->ptr, &buf->size);
    }
  p = buf->ptr;

  for (; *str; str++)
    switch (*str)
      {
      default: *p++ = *str; break;
      case '&': p = stpcpy (p, "&amp;" ); break;
      case '<': p = stpcpy (p, "&lt;"  ); break;
      case '>': p = stpcpy (p, "&gt;"  ); break;
      case '"': p = stpcpy (p, "&quot;"); break;
      }

  *p = '\0';
  return buf->ptr;
}
Esempio n. 3
0
char *
namebuf_name (namebuf_t buf, const char *name)
{
  size_t len = strlen (name);
  while (buf->dir_length + len + 1 >= buf->buffer_size)
    buf->buffer = x2realloc (buf->buffer, &buf->buffer_size);
  strcpy (buf->buffer + buf->dir_length, name);
  return buf->buffer;
}
Esempio n. 4
0
int
add_exclude_fp (void (*add_func) (struct exclude *, char const *, int, void *),
		struct exclude *ex, FILE *fp, int options,
		char line_end,
		void *data)
{
  char *buf = NULL;
  char *p;
  char *pattern;
  char const *lim;
  size_t buf_alloc = 0;
  size_t buf_count = 0;
  int c;
  int e = 0;

  while ((c = getc (fp)) != EOF)
    {
      if (buf_count == buf_alloc)
        buf = x2realloc (buf, &buf_alloc);
      buf[buf_count++] = c;
    }

  if (ferror (fp))
    e = errno;

  buf = xrealloc (buf, buf_count + 1);
  buf[buf_count] = line_end;
  lim = buf + buf_count + ! (buf_count == 0 || buf[buf_count - 1] == line_end);

  exclude_add_pattern_buffer (ex, buf);

  pattern = buf;

  for (p = buf; p < lim; p++)
    if (*p == line_end)
      {
        char *pattern_end = p;

        if (isspace ((unsigned char) line_end))
          {
            for (; ; pattern_end--)
              if (pattern_end == pattern)
                goto next_pattern;
              else if (! isspace ((unsigned char) pattern_end[-1]))
                break;
          }

        *pattern_end = '\0';
        (*add_func) (ex, pattern, options, data);

      next_pattern:
        pattern = p + 1;
      }

  errno = e;
  return e ? -1 : 0;
}
Esempio n. 5
0
/* xattr_encode_keyword() substitutes '=' ~~> '%3D' and '%' ~~> '%25'
   in extended attribute keywords.  This is needed because the '=' character
   has special purpose in extended attribute header - it splits keyword and
   value part of header.  If there was the '=' occurrence allowed inside
   keyword, there would be no unambiguous way how to decode this extended
   attribute.

   (http://lists.gnu.org/archive/html/bug-tar/2012-10/msg00017.html)
 */
static char *
xattr_encode_keyword(const char *keyword)
{
  static char *encode_buffer = NULL;
  static size_t encode_buffer_size = 0;
  size_t bp; /* keyword/buffer pointers */

  if (!encode_buffer)
    {
      encode_buffer_size = 256;
      encode_buffer = xmalloc (encode_buffer_size);
    }
  else
    *encode_buffer = 0;

  for (bp = 0; *keyword != 0; ++bp, ++keyword)
    {
      char c = *keyword;

      if (bp + 2 /* enough for URL encoding also.. */ >= encode_buffer_size)
        {
          encode_buffer = x2realloc (encode_buffer, &encode_buffer_size);
        }

      if (c == '%')
        {
          strcpy (encode_buffer + bp, "%25");
          bp += 2;
        }
      else if (c == '=')
        {
          strcpy (encode_buffer + bp, "%3D");
          bp += 2;
        }
      else
        encode_buffer[bp] = c;
    }

  encode_buffer[bp] = 0;

  return encode_buffer;
}
Esempio n. 6
0
/* Read an arbitrarily long line of text from STREAM into LINEBUFFER.
   Consider lines to be terminated by DELIMITER.
   Keep the delimiter; append DELIMITER if it's the last line of a file
   that ends in a character other than DELIMITER.  Do not NUL-terminate.
   Therefore the stream can contain NUL bytes, and the length
   (including the delimiter) is returned in linebuffer->length.
   Return NULL when stream is empty.  Return NULL and set errno upon
   error; callers can distinguish this case from the empty case by
   invoking ferror (stream).
   Otherwise, return LINEBUFFER.  */
struct linebuffer *
readlinebuffer_delim (struct linebuffer *linebuffer, FILE *stream,
                      char delimiter)
{
  int c;
  char *buffer = linebuffer->buffer;
  char *p = linebuffer->buffer;
  char *end = buffer + linebuffer->size; /* Sentinel. */

  if (feof (stream))
    return NULL;

  do
    {
      c = getc (stream);
      if (c == EOF)
        {
          if (p == buffer || ferror (stream))
            return NULL;
          if (p[-1] == delimiter)
            break;
          c = delimiter;
        }
      if (p == end)
        {
          size_t oldsize = linebuffer->size;
          buffer = x2realloc (buffer, &linebuffer->size);
          p = buffer + oldsize;
          linebuffer->buffer = buffer;
          end = buffer + linebuffer->size;
        }
      *p++ = c;
    }
  while (c != delimiter);

  linebuffer->length = p - buffer;
  return linebuffer;
}
Esempio n. 7
0
/* Return the current hostname in malloc'd storage.
   If malloc fails, exit.
   Upon any other failure, return NULL and set errno.  */
char *
xgethostname (void)
{
    char *hostname = NULL;
    size_t size = INITIAL_HOSTNAME_LENGTH;

    while (1)
    {
        /* Use SIZE_1 here rather than SIZE to work around the bug in
        SunOS 5.5's gethostname whereby it NUL-terminates HOSTNAME
         even when the name is as long as the supplied buffer.  */
        size_t size_1;

        hostname = (char*) x2realloc (hostname, &size);
        size_1 = size - 1;
        hostname[size_1 - 1] = '\0';
        errno = 0;

        if (gethostname (hostname, size_1) == 0)
        {
            if (! hostname[size_1 - 1])
                break;
        }
        else if (errno != 0 && errno != ENAMETOOLONG && errno != EINVAL
                 /* OSX/Darwin does this when the buffer is not large enough */
                 && errno != ENOMEM)
        {
            int saved_errno = errno;
            free (hostname);
            errno = saved_errno;
            return NULL;
        }
    }

    return hostname;
}
Esempio n. 8
0
int
add_exclude_file (void (*add_func) (struct exclude *, char const *, int),
		  struct exclude *ex, char const *filename, int options,
		  char line_end)
{
  bool use_stdin = filename[0] == '-' && !filename[1];
  FILE *in;
  char *buf = NULL;
  char *p;
  char const *pattern;
  char const *lim;
  size_t buf_alloc = 0;
  size_t buf_count = 0;
  int c;
  int e = 0;

  if (use_stdin)
    in = stdin;
  else if (! (in = fopen (filename, "r")))
    return -1;

  while ((c = getc (in)) != EOF)
    {
      if (buf_count == buf_alloc)
	buf = x2realloc (buf, &buf_alloc);
      buf[buf_count++] = c;
    }

  if (ferror (in))
    e = errno;

  if (!use_stdin && fclose (in) != 0)
    e = errno;

  buf = xrealloc (buf, buf_count + 1);
  buf[buf_count] = line_end;
  lim = buf + buf_count + ! (buf_count == 0 || buf[buf_count - 1] == line_end);
  pattern = buf;

  for (p = buf; p < lim; p++)
    if (*p == line_end)
      {
	char *pattern_end = p;

	if (is_space (line_end))
	  {
	    for (; ; pattern_end--)
	      if (pattern_end == pattern)
		goto next_pattern;
	      else if (! is_space (pattern_end[-1]))
		break;
	  }

	*pattern_end = '\0';
	(*add_func) (ex, pattern, options);

      next_pattern:
	pattern = p + 1;
      }

  errno = e;
  return e ? -1 : 0;
}
Esempio n. 9
0
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;
}