Basic support for emailing a report of a problem (#43).
authorCarl Hetherington <cth@carlh.net>
Thu, 6 Nov 2014 21:41:43 +0000 (21:41 +0000)
committerCarl Hetherington <cth@carlh.net>
Thu, 6 Nov 2014 21:41:43 +0000 (21:41 +0000)
15 files changed:
ChangeLog
src/lib/job.cc
src/lib/log.cc
src/lib/log.h
src/lib/send_problem_report_job.cc [new file with mode: 0644]
src/lib/send_problem_report_job.h [new file with mode: 0644]
src/lib/util.h
src/lib/wscript
src/tools/dcpomatic.cc
src/tools/dcpomatic_server.cc
src/wx/report_problem_dialog.cc [new file with mode: 0644]
src/wx/report_problem_dialog.h [new file with mode: 0644]
src/wx/wscript
test/file_log_test.cc [new file with mode: 0644]
test/wscript

index 106e845..9630702 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2014-11-06  Carl Hetherington  <cth@carlh.net>
+
+       * Basic support for emailing a report of
+       a problem (#43).
+
 2014-11-05  c.hetherington  <cth@carlh.net>
 
        * Add a few more hints.
index 7be1714..3cd7295 100644 (file)
@@ -109,7 +109,7 @@ Job::run_wrapper ()
 
                set_error (
                        e.what (),
-                       _("It is not known what caused this error.  Please report the problem to the DCP-o-matic author (carl@dcpomatic.com).")
+                       string (_("It is not known what caused this error.")) + "  " + REPORT_PROBLEM
                        );
 
                set_progress (1);
@@ -119,7 +119,7 @@ Job::run_wrapper ()
 
                set_error (
                        _("Unknown error"),
-                       _("It is not known what caused this error.  Please report the problem to the DCP-o-matic author (carl@dcpomatic.com).")
+                       string (_("It is not known what caused this error.")) + "  " + REPORT_PROBLEM
                        );
 
                set_progress (1);
index 5e8277a..156f8cf 100644 (file)
@@ -123,3 +123,42 @@ FileLog::do_log (string m)
        fclose (f);
 }
 
+string
+FileLog::head_and_tail () const
+{
+       boost::mutex::scoped_lock lm (_mutex);
+
+       uintmax_t head_amount = 1024;
+       uintmax_t tail_amount = 1024;
+       uintmax_t size = boost::filesystem::file_size (_file);
+
+       if (size < (head_amount + tail_amount)) {
+               head_amount = size;
+               tail_amount = 0;
+       }
+       
+       FILE* f = fopen_boost (_file, "r");
+       if (!f) {
+               return "";
+       }
+
+       string out;
+
+       char* buffer = new char[max(head_amount, tail_amount) + 1];
+       
+       int N = fread (buffer, 1, head_amount, f);
+       buffer[N] = '\0';
+       out += buffer;
+
+       fseek (f, tail_amount, SEEK_END);
+       
+       N = fread (buffer, 1, tail_amount, f);
+       buffer[N] = '\0';
+       out += buffer;
+
+       delete[] buffer;
+
+       fclose (f);
+
+       return out;
+}
index 94d30de..f20b0a1 100644 (file)
@@ -48,12 +48,17 @@ public:
 
        void set_types (int types);
 
+       virtual std::string head_and_tail () const = 0;
+
+protected:
+       
+       /** mutex to protect the log */
+       mutable boost::mutex _mutex;
+       
 private:
        virtual void do_log (std::string m) = 0;
        void config_changed ();
        
-       /** mutex to protect the log */
-       boost::mutex _mutex;
        /** bit-field of log types which should be put into the log (others are ignored) */
        int _types;
        boost::signals2::scoped_connection _config_connection;
@@ -64,6 +69,8 @@ class FileLog : public Log
 public:
        FileLog (boost::filesystem::path file);
 
+       std::string head_and_tail () const;
+
 private:
        void do_log (std::string m);
        /** filename to write to */
@@ -73,6 +80,9 @@ private:
 class NullLog : public Log
 {
 public:
+       std::string head_and_tail () const {
+               return "";
+       }
 
 private:       
        void do_log (std::string) {}
diff --git a/src/lib/send_problem_report_job.cc b/src/lib/send_problem_report_job.cc
new file mode 100644 (file)
index 0000000..32fec69
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+    Copyright (C) 2014 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 "send_problem_report_job.h"
+#include "compose.hpp"
+#include "film.h"
+#include "cross.h"
+#include "film.h"
+#include "log.h"
+#include <quickmail.h>
+
+#include "i18n.h"
+
+using std::string;
+using std::list;
+using boost::shared_ptr;
+
+SendProblemReportJob::SendProblemReportJob (
+       shared_ptr<const Film> film,
+       string from,
+       string summary
+       )
+       : Job (film)
+       , _from (from)
+       , _summary (summary)
+{
+
+}
+
+string
+SendProblemReportJob::name () const
+{
+       return String::compose (_("Email problem report for %1"), _film->name());
+}
+
+void
+SendProblemReportJob::run ()
+{
+       set_progress_unknown ();
+       
+       quickmail mail = quickmail_create (_from.c_str(), "DCP-o-matic problem report");
+       
+       quickmail_add_to (mail, "carl@dcpomatic.com");
+       
+       string body = _summary;
+       
+       body += "log head and tail:\n";
+       body += "---<8----\n";
+       body += _film->log()->head_and_tail ();
+       body += "---<8----\n\n";
+       
+       FILE* ffprobe = fopen_boost (_film->file ("ffprobe.log"), "r");
+       if (ffprobe) {
+               body += "ffprobe.log:\n";
+               body += "---<8----\n";
+               uintmax_t const size = boost::filesystem::file_size (_film->file ("ffprobe.log"));
+               char* buffer = new char[size + 1];
+               int const N = fread (buffer, size, 1, ffprobe);
+               buffer[N] = '\0';
+               body += buffer;
+               delete[] buffer;
+               body += "---<8----\n\n";
+               fclose (ffprobe);
+       }
+       
+       quickmail_set_body (mail, body.c_str());
+       
+       char const* error = quickmail_send (mail, "main.carlh.net", 2525, 0, 0);
+       
+       if (error) {
+               set_state (FINISHED_ERROR);
+               set_error (error, "");
+       } else {
+               set_state (FINISHED_OK);
+       }
+       
+       quickmail_destroy (mail);
+
+       set_progress (1);
+}
diff --git a/src/lib/send_problem_report_job.h b/src/lib/send_problem_report_job.h
new file mode 100644 (file)
index 0000000..76a920a
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+    Copyright (C) 2014 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 <boost/filesystem.hpp>
+#include <dcp/types.h>
+#include "job.h"
+
+class SendProblemReportJob : public Job
+{
+public:
+       SendProblemReportJob (
+               boost::shared_ptr<const Film>,
+               std::string from,
+               std::string summary
+               );
+
+       std::string name () const;
+       void run ();
+
+private:
+       std::string _from;
+       std::string _summary;
+};
index 724e893..93d0587 100644 (file)
@@ -46,6 +46,7 @@ extern "C" {
 #define MAX_DCP_AUDIO_CHANNELS 12
 #define DCPOMATIC_HELLO "Boys, you gotta learn not to talk to nuns that way"
 #define HISTORY_SIZE 10
+#define REPORT_PROBLEM _("Please report this problem by using Help -> Report a problem or via email to carl@dcpomatic.com")
 
 class Job;
 struct AVSubtitle;
index d62c22b..69483a8 100644 (file)
@@ -70,6 +70,7 @@ sources = """
           scp_dcp_job.cc
           scaler.cc
           send_kdm_email_job.cc
+          send_problem_report_job.cc
           server.cc
           server_finder.cc
           single_stream_audio_content.cc
index edd04fd..3edc241 100644 (file)
@@ -46,6 +46,7 @@
 #include "wx/hints_dialog.h"
 #include "wx/update_dialog.h"
 #include "wx/content_panel.h"
+#include "wx/report_problem_dialog.h"
 #include "lib/film.h"
 #include "lib/config.h"
 #include "lib/util.h"
@@ -127,6 +128,7 @@ enum {
        ID_tools_hints,
        ID_tools_encoding_servers,
        ID_tools_check_for_updates,
+       ID_help_report_a_problem,
        /* IDs for shortcuts (with no associated menu item) */
        ID_add_file
 };
@@ -188,6 +190,7 @@ public:
                Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&Frame::tools_encoding_servers, this),  ID_tools_encoding_servers);
                Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&Frame::tools_check_for_updates, this), ID_tools_check_for_updates);
                Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&Frame::help_about, this),              wxID_ABOUT);
+               Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&Frame::help_report_a_problem, this),   ID_help_report_a_problem);
 
                Bind (wxEVT_CLOSE_WINDOW, boost::bind (&Frame::close, this, _1));
 
@@ -501,6 +504,15 @@ private:
                d->Destroy ();
        }
 
+       void help_report_a_problem ()
+       {
+               ReportProblemDialog* d = new ReportProblemDialog (this, _film);
+               if (d->ShowModal () == wxID_OK) {
+                       d->report ();
+               }
+               d->Destroy ();
+       }
+
        bool should_close ()
        {
                if (!JobManager::instance()->work_to_do ()) {
@@ -647,6 +659,7 @@ private:
 #else  
                add_item (help, _("About"), wxID_ABOUT, ALWAYS);
 #endif 
+               add_item (help, _("Report a problem..."), ID_help_report_a_problem, ALWAYS);
                
                m->Append (_file_menu, _("&File"));
 #ifndef __WXOSX__      
@@ -824,9 +837,8 @@ class App : public wxApp
                try {
                        throw;
                } catch (exception& e) {
-                       error_dialog (0, wxString::Format (_("An exception occurred (%s).  Please report this problem to the DCP-o-matic author (carl@dcpomatic.com)."), e.what ()));
-               } catch (...) {
-                       error_dialog (0, _("An unknown exception occurred.  Please report this problem to the DCP-o-matic author (carl@dcpomatic.com)."));
+                       error_dialog (0, wxString::Format (_("An exception occurred (%s)."), e.what ()) + "  " + REPORT_PROBLEM);               } catch (...) {
+                       error_dialog (0, _("An unknown exception occurred.") + "  " + REPORT_PROBLEM);
                }
 
                /* This will terminate the program */
@@ -835,7 +847,7 @@ class App : public wxApp
        
        void OnUnhandledException ()
        {
-               error_dialog (0, _("An unknown exception occurred.  Please report this problem to the DCP-o-matic author (carl@dcpomatic.com)."));
+               error_dialog (0, _("An unknown exception occurred.") + "  " + REPORT_PROBLEM);
        }
 
        void idle ()
index a82478d..bcd5c8d 100644 (file)
@@ -47,6 +47,14 @@ public:
                return _log;
        }
 
+       string head_and_tail () const {
+               if (_log.size () < 2048) {
+                       return _log;
+               }
+
+               return _log.substr (0, 1024) + _log.substr (_log.size() - 1025, 1024);
+       }
+
 private:
        void do_log (string m)
        {
diff --git a/src/wx/report_problem_dialog.cc b/src/wx/report_problem_dialog.cc
new file mode 100644 (file)
index 0000000..78c0929
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+    Copyright (C) 2014 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 "report_problem_dialog.h"
+#include "wx_util.h"
+#include "lib/config.h"
+#include "lib/job_manager.h"
+#include "lib/send_problem_report_job.h"
+#include <wx/sizer.h>
+
+using std::string;
+using boost::shared_ptr;
+
+ReportProblemDialog::ReportProblemDialog (wxWindow* parent, shared_ptr<Film> film)
+       : wxDialog (parent, wxID_ANY, _("Report A Problem"))
+       , _film (film)
+{
+       _overall_sizer = new wxBoxSizer (wxVERTICAL);
+       SetSizer (_overall_sizer);
+
+       _table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
+       _table->AddGrowableCol (1, 1);
+
+       _overall_sizer->Add (_table, 1, wxEXPAND | wxALL, DCPOMATIC_DIALOG_BORDER);
+
+       wxSizer* buttons = CreateSeparatedButtonSizer (wxOK | wxCANCEL);
+       if (buttons) {
+               _overall_sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
+       }
+
+       wxString t = _("My problem is");
+       int flags = wxALIGN_TOP | wxLEFT | wxRIGHT;
+#ifdef __WXOSX__
+       if (left) {
+               flags |= wxALIGN_RIGHT;
+               t += wxT (":");
+       }
+#endif 
+       wxStaticText* m = new wxStaticText (this, wxID_ANY, t);
+       _table->Add (m, 1, flags, 6);
+
+       _summary = new wxTextCtrl (this, wxID_ANY, wxT (""), wxDefaultPosition, wxSize (320, 240), wxTE_MULTILINE);
+       _table->Add (_summary, 1, wxEXPAND | wxALIGN_TOP);
+
+       _send_logs = new wxCheckBox (this, wxID_ANY, _("Send logs"));
+       _send_logs->SetValue (true);
+       _table->Add (_send_logs, 1, wxEXPAND);
+       _table->AddSpacer (0);
+
+       add_label_to_sizer (_table, this, _("Contact email"), true);
+       _email = new wxTextCtrl (this, wxID_ANY, wxT (""));
+       _email->SetValue (std_to_wx (Config::instance()->kdm_from ()));
+       _table->Add (_email, 1, wxEXPAND);
+
+       _overall_sizer->Layout ();
+       _overall_sizer->SetSizeHints (this);
+}
+
+void
+ReportProblemDialog::report ()
+{
+       if (_email->GetValue().IsEmpty ()) {
+               error_dialog (this, _("Please enter an email address so that we can contact you with any queries about the problem."));
+               return;
+       }
+
+       JobManager::instance()->add (shared_ptr<Job> (new SendProblemReportJob (_film, wx_to_std (_email->GetValue ()), wx_to_std (_summary->GetValue ()))));
+}
diff --git a/src/wx/report_problem_dialog.h b/src/wx/report_problem_dialog.h
new file mode 100644 (file)
index 0000000..a8da372
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+    Copyright (C) 2014 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 <wx/dialog.h>
+#include <boost/shared_ptr.hpp>
+
+class wxTextCtrl;
+class wxFlexGridSizer;
+class wxCheckBox;
+class Film;
+
+class ReportProblemDialog : public wxDialog
+{
+public:
+       ReportProblemDialog (wxWindow* parent, boost::shared_ptr<Film>);
+
+       void report ();
+
+private:
+       boost::shared_ptr<Film> _film;
+
+       wxSizer* _overall_sizer;
+       wxFlexGridSizer* _table;
+       wxTextCtrl* _summary;
+       wxCheckBox* _send_logs;
+       wxTextCtrl* _email;
+};
+
index 8801ccc..6a6021c 100644 (file)
@@ -37,6 +37,7 @@ sources = """
           preset_colour_conversion_dialog.cc
           properties_dialog.cc
           repeat_dialog.cc
+          report_problem_dialog.cc
           screen_dialog.cc
           server_dialog.cc
           servers_list_dialog.cc
diff --git a/test/file_log_test.cc b/test/file_log_test.cc
new file mode 100644 (file)
index 0000000..4769503
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+    Copyright (C) 2014 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 <boost/test/unit_test.hpp>
+#include "lib/log.h"
+
+using std::cout;
+
+BOOST_AUTO_TEST_CASE (file_log_test)
+{
+       FileLog log ("test/data/short.log");
+       BOOST_CHECK_EQUAL (log.head_and_tail(), "This is a short log.\nWith only two lines.\n");
+}
index 371ea7a..4571641 100644 (file)
@@ -34,6 +34,7 @@ def build(bld):
                  ffmpeg_examiner_test.cc
                  ffmpeg_pts_offset_test.cc
                  file_group_test.cc
+                 file_log_test.cc
                  film_metadata_test.cc
                  frame_rate_test.cc
                  image_test.cc