Assorted C++11/formatting cleanups.
[dcpomatic.git] / src / lib / kdm_with_metadata.cc
1 /*
2     Copyright (C) 2013-2021 Carl Hetherington <cth@carlh.net>
3
4     This file is part of DCP-o-matic.
5
6     DCP-o-matic is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     DCP-o-matic is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
18
19 */
20
21
22 #include "kdm_with_metadata.h"
23 #include "cinema.h"
24 #include "screen.h"
25 #include "util.h"
26 #include "zipper.h"
27 #include "config.h"
28 #include "dcpomatic_log.h"
29 #include "emailer.h"
30 #include <boost/function.hpp>
31 #include <boost/function.hpp>
32
33 #include "i18n.h"
34
35
36 using std::string;
37 using std::cout;
38 using std::list;
39 using std::shared_ptr;
40 using boost::optional;
41 using boost::function;
42
43
44 int
45 write_files (
46         list<KDMWithMetadataPtr> kdms,
47         boost::filesystem::path directory,
48         dcp::NameFormat name_format,
49         boost::function<bool (boost::filesystem::path)> confirm_overwrite
50         )
51 {
52         int written = 0;
53
54         if (directory == "-") {
55                 /* Write KDMs to the stdout */
56                 for (auto i: kdms) {
57                         cout << i->kdm_as_xml ();
58                         ++written;
59                 }
60
61                 return written;
62         }
63
64         if (!boost::filesystem::exists (directory)) {
65                 boost::filesystem::create_directories (directory);
66         }
67
68         /* Write KDMs to the specified directory */
69         for (auto i: kdms) {
70                 auto out = directory / careful_string_filter(name_format.get(i->name_values(), ".xml"));
71                 if (!boost::filesystem::exists (out) || confirm_overwrite (out)) {
72                         i->kdm_as_xml (out);
73                         ++written;
74                 }
75         }
76
77         return written;
78 }
79
80
81 optional<string>
82 KDMWithMetadata::get (char k) const
83 {
84         auto i = _name_values.find (k);
85         if (i == _name_values.end()) {
86                 return {};
87         }
88
89         return i->second;
90 }
91
92
93 void
94 make_zip_file (list<KDMWithMetadataPtr> kdms, boost::filesystem::path zip_file, dcp::NameFormat name_format)
95 {
96         Zipper zipper (zip_file);
97
98         for (auto i: kdms) {
99                 auto const name = careful_string_filter(name_format.get(i->name_values(), ".xml"));
100                 zipper.add (name, i->kdm_as_xml());
101         }
102
103         zipper.close ();
104 }
105
106
107 /** Collect a list of KDMWithMetadatas into a list of lists so that
108  *  each list contains the KDMs for one list.
109  */
110 list<list<KDMWithMetadataPtr>>
111 collect (list<KDMWithMetadataPtr> kdms)
112 {
113         list<list<KDMWithMetadataPtr>> grouped;
114
115         for (auto i: kdms) {
116
117                 auto j = grouped.begin ();
118
119                 while (j != grouped.end()) {
120                         if (j->front()->group() == i->group()) {
121                                 j->push_back (i);
122                                 break;
123                         }
124                         ++j;
125                 }
126
127                 if (j == grouped.end()) {
128                         grouped.push_back (list<KDMWithMetadataPtr>());
129                         grouped.back().push_back (i);
130                 }
131         }
132
133         return grouped;
134 }
135
136
137 /** Write one directory per list into another directory */
138 int
139 write_directories (
140         list<list<KDMWithMetadataPtr>> kdms,
141         boost::filesystem::path directory,
142         dcp::NameFormat container_name_format,
143         dcp::NameFormat filename_format,
144         function<bool (boost::filesystem::path)> confirm_overwrite
145         )
146 {
147         int written = 0;
148
149         for (auto const& i: kdms) {
150                 boost::filesystem::path path = directory;
151                 path /= container_name_format.get(i.front()->name_values(), "", "s");
152                 if (!boost::filesystem::exists (path) || confirm_overwrite (path)) {
153                         boost::filesystem::create_directories (path);
154                         write_files (i, path, filename_format, confirm_overwrite);
155                 }
156                 written += i.size();
157         }
158
159         return written;
160 }
161
162
163 /** Write one ZIP file per cinema into a directory */
164 int
165 write_zip_files (
166         list<list<KDMWithMetadataPtr>> kdms,
167         boost::filesystem::path directory,
168         dcp::NameFormat container_name_format,
169         dcp::NameFormat filename_format,
170         function<bool (boost::filesystem::path)> confirm_overwrite
171         )
172 {
173         int written = 0;
174
175         for (auto const& i: kdms) {
176                 auto path = directory;
177                 path /= container_name_format.get(i.front()->name_values(), ".zip", "s");
178                 if (!boost::filesystem::exists (path) || confirm_overwrite (path)) {
179                         if (boost::filesystem::exists (path)) {
180                                 /* Creating a new zip file over an existing one is an error */
181                                 boost::filesystem::remove (path);
182                         }
183                         make_zip_file (i, path, filename_format);
184                         written += i.size();
185                 }
186         }
187
188         return written;
189 }
190
191
192 /** Email one ZIP file per cinema to the cinema.
193  *  @param kdms KDMs to email.
194  *  @param container_name_format Format of folder / ZIP to use.
195  *  @param filename_format Format of filenames to use.
196  *  @param name_values Values to substitute into \p container_name_format and \p filename_format.
197  *  @param cpl_name Name of the CPL that the KDMs are for.
198  */
199 void
200 email (
201         list<list<KDMWithMetadataPtr>> kdms,
202         dcp::NameFormat container_name_format,
203         dcp::NameFormat filename_format,
204         string cpl_name
205         )
206 {
207         auto config = Config::instance ();
208
209         if (config->mail_server().empty()) {
210                 throw NetworkError (_("No mail server configured in preferences"));
211         }
212
213         for (auto const& i: kdms) {
214
215                 if (i.front()->emails().empty()) {
216                         continue;
217                 }
218
219                 auto zip_file = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
220                 boost::filesystem::create_directories (zip_file);
221                 zip_file /= container_name_format.get(i.front()->name_values(), ".zip");
222                 make_zip_file (i, zip_file, filename_format);
223
224                 auto subject = config->kdm_subject();
225                 boost::algorithm::replace_all (subject, "$CPL_NAME", cpl_name);
226                 boost::algorithm::replace_all (subject, "$START_TIME", i.front()->get('b').get_value_or(""));
227                 boost::algorithm::replace_all (subject, "$END_TIME", i.front()->get('e').get_value_or(""));
228                 boost::algorithm::replace_all (subject, "$CINEMA_NAME", i.front()->get('c').get_value_or(""));
229
230                 auto body = config->kdm_email();
231                 boost::algorithm::replace_all (body, "$CPL_NAME", cpl_name);
232                 boost::algorithm::replace_all (body, "$START_TIME", i.front()->get('b').get_value_or(""));
233                 boost::algorithm::replace_all (body, "$END_TIME", i.front()->get('e').get_value_or(""));
234                 boost::algorithm::replace_all (body, "$CINEMA_NAME", i.front()->get('c').get_value_or(""));
235
236                 string screens;
237                 for (auto j: i) {
238                         auto screen_name = j->get('n');
239                         if (screen_name) {
240                                 screens += *screen_name + ", ";
241                         }
242                 }
243                 boost::algorithm::replace_all (body, "$SCREENS", screens.substr (0, screens.length() - 2));
244
245                 Emailer email (config->kdm_from(), i.front()->emails(), subject, body);
246
247                 for (auto i: config->kdm_cc()) {
248                         email.add_cc (i);
249                 }
250                 if (!config->kdm_bcc().empty()) {
251                         email.add_bcc (config->kdm_bcc());
252                 }
253
254                 email.add_attachment (zip_file, container_name_format.get(i.front()->name_values(), ".zip"), "application/zip");
255
256                 try {
257                         email.send (config->mail_server(), config->mail_port(), config->mail_protocol(), config->mail_user(), config->mail_password());
258                 } catch (...) {
259                         boost::filesystem::remove (zip_file);
260                         dcpomatic_log->log ("Email content follows", LogEntry::TYPE_DEBUG_EMAIL);
261                         dcpomatic_log->log (email.email(), LogEntry::TYPE_DEBUG_EMAIL);
262                         dcpomatic_log->log ("Email session follows", LogEntry::TYPE_DEBUG_EMAIL);
263                         dcpomatic_log->log (email.notes(), LogEntry::TYPE_DEBUG_EMAIL);
264                         throw;
265                 }
266
267                 boost::filesystem::remove (zip_file);
268
269                 dcpomatic_log->log ("Email content follows", LogEntry::TYPE_DEBUG_EMAIL);
270                 dcpomatic_log->log (email.email(), LogEntry::TYPE_DEBUG_EMAIL);
271                 dcpomatic_log->log ("Email session follows", LogEntry::TYPE_DEBUG_EMAIL);
272                 dcpomatic_log->log (email.notes(), LogEntry::TYPE_DEBUG_EMAIL);
273         }
274 }