Add FTP uploader using curl.
authorCarl Hetherington <cth@carlh.net>
Fri, 17 Jul 2015 14:37:23 +0000 (15:37 +0100)
committerCarl Hetherington <cth@carlh.net>
Fri, 17 Jul 2015 14:40:56 +0000 (15:40 +0100)
src/lib/config.cc
src/lib/config.h
src/lib/curl_uploader.cc [new file with mode: 0644]
src/lib/curl_uploader.h [new file with mode: 0644]
src/lib/scp_uploader.cc
src/lib/types.h
src/lib/upload_job.cc
src/lib/uploader.cc
src/lib/uploader.h
src/lib/wscript
src/wx/config_dialog.cc

index b5e87267ad9ec6edca742b1c0c64dbaffa56869b..6eecc8574b9561e8d3649ea72aad94492bb5edb3 100644 (file)
@@ -21,6 +21,7 @@
 #include "server.h"
 #include "filter.h"
 #include "ratio.h"
+#include "types.h"
 #include "dcp_content_type.h"
 #include "cinema_sound_processor.h"
 #include "colour_conversion.h"
@@ -67,6 +68,7 @@ Config::set_defaults ()
        _num_local_encoding_threads = max (2U, boost::thread::hardware_concurrency ());
        _server_port_base = 6192;
        _use_any_servers = true;
+       _tms_protocol = PROTOCOL_SCP;
        _tms_path = ".";
        _cinema_sound_processor = CinemaSoundProcessor::from_id (N_("dolby_cp750"));
        _allow_any_dcp_frame_rate = false;
@@ -140,6 +142,7 @@ Config::read ()
                }
        }
 
+       _tms_protocol = static_cast<Protocol> (f.optional_number_child<int> ("TMSProtocol").get_value_or (static_cast<int> (PROTOCOL_SCP)));
        _tms_ip = f.string_child ("TMSIP");
        _tms_path = f.string_child ("TMSPath");
        _tms_user = f.string_child ("TMSUser");
@@ -315,6 +318,7 @@ Config::write () const
                root->add_child("Server")->add_child_text (*i);
        }
 
+       root->add_child("TMSProtocol")->add_child_text (raw_convert<string> (_tms_protocol));
        root->add_child("TMSIP")->add_child_text (_tms_ip);
        root->add_child("TMSPath")->add_child_text (_tms_path);
        root->add_child("TMSUser")->add_child_text (_tms_user);
index 312118a55f9e24c7b4e93b140b70fae4fab8e96d..b008fe22f8e97ca3ed10caac11a4daa58157b4fd 100644 (file)
@@ -90,6 +90,10 @@ public:
                return _servers;
        }
 
+       Protocol tms_protocol () const {
+               return _tms_protocol;
+       }
+
        /** @return The IP address of a TMS that we can copy DCPs to */
        std::string tms_ip () const {
                return _tms_ip;
@@ -247,6 +251,10 @@ public:
                maybe_set (_server_port_base, p);
        }
 
