1 /*
2     This file is part of darktable,
3     Copyright (C) 2010-2020 darktable developers.
4 
5     darktable is free software: you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation, either version 3 of the License, or
8     (at your option) any later version.
9 
10     darktable is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU General Public License
16     along with darktable.  If not, see <http://www.gnu.org/licenses/>.
17 */
18 
19 #include "common/darktable.h"
20 #include "common/file_location.h"
21 #include "common/image.h"
22 #include "common/image_cache.h"
23 #include "common/imageio.h"
24 #include "common/imageio_module.h"
25 #include "control/conf.h"
26 #include "control/control.h"
27 #include "dtgtk/button.h"
28 #include "dtgtk/paint.h"
29 #include "gui/gtk.h"
30 #include "imageio/storage/imageio_storage_api.h"
31 #include <stdio.h>
32 #include <stdlib.h>
33 
34 DT_MODULE(2)
35 
36 typedef struct _email_attachment_t
37 {
38   uint32_t imgid; // The image id of exported image
39   gchar *file;    // Full filename of exported image
40 } _email_attachment_t;
41 
42 // saved params
43 typedef struct dt_imageio_email_t
44 {
45   char filename[DT_MAX_PATH_FOR_PARAMS];
46   GList *images;
47 } dt_imageio_email_t;
48 
49 
name(const struct dt_imageio_module_storage_t * self)50 const char *name(const struct dt_imageio_module_storage_t *self)
51 {
52   return _("send as email");
53 }
54 
legacy_params(dt_imageio_module_storage_t * self,const void * const old_params,const size_t old_params_size,const int old_version,const int new_version,size_t * new_size)55 void *legacy_params(dt_imageio_module_storage_t *self, const void *const old_params,
56                     const size_t old_params_size, const int old_version, const int new_version,
57                     size_t *new_size)
58 {
59   if(old_version == 1 && new_version == 2)
60   {
61     typedef struct dt_imageio_email_v1_t
62     {
63       char filename[1024];
64       GList *images;
65     } dt_imageio_email_v1_t;
66 
67     dt_imageio_email_t *n = (dt_imageio_email_t *)malloc(sizeof(dt_imageio_email_t));
68     dt_imageio_email_v1_t *o = (dt_imageio_email_v1_t *)old_params;
69 
70     g_strlcpy(n->filename, o->filename, sizeof(n->filename));
71 
72     *new_size = self->params_size(self);
73     return n;
74   }
75   return NULL;
76 }
77 
recommended_dimension(struct dt_imageio_module_storage_t * self,dt_imageio_module_data_t * data,uint32_t * width,uint32_t * height)78 int recommended_dimension(struct dt_imageio_module_storage_t *self, dt_imageio_module_data_t *data, uint32_t *width, uint32_t *height)
79 {
80   *width = 1536;
81   *height = 1536;
82   return 1;
83 }
84 
85 
gui_init(dt_imageio_module_storage_t * self)86 void gui_init(dt_imageio_module_storage_t *self)
87 {
88 }
89 
gui_cleanup(dt_imageio_module_storage_t * self)90 void gui_cleanup(dt_imageio_module_storage_t *self)
91 {
92   free(self->gui_data);
93 }
94 
gui_reset(dt_imageio_module_storage_t * self)95 void gui_reset(dt_imageio_module_storage_t *self)
96 {
97 }
98 
store(dt_imageio_module_storage_t * self,dt_imageio_module_data_t * sdata,const int imgid,dt_imageio_module_format_t * format,dt_imageio_module_data_t * fdata,const int num,const int total,const gboolean high_quality,const gboolean upscale,const gboolean export_masks,dt_colorspaces_color_profile_type_t icc_type,const gchar * icc_filename,dt_iop_color_intent_t icc_intent,dt_export_metadata_t * metadata)99 int store(dt_imageio_module_storage_t *self, dt_imageio_module_data_t *sdata, const int imgid,
100           dt_imageio_module_format_t *format, dt_imageio_module_data_t *fdata, const int num, const int total,
101           const gboolean high_quality, const gboolean upscale, const gboolean export_masks,
102           dt_colorspaces_color_profile_type_t icc_type, const gchar *icc_filename, dt_iop_color_intent_t icc_intent,
103           dt_export_metadata_t *metadata)
104 {
105   dt_imageio_email_t *d = (dt_imageio_email_t *)sdata;
106 
107   _email_attachment_t *attachment = (_email_attachment_t *)g_malloc(sizeof(_email_attachment_t));
108   attachment->imgid = imgid;
109 
110   /* construct a temporary file name */
111   char tmpdir[PATH_MAX] = { 0 };
112   dt_loc_get_tmp_dir(tmpdir, sizeof(tmpdir));
113 
114   char dirname[PATH_MAX] = { 0 };
115   gboolean from_cache = FALSE;
116   dt_image_full_path(imgid, dirname, sizeof(dirname), &from_cache);
117   gchar *filename = g_path_get_basename(dirname);
118 
119   g_strlcpy(dirname, filename, sizeof(dirname));
120 
121   dt_image_path_append_version(imgid, dirname, sizeof(dirname));
122 
123   gchar *end = g_strrstr(dirname, ".") + 1;
124 
125   if(end) *end = '\0';
126 
127   g_strlcat(dirname, format->extension(fdata), sizeof(dirname));
128 
129   // set exported filename
130 
131   attachment->file = g_build_filename(tmpdir, dirname, (char *)NULL);
132 
133   if(dt_imageio_export(imgid, attachment->file, format, fdata, high_quality, upscale, TRUE, export_masks, icc_type,
134                        icc_filename, icc_intent, self, sdata, num, total, metadata) != 0)
135   {
136     fprintf(stderr, "[imageio_storage_email] could not export to file: `%s'!\n", attachment->file);
137     dt_control_log(_("could not export to file `%s'!"), attachment->file);
138     g_free(attachment->file);
139     g_free(attachment);
140     g_free(filename);
141     return 1;
142   }
143 
144   dt_control_log(ngettext("%d/%d exported to `%s'", "%d/%d exported to `%s'", num),
145                  num, total, attachment->file);
146 
147 #ifdef _OPENMP // store can be called in parallel, so synch access to shared memory
148 #pragma omp critical
149 #endif
150   d->images = g_list_append(d->images, attachment);
151 
152   g_free(filename);
153 
154   return 0;
155 }
156 
params_size(dt_imageio_module_storage_t * self)157 size_t params_size(dt_imageio_module_storage_t *self)
158 {
159   return sizeof(dt_imageio_email_t) - sizeof(GList *);
160 }
161 
init(dt_imageio_module_storage_t * self)162 void init(dt_imageio_module_storage_t *self)
163 {
164 }
165 
get_params(dt_imageio_module_storage_t * self)166 void *get_params(dt_imageio_module_storage_t *self)
167 {
168   dt_imageio_email_t *d = (dt_imageio_email_t *)g_malloc0(sizeof(dt_imageio_email_t));
169   return d;
170 }
171 
set_params(dt_imageio_module_storage_t * self,const void * params,const int size)172 int set_params(dt_imageio_module_storage_t *self, const void *params, const int size)
173 {
174   if(size != self->params_size(self)) return 1;
175   return 0;
176 }
177 
free_params(dt_imageio_module_storage_t * self,dt_imageio_module_data_t * params)178 void free_params(dt_imageio_module_storage_t *self, dt_imageio_module_data_t *params)
179 {
180   if(!params) return;
181   free(params);
182 }
183 
finalize_store(dt_imageio_module_storage_t * self,dt_imageio_module_data_t * params)184 void finalize_store(dt_imageio_module_storage_t *self, dt_imageio_module_data_t *params)
185 {
186   dt_imageio_email_t *d = (dt_imageio_email_t *)params;
187 
188   const gchar *imageBodyFormat = " - %s (%s)\\n";      // filename, exif oneliner
189   const gint nb_images = g_list_length(d->images);
190   const gint argc = 5 + (2 * nb_images);
191 
192   char **argv = g_malloc0(sizeof(char *) * (argc + 1));
193 
194   gchar *body = NULL;
195 
196   argv[0] = "xdg-email";
197   argv[1] = "--subject";
198   argv[2] = _("images exported from darktable");
199   argv[3] = "--body";
200   int n = 5;
201 
202   for(GList *iter = d->images; iter; iter = g_list_next(iter))
203   {
204     gchar exif[256] = { 0 };
205     _email_attachment_t *attachment = (_email_attachment_t *)iter->data;
206     gchar *filename = g_path_get_basename(attachment->file);
207     const dt_image_t *img = dt_image_cache_get(darktable.image_cache, attachment->imgid, 'r');
208     dt_image_print_exif(img, exif, sizeof(exif));
209     dt_image_cache_read_release(darktable.image_cache, img);
210 
211     gchar *imgbody = g_strdup_printf(imageBodyFormat, filename, exif);
212     if (body != NULL) {
213       gchar *body_bak = body;
214       body = g_strconcat(body_bak, imgbody, NULL);
215       g_free(body_bak);
216     }
217     else
218     {
219       body = g_strdup(imgbody);
220     }
221     g_free(imgbody);
222     g_free(filename);
223 
224     argv[n]   = g_strdup("--attach");
225     // use attachment->file directly as we need to freed it, and this way it will be
226     // freed as part of the argument release after the spawn below.
227     argv[n+1] = attachment->file;
228     n += 2;
229   }
230   g_list_free_full(d->images, g_free);
231   d->images = NULL;
232 
233   argv[4] = body;
234 
235   argv[argc] = NULL;
236 
237   fprintf(stderr, "[email] launching '");
238   for (int k=0; k<argc; k++) fprintf(stderr, " %s", argv[k]);
239   fprintf(stderr, "'\n");
240 
241   gint exit_status = 0;
242 
243   g_spawn_sync (NULL, argv, NULL, G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL,
244                 NULL, NULL, NULL, NULL, &exit_status, NULL);
245 
246   for (int k=4; k<argc; k++) g_free(argv[k]);
247   g_free(argv);
248 
249   if(exit_status)
250   {
251     dt_control_log(_("could not launch email client!"));
252   }
253 }
254 
supported(struct dt_imageio_module_storage_t * storage,struct dt_imageio_module_format_t * format)255 int supported(struct dt_imageio_module_storage_t *storage, struct dt_imageio_module_format_t *format)
256 {
257   const char *mime = format->mime(NULL);
258   if(mime[0] == '\0') // this seems to be the copy format
259     return 0;
260 
261   return 1;
262 }
263 
264 // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
265 // vim: shiftwidth=2 expandtab tabstop=2 cindent
266 // kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
267