diff options
author | Sebastian Rasmussen <sebrn@axis.com> | 2012-04-25 19:46:53 +0200 |
---|---|---|
committer | Tim-Philipp Müller <tim.muller@collabora.co.uk> | 2012-05-12 11:59:01 +0100 |
commit | 3f20bbdcf4b437bf63a7426a5deac45a3c8267d7 (patch) | |
tree | 592f435990f3f56244be83ab2b2fa3733d495f75 | |
parent | 766dd8bb717b1ba5317fbd2c5d7860d6beb34834 (diff) | |
download | gstreamer-plugins-bad-3f20bbdcf4b437bf63a7426a5deac45a3c8267d7.tar.gz |
curl: new curlsmtpsink element
https://bugzilla.gnome.org/show_bug.cgi?id=653741
-rw-r--r-- | docs/plugins/Makefile.am | 7 | ||||
-rw-r--r-- | ext/curl/Makefile.am | 6 | ||||
-rw-r--r-- | ext/curl/gstcurl.c | 5 | ||||
-rw-r--r-- | ext/curl/gstcurlsmtpsink.c | 928 | ||||
-rw-r--r-- | ext/curl/gstcurlsmtpsink.h | 87 | ||||
-rw-r--r-- | tests/check/Makefile.am | 3 | ||||
-rw-r--r-- | tests/check/elements/curlsmtpsink.c | 208 |
7 files changed, 1237 insertions, 7 deletions
diff --git a/docs/plugins/Makefile.am b/docs/plugins/Makefile.am index 8f10bfc76..5a0978c89 100644 --- a/docs/plugins/Makefile.am +++ b/docs/plugins/Makefile.am @@ -61,16 +61,15 @@ IGNORE_CFILES = EXAMPLE_CFILES = \ $(top_srcdir)/ext/directfb/dfb-example.c -# $(top_srcdir)/ext/curl/gstcurlfilesink.h -# $(top_srcdir)/ext/curl/gstcurlftpsink.h -# $(top_srcdir)/ext/curl/gstcurlsmtpsink.h - EXTRA_HFILES = \ $(top_srcdir)/ext/assrender/gstassrender.h \ $(top_srcdir)/ext/celt/gstceltdec.h \ $(top_srcdir)/ext/celt/gstceltenc.h \ $(top_srcdir)/ext/curl/gstcurlbasesink.h \ + $(top_srcdir)/ext/curl/gstcurlfilesink.h \ + $(top_srcdir)/ext/curl/gstcurlftpsink.h \ $(top_srcdir)/ext/curl/gstcurlhttpsink.h \ + $(top_srcdir)/ext/curl/gstcurlsmtpsink.h \ $(top_srcdir)/ext/curl/gstcurltlssink.h \ $(top_srcdir)/ext/dc1394/gstdc1394.h \ $(top_srcdir)/ext/directfb/dfbvideosink.h \ diff --git a/ext/curl/Makefile.am b/ext/curl/Makefile.am index 2f68b7c1a..5d916b0fb 100644 --- a/ext/curl/Makefile.am +++ b/ext/curl/Makefile.am @@ -5,7 +5,8 @@ libgstcurl_la_SOURCES = gstcurl.c \ gstcurltlssink.c \ gstcurlhttpsink.c \ gstcurlfilesink.c \ - gstcurlftpsink.c + gstcurlftpsink.c \ + gstcurlsmtpsink.c libgstcurl_la_CFLAGS = \ $(GST_PLUGINS_BAD_CFLAGS) \ $(GST_BASE_CFLAGS) \ @@ -23,4 +24,5 @@ noinst_HEADERS = gstcurlbasesink.h \ gstcurltlssink.h \ gstcurlhttpsink.h \ gstcurlfilesink.h \ - gstcurlftpsink.h + gstcurlftpsink.h \ + gstcurlsmtpsink.h diff --git a/ext/curl/gstcurl.c b/ext/curl/gstcurl.c index 5cf278cfc..c6b9df4c7 100644 --- a/ext/curl/gstcurl.c +++ b/ext/curl/gstcurl.c @@ -25,6 +25,7 @@ #include "gstcurlhttpsink.h" #include "gstcurlfilesink.h" #include "gstcurlftpsink.h" +#include "gstcurlsmtpsink.h" static gboolean plugin_init (GstPlugin * plugin) @@ -42,6 +43,10 @@ plugin_init (GstPlugin * plugin) GST_TYPE_CURL_FTP_SINK)) return FALSE; + if (!gst_element_register (plugin, "curlsmtpsink", GST_RANK_NONE, + GST_TYPE_CURL_SMTP_SINK)) + return FALSE; + return TRUE; } diff --git a/ext/curl/gstcurlsmtpsink.c b/ext/curl/gstcurlsmtpsink.c new file mode 100644 index 000000000..283f86a3f --- /dev/null +++ b/ext/curl/gstcurlsmtpsink.c @@ -0,0 +1,928 @@ +/* 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 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 BOUNDARY_STRING_END "--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); + +#define gst_curl_smtp_sink_parent_class parent_class +G_DEFINE_TYPE (GstCurlSmtpSink, gst_curl_smtp_sink, GST_TYPE_CURL_TLS_SINK); + +static void +gst_curl_smtp_sink_notify_transfer_end_unlocked (GstCurlSmtpSink * sink) +{ + GST_LOG ("transfer completed: %d", sink->transfer_end); + sink->transfer_end = TRUE; + g_cond_signal (sink->cond_transfer_end); +} + +static void +gst_curl_smtp_sink_wait_for_transfer_end_unlocked (GstCurlSmtpSink * sink) +{ + GST_LOG ("waiting for final data do be sent: %d", sink->transfer_end); + + while (!sink->transfer_end) { + g_cond_wait (sink->cond_transfer_end, GST_OBJECT_GET_LOCK (sink)); + } + GST_LOG ("final data sent"); +} + +static gboolean +gst_curl_smtp_sink_event (GstBaseSink * bsink, GstEvent * event) +{ + GstCurlBaseSink *bcsink = GST_CURL_BASE_SINK (bsink); + GstCurlSmtpSink *sink = GST_CURL_SMTP_SINK (bsink); + + GByteArray *array; + gchar *boundary_end; + + switch (event->type) { + case GST_EVENT_EOS: + GST_DEBUG_OBJECT (sink, "received EOS"); + gst_curl_base_sink_set_live (bcsink, FALSE); + + GST_OBJECT_LOCK (sink); + sink->eos = TRUE; + GST_OBJECT_UNLOCK (sink); + + if (sink->base64_chunk != NULL) { + gsize len; + gint save, state; + gchar *data_out; + + array = sink->base64_chunk->chunk_array; + g_assert (array); + + GST_DEBUG ("adding final boundary"); + + /* it will need up to 5 bytes if line-breaking is enabled + * additional byte is needed for <CR> as it is not automatically added by glib */ + data_out = g_malloc (6); + save = sink->base64_chunk->save; + state = sink->base64_chunk->state; + len = g_base64_encode_close (TRUE, data_out, &state, &save); + /* workaround */ + data_out[len - 1] = '\r'; + data_out[len] = '\n'; + /* +1 for CR */ + g_byte_array_append (array, (guint8 *) data_out, (guint) (len + 1)); + g_free (data_out); + + boundary_end = g_strdup_printf ("\r\n%s\r\n", BOUNDARY_STRING_END); + g_byte_array_append (array, (guint8 *) boundary_end, + strlen (boundary_end)); + g_free (boundary_end); + } + + gst_curl_base_sink_transfer_thread_notify_unlocked (bcsink); + + GST_OBJECT_LOCK (sink); + if (sink->base64_chunk != NULL && bcsink->flow_ret == GST_FLOW_OK) { + gst_curl_smtp_sink_wait_for_transfer_end_unlocked (sink); + } + GST_OBJECT_UNLOCK (sink); + + gst_curl_base_sink_transfer_thread_close (bcsink); + + break; + + default: + break; + } + return TRUE; +} + +static void +gst_curl_smtp_sink_class_init (GstCurlSmtpSinkClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GstBaseSinkClass *gstbasesink_class = (GstBaseSinkClass *) klass; + GstCurlBaseSinkClass *gstcurlbasesink_class = (GstCurlBaseSinkClass *) klass; + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + + GST_DEBUG_CATEGORY_INIT (gst_curl_smtp_sink_debug, "curlsmtpsink", 0, + "curl smtp sink element"); + GST_DEBUG_OBJECT (klass, "class_init"); + + 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>"); + + 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; + + gstbasesink_class->event = gst_curl_smtp_sink_event; + 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", "UTF-8 encoded 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", "UTF-8 encoded 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) +{ + 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->cond_transfer_end = g_cond_new (); + sink->transfer_end = FALSE; + sink->eos = FALSE; + + 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); + + g_cond_free (this->cond_transfer_end); + + 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 append_headers = FALSE; + + if (sink->reset_transfer_options) { + g_assert (!bcsink->is_live); + sink->reset_transfer_options = FALSE; + + /* all data has been sent in the previous transfer, setup headers for + * a new transfer */ + gst_curl_smtp_sink_set_transfer_options_unlocked (bcsink); + append_headers = TRUE; + } + + if (sink->payload_headers == NULL) { + sink->payload_headers = g_byte_array_new (); + append_headers = TRUE; + } + + if (sink->base64_chunk == NULL) { + g_assert (!bcsink->is_live); + /* we are just about to send the very first attachment in this transfer. + * This is the only place where base64_chunk and its array are allocated. + */ + sink->base64_chunk = g_malloc (sizeof (Base64Chunk)); + sink->base64_chunk->chunk_array = g_byte_array_new (); + append_headers = TRUE; + } 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; + + if (G_UNLIKELY (!append_headers)) { + if (sink->base64_chunk != NULL) { + g_byte_array_free (sink->base64_chunk->chunk_array, TRUE); + sink->base64_chunk->chunk_array = NULL; + g_free (sink->base64_chunk); + sink->base64_chunk = NULL; + } + return FALSE; + } + + hdrs = g_strdup_printf ("\r\n\r\n--%s\r\n" + "Content-Type: application/octet-stream; name=\"%s\"\r\n" + /* TODO: support for other encodings */ + "Content-Transfer-Encoding: BASE64\r\n" + "Content-Disposition: attachment; filename=\"%s\"\r\n\r\n" + "\r\n", BOUNDARY_STRING, bcsink->file_name, bcsink->file_name); + g_byte_array_append (sink->payload_headers, (guint8 *) hdrs, strlen (hdrs)); + g_free (hdrs); + + return TRUE; +} + +/* MIME encoded-word syntax (RFC 2047): + * =?charset?encoding?encoded text?= */ +static gchar * +generate_encoded_word (gchar * str) +{ + gchar *encoded_word; + + g_assert (str); + + if (g_utf8_validate (str, -1, NULL)) { + gchar *base64_str; + + base64_str = g_base64_encode ((const guchar *) str, strlen (str)); + encoded_word = g_strdup_printf ("=?utf-8?B?%s?=", base64_str); + g_free (base64_str); + } else { + GST_WARNING ("string is not a valid UTF-8 string"); + encoded_word = g_strdup (str); + } + + /* TODO: 75 character limit */ + return encoded_word; +} + +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; + gchar *subject_header = NULL; + gchar *message_body = NULL; + gchar *rcpt_header = NULL; + gchar *enc_rcpt; + gchar *from_header = NULL; + gchar *enc_from; + gint i; + + g_assert (sink->payload_headers == NULL); + g_assert (sink->mail_rcpt != NULL); + g_assert (sink->mail_from != 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); + + /* recipient, sender and subject are all UTF-8 strings, which are additionally + * base64-encoded */ + + /* recipient */ + enc_rcpt = generate_encoded_word (sink->mail_rcpt); + rcpt_header = g_strdup_printf ("%s <%s>", enc_rcpt, sink->mail_rcpt); + g_free (enc_rcpt); + + /* sender */ + enc_from = generate_encoded_word (sink->mail_from); + from_header = g_strdup_printf ("%s <%s>", enc_from, sink->mail_from); + g_free (enc_from); + + /* subject */ + if (sink->subject != NULL) { + subject_header = generate_encoded_word (sink->subject); + } + + /* message */ + if (sink->message_body != NULL) { + message_body = g_base64_encode ((const guchar *) sink->message_body, + strlen (sink->message_body)); + } + + request_headers = g_strdup_printf ( + /* headers */ + "To: %s\r\n" + "From: %s\r\n" + "Subject: %s\r\n" + "Date: %s\r\n" + MIME_VERSION "\r\n" + "Content-Type: multipart/mixed; boundary=%s\r\n" "\r\n" + /* body headers */ + "--" BOUNDARY_STRING "\r\n" + "Content-Type: text/plain; charset=utf-8\r\n" + "Content-Transfer-Encoding: BASE64\r\n" + /* message body */ + "\r\n%s\r\n", + rcpt_header, + from_header, + subject_header ? subject_header : "", + date_str, BOUNDARY_STRING, message_body ? 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 (subject_header); + g_free (message_body); + g_free (rcpt_header); + g_free (from_header); + 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) || sink->eos) { + bcsink->is_live = FALSE; + sink->reset_transfer_options = TRUE; + + GST_DEBUG ("returning 0, no more data to send in this transfer"); + + return 0; + } + + /* it will need up to 5 bytes if line-breaking is enabled, however an + * additional byte is needed for <CR> as it is not automatically added by glib */ + data_out = g_malloc (6); + len = g_base64_encode_close (TRUE, data_out, &state, &save); + chunk->state = state; + chunk->save = save; + /* workaround */ + data_out[len - 1] = '\r'; + data_out[len] = '\n'; + /* +1 for CR */ + g_byte_array_append (array, (guint8 *) data_out, (guint) (len + 1)); + g_free (data_out); + + 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); + } + + bytes_to_send = MIN (block_size, array->len); + memcpy ((guint8 *) curl_ptr, array->data, bytes_to_send); + g_byte_array_remove_range (array, 0, bytes_to_send); + + 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; + gchar *ptr_in; + gchar *ptr_out; + gsize size_out; + gint i; + + /* if line-breaking is enabled, at least: ((len / 3 + 1) * 4 + 4) / 72 + 1 + * bytes of extra space is required. However, additional <CR>'s are required, + * thus we need ((len / 3 + 2) * 4 + 4) / 72 + 2 extra bytes. + */ + size_out = (bytes_to_send / 3 + 1) * 4 + 4 + bytes_to_send + + ((bytes_to_send / 3 + 2) * 4 + 4) / 72 + 2; + + data_out = g_malloc (size_out); + len = g_base64_encode_step (data_in + data_in_offset, bytes_to_send, TRUE, + data_out, &state, &save); + chunk->state = state; + chunk->save = save; + + /* LF->CRLF filter */ + ptr_in = ptr_out = data_out; + for (i = 0; i < len; i++) { + if (*ptr_in == '\n') { + *ptr_in = '\r'; + g_byte_array_append (array, (guint8 *) ptr_out, ptr_in - ptr_out); + g_byte_array_append (array, (guint8 *) "\r\n", strlen ("\r\n")); + ptr_out = ptr_in + 1; + } + ptr_in++; + } + if (ptr_in - ptr_out) { + g_byte_array_append (array, (guint8 *) ptr_out, ptr_in - ptr_out); + } + + 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 ((guint8 *) curl_ptr, 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 ((guint8 *) curl_ptr, 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); + + GST_OBJECT_LOCK (sink); + if (sink->eos) { + gst_curl_smtp_sink_notify_transfer_end_unlocked (sink); + } + GST_OBJECT_UNLOCK (sink); + + 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; +} diff --git a/ext/curl/gstcurlsmtpsink.h b/ext/curl/gstcurlsmtpsink.h new file mode 100644 index 000000000..ce8dc20cf --- /dev/null +++ b/ext/curl/gstcurlsmtpsink.h @@ -0,0 +1,87 @@ +/* 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. + */ + +#ifndef __GST_CURL_SMTP_SINK__ +#define __GST_CURL_SMTP_SINK__ + +#include <gst/gst.h> +#include <gst/base/gstbasesink.h> +#include <curl/curl.h> +#include "gstcurltlssink.h" + +G_BEGIN_DECLS +#define GST_TYPE_CURL_SMTP_SINK \ + (gst_curl_smtp_sink_get_type()) +#define GST_CURL_SMTP_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_CURL_SMTP_SINK, GstCurlSmtpSink)) +#define GST_CURL_SMTP_SINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_CURL_SMTP_SINK, GstCurlSmtpSinkClass)) +#define GST_IS_CURL_SMTP_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_CURL_SMTP_SINK)) +#define GST_IS_CURL_SMTP_SINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_CURL_SMTP_SINK)) +typedef struct _GstCurlSmtpSink GstCurlSmtpSink; +typedef struct _GstCurlSmtpSinkClass GstCurlSmtpSinkClass; + +typedef struct _Base64Chunk Base64Chunk; + +struct _Base64Chunk +{ + GByteArray *chunk_array; + gint save; + gint state; +}; + +struct _GstCurlSmtpSink +{ + GstCurlTlsSink parent; + + /*< private > */ + Base64Chunk *base64_chunk; + GByteArray *payload_headers; + struct curl_slist *curl_recipients; + gchar *mail_rcpt; + gchar *mail_from; + gchar *subject; + gchar *message_body; + gchar *content_type; + gboolean use_ssl; + gint nbr_attachments; + gchar *pop_user; + gchar *pop_passwd; + gchar *pop_location; + CURL *pop_curl; + + gboolean transfer_end; + GCond *cond_transfer_end; + + gint nbr_attachments_left; + gboolean reset_transfer_options; + gboolean eos; +}; + +struct _GstCurlSmtpSinkClass +{ + GstCurlTlsSinkClass parent_class; +}; + +GType gst_curl_smtp_sink_get_type (void); + +G_END_DECLS +#endif diff --git a/tests/check/Makefile.am b/tests/check/Makefile.am index a5c1ca229..e4e6aa9a6 100644 --- a/tests/check/Makefile.am +++ b/tests/check/Makefile.am @@ -150,7 +150,8 @@ endif if USE_CURL check_curl = elements/curlhttpsink \ elements/curlfilesink \ - elements/curlftpsink + elements/curlftpsink \ + elements/curlsmtpsink else check_curl = endif diff --git a/tests/check/elements/curlsmtpsink.c b/tests/check/elements/curlsmtpsink.c new file mode 100644 index 000000000..8b401e129 --- /dev/null +++ b/tests/check/elements/curlsmtpsink.c @@ -0,0 +1,208 @@ +/* + * Unittest for curlsmtpsink + */ + +#include <gst/check/gstcheck.h> +#include <glib/gstdio.h> +#include <curl/curl.h> + +static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +static GstPad *srcpad; + +static GstElement *sink; + +static GstElement * +setup_curlsmtpsink (void) +{ + GST_DEBUG ("setup_curlsmtpsink"); + sink = gst_check_setup_element ("curlsmtpsink"); + srcpad = gst_check_setup_src_pad (sink, &srctemplate); + gst_pad_set_active (srcpad, TRUE); + + return sink; +} + +static void +cleanup_curlsmtpsink (GstElement * sink) +{ + GST_DEBUG ("cleanup_curlsmtpsink"); + + gst_check_teardown_src_pad (sink); + gst_check_teardown_element (sink); +} + +GST_START_TEST (test_properties) +{ + GstElement *sink; + gchar *res_location = NULL; + gchar *res_file_name = NULL; + gchar *res_mail_rcpt = NULL; + gchar *res_mail_from = NULL; + gchar *res_subj = NULL; + gchar *res_msg = NULL; + gchar *res_usr = NULL; + gchar *res_passwd = NULL; + gchar *res_pop_location = NULL; + gchar *res_pop_usr = NULL; + gchar *res_pop_passwd = NULL; + guint res_nbr_attach; + gboolean res_use_ssl; + + sink = setup_curlsmtpsink (); + + g_object_set (G_OBJECT (sink), "location", "mylocation", NULL); + g_object_set (G_OBJECT (sink), "file-name", "myfile", NULL); + g_object_set (G_OBJECT (sink), "user", "usr", NULL); + g_object_set (G_OBJECT (sink), "passwd", "passwd", NULL); + g_object_set (G_OBJECT (sink), "mail-rcpt", "rcpt", NULL); + g_object_set (G_OBJECT (sink), "mail-from", "sender", NULL); + g_object_set (G_OBJECT (sink), "subject", "subject", NULL); + g_object_set (G_OBJECT (sink), "message-body", "message", NULL); + g_object_set (G_OBJECT (sink), "nbr-attachments", 5, NULL); + g_object_set (G_OBJECT (sink), "use-ssl", TRUE, NULL); + g_object_set (G_OBJECT (sink), "pop-location", "poploc", NULL); + g_object_set (G_OBJECT (sink), "pop-user", "popusr", NULL); + g_object_set (G_OBJECT (sink), "pop-passwd", "poppasswd", NULL); + + g_object_get (sink, + "location", &res_location, + "file-name", &res_file_name, + "user", &res_usr, + "passwd", &res_passwd, + "mail-rcpt", &res_mail_rcpt, + "mail-from", &res_mail_from, + "subject", &res_subj, + "message-body", &res_msg, + "nbr-attachments", &res_nbr_attach, + "use-ssl", &res_use_ssl, + "pop-location", &res_pop_location, + "pop_user", &res_pop_usr, + "pop-passwd", &res_pop_passwd, + NULL); + + fail_unless (strncmp (res_location, "mylocation", strlen ("mylocation")) + == 0); + fail_unless (strncmp (res_file_name, "myfile", strlen ("myfile")) + == 0); + fail_unless (strncmp (res_usr, "usr", strlen ("usr")) + == 0); + fail_unless (strncmp (res_passwd, "passwd", strlen ("passwd")) + == 0); + fail_unless (strncmp (res_mail_rcpt, "rcpt", strlen ("rcpt")) + == 0); + fail_unless (strncmp (res_mail_from, "sender", strlen ("sender")) + == 0); + fail_unless (strncmp (res_subj, "subject", strlen ("subject")) + == 0); + fail_unless (strncmp (res_msg, "message", strlen ("message")) + == 0); + fail_unless (strncmp (res_pop_location, "poploc", strlen ("poploc")) + == 0); + fail_unless (strncmp (res_pop_usr, "popusr", strlen ("popusr")) + == 0); + fail_unless (strncmp (res_pop_passwd, "poppasswd", strlen ("poppasswd")) + == 0); + fail_unless (res_nbr_attach == 5); + fail_unless (res_use_ssl == TRUE); + g_free (res_location); + g_free (res_file_name); + g_free (res_usr); + g_free (res_passwd); + g_free (res_mail_rcpt); + g_free (res_mail_from); + g_free (res_subj); + g_free (res_msg); + g_free (res_pop_location); + g_free (res_pop_usr); + g_free (res_pop_passwd); + + /* change properties */ + g_object_set (G_OBJECT (sink), "location", "newlocation", NULL); + g_object_set (G_OBJECT (sink), "file-name", "newfilename", NULL); + g_object_set (G_OBJECT (sink), "user", "newusr", NULL); + g_object_set (G_OBJECT (sink), "passwd", "newpasswd", NULL); + g_object_set (G_OBJECT (sink), "mail-rcpt", "rcpt1,rcpt2,rcpt3", NULL); + g_object_set (G_OBJECT (sink), "mail-from", "newsender", NULL); + g_object_set (G_OBJECT (sink), "subject", "newsubject", NULL); + g_object_set (G_OBJECT (sink), "message-body", "newmessage", NULL); + g_object_set (G_OBJECT (sink), "nbr-attachments", 1, NULL); + g_object_set (G_OBJECT (sink), "use-ssl", FALSE, NULL); + g_object_set (G_OBJECT (sink), "pop-location", "newpoploc", NULL); + g_object_set (G_OBJECT (sink), "pop-user", "newpopusr", NULL); + g_object_set (G_OBJECT (sink), "pop-passwd", "newpoppasswd", NULL); + + g_object_get (sink, + "location", &res_location, + "file-name", &res_file_name, + "user", &res_usr, + "passwd", &res_passwd, + "pop_user", &res_pop_usr, + "pop-passwd", &res_pop_passwd, + "pop-location", &res_pop_location, + "nbr-attachments", &res_nbr_attach, + "subject", &res_subj, + "use-ssl", &res_use_ssl, + "message-body", &res_msg, + "mail-from", &res_mail_from, + "mail-rcpt", &res_mail_rcpt, + NULL); + + fail_unless (strncmp (res_location, "newlocation", strlen ("newlocation")) + == 0); + fail_unless (strncmp (res_file_name, "newfilename", strlen ("newfilename")) + == 0); + fail_unless (strncmp (res_usr, "newusr", strlen ("newusr")) + == 0); + fail_unless (strncmp (res_passwd, "newpasswd", strlen ("newpasswd")) + == 0); + fail_unless (strncmp (res_mail_rcpt, "rcpt1,rcpt2,rcpt3", + strlen ("rcpt1,rcpt2,rcpt3")) == 0); + fail_unless (strncmp (res_mail_from, "newsender", strlen ("newsender")) + == 0); + fail_unless (strncmp (res_subj, "newsubject", strlen ("newsubject")) + == 0); + fail_unless (strncmp (res_msg, "newmessage", strlen ("newmessage")) + == 0); + fail_unless (strncmp (res_pop_location, "newpoploc", strlen ("newpoploc")) + == 0); + fail_unless (strncmp (res_pop_usr, "newpopusr", strlen ("newpopusr")) + == 0); + fail_unless (strncmp (res_pop_passwd, "newpoppasswd", strlen ("newpoppasswd")) + == 0); + + fail_unless (res_nbr_attach == 1); + fail_unless (res_use_ssl == FALSE); + g_free (res_location); + g_free (res_file_name); + g_free (res_usr); + g_free (res_passwd); + g_free (res_mail_from); + g_free (res_mail_rcpt); + g_free (res_subj); + g_free (res_msg); + g_free (res_pop_location); + g_free (res_pop_usr); + g_free (res_pop_passwd); + + cleanup_curlsmtpsink (sink); +} +GST_END_TEST; + +static Suite * +curlsink_suite (void) +{ + Suite *s = suite_create ("curlsmtpsink"); + TCase *tc_chain = tcase_create ("general"); + + suite_add_tcase (s, tc_chain); + tcase_set_timeout (tc_chain, 20); + tcase_add_test (tc_chain, test_properties); + + return s; +} + +GST_CHECK_MAIN (curlsink); |