+       void set_tms_protocol (Protocol p) {
+               maybe_set (_tms_protocol, p);
+       }
+
        /** @param i IP address of a TMS that we can copy DCPs to */
        void set_tms_ip (std::string i) {
                maybe_set (_tms_ip, i);
@@ -447,6 +455,7 @@ private:
        bool _use_any_servers;
        /** J2K encoding servers that should definitely be used */
        std::vector<std::string> _servers;
+       Protocol _tms_protocol;
        /** The IP address of a TMS that we can copy DCPs to */
        std::string _tms_ip;
        /** The path on a TMS that we should write DCPs to */
diff --git a/src/lib/curl_uploader.cc b/src/lib/curl_uploader.cc
new file mode 100644 (file)
index 0000000..b888985
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+    Copyright (C) 2015 Carl Hetherington <cth@carlh.net>
+
+    This program 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 of the License, or
+    (at your option) any later version.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "curl_uploader.h"
+#include "exceptions.h"
+#include "config.h"
+#include "cross.h"
+#include "compose.hpp"
+
+#include "i18n.h"
+
+using std::string;
+using std::cout;
+using boost::function;
+
+static size_t
+read_callback (void* ptr, size_t size, size_t nmemb, void* object)
+{
+       CurlUploader* u = reinterpret_cast<CurlUploader*> (object);
+       return u->read_callback (ptr, size, nmemb);
+}
+
+CurlUploader::CurlUploader (function<void (string)> set_status, function<void (float)> set_progress)
+       : Uploader (set_status, set_progress)
+       , _file (0)
+       , _transferred (0)
+       , _total_size (0)
+{
+       _curl = curl_easy_init ();
+       if (!_curl) {
+               throw NetworkError (_("Could not start transfer"));
+       }
+
+       curl_easy_setopt (_curl, CURLOPT_READFUNCTION, ::read_callback);
+       curl_easy_setopt (_curl, CURLOPT_READDATA, this);
+       curl_easy_setopt (_curl, CURLOPT_UPLOAD, 1L);
+       curl_easy_setopt (_curl, CURLOPT_FTP_CREATE_MISSING_DIRS, 1L);
+       curl_easy_setopt (_curl, CURLOPT_READDATA, this);
+       curl_easy_setopt (_curl, CURLOPT_USERNAME, Config::instance()->tms_user().c_str ());
+       curl_easy_setopt (_curl, CURLOPT_PASSWORD, Config::instance()->tms_password().c_str ());
+}
+
+CurlUploader::~CurlUploader ()
+{
+       if (_file) {
+               fclose (_file);
+       }
+       curl_easy_cleanup (_curl);
+}
+
+void
+CurlUploader::create_directory (boost::filesystem::path)
+{
+       /* this is done by libcurl */
+}
+
+void
+CurlUploader::upload_file (boost::filesystem::path from, boost::filesystem::path to, boost::uintmax_t& transferred, boost::uintmax_t total_size)
+{
+       curl_easy_setopt (
+               _curl, CURLOPT_URL,
+               String::compose ("ftp://%1/%2/%3", Config::instance()->tms_ip(), Config::instance()->tms_path(), to.string ()).c_str ()
+               );
+
+       cout << String::compose ("ftp://%1/%2/%3", Config::instance()->tms_ip(), Config::instance()->tms_path(), to.string ()) << "\n";
+
+       _file = fopen_boost (from, "rb");
+       if (!_file) {
+               throw NetworkError (String::compose (_("Could not open %1 to send"), from));
+       }
+       _transferred = &transferred;
+       _total_size = total_size;
+
+       CURLcode const r = curl_easy_perform (_curl);
+       if (r != CURLE_OK) {
+               throw NetworkError (String::compose (_("Could not write to remote file (%1)"), curl_easy_strerror (r)));
+       }
+
+       fclose (_file);
+}
+
+size_t
+CurlUploader::read_callback (void* ptr, size_t size, size_t nmemb)
+{
+       size_t const r = fread (ptr, size, nmemb, _file);
+       *_transferred += size * nmemb;
+
+       if (_total_size > 0) {
+               _set_progress ((double) *_transferred / _total_size);
+       }
+
+       return r;
+}
diff --git a/src/lib/curl_uploader.h b/src/lib/curl_uploader.h
new file mode 100644 (file)
index 0000000..d83c71e
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+    Copyright (C) 2015 Carl Hetherington <cth@carlh.net>
+
+    This program 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 of the License, or
+    (at your option) any later version.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "uploader.h"
+#include <curl/curl.h>
+
+class CurlUploader : public Uploader
+{
+public:
+       CurlUploader (boost::function<void (std::string)> set_status, boost::function<void (float)> set_progress);
+       ~CurlUploader ();
+
+       size_t read_callback (void* ptr, size_t size, size_t nmemb);
+
+protected:
+       virtual void create_directory (boost::filesystem::path directory);
+       virtual void upload_file (boost::filesystem::path from, boost::filesystem::path to, boost::uintmax_t& transferred, boost::uintmax_t total_size);
+
+private:
+       CURL* _curl;
+
+       FILE* _file;
+       boost::uintmax_t* _transferred;
+       boost::uintmax_t _total_size;
+};
index 7fb716813dc45f02a0ebf6c32947167f55d7c2a2..5e2d2de13d35e7c1cba73540c4229bbee128e0ca 100644 (file)
@@ -39,8 +39,6 @@ SCPUploader::SCPUploader (function<void (string)> set_status, function<void (flo
                throw NetworkError (_("could not start SSH session"));
        }
 
-       _set_status (_("connecting"));
-
        ssh_options_set (_session, SSH_OPTIONS_HOST, Config::instance()->tms_ip().c_str ());
        ssh_options_set (_session, SSH_OPTIONS_USER, Config::instance()->tms_user().c_str ());
        int const port = 22;
@@ -91,8 +89,6 @@ SCPUploader::create_directory (boost::filesystem::path directory)
 void
 SCPUploader::upload_file (boost::filesystem::path from, boost::filesystem::path to, boost::uintmax_t& transferred, boost::uintmax_t total_size)
 {
-       _set_status (String::compose (_("copying %1"), from.leaf ()));
-
        boost::uintmax_t to_do = boost::filesystem::file_size (from);
        ssh_scp_push_file (_scp, to.string().c_str(), to_do, S_IRUSR | S_IWUSR);
 
index b0e9e51aca8314ae0b7769148bc7df49fd564d0a..adb2268e38cbd6ca095166b749aaa3a155e81d6e 100644 (file)
@@ -148,4 +148,9 @@ enum Resolution {
 std::string resolution_to_string (Resolution);
 Resolution string_to_resolution (std::string);
 
+enum Protocol {
+       PROTOCOL_SCP,
+       PROTOCOL_FTP
+};
+
 #endif
index 89ce5252d2a0d976eb7186755bf3fc42d25c3662..44114b8a89c45c731c0cc1cdb7aac3a20123d31b 100644 (file)
  *  @brief A job to copy DCPs to a server using libcurl.
  */
 
-#include <iostream>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <fcntl.h>
-#include <boost/filesystem.hpp>
-#include <libssh/libssh.h>
 #include "compose.hpp"
 #include "upload_job.h"
-#include "exceptions.h"
 #include "config.h"
 #include "log.h"
 #include "film.h"
-#include "cross.h"
 #include "scp_uploader.h"
+#include "curl_uploader.h"
+#include <iostream>
 
 #include "i18n.h"
 
@@ -43,6 +37,7 @@
 using std::string;
 using std::min;
 using boost::shared_ptr;
+using boost::scoped_ptr;
 
 UploadJob::UploadJob (shared_ptr<const Film> film)
        : Job (film)
@@ -68,8 +63,17 @@ UploadJob::run ()
 {
        LOG_GENERAL_NC (N_("Upload job starting"));
 
-       SCPUploader uploader (bind (&UploadJob::set_status, this, _1), bind (&UploadJob::set_progress, this, _1, false));
-       uploader.upload (_film->dir (_film->dcp_name ()));
+       scoped_ptr<Uploader> uploader;
+       switch (Config::instance()->tms_protocol ()) {
+       case PROTOCOL_SCP:
+               uploader.reset (new SCPUploader (bind (&UploadJob::set_status, this, _1), bind (&UploadJob::set_progress, this, _1, false)));
+               break;
+       case PROTOCOL_FTP:
+               uploader.reset (new CurlUploader (bind (&UploadJob::set_status, this, _1), bind (&UploadJob::set_progress, this, _1, false)));
+               break;
+       }
+
+       uploader->upload (_film->dir (_film->dcp_name ()));
 
        set_progress (1);
        set_status (N_(""));
index b6b23ed8e5794445594714cf5e055e418ad3d8d1..fe15c5d8376e67b89cec9db8ccb99faaae32a81b 100644 (file)
 
 #include "uploader.h"
 #include "dcpomatic_assert.h"
+#include "compose.hpp"
+
+#include "i18n.h"
 
 using std::string;
 using boost::shared_ptr;
 using boost::function;
 
 Uploader::Uploader (function<void (string)> set_status, function<void (float)> set_progress)
-       : _set_status (set_status)
-       , _set_progress (set_progress)
+       : _set_progress (set_progress)
+       , _set_status (set_status)
 {
-
+       _set_status (_("connecting"));
 }
 
 boost::uintmax_t
@@ -66,6 +69,7 @@ Uploader::upload_directory (boost::filesystem::path base, boost::filesystem::pat
                if (is_directory (i->path ())) {
                        upload_directory (base, i->path (), transferred, total_size);
                } else {
+                       _set_status (String::compose (_("copying %1"), i->path().leaf ()));
                        upload_file (i->path (), remove_prefix (base, i->path ()), transferred, total_size);
                }
        }
index fcb9c504533e8229d12ee3710e807cdb0c29ef2c..d504ed0f8bd98d4923b987edf9a1c15313c4d2f7 100644 (file)
@@ -17,6 +17,9 @@
 
 */
 
+#ifndef DCPOMATIC_UPLOADER_H
+#define DCPOMATIC_UPLOADER_H
+
 #include <boost/shared_ptr.hpp>
 #include <boost/filesystem.hpp>
 #include <boost/function.hpp>
@@ -34,11 +37,14 @@ protected:
        virtual void create_directory (boost::filesystem::path directory) = 0;
        virtual void upload_file (boost::filesystem::path from, boost::filesystem::path to, boost::uintmax_t& transferred, boost::uintmax_t total_size) = 0;
 
-       boost::function<void (std::string)> _set_status;
        boost::function<void (float)> _set_progress;
 
 private:
        void upload_directory (boost::filesystem::path base, boost::filesystem::path directory, boost::uintmax_t& transferred, boost::uintmax_t total_size);
        boost::uintmax_t count_file_sizes (boost::filesystem::path) const;
        boost::filesystem::path remove_prefix (boost::filesystem::path prefix, boost::filesystem::path target) const;
+
+       boost::function<void (std::string)> _set_status;
 };
+
+#endif
index eb22389603ec85c18eaf55aad77d4038d8368b55..3457329e65b6d93ee2ab8e82816fc2d7f2025c5d 100644 (file)
@@ -37,6 +37,7 @@ sources = """
           content.cc
           content_factory.cc
           cross.cc
+          curl_uploader.cc
           data.cc
           dcp_content.cc
           dcp_content_type.cc
index 86ad2dbe17c5d8d53b87d8c0aab0281030192ccb..92a700d4d7532235112da53d7426468ac408d3f8 100644 (file)
@@ -910,6 +910,10 @@ private:
                table->AddGrowableCol (1, 1);
                _panel->GetSizer()->Add (table, 1, wxALL | wxEXPAND, _border);
 
+               add_label_to_sizer (table, _panel, _("Protocol"), true);
+               _tms_protocol = new wxChoice (_panel, wxID_ANY);
+               table->Add (_tms_protocol, 1, wxEXPAND);
+
                add_label_to_sizer (table, _panel, _("IP address"), true);
                _tms_ip = new wxTextCtrl (_panel, wxID_ANY);
                table->Add (_tms_ip, 1, wxEXPAND);
@@ -926,6 +930,10 @@ private:
                _tms_password = new wxTextCtrl (_panel, wxID_ANY);
                table->Add (_tms_password, 1, wxEXPAND);
 
+               _tms_protocol->Append (_("SCP (for AAM)"));
+               _tms_protocol->Append (_("FTP (for Dolby)"));
+
+               _tms_protocol->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&TMSPage::tms_protocol_changed, this));
                _tms_ip->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_ip_changed, this));
                _tms_path->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_path_changed, this));
                _tms_user->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TMSPage::tms_user_changed, this));
@@ -936,12 +944,18 @@ private:
        {
                Config* config = Config::instance ();
 
+               checked_set (_tms_protocol, config->tms_protocol ());
                checked_set (_tms_ip, config->tms_ip ());
                checked_set (_tms_path, config->tms_path ());
                checked_set (_tms_user, config->tms_user ());
                checked_set (_tms_password, config->tms_password ());
        }
 
+       void tms_protocol_changed ()
+       {
+               Config::instance()->set_tms_protocol (static_cast<Protocol> (_tms_protocol->GetSelection ()));
+       }
+
        void tms_ip_changed ()
        {
                Config::instance()->set_tms_ip (wx_to_std (_tms_ip->GetValue ()));
@@ -962,6 +976,7 @@ private:
                Config::instance()->set_tms_password (wx_to_std (_tms_password->GetValue ()));
        }
 
+       wxChoice* _tms_protocol;
        wxTextCtrl* _tms_ip;
        wxTextCtrl* _tms_path;
        wxTextCtrl* _tms_user;