/* GNU Mailutils -- a suite of utilities for electronic mail
   Copyright (C) 2003, 2007 Free Software Foundation, Inc.

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

   This library 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
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General
   Public License along with this library; if not, write to the
   Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301 USA */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <mailutils/sys/pop3.h>

/* Implementation of the stream for TOP and RETR.  */
struct mu_pop3_stream
{
  mu_pop3_t pop3;
  int done;
};

static void
mu_pop3_stream_destroy (mu_stream_t stream)
{
  struct mu_pop3_stream *pop3_stream = mu_stream_get_owner (stream);
  if (pop3_stream)
    {
      free (pop3_stream);
    }
}

static int
mu_pop3_stream_read (mu_stream_t stream, char *buf, size_t buflen, mu_off_t offset, size_t *pn)
{
  struct mu_pop3_stream *pop3_stream = mu_stream_get_owner (stream);
  size_t n = 0;
  int status = 0;
  char *p = buf;

  (void)offset;
  if (pop3_stream)
    {
      if (!pop3_stream->done)
	{
	  do
	    {
	      size_t nread = 0;

	      /* The pop3_readline () function will always read one less to
		 be able to null terminate the buffer, this will cause
		 serious grief for mu_stream_read() where it is legitimate to
		 have a buffer of 1 char.  So we must catch it here.  */
	      if (buflen == 1)
		{
		  char buffer[2];
		  *buffer = '\0';
		  status = mu_pop3_readline (pop3_stream->pop3, buffer, 2, &nread);
		  *p = *buffer;
		}
	      else
		status = mu_pop3_readline (pop3_stream->pop3, p, buflen, &nread);

	      if (status != 0)
		break;
	      if (nread == 0)
		{
		  pop3_stream->pop3->state = MU_POP3_NO_STATE;
		  pop3_stream->done = 1;
		  break;
		}
	      n += nread;
	      buflen -= nread;
	      p += nread;
	    }
	  while (buflen > 0);
	}
    }
  if (pn)
    *pn = n;
  return status;
}

static int
mu_pop3_stream_readline (mu_stream_t stream, char *buf, size_t buflen, mu_off_t offset, size_t *pn)
{
  struct mu_pop3_stream *pop3_stream = mu_stream_get_owner (stream);
  size_t n = 0;
  int status = 0;

  (void)offset;
  if (pop3_stream)
    {
      if (!pop3_stream->done)
	{
	  status = mu_pop3_readline (pop3_stream->pop3, buf, buflen, &n);
	  if (n == 0)
	    {
	      pop3_stream->pop3->state = MU_POP3_NO_STATE;
	      pop3_stream->done = 1;
	    }
	}
    }
  if (pn)
    *pn = n;
  return status;
}

int
mu_pop3_stream_create (mu_pop3_t pop3, mu_stream_t *pstream)
{
  struct mu_pop3_stream *pop3_stream;
  int status;

  pop3_stream = malloc (sizeof *pop3_stream);
  if (pop3_stream == NULL)
    return ENOMEM;

  pop3_stream->pop3 = pop3;
  pop3_stream->done = 0;

  status = mu_stream_create (pstream, MU_STREAM_READ | MU_STREAM_NO_CLOSE | MU_STREAM_NO_CHECK, pop3_stream);
  if (status != 0)
    {
      free (pop3_stream);
      return status;
    }
 
  mu_stream_set_read (*pstream, mu_pop3_stream_read, pop3_stream);
  mu_stream_set_readline (*pstream, mu_pop3_stream_readline, pop3_stream);
  mu_stream_set_destroy (*pstream, mu_pop3_stream_destroy, pop3_stream);
                                                                                                                             
  return 0;
}