/* poputil.c
   Copyright (C) 1995-1996 Eberhard Mattes

This file is part of GNU Emacs.

GNU Emacs is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

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
along with GNU Emacs; see the file COPYING.  If not, write to
the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.  */


#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <process.h>
#include <errno.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include "mailutil.h"
#include "poputil.h"


char *pop_getpass (const char *prompt)
{
  static char buf[POP_GETPASS_LEN+1];
  int i, pid, n, s;
  int pipe_fd[2], saved_stdout;

  if (pipe (pipe_fd) != 0)
    {
      perror ("pipe()");
      return (NULL);
    }
  if ((saved_stdout = dup (STDOUT_FILENO)) < 0)
    {
      perror ("dup()");
      close (pipe_fd[0]); close (pipe_fd[1]);
      return (NULL);
    }
  if (dup2 (pipe_fd[1], STDOUT_FILENO) < 0)
    {
      perror ("dup2()");
      close (pipe_fd[0]); close (pipe_fd[1]); close (saved_stdout);
      return (NULL);
    }
  close (pipe_fd[1]);
  fcntl (saved_stdout, F_SETFD, FD_CLOEXEC);
  fcntl (pipe_fd[0], F_SETFD, FD_CLOEXEC);
  pid = spawnlp (P_PM, "pmgetpw.exe", "pmgetpw.exe", prompt, NULL);
  if (pid < 0)
    {
      perror ("Starting pmgetpw");
      dup2 (saved_stdout, STDOUT_FILENO);
      close (pipe_fd[0]); close (saved_stdout);
      return (NULL);
    }
  dup2 (saved_stdout, STDOUT_FILENO);
  close (saved_stdout);
  i = 0;
  while (i < sizeof (buf) - 1)
    {
      n = read (pipe_fd[0], buf + i, sizeof (buf) - 1 - i);
      if (n < 0)
        {
          perror ("read()");
          close (pipe_fd[0]);
          return (NULL);
        }
      if (n == 0)
        break;
      while (n > 0)
        {
          if (buf[i] == '\n')
            break;
          ++i; --n;
        }
      if (n > 0)
        break;
    }
  buf[i] = 0;
  close (pipe_fd[0]);
  n = waitpid (pid, &s, 0);
  if (n < 0)
    {
      perror ("waitpid()");
      return (NULL);
    }
  if (!WIFEXITED (s) || WEXITSTATUS (s) != 0)
    {
      fprintf (stderr, "pmgetpw failed\n");
      return (NULL);
    }
  return (buf);
}


int pop_user_host (const char *input, char **user, char **host)
{
  const char *p;
  char *t;
  int len;

  p = strchr (input, '@');
  if (p == NULL)
    {
      if ((*user = strdup (input)) == NULL)
        {
          errno = ENOMEM; return (EX_NOMEM);
        }
      *host = getenv ("MAILHOST");
      if (*host == NULL)
        return (EX_MAILHOST);
    }
  else
    {
      len = p - input;
      if ((t = malloc (len + 1)) == NULL)
        {
          errno = ENOMEM; return (EX_NOMEM);
        }
      memcpy (t, input, len);
      t[len] = 0;
      *user = t;
      if ((*host = strdup (p + 1)) == NULL)
        {
          errno = ENOMEM; return (EX_NOMEM);
        }
    }
  return (EX_OK);
}


char *pop_cookie (const char *user, const char *host)
{
  char *cookie;

  cookie = xmalloc (strlen (user) + strlen (host) + 2);
  sprintf (cookie, "%s@%s", user, host);
  return (cookie);
}


static int dot_count (const char *s)
{
  int n;

  n = 0;
  while (*s != 0)
    if (*s++ == '.')
      ++n;
  return (n);
}


int pop_open (char *server_name, int port, FILE **result)
{
  struct sockaddr_in in_local, in_server;
  unsigned long ip;
  int s_server, i;
  FILE *f;

  *result = NULL;
  memset (&in_server, 0, sizeof (in_server));

  ip = inet_addr (server_name);
  if (ip != INADDR_NONE && dot_count (server_name) == 3)
    {
      in_server.sin_family = AF_INET;
      in_server.sin_addr.s_addr = ip;
    }
  else
    {
      struct hostent *host;

      host = gethostbyname (server_name);
      if (host == NULL)
        {
          herror ("gethostbyname()");
          return (EX_OSERR);
        }
      in_server.sin_family = host->h_addrtype;
      memcpy (&in_server.sin_addr, host->h_addr, host->h_length);
    }
  in_server.sin_port = htons (port);

  s_server = socket (AF_INET, SOCK_STREAM, 0);
  if (s_server < 0)
    {
      perror ("socket()");
      return (EX_OSERR);
    }

  i = 0;
  if (setsockopt (s_server, SOL_SOCKET, SO_REUSEADDR, &i, sizeof (i)) != 0)
    {
      perror ("setsockopt()");
      return (EX_OSERR);
    }

  memset (&in_local, 0, sizeof (in_local));
  in_local.sin_family = AF_INET;
  in_local.sin_addr.s_addr = INADDR_ANY;
  in_local.sin_port = 0;
  if (bind (s_server, (struct sockaddr *)&in_local, sizeof (in_local)) < 0)
    {
      perror ("bind()");
      return (EX_OSERR);
    }

  i = sizeof (in_local);
  if (getsockname (s_server, (struct sockaddr *)&in_local, &i) != 0)
    {
      perror ("getsockname()");
      return (EX_OSERR);
    }

  if (connect (s_server, (struct sockaddr *)&in_server,
               sizeof (in_server)) < 0)
    {
      perror ("connect()");
      close (s_server);
      return (EX_OSERR);
    }
  f = fdopen (s_server, "rb");
  if (f == NULL)
    {
      perror ("fdopen()");
      close (s_server);
      return (EX_OSERR);
    }
  *result = f;
  return (EX_OK);
}


static int send_str (FILE *f, const char *p, int len)
{
  int rc;

  rc = write (fileno (f), p, len);
  if (rc == -1)
    return (EX_OSERR);
  return (EX_OK);
}


static int send_line (FILE *f, const char *p)
{
  int rc;

  rc = send_str (f, p, strlen (p));
  if (rc == EX_OK)
    rc = send_str (f, "\r\n", 2);
  return (rc);
}


int pop_answer (FILE *f, char **result)
{
  int rc;

  rc = read_line (f, 0, result);
  if (rc != EX_OK) return (rc);
  if (strncmp (*result, "+OK", 3) != 0)
    return (EX_POPERR);
  return (EX_OK);
}


int pop_vcmd (FILE *f, char **result, const char *fmt, va_list arg_ptr)
{
  char buf[512];
  int rc;

  vsprintf (buf, fmt, arg_ptr);
  rc = send_line (f, buf);
  if (rc == EX_OK)
    rc = pop_answer (f, result);
  return (rc);
}


int pop_cmd (FILE *f, char **result, const char *fmt, ...)
{
  va_list arg_ptr;
  int rc;

  va_start (arg_ptr, fmt);
  rc = pop_vcmd (f, result, fmt, arg_ptr);
  va_end (arg_ptr);
  return (rc);
}


void pop_abort (FILE *f)
{
  char *p;
  int rc;

  rc = send_line (f, "QUIT");
  if (rc == EX_OK)
    rc = read_line (f, 0, &p);
}
