Basic email of KDMs works.
authorCarl Hetherington <cth@carlh.net>
Wed, 25 Sep 2013 12:49:13 +0000 (13:49 +0100)
committerCarl Hetherington <cth@carlh.net>
Wed, 25 Sep 2013 12:49:13 +0000 (13:49 +0100)
src/lib/film.cc
src/lib/film.h
src/lib/util.cc
src/tools/dcpomatic.cc
src/tools/wscript
src/wx/kdm_dialog.cc
src/wx/kdm_dialog.h
wscript

index d218524f467c8828e51c6dbe79339a3b571a98f3..8252b492c9be72a6c33acc5b6bff28884dc51fbd 100644 (file)
@@ -895,12 +895,11 @@ Film::full_frame () const
        return libdcp::Size ();
 }
 
-void
+list<libdcp::KDM>
 Film::make_kdms (
        list<shared_ptr<Screen> > screens,
        boost::posix_time::ptime from,
-       boost::posix_time::ptime until,
-       string directory
+       boost::posix_time::ptime until
        ) const
 {
        boost::filesystem::path const sd = Config::instance()->signer_chain_directory ();
@@ -948,6 +947,8 @@ Film::make_kdms (
                throw KDMError (_("More than one possible DCP to make KDM for"));
        }
 
+       list<libdcp::KDM> kdms;
+
        for (list<shared_ptr<Screen> >::iterator i = screens.begin(); i != screens.end(); ++i) {
 
                libdcp::DCP dcp (dcps.front ());
@@ -963,14 +964,10 @@ Film::make_kdms (
                string const issue_date = libdcp::tm_to_string (tm);
 
                dcp.cpls().front()->set_mxf_keys (key ());
-               
-               libdcp::KDM kdm (
-                       dcp.cpls().front(), signer, (*i)->certificate, from, until, "DCP-o-matic", issue_date
-                       );
 
-               boost::filesystem::path out = directory;
-               out /= tidy_for_filename ((*i)->cinema->name) + "_" + tidy_for_filename ((*i)->name) + ".kdm.xml";
-               kdm.as_xml (out);
+               kdms.push_back (libdcp::KDM (dcp.cpls().front(), signer, (*i)->certificate, from, until, "DCP-o-matic", issue_date));
        }
+
+       return kdms;
 }
        
index 26623e69eb401bff6ab5948980527ab07fda6566..a30a630b99096ee82a68572c1378c0a7677d79d1 100644 (file)
@@ -32,6 +32,7 @@
 #include <boost/enable_shared_from_this.hpp>
 #include <boost/filesystem.hpp>
 #include <libdcp/key.h>
+#include <libdcp/kdm.h>
 #include "util.h"
 #include "types.h"
 #include "dci_metadata.h"
@@ -115,11 +116,10 @@ public:
        bool has_subtitles () const;
        OutputVideoFrame best_video_frame_rate () const;
 
-       void make_kdms (
+       std::list<libdcp::KDM> make_kdms (
                std::list<boost::shared_ptr<Screen> >,
                boost::posix_time::ptime from,
-               boost::posix_time::ptime until,
-               std::string directory
+               boost::posix_time::ptime until
                ) const;
 
        libdcp::Key key () const {
index 739a327d6a91ed319db66417e74331622193ea33..6746b4773e41aeceb4212723c97d0a480e19a5e6 100644 (file)
@@ -54,7 +54,6 @@ extern "C" {
 #include <libpostproc/postprocess.h>
 #include <libavutil/pixfmt.h>
 }
-#include <curl/curl.h>
 #include "util.h"
 #include "exceptions.h"
 #include "scaler.h"
@@ -817,53 +816,3 @@ tidy_for_filename (string f)
 
        return t;
 }
-
-struct EmailState
-{
-       string message;
-       int done;
-};
-
-static size_t
-send_email_function (void* ptr, size_t size, size_t nmemb, void* userdata)
-{
-       EmailState* state = reinterpret_cast<EmailState*> (userdata);
-
-       int const now = min (size * nmemb, state->message.length() - state->done);
-
-       memcpy (ptr, state->message.c_str() + state->done, now);
-       state->done += now;
-
-       return now;
-}
-       
-bool
-send_email (string from, string to, string message)
-{
-       CURL* curl = curl_easy_init ();
-       if (!curl) {
-               return true;
-       }
-
-       string const url = "smtp://" + Config::instance()->mail_server();
-
-       curl_easy_setopt (curl, CURLOPT_URL, url.c_str ());
-       curl_easy_setopt (curl, CURLOPT_MAIL_FROM, from.c_str ());
-       struct curl_slist* recipients = 0;
-       recipients = curl_slist_append (recipients, to.c_str ());
-       curl_easy_setopt (curl, CURLOPT_READFUNCTION, send_email_function);
-
-       EmailState state;
-       state.message = message;
-       state.done = 0;
-       curl_easy_setopt (curl, CURLOPT_READDATA, &state);
-
-       if (curl_easy_perform (curl) != CURLE_OK) {
-               return true;
-       }
-
-       curl_slist_free_all (recipients);
-       curl_easy_cleanup (curl);
-
-       return false;
-}
index f61ef19e2408478e30a1174e00c6cf7d95ef6db4..86886ed9e294e184e45407f4f3f1c258966e1bfb 100644 (file)
@@ -29,6 +29,8 @@
 #include <wx/generic/aboutdlgg.h>
 #include <wx/stdpaths.h>
 #include <wx/cmdline.h>
+#include <quickmail.h>
+#include <zip.h>
 #include "wx/film_viewer.h"
 #include "wx/film_editor.h"
 #include "wx/job_manager_view.h"
@@ -49,6 +51,7 @@
 #include "lib/job_manager.h"
 #include "lib/transcode_job.h"
 #include "lib/exceptions.h"
+#include "lib/cinema.h"
 
 using std::cout;
 using std::string;
@@ -223,6 +226,25 @@ setup_menu (wxMenuBar* m)
        m->Append (help, _("&Help"));
 }
 
+struct ScreenKDM
+{
+       ScreenKDM (shared_ptr<Screen> s, libdcp::KDM k)
+               : screen (s)
+               , kdm (k)
+       {}
+       
+       shared_ptr<Screen> screen;
+       libdcp::KDM kdm;
+};
+
+/* Not complete but sufficient for our purposes (we're using
+   ScreenKDM in a list where all the screens will be unique).
+*/
+bool operator== (ScreenKDM const & a, ScreenKDM const & b)
+{
+       return a.screen == b.screen;
+}
+
 class Frame : public wxFrame
 {
 public:
@@ -420,19 +442,120 @@ private:
                }
                
                KDMDialog* d = new KDMDialog (this);
-               if (d->ShowModal () == wxID_OK) {
-                       try {
-                               film->make_kdms (
-                                       d->screens (),
-                                       d->from (),
-                                       d->until (),
-                                       d->directory ()
-                                       );
-                       } catch (KDMError& e) {
-                               error_dialog (this, e.what ());
-                       }
+               if (d->ShowModal () != wxID_OK) {
+                       d->Destroy ();
+                       return;
                }
                
+               try {
+                       list<shared_ptr<Screen> > screens = d->screens ();
+                       list<libdcp::KDM> kdms = film->make_kdms (
+                               screens,
+                               d->from (),
+                               d->until ()
+                               );
+
+                       list<ScreenKDM> screen_kdms;
+                       
+                       list<shared_ptr<Screen> >::iterator i = screens.begin ();
+                       list<libdcp::KDM>::iterator j = kdms.begin ();
+                       while (i != screens.end() && j != kdms.end ()) {
+                               screen_kdms.push_back (ScreenKDM (*i, *j));
+                               ++i;
+                               ++j;
+                       }
+                       
+                       if (d->write_to ()) {
+                               /* Write KDMs to the specified directory */
+                               for (list<ScreenKDM>::iterator i = screen_kdms.begin(); i != screen_kdms.end(); ++i) {
+                                       boost::filesystem::path out = d->directory ();
+                                       out /= tidy_for_filename (i->screen->cinema->name) + "_" + tidy_for_filename (i->screen->name) + ".kdm.xml";
+                                       i->kdm.as_xml (out);
+                               }
+                       } else {
+                               while (!screen_kdms.empty ()) {
+
+                                       /* Get all the screens from a single cinema */
+
+                                       shared_ptr<Cinema> cinema;
+                                       list<ScreenKDM> cinema_screen_kdms;
+
+                                       list<ScreenKDM>::iterator i = screen_kdms.begin ();
+                                       cinema = i->screen->cinema;
+                                       cinema_screen_kdms.push_back (*i);
+                                       list<ScreenKDM>::iterator j = i;
+                                       ++i;
+                                       screen_kdms.remove (*j);
+
+                                       while (i != screen_kdms.end ()) {
+                                               if (i->screen->cinema == cinema) {
+                                                       cinema_screen_kdms.push_back (*i);
+                                                       list<ScreenKDM>::iterator j = i;
+                                                       ++i;
+                                                       screen_kdms.remove (*j);
+                                               } else {
+                                                       ++i;
+                                               }
+                                       }
+
+                                       /* Make a ZIP file of this cinema's KDMs */
+                                       
+                                       boost::filesystem::path zip_file = boost::filesystem::temp_directory_path ();
+                                       zip_file /= boost::filesystem::unique_path().string() + ".zip";
+                                       struct zip* zip = zip_open (zip_file.c_str(), ZIP_CREATE | ZIP_EXCL, 0);
+                                       if (!zip) {
+                                               throw FileError ("could not create ZIP file", zip_file);
+                                       }
+
+                                       list<shared_ptr<string> > kdm_strings;
+
+                                       for (list<ScreenKDM>::const_iterator i = cinema_screen_kdms.begin(); i != cinema_screen_kdms.end(); ++i) {
+                                               shared_ptr<string> kdm (new string (i->kdm.as_xml ()));
+                                               kdm_strings.push_back (kdm);
+                                               
+                                               struct zip_source* source = zip_source_buffer (zip, kdm->c_str(), kdm->length(), 0);
+                                               if (!source) {
+                                                       throw StringError ("could not create ZIP source");
+                                               }
+                                               
+                                               string const name = tidy_for_filename (i->screen->cinema->name) + "_" +
+                                                       tidy_for_filename (i->screen->name) + ".kdm.xml";
+                                               
+                                               if (zip_add (zip, name.c_str(), source) == -1) {
+                                                       throw StringError ("failed to add KDM to ZIP archive");
+                                               }
+                                       }
+
+                                       if (zip_close (zip) == -1) {
+                                               throw StringError ("failed to close ZIP archive");
+                                       }
+
+                                       /* Send email */
+
+                                       quickmail_initialize ();
+                                       quickmail mail = quickmail_create (Config::instance()->kdm_from().c_str(), "KDM delivery");
+                                       quickmail_add_to (mail, cinema->email.c_str ());
+
+                                       string body = Config::instance()->kdm_email().c_str();
+                                       boost::algorithm::replace_all (body, "$DCP_NAME", film->dcp_name ());
+                                       
+                                       quickmail_set_body (mail, body.c_str());
+                                       quickmail_add_attachment_file (mail, zip_file.c_str());
+                                       char const* error = quickmail_send (mail, Config::instance()->mail_server().c_str(), 25, "", "");
+                                       if (error) {
+                                               quickmail_destroy (mail);
+                                               throw StringError (String::compose ("Failed to send KDM email (%1)", error));
+                                       }
+                                       quickmail_destroy (mail);
+
+                                       film->log()->log (String::compose ("Send KDM email to %1", cinema->email));
+                               }
+                       }
+                       
+               } catch (KDMError& e) {
+                       error_dialog (this, e.what ());
+               }
+       
                d->Destroy ();
        }
        
index 42fc90adbeac9e073f7ab101fcd5eef6c40141dd..4adf84382669e50b90a74dabdbe92d03b773d637 100644 (file)
@@ -15,7 +15,7 @@ def build(bld):
     if not bld.env.DISABLE_GUI:
         for t in ['dcpomatic', 'dcpomatic_batch', 'dcpomatic_server']:
             obj = bld(features = 'cxx cxxprogram')
-            obj.uselib = 'DCP OPENJPEG AVFORMAT AVFILTER AVCODEC AVUTIL SWSCALE POSTPROC CXML WXWIDGETS'
+            obj.uselib = 'DCP OPENJPEG AVFORMAT AVFILTER AVCODEC AVUTIL SWSCALE POSTPROC CXML WXWIDGETS ZIP QUICKMAIL'
             if bld.env.STATIC:
                 obj.uselib += ' GTK'
             obj.includes = ['..']
index 9db512780754a69196a640dff7ddf4c50d2ca7fa..6164f7fd95d0b6aea94837d14943b1b8c095f219 100644 (file)
@@ -385,3 +385,9 @@ KDMDialog::directory () const
 {
        return wx_to_std (_folder->GetPath ());
 }
+
+bool
+KDMDialog::write_to () const
+{
+       return _write_to->GetValue ();
+}
index 0acf9a61e1ac15865abbc9cb04030b370cf297ce..ec4a50ac36f4d79b9173fa2c3987f8d788a12fe5 100644 (file)
@@ -42,6 +42,7 @@ public:
        boost::posix_time::ptime from () const;
        boost::posix_time::ptime until () const;
        std::string directory () const;
+       bool write_to () const;
 
 private:
        void add_cinema (boost::shared_ptr<Cinema>);
diff --git a/wscript b/wscript
index 7d582f9e56d0fd01322a80b71dc4c0a7c73e7068..95b0768e8f182d1b3af7901984d8c3b83ff22e3d 100644 (file)
--- a/wscript
+++ b/wscript
@@ -119,6 +119,17 @@ def configure(conf):
         conf.env.STLIB_POSTPROC   = ['postproc']
         conf.env.STLIB_SWRESAMPLE = ['swresample']
         conf.env.STLIB_OPENJPEG   = ['openjpeg']
+        conf.env.STLIB_QUICKMAIL  = ['quickmail']
+    else:
+        conf.check_cxx(fragment="""
+                            #include <quickmail.h>
+                            int main(void) { quickmail_initialize (); }
+                            """,
+                       mandatory=True,
+                       msg='Checking for quickmail',
+                       libpath='/usr/local/lib',
+                       lib='quickmail',
+                       uselib_store='QUICKMAIL')
 
     # Dependencies which are always dynamically linked
     conf.check_cfg(package='sndfile', args='--cflags --libs', uselib_store='SNDFILE', mandatory=True)
@@ -127,6 +138,15 @@ def configure(conf):
     conf.check_cfg(package='libxml++-2.6', args='--cflags --libs', uselib_store='XML++', mandatory=True)
     conf.check_cfg(package='libcurl', args='--cflags --libs', uselib_store='CURL', mandatory=True)
 
+    conf.check_cxx(fragment="""
+                            #include <zip.h>
+                            int main(void) { zip_open ("foo", 0, 0); }
+                            """,
+                   mandatory=True,
+                   msg='Checking for libzip',
+                   lib='zip',
+                   uselib_store='ZIP')
+
     conf.check_cxx(fragment="""
                             #include <boost/version.hpp>\n
                             #if BOOST_VERSION < 104500\n