/* GStreamer
 * Copyright (C) 2011 Axis Communications <dev-gstreamer@axis.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

/**
 * SECTION:element-curlsink
 * @short_description: sink that uploads data to a server using libcurl
 * @see_also:
 *
 * This is a network sink that uses libcurl as a client to upload data to
 * an SMTP server.
 *
 * <refsect2>
 * <title>Example launch line (upload a JPEG file to an SMTP server)</title>
 * |[
 * gst-launch filesrc filesrc location=image.jpg ! jpegparse ! curlsmtpsink  \
 *     file-name=image.jpg  \
 *     location=smtp://smtp.gmail.com:507 \
 *     user=test passwd=test  \
 *     subject=my image \
 *     mail-from="me@gmail.com" \
 *     mail-rcpt="you@gmail.com,she@gmail.com" \
 *     use-ssl=TRUE  \
 *     insecure=TRUE
 * ]|
 * </refsect2>
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <curl/curl.h>
#include <string.h>
#include <stdio.h>

#include <sys/socket.h>
#include <sys/types.h>
#include <sys/time.h>
#include <pwd.h>
#include <netinet/in.h>
#include <unistd.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <sys/stat.h>
#include <fcntl.h>

#include "gstcurltlssink.h"
#include "gstcurlsmtpsink.h"

/* Default values */
#define GST_CAT_DEFAULT                gst_curl_smtp_sink_debug
#define DEFAULT_USE_SSL                FALSE
#define DEFAULT_NBR_ATTACHMENTS        1

/* MIME definitions */
#define MIME_VERSION                   "MIME-version: 1.0"
#define BOUNDARY_STRING                "curlsink-boundary"

#define MAIL_RCPT_DELIMITER            ","

/* Plugin specific settings */

GST_DEBUG_CATEGORY_STATIC (gst_curl_smtp_sink_debug);

enum
{
  PROP_0,
  PROP_MAIL_RCPT,
  PROP_MAIL_FROM,
  PROP_SUBJECT,
  PROP_MESSAGE_BODY,
  PROP_POP_USER_NAME,
  PROP_POP_USER_PASSWD,
  PROP_POP_LOCATION,
  PROP_NBR_ATTACHMENTS,
  PROP_CONTENT_TYPE,
  PROP_USE_SSL
};


/* Object class function declarations */
static void gst_curl_smtp_sink_finalize (GObject * gobject);
static void gst_curl_smtp_sink_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec);
static void gst_curl_smtp_sink_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec);

static gboolean gst_curl_smtp_sink_set_payload_headers_unlocked (
    GstCurlBaseSink *sink);
static gboolean gst_curl_smtp_sink_set_transfer_options_unlocked (
    GstCurlBaseSink * sink);
static void gst_curl_smtp_sink_set_mime_type (GstCurlBaseSink * bcsink,
    GstCaps *caps);
static gboolean gst_curl_smtp_sink_prepare_transfer (GstCurlBaseSink * bcsink);
static size_t gst_curl_smtp_sink_transfer_data_buffer (GstCurlBaseSink * sink,
    void * curl_ptr, size_t block_size, guint * last_chunk);
static size_t gst_curl_smtp_sink_flush_data_unlocked (GstCurlBaseSink * bcsink,
    void * curl_ptr, size_t block_size, gboolean new_file);

/* private functions */

static size_t transfer_payload_headers (GstCurlSmtpSink * sink, void * curl_ptr,
    size_t block_size);


static void
_do_init (GType type)
{
  GST_DEBUG_CATEGORY_INIT (gst_curl_smtp_sink_debug, "curlsmtpsink", 0,
      "curl smtp sink element");
}

GST_BOILERPLATE_FULL (GstCurlSmtpSink, gst_curl_smtp_sink, GstCurlTlsSink,
    GST_TYPE_CURL_TLS_SINK, _do_init);

static void
gst_curl_smtp_sink_base_init (gpointer g_class)
{
  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);

  gst_element_class_set_details_simple (element_class,
      "Curl smtp sink",
      "Sink/Network",
      "Upload data over SMTP protocol using libcurl",
      "Patricia Muscalu <patricia@axis.com>");
}

