From 766dd8bb717b1ba5317fbd2c5d7860d6beb34834 Mon Sep 17 00:00:00 2001 From: Patricia Muscalu Date: Mon, 23 Jan 2012 09:45:24 +0100 Subject: [PATCH] curl: new curlftpsink element http://bugzilla.gnome.org/show_bug.cgi?id=653741 --- ext/curl/Makefile.am | 6 +- ext/curl/gstcurl.c | 5 + ext/curl/gstcurlftpsink.c | 262 +++++++++++++++++++++++++++++++++++++ ext/curl/gstcurlftpsink.h | 60 +++++++++ tests/check/Makefile.am | 3 +- tests/check/elements/curlftpsink.c | 119 +++++++++++++++++ 6 files changed, 452 insertions(+), 3 deletions(-) create mode 100644 ext/curl/gstcurlftpsink.c create mode 100644 ext/curl/gstcurlftpsink.h create mode 100644 tests/check/elements/curlftpsink.c diff --git a/ext/curl/Makefile.am b/ext/curl/Makefile.am index fb2fe2e..2f68b7c 100644 --- a/ext/curl/Makefile.am +++ b/ext/curl/Makefile.am @@ -4,7 +4,8 @@ libgstcurl_la_SOURCES = gstcurl.c \ gstcurlbasesink.c \ gstcurltlssink.c \ gstcurlhttpsink.c \ - gstcurlfilesink.c + gstcurlfilesink.c \ + gstcurlftpsink.c libgstcurl_la_CFLAGS = \ $(GST_PLUGINS_BAD_CFLAGS) \ $(GST_BASE_CFLAGS) \ @@ -21,4 +22,5 @@ libgstcurl_la_LIBTOOLFLAGS = --tag=disable-static noinst_HEADERS = gstcurlbasesink.h \ gstcurltlssink.h \ gstcurlhttpsink.h \ - gstcurlfilesink.h + gstcurlfilesink.h \ + gstcurlftpsink.h diff --git a/ext/curl/gstcurl.c b/ext/curl/gstcurl.c index 4bc96e9..5cf278c 100644 --- a/ext/curl/gstcurl.c +++ b/ext/curl/gstcurl.c @@ -24,6 +24,7 @@ #include "gstcurltlssink.h" #include "gstcurlhttpsink.h" #include "gstcurlfilesink.h" +#include "gstcurlftpsink.h" static gboolean plugin_init (GstPlugin * plugin) @@ -37,6 +38,10 @@ plugin_init (GstPlugin * plugin) GST_TYPE_CURL_FILE_SINK)) return FALSE; + if (!gst_element_register (plugin, "curlftpsink", GST_RANK_NONE, + GST_TYPE_CURL_FTP_SINK)) + return FALSE; + return TRUE; } diff --git a/ext/curl/gstcurlftpsink.c b/ext/curl/gstcurlftpsink.c new file mode 100644 index 0000000..d535657 --- /dev/null +++ b/ext/curl/gstcurlftpsink.c @@ -0,0 +1,262 @@ +/* GStreamer + * Copyright (C) 2011 Axis Communications + * + * 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-curlftpsink + * @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 FTP server. + * + * + * Example launch line (upload a JPEG file to /home/test/images directory) + * |[ + * gst-launch filesrc location=image.jpg ! jpegparse ! curlftpsink \ + * file-name=image.jpg \ + * location=ftp://192.168.0.1/images/ + * ]| + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gstcurltlssink.h" +#include "gstcurlftpsink.h" + +/* Default values */ +#define GST_CAT_DEFAULT gst_curl_ftp_sink_debug + + +/* Plugin specific settings */ + +GST_DEBUG_CATEGORY_STATIC (gst_curl_ftp_sink_debug); + +enum +{ + PROP_0, + PROP_FTP_PORT_ARG, + PROP_EPSV_MODE, + PROP_CREATE_DIRS +}; + + +/* Object class function declarations */ + + +/* private functions */ +static void gst_curl_ftp_sink_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_curl_ftp_sink_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); +static void gst_curl_ftp_sink_finalize (GObject * gobject); + +static gboolean set_ftp_options_unlocked (GstCurlBaseSink * curlbasesink); +static gboolean set_ftp_dynamic_options_unlocked + (GstCurlBaseSink * curlbasesink); + +#define gst_curl_ftp_sink_parent_class parent_class +G_DEFINE_TYPE (GstCurlFtpSink, gst_curl_ftp_sink, GST_TYPE_CURL_TLS_SINK); + +static void +gst_curl_ftp_sink_class_init (GstCurlFtpSinkClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GstCurlBaseSinkClass *gstcurlbasesink_class = (GstCurlBaseSinkClass *) klass; + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + + GST_DEBUG_CATEGORY_INIT (gst_curl_ftp_sink_debug, "curlftpsink", 0, + "curl ftp sink element"); + GST_DEBUG_OBJECT (klass, "class_init"); + + gst_element_class_set_details_simple (element_class, + "Curl ftp sink", + "Sink/Network", + "Upload data over FTP protocol using libcurl", + "Patricia Muscalu "); + + gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_curl_ftp_sink_finalize); + + gobject_class->set_property = gst_curl_ftp_sink_set_property; + gobject_class->get_property = gst_curl_ftp_sink_get_property; + + gstcurlbasesink_class->set_protocol_dynamic_options_unlocked = + set_ftp_dynamic_options_unlocked; + gstcurlbasesink_class->set_options_unlocked = set_ftp_options_unlocked; + + g_object_class_install_property (gobject_class, PROP_FTP_PORT_ARG, + g_param_spec_string ("ftp-port", "IP address for FTP PORT instruction", + "The PORT instruction tells the remote server to connect to" + " the IP address", "", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_EPSV_MODE, + g_param_spec_boolean ("epsv-mode", "Extended passive mode", + "Enable the use of the EPSV command when doing passive FTP transfers", + TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_CREATE_DIRS, + g_param_spec_boolean ("create-dirs", "Create missing directories", + "Attempt to create missing directory included in the path", + FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); +} + +static void +gst_curl_ftp_sink_init (GstCurlFtpSink * sink) +{ +} + +static void +gst_curl_ftp_sink_finalize (GObject * gobject) +{ + GstCurlFtpSink *this = GST_CURL_FTP_SINK (gobject); + + GST_DEBUG ("finalizing curlftpsink"); + g_free (this->ftp_port_arg); + + G_OBJECT_CLASS (parent_class)->finalize (gobject); +} + +static gboolean +set_ftp_dynamic_options_unlocked (GstCurlBaseSink * basesink) +{ + gchar *tmp = g_strdup_printf ("%s%s", basesink->url, basesink->file_name); + + curl_easy_setopt (basesink->curl, CURLOPT_URL, tmp); + + g_free (tmp); + + return TRUE; +} + +static gboolean +set_ftp_options_unlocked (GstCurlBaseSink * basesink) +{ + GstCurlFtpSink *sink = GST_CURL_FTP_SINK (basesink); + + curl_easy_setopt (basesink->curl, CURLOPT_UPLOAD, 1L); + + if (sink->ftp_port_arg != NULL && (strlen (sink->ftp_port_arg) > 0)) { + /* Connect data stream actively. */ + CURLcode res = curl_easy_setopt (basesink->curl, CURLOPT_FTPPORT, + sink->ftp_port_arg); + + if (res != CURLE_OK) { + GST_DEBUG_OBJECT (sink, "Failed to set up active mode: %s", + curl_easy_strerror (res)); + GST_ELEMENT_ERROR (sink, RESOURCE, WRITE, + ("Failed to set up active mode: %s", curl_easy_strerror (res)), + (NULL)); + + return FALSE; + } + + goto end; + } + + /* Connect data stream passively. + * libcurl will always attempt to use EPSV before PASV. + */ + if (!sink->epsv_mode) { + /* send only plain PASV command */ + curl_easy_setopt (basesink->curl, CURLOPT_FTP_USE_EPSV, 0); + } + +end: + if (sink->create_dirs) { + curl_easy_setopt (basesink->curl, CURLOPT_FTP_CREATE_MISSING_DIRS, 1L); + } + + return TRUE; +} + +static void +gst_curl_ftp_sink_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstCurlFtpSink *sink; + GstState cur_state; + + g_return_if_fail (GST_IS_CURL_FTP_SINK (object)); + sink = GST_CURL_FTP_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_FTP_PORT_ARG: + g_free (sink->ftp_port_arg); + sink->ftp_port_arg = g_value_dup_string (value); + GST_DEBUG_OBJECT (sink, "ftp-port set to %s", sink->ftp_port_arg); + break; + case PROP_EPSV_MODE: + sink->epsv_mode = g_value_get_boolean (value); + GST_DEBUG_OBJECT (sink, "epsv-mode set to %d", sink->epsv_mode); + break; + case PROP_CREATE_DIRS: + sink->create_dirs = g_value_get_boolean (value); + GST_DEBUG_OBJECT (sink, "create-dirs set to %d", sink->create_dirs); + break; + + default: + GST_DEBUG_OBJECT (sink, "invalid property id %d", prop_id); + break; + } + + GST_OBJECT_UNLOCK (sink); + } +} + +static void +gst_curl_ftp_sink_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstCurlFtpSink *sink; + + g_return_if_fail (GST_IS_CURL_FTP_SINK (object)); + sink = GST_CURL_FTP_SINK (object); + + switch (prop_id) { + case PROP_FTP_PORT_ARG: + g_value_set_string (value, sink->ftp_port_arg); + break; + case PROP_EPSV_MODE: + g_value_set_boolean (value, sink->epsv_mode); + break; + case PROP_CREATE_DIRS: + g_value_set_boolean (value, sink->create_dirs); + break; + default: + GST_DEBUG_OBJECT (sink, "invalid property id"); + break; + } +} diff --git a/ext/curl/gstcurlftpsink.h b/ext/curl/gstcurlftpsink.h new file mode 100644 index 0000000..2f9b6c3 --- /dev/null +++ b/ext/curl/gstcurlftpsink.h @@ -0,0 +1,60 @@ +/* GStreamer + * Copyright (C) 2011 Axis Communications + * + * 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_FTP_SINK__ +#define __GST_CURL_FTP_SINK__ + +#include +#include +#include +#include "gstcurltlssink.h" + +G_BEGIN_DECLS +#define GST_TYPE_CURL_FTP_SINK \ + (gst_curl_ftp_sink_get_type()) +#define GST_CURL_FTP_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_CURL_FTP_SINK, GstCurlFtpSink)) +#define GST_CURL_FTP_SINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_CURL_FTP_SINK, GstCurlFtpSinkClass)) +#define GST_IS_CURL_FTP_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_CURL_FTP_SINK)) +#define GST_IS_CURL_FTP_SINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_CURL_FTP_SINK)) +typedef struct _GstCurlFtpSink GstCurlFtpSink; +typedef struct _GstCurlFtpSinkClass GstCurlFtpSinkClass; + +struct _GstCurlFtpSink +{ + GstCurlTlsSink parent; + + /*< private > */ + gchar *ftp_port_arg; + gboolean epsv_mode; + gboolean create_dirs; +}; + +struct _GstCurlFtpSinkClass +{ + GstCurlTlsSinkClass parent_class; +}; + +GType gst_curl_ftp_sink_get_type (void); + +G_END_DECLS +#endif diff --git a/tests/check/Makefile.am b/tests/check/Makefile.am index 87ea1a7..a5c1ca2 100644 --- a/tests/check/Makefile.am +++ b/tests/check/Makefile.am @@ -149,7 +149,8 @@ endif if USE_CURL check_curl = elements/curlhttpsink \ - elements/curlfilesink + elements/curlfilesink \ + elements/curlftpsink else check_curl = endif diff --git a/tests/check/elements/curlftpsink.c b/tests/check/elements/curlftpsink.c new file mode 100644 index 0000000..05a9f60 --- /dev/null +++ b/tests/check/elements/curlftpsink.c @@ -0,0 +1,119 @@ +/* + * Unittest for curlftpsink + */ + +#include +#include +#include + +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_curlftpsink (void) +{ + GST_DEBUG ("setup_curlftpsink"); + sink = gst_check_setup_element ("curlftpsink"); + srcpad = gst_check_setup_src_pad (sink, &srctemplate); + gst_pad_set_active (srcpad, TRUE); + + return sink; +} + +static void +cleanup_curlftpsink (GstElement * sink) +{ + GST_DEBUG ("cleanup_curlftpsink"); + + 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_ftp_port = NULL; + gboolean res_epsv_mode; + gboolean res_create_dirs; + + sink = setup_curlftpsink (); + + 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), "ftp-port", "1.2.3.4:0", NULL); + g_object_set (G_OBJECT (sink), "epsv-mode", FALSE, NULL); + g_object_set (G_OBJECT (sink), "create-dirs", FALSE, NULL); + + g_object_get (sink, + "location", &res_location, + "file-name", &res_file_name, + "ftp-port", &res_ftp_port, + "epsv-mode", &res_epsv_mode, + "create-dirs", &res_create_dirs, + NULL); + + fail_unless (strncmp (res_location, "mylocation", strlen ("mylocation")) + == 0); + fail_unless (strncmp (res_file_name, "myfile", strlen ("myfile")) + == 0); + fail_unless (strncmp (res_ftp_port, "1.2.3.4:0", strlen ("1.2.3.4:0")) + == 0); + fail_unless (res_epsv_mode == FALSE); + fail_unless (res_create_dirs == FALSE); + g_free (res_location); + g_free (res_file_name); + g_free (res_ftp_port); + + /* 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), "ftp-port", "", NULL); + g_object_set (G_OBJECT (sink), "epsv-mode", TRUE, NULL); + g_object_set (G_OBJECT (sink), "create-dirs", TRUE, NULL); + + g_object_get (sink, + "location", &res_location, + "file-name", &res_file_name, + "ftp-port", &res_ftp_port, + "epsv-mode", &res_epsv_mode, + "create-dirs", &res_create_dirs, + NULL); + + fail_unless (strncmp (res_location, "newlocation", strlen ("newlocation")) + == 0); + fail_unless (strncmp (res_file_name, "newfilename", strlen ("newfilename")) + == 0); + fail_unless (strncmp (res_ftp_port, "", strlen ("")) + == 0); + fail_unless (res_epsv_mode == TRUE); + fail_unless (res_create_dirs == TRUE); + g_free (res_location); + g_free (res_file_name); + g_free (res_ftp_port); + + cleanup_curlftpsink (sink); +} +GST_END_TEST; + +static Suite * +curlsink_suite (void) +{ + Suite *s = suite_create ("curlftpsink"); + 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); -- 2.7.4