static void
gst_curl_smtp_sink_class_init (GstCurlSmtpSinkClass * klass)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
  GstCurlBaseSinkClass *gstcurlbasesink_class = (GstCurlBaseSinkClass *) klass;

  GST_DEBUG_OBJECT (klass, "class_init");

  gstcurlbasesink_class->set_protocol_dynamic_options_unlocked =
    gst_curl_smtp_sink_set_payload_headers_unlocked;
  gstcurlbasesink_class->set_options_unlocked =
    gst_curl_smtp_sink_set_transfer_options_unlocked;
  gstcurlbasesink_class->set_mime_type = gst_curl_smtp_sink_set_mime_type;
  gstcurlbasesink_class->prepare_transfer = gst_curl_smtp_sink_prepare_transfer;
  gstcurlbasesink_class->transfer_data_buffer =
    gst_curl_smtp_sink_transfer_data_buffer;
  gstcurlbasesink_class->flush_data_unlocked = gst_curl_smtp_sink_flush_data_unlocked;

  gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_curl_smtp_sink_finalize);
  gobject_class->set_property = gst_curl_smtp_sink_set_property;
  gobject_class->get_property = gst_curl_smtp_sink_get_property;

  g_object_class_install_property (gobject_class, PROP_MAIL_RCPT,
      g_param_spec_string ("mail-rcpt", "Mail recipient",
          "Single address that the given mail should get sent to", NULL,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
  g_object_class_install_property (gobject_class, PROP_MAIL_FROM,
      g_param_spec_string ("mail-from", "Mail sender",
          "Single address that the given mail should get sent from", NULL,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
  g_object_class_install_property (gobject_class, PROP_CONTENT_TYPE,
      g_param_spec_string ("content-type", "Content type",
          "The mime type of the body of the request", NULL,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
  g_object_class_install_property (gobject_class, PROP_SUBJECT,
      g_param_spec_string ("subject", "Mail subject",
          "Mail subject", NULL,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
  g_object_class_install_property (gobject_class, PROP_MESSAGE_BODY,
      g_param_spec_string ("message-body", "Message body",
          "Message body", NULL,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
  g_object_class_install_property (gobject_class, PROP_USE_SSL,
      g_param_spec_boolean ("use-ssl", "Use SSL",
          "Use SSL/TLS for the connection", DEFAULT_USE_SSL,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
  g_object_class_install_property (gobject_class, PROP_NBR_ATTACHMENTS,
      g_param_spec_int ("nbr-attachments", "Number attachments",
          "Number attachments to send", G_MININT, G_MAXINT,
          DEFAULT_NBR_ATTACHMENTS,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
  g_object_class_install_property (gobject_class, PROP_POP_USER_NAME,
      g_param_spec_string ("pop-user", "User name",
          "User name to use for POP server authentication", NULL,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
  g_object_class_install_property (gobject_class, PROP_POP_USER_PASSWD,
      g_param_spec_string ("pop-passwd", "User password",
          "User password to use for POP server authentication", NULL,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
  g_object_class_install_property (gobject_class, PROP_POP_LOCATION,
      g_param_spec_string ("pop-location", "POP location",
          "URL POP used for authentication", NULL,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

}

static void
gst_curl_smtp_sink_init (GstCurlSmtpSink * sink, GstCurlSmtpSinkClass * klass)
{
  sink->curl_recipients = NULL;
  sink->mail_rcpt = NULL;
  sink->mail_from = NULL;
  sink->subject = NULL;
  sink->message_body = NULL;
  sink->payload_headers = NULL;
  sink->base64_chunk = NULL;

  sink->reset_transfer_options = FALSE;
  sink->use_ssl = DEFAULT_USE_SSL;

  sink->pop_user = NULL;
  sink->pop_passwd = NULL;
  sink->pop_location = NULL;
  sink->pop_curl = NULL;
}

static void
gst_curl_smtp_sink_finalize (GObject * gobject)
{
  GstCurlSmtpSink *this = GST_CURL_SMTP_SINK (gobject);

  GST_DEBUG ("finalizing curlsmtpsink");

  if (this->curl_recipients != NULL) {
    curl_slist_free_all (this->curl_recipients);
  }
  g_free (this->mail_rcpt);
  g_free (this->mail_from);
  g_free (this->subject);
  g_free (this->message_body);
  g_free (this->content_type);

  if (this->base64_chunk != NULL) {
    if (this->base64_chunk->chunk_array != NULL) {
      g_byte_array_free (this->base64_chunk->chunk_array, TRUE);
    }
    g_free (this->base64_chunk);
  }

  if (this->payload_headers != NULL) {
    g_byte_array_free (this->payload_headers, TRUE);
  }

  g_free (this->pop_user);
  g_free (this->pop_passwd);
  if (this->pop_curl != NULL) {
    curl_easy_cleanup (this->pop_curl);
    this->pop_curl = NULL;
  }
  g_free (this->pop_location);

  G_OBJECT_CLASS (parent_class)->finalize (gobject);
}

static void
gst_curl_smtp_sink_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec)
{
  GstCurlSmtpSink *sink;
  GstState cur_state;

  g_return_if_fail (GST_IS_CURL_SMTP_SINK (object));
  sink = GST_CURL_SMTP_SINK (object);

  gst_element_get_state (GST_ELEMENT (sink), &cur_state, NULL, 0);
  if (cur_state != GST_STATE_PLAYING && cur_state != GST_STATE_PAUSED) {
    GST_OBJECT_LOCK (sink);

    switch (prop_id) {
      case PROP_MAIL_RCPT:
        g_free (sink->mail_rcpt);
        sink->mail_rcpt =  g_value_dup_string (value);
        GST_DEBUG_OBJECT (sink, "mail-rcpt set to %s", sink->mail_rcpt);
        break;
      case PROP_MAIL_FROM:
        g_free (sink->mail_from);
        sink->mail_from = g_value_dup_string (value);
        GST_DEBUG_OBJECT (sink, "mail-from set to %s", sink->mail_from);
        break;
      case PROP_SUBJECT:
        g_free (sink->subject);
        sink->subject = g_value_dup_string (value);
        GST_DEBUG_OBJECT (sink, "subject set to %s", sink->subject);
        break;
      case PROP_MESSAGE_BODY:
        g_free (sink->message_body);
        sink->message_body = g_value_dup_string (value);
        GST_DEBUG_OBJECT (sink, "message-body set to %s", sink->message_body);
        break;
      case PROP_CONTENT_TYPE:
        g_free (sink->content_type);
        sink->content_type = g_value_dup_string (value);
        GST_DEBUG_OBJECT (sink, "content-type set to %s", sink->content_type);
        break;
      case PROP_USE_SSL:
        sink->use_ssl = g_value_get_boolean (value);
        GST_DEBUG_OBJECT (sink, "use-ssl set to %d", sink->use_ssl);
        break;
      case PROP_NBR_ATTACHMENTS:
        sink->nbr_attachments = g_value_get_int (value);
        sink->nbr_attachments_left = sink->nbr_attachments;
        GST_DEBUG_OBJECT (sink, "nbr-attachments set to %d", sink->nbr_attachments);
        break;
      case PROP_POP_USER_NAME:
        g_free (sink->pop_user);
        sink->pop_user = g_value_dup_string (value);
        GST_DEBUG_OBJECT (sink, "pop-user set to %s", sink->pop_user);
        break;
      case PROP_POP_USER_PASSWD:
        g_free (sink->pop_passwd);
        sink->pop_passwd = g_value_dup_string (value);
        GST_DEBUG_OBJECT (sink, "pop-passwd set to %s", sink->pop_passwd);
        break;
      case PROP_POP_LOCATION:
        g_free (sink->pop_location);
        sink->pop_location = g_value_dup_string (value);
        GST_DEBUG_OBJECT (sink, "pop-location set to %s", sink->pop_location);
        break;

      default:
        GST_DEBUG_OBJECT (sink, "invalid property id %d", prop_id);
        break;
    }

    GST_OBJECT_UNLOCK (sink);

    return;
  }

  /* in PLAYING or PAUSED state */
  GST_OBJECT_LOCK (sink);

  switch (prop_id) {
    case PROP_CONTENT_TYPE:
      g_free (sink->content_type);
      sink->content_type = g_value_dup_string (value);
      GST_DEBUG_OBJECT (sink, "content type set to %s", sink->content_type);
      break;
    default:
      GST_WARNING_OBJECT (sink, "cannot set property when PLAYING");
      break;
  }

  GST_OBJECT_UNLOCK (sink);
}

static void
gst_curl_smtp_sink_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec)
{
  GstCurlSmtpSink *sink;

  g_return_if_fail (GST_IS_CURL_SMTP_SINK (object));
  sink = GST_CURL_SMTP_SINK (object);

  switch (prop_id) {
    case PROP_MAIL_RCPT:
      g_value_set_string (value, sink->mail_rcpt);
      break;
    case PROP_MAIL_FROM:
      g_value_set_string (value, sink->mail_from);
      break;
    case PROP_SUBJECT:
      g_value_set_string (value, sink->subject);
      break;
    case PROP_MESSAGE_BODY:
      g_value_set_string (value, sink->message_body);
      break;
    case PROP_CONTENT_TYPE:
      g_value_set_string (value, sink->content_type);
      break;
    case PROP_USE_SSL:
      g_value_set_boolean (value, sink->use_ssl);
      break;
    case PROP_NBR_ATTACHMENTS:
      g_value_set_int (value, sink->nbr_attachments);
      break;
    case PROP_POP_USER_NAME:
      g_value_set_string (value, sink->pop_user);
      break;
    case PROP_POP_USER_PASSWD:
      g_value_set_string (value, sink->pop_passwd);
      break;
    case PROP_POP_LOCATION:
      g_value_set_string (value, sink->pop_location);
      break;

    default:
      GST_DEBUG_OBJECT (sink, "invalid property id");
      break;
  }
}

static gboolean
gst_curl_smtp_sink_set_payload_headers_unlocked (GstCurlBaseSink *bcsink)
{
  GstCurlSmtpSink *sink = GST_CURL_SMTP_SINK (bcsink);
  gchar *hdrs;
  gboolean encode = FALSE;
  const gchar *enc_str = "Content-Transfer-Encoding: BASE64";

  if (sink->reset_transfer_options) {
    sink->reset_transfer_options = FALSE;

    gst_curl_smtp_sink_set_transfer_options_unlocked (bcsink);
  }

  if (sink->payload_headers == NULL) {
    sink->payload_headers = g_byte_array_new ();
  }

  if ((g_strcmp0 (sink->content_type, "image/jpeg") == 0) ||
      (g_strcmp0 (sink->content_type, "video/x-matroska") == 0)) {
    encode = TRUE;

    if (sink->base64_chunk == NULL) {
      sink->base64_chunk = g_malloc (sizeof (Base64Chunk));
      sink->base64_chunk->chunk_array = g_byte_array_new ();
    } else {
      g_assert (sink->base64_chunk->chunk_array != NULL);
      g_assert (sink->base64_chunk->chunk_array->len == 0);
    }

    sink->base64_chunk->state = 0;
    sink->base64_chunk->save = 0;
  }

  hdrs = g_strdup_printf ("\n--%s\n"
                          "Content-Type: application/octet-stream; name=\"%s\"\n"
                          "%s\n"
                          "Content-Disposition: attachment; filename=\"%s\"\n"
                          "\n",
                          BOUNDARY_STRING,
                          bcsink->file_name,
                          encode ? enc_str : "",
                          bcsink->file_name);

  g_byte_array_append (sink->payload_headers, (guint8 *) hdrs,
      strlen (hdrs));
  g_free (hdrs);

  return TRUE;
}

static gboolean
gst_curl_smtp_sink_set_transfer_options_unlocked (GstCurlBaseSink * bcsink)
{
  GstCurlSmtpSink *sink = GST_CURL_SMTP_SINK (bcsink);
  GstCurlTlsSinkClass *parent_class;
  gchar *request_headers;
  GDateTime *date;
  gchar *date_str;
  gchar **tmp_list = NULL;
  gint i;

  g_assert (sink->payload_headers == NULL);

  /* time */
  date = g_date_time_new_now_local ();
  date_str = g_date_time_format (date, "%a %b %e %H:%M:%S %Y");
  g_date_time_unref (date);

  request_headers = g_strdup_printf (
      /* headers */
      "To: %s\n"
      "From: %s\n"
      "Subject: %s\n"
      "Date: %s\n"
      MIME_VERSION"\n"
      "Content-Type: multipart/mixed; boundary=%s\n"
      "\n"
      /* body headers */
      "--"BOUNDARY_STRING"\n"
      "Content-Type: text/plain; charset=\n"
      "Content-Transfer-Encoding: quoted-printable\n"
      /* message body */
      "\n%s\n",
      sink->mail_rcpt,
      sink->mail_from,
      sink->subject,
      date_str,
      BOUNDARY_STRING,
      sink->message_body ? sink->message_body : "");

  sink->payload_headers = g_byte_array_new ();

  g_byte_array_append (sink->payload_headers, (guint8 *) request_headers,
      strlen (request_headers));
  g_free (date_str);
  g_free (request_headers);

  curl_easy_setopt (bcsink->curl, CURLOPT_MAIL_FROM, sink->mail_from);

  if (sink->curl_recipients != NULL) {
    curl_slist_free_all (sink->curl_recipients);
    sink->curl_recipients = NULL;
  }

  tmp_list = g_strsplit_set (sink->mail_rcpt, MAIL_RCPT_DELIMITER, -1);
  for (i = 0; i < g_strv_length (tmp_list); i++) {
    sink->curl_recipients = curl_slist_append (sink->curl_recipients,
        tmp_list[i]);
  }
  g_strfreev (tmp_list);

  /* note that the CURLOPT_MAIL_RCPT takes a list, not a char array */
  curl_easy_setopt (bcsink->curl, CURLOPT_MAIL_RCPT, sink->curl_recipients);

  parent_class = GST_CURL_TLS_SINK_GET_CLASS (sink);

  if (sink->use_ssl) {
    return parent_class->set_options_unlocked (bcsink);
  }

  return TRUE;
}

static void // FIXME: exactly the same function as in http sink
gst_curl_smtp_sink_set_mime_type (GstCurlBaseSink * bcsink, GstCaps *caps)
{
  GstCurlSmtpSink *sink = GST_CURL_SMTP_SINK (bcsink);
  GstStructure *structure;
  const gchar *mime_type;

  if (sink->content_type != NULL) {
    return;
  }

  structure = gst_caps_get_structure (caps, 0);
  mime_type = gst_structure_get_name (structure);
  sink->content_type = g_strdup (mime_type);
}

static size_t
gst_curl_smtp_sink_flush_data_unlocked (GstCurlBaseSink * bcsink,
    void *curl_ptr, size_t block_size, gboolean new_file)
{
  GstCurlSmtpSink *sink = GST_CURL_SMTP_SINK (bcsink);
  Base64Chunk *chunk = sink->base64_chunk;
  gint state = chunk->state;
  gint save = chunk->save;
  GByteArray *array = chunk->chunk_array;
  size_t bytes_to_send;
  gint len;
  gchar *data_out;

  if ((bcsink->is_live && (sink->nbr_attachments_left == sink->nbr_attachments)) ||
      (sink->nbr_attachments == 1)) {
    bcsink->is_live = FALSE;
    sink->reset_transfer_options = TRUE;

    return 0;
  }

  if ((state == 0) && (save == 0)) {
    GST_LOG ("returning 0, no more data to send in this transfer");

    return 0;
  }

  data_out = g_malloc (100);
  len = g_base64_encode_close (TRUE, data_out, &state, &save);
  chunk->state = state;
  chunk->save = save;
  g_byte_array_append (array, (guint8 *) data_out, (guint) len);
  g_free (data_out);

  bytes_to_send = MIN (block_size, array->len);
  memcpy (curl_ptr, (gchar*) array->data, bytes_to_send);
  g_byte_array_remove_range (array, 0, bytes_to_send);

  if (new_file) {
    sink->nbr_attachments_left--;

    bcsink->is_live = TRUE;
    if (sink->nbr_attachments_left <= 1)  {
      sink->nbr_attachments_left = sink->nbr_attachments;
    }

    /* reset flag */
    bcsink->new_file = FALSE;

    /* set payload headers for new file */
    gst_curl_smtp_sink_set_payload_headers_unlocked (bcsink);
  }

  return bytes_to_send;
}

static size_t
transfer_chunk (void *curl_ptr, TransferBuffer *buffer, Base64Chunk *chunk,
    size_t block_size, guint *last_chunk)
{
  size_t bytes_to_send;
  const guchar *data_in = buffer->ptr;
  size_t data_in_offset = buffer->offset;
  gint state = chunk->state;
  gint save = chunk->save;
  GByteArray *array = chunk->chunk_array;
  gchar *data_out;

  bytes_to_send = MIN (block_size, buffer->len);

  if (bytes_to_send == 0) {
    bytes_to_send = MIN (block_size, array->len);
  }

  /* base64 encode data */
  if (buffer->len > 0) {
    gsize len;

    data_out = g_malloc (bytes_to_send * 4);
    len = g_base64_encode_step (data_in + data_in_offset, bytes_to_send, TRUE,
        data_out, &state, &save);
    chunk->state = state;
    chunk->save = save;
    g_byte_array_append (array, (guint8 *) data_out, (guint) len);
    g_free (data_out);
    data_out = NULL;

    buffer->offset += bytes_to_send;
    buffer->len -= bytes_to_send;

    bytes_to_send = MIN (block_size, array->len);
    memcpy (curl_ptr, (gchar*) array->data, bytes_to_send);
    g_byte_array_remove_range (array, 0, bytes_to_send);

    if (array->len == 0) {
      *last_chunk = 1;
    }

    return bytes_to_send;
  }

  /* at this point all data has been encoded */
  memcpy (curl_ptr, (gchar*) array->data, bytes_to_send);
  g_byte_array_remove_range (array, 0, bytes_to_send);
  if (array->len == 0) {
    *last_chunk = 1;
  }

  return bytes_to_send;
}


static size_t
gst_curl_smtp_sink_transfer_data_buffer (GstCurlBaseSink * bcsink,
    void * curl_ptr, size_t block_size, guint * last_chunk)
{
  GstCurlSmtpSink *sink = GST_CURL_SMTP_SINK (bcsink);
  size_t bytes_to_send;

  if (sink->payload_headers && sink->payload_headers->len) {
    return transfer_payload_headers (sink, curl_ptr, block_size);
  }

  if (sink->base64_chunk != NULL) {
    bytes_to_send = transfer_chunk (curl_ptr, bcsink->transfer_buf, sink->base64_chunk,
        block_size, last_chunk);

    return bytes_to_send;
  }

  /* we should never get here */
  return 0;
}

static size_t
transfer_payload_headers (GstCurlSmtpSink * sink,
    void * curl_ptr, size_t block_size)
{
  size_t bytes_to_send;
  GByteArray *headers = sink->payload_headers;

  bytes_to_send = MIN (block_size, headers->len);
  memcpy ((guint8 *) curl_ptr, headers->data, bytes_to_send);
  g_byte_array_remove_range (headers, 0, bytes_to_send);

  if (headers->len == 0) {
    g_byte_array_free (headers, TRUE);
    sink->payload_headers = NULL;
  }

  return bytes_to_send;
}

static gboolean
gst_curl_smtp_sink_prepare_transfer (GstCurlBaseSink * bcsink)
{
  GstCurlSmtpSink *sink = GST_CURL_SMTP_SINK (bcsink);
  CURLcode res;
  gboolean ret = TRUE;

  if (sink->pop_location && strlen (sink->pop_location)) {
    if ((sink->pop_curl = curl_easy_init ()) == NULL) {
      GST_DEBUG_OBJECT (sink, "POP protocol: failed to create handler");
      GST_ELEMENT_ERROR (sink, RESOURCE, WRITE,
          ("POP protocol: failed to create handler"), (NULL));

      return FALSE;
    }

    curl_easy_setopt (sink->pop_curl, CURLOPT_URL, sink->pop_location);
    if (sink->pop_user != NULL && strlen (sink->pop_user) &&
        sink->pop_passwd != NULL && strlen (sink->pop_passwd)) {
      curl_easy_setopt (sink->pop_curl, CURLOPT_USERNAME, sink->pop_user);
      curl_easy_setopt (sink->pop_curl, CURLOPT_PASSWORD, sink->pop_passwd);
    }
  }

  if (sink->pop_curl == NULL) {
    goto end;
  }

  /* ready to initialize connection to POP server */
  res = curl_easy_perform (sink->pop_curl);
  if (res != CURLE_OK) {
    GST_DEBUG_OBJECT (sink, "POP transfer failed: %s", curl_easy_strerror (res));
    GST_ELEMENT_ERROR (sink, RESOURCE, WRITE,
          ("POP transfer failed: %s", curl_easy_strerror (res)), (NULL));

    ret = FALSE;
  }

  curl_easy_cleanup (sink->pop_curl);
  sink->pop_curl = NULL;

end:
  return ret;
}
