1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  * Copyright (C) 1997 Daniel Risacher
4  *
5  * This program 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  * This program 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 this program.  If not, see <https://www.gnu.org/licenses/>.
17  */
18 
19 /*
20  *   GUMP - Gimp Useless Mail Plugin
21  *          (or Gump Useless Mail Plugin if you prefer)
22  *
23  *   by Adrian Likins <adrian@gimp.org>
24  *      MIME encapsulation by Reagan Blundell <reagan@emails.net>
25  *
26  * As always: The utility of this plugin is left as an exercise for
27  * the reader
28  *
29  */
30 
31 #include "config.h"
32 
33 #include <string.h>
34 
35 #ifdef SENDMAIL
36 #include <sys/types.h>
37 #include <sys/wait.h>
38 #endif
39 
40 #include <glib/gstdio.h>
41 
42 #include <libgimp/gimp.h>
43 #include <libgimp/gimpui.h>
44 
45 #include "libgimp/stdplugins-intl.h"
46 
47 #define BUFFER_SIZE 256
48 
49 #define PLUG_IN_PROC   "plug-in-mail-image"
50 #define PLUG_IN_BINARY "mail"
51 #define PLUG_IN_ROLE   "gimp-mail"
52 
53 typedef struct
54 {
55   gchar filename[BUFFER_SIZE];
56   gchar receipt[BUFFER_SIZE];
57   gchar from[BUFFER_SIZE];
58   gchar subject[BUFFER_SIZE];
59   gchar comment[BUFFER_SIZE];
60 } m_info;
61 
62 
63 static void               query                   (void);
64 static void               run                     (const gchar      *name,
65                                                    gint              nparams,
66                                                    const GimpParam  *param,
67                                                    gint             *nreturn_vals,
68                                                    GimpParam       **return_vals);
69 
70 static GimpPDBStatusType  send_image              (const gchar      *filename,
71                                                    gint32            image_ID,
72                                                    gint32            drawable_ID,
73                                                    gint32            run_mode);
74 
75 static gboolean           send_dialog             (void);
76 static void               mail_entry_callback     (GtkWidget        *widget,
77                                                    gchar            *data);
78 static gboolean           valid_file              (const gchar      *filename);
79 static gchar            * find_extension          (const gchar      *filename);
80 
81 #ifdef SENDMAIL
82 static void               mesg_body_callback      (GtkTextBuffer    *buffer,
83                                                    gpointer          data);
84 
85 static gchar            * sendmail_content_type   (const gchar      *filename);
86 static void               sendmail_create_headers (FILE             *mailpipe);
87 static gboolean           sendmail_to64           (const gchar      *filename,
88                                                    FILE             *outfile,
89                                                    GError          **error);
90 static FILE             * sendmail_pipe           (gchar           **cmd,
91                                                    GPid             *pid);
92 #endif
93 
94 
95 const GimpPlugInInfo PLUG_IN_INFO =
96 {
97   NULL,  /* init_proc  */
98   NULL,  /* quit_proc  */
99   query, /* query_proc */
100   run,   /* run_proc   */
101 };
102 
103 static m_info mail_info =
104 {
105   "", "", "", "", ""
106 };
107 
108 static gchar *mesg_body = NULL;
109 
110 
MAIN()111 MAIN ()
112 
113 static void
114 query (void)
115 {
116   gchar *email_bin;
117 
118   static const GimpParamDef args[] =
119   {
120     { GIMP_PDB_INT32,    "run-mode",      "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
121     { GIMP_PDB_IMAGE,    "image",         "Input image" },
122     { GIMP_PDB_DRAWABLE, "drawable",      "Drawable to save" },
123     { GIMP_PDB_STRING,   "filename",      "The name of the file to save the image in" },
124     { GIMP_PDB_STRING,   "to-address",    "The email address to send to" },
125     { GIMP_PDB_STRING,   "from-address",  "The email address for the From: field" },
126     { GIMP_PDB_STRING,   "subject",       "The subject" },
127     { GIMP_PDB_STRING,   "comment",       "The Comment" },
128     { GIMP_PDB_INT32,    "encapsulation", "ignored" }
129   };
130 
131   /* Check if xdg-email or sendmail is installed.
132    * TODO: allow setting the location of the executable in preferences.
133    */
134 #ifdef SENDMAIL
135   if (strlen (SENDMAIL) == 0)
136     {
137       email_bin = g_find_program_in_path ("sendmail");
138     }
139   else
140     {
141       /* If a directory has been set at build time, we assume that sendmail
142        * can only be in this directory. */
143       email_bin = g_build_filename (SENDMAIL, "sendmail", NULL);
144       if (! g_file_test (email_bin, G_FILE_TEST_IS_EXECUTABLE))
145         {
146           g_free (email_bin);
147           email_bin = NULL;
148         }
149     }
150 #else
151   email_bin = g_find_program_in_path ("xdg-email");
152 #endif
153 
154   if (email_bin == NULL)
155     return;
156 
157   gimp_install_procedure (PLUG_IN_PROC,
158                           N_("Send the image by email"),
159 #ifdef SENDMAIL
160                           "Sendmail is used to send emails and must be properly configured.",
161 #else /* xdg-email */
162                           "The preferred email composer is used to send emails and must be properly configured.",
163 #endif
164                           "Adrian Likins, Reagan Blundell",
165                           "Adrian Likins, Reagan Blundell, Daniel Risacher, "
166                           "Spencer Kimball and Peter Mattis",
167                           "1995-1997",
168                           N_("Send by E_mail..."),
169                           "*",
170                           GIMP_PLUGIN,
171                           G_N_ELEMENTS (args), 0,
172                           args, NULL);
173 
174   gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/File/Send");
175   gimp_plugin_icon_register (PLUG_IN_PROC, GIMP_ICON_TYPE_ICON_NAME,
176                              (const guint8 *) GIMP_ICON_EDIT);
177 
178   g_free (email_bin);
179 }
180 
181 static void
run(const gchar * name,gint nparams,const GimpParam * param,gint * nreturn_vals,GimpParam ** return_vals)182 run (const gchar      *name,
183      gint              nparams,
184      const GimpParam  *param,
185      gint             *nreturn_vals,
186      GimpParam       **return_vals)
187 {
188   static GimpParam   values[2];
189   GimpRunMode        run_mode;
190   GimpPDBStatusType  status = GIMP_PDB_SUCCESS;
191   gint32             image_ID;
192   gint32             drawable_ID;
193 
194   INIT_I18N ();
195 
196   run_mode    = param[0].data.d_int32;
197   image_ID    = param[1].data.d_image;
198   drawable_ID = param[2].data.d_drawable;
199 
200   *nreturn_vals = 1;
201   *return_vals  = values;
202 
203   values[0].type          = GIMP_PDB_STATUS;
204   values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
205 
206   if (strcmp (name, PLUG_IN_PROC) == 0)
207     {
208       switch (run_mode)
209         {
210         case GIMP_RUN_INTERACTIVE:
211           gimp_get_data (PLUG_IN_PROC, &mail_info);
212           {
213             gchar *filename = gimp_image_get_filename (image_ID);
214 
215             if (filename)
216               {
217                 gchar *basename = g_filename_display_basename (filename);
218 
219                 g_strlcpy (mail_info.filename, basename, BUFFER_SIZE);
220                 g_free (basename);
221                 g_free (filename);
222               }
223           }
224 
225           if (! send_dialog ())
226             status = GIMP_PDB_CANCEL;
227           break;
228 
229         case GIMP_RUN_NONINTERACTIVE:
230           /*  Make sure all the arguments are there!  */
231           if (nparams < 8)
232             {
233               status = GIMP_PDB_CALLING_ERROR;
234             }
235           else
236             {
237               g_strlcpy (mail_info.filename,
238                          param[3].data.d_string, BUFFER_SIZE);
239               g_strlcpy (mail_info.receipt,
240                          param[4].data.d_string, BUFFER_SIZE);
241               g_strlcpy (mail_info.from,
242                          param[5].data.d_string, BUFFER_SIZE);
243               g_strlcpy (mail_info.subject,
244                          param[6].data.d_string, BUFFER_SIZE);
245               g_strlcpy (mail_info.comment,
246                          param[7].data.d_string, BUFFER_SIZE);
247             }
248           break;
249 
250         case GIMP_RUN_WITH_LAST_VALS:
251           gimp_get_data (PLUG_IN_PROC, &mail_info);
252           break;
253 
254         default:
255           break;
256         }
257 
258       if (status == GIMP_PDB_SUCCESS)
259         {
260           status = send_image (mail_info.filename,
261                                image_ID,
262                                drawable_ID,
263                                run_mode);
264 
265           if (status == GIMP_PDB_SUCCESS)
266             {
267               if (mesg_body)
268                 g_strlcpy (mail_info.comment, mesg_body, BUFFER_SIZE);
269 
270               gimp_set_data (PLUG_IN_PROC, &mail_info, sizeof (m_info));
271             }
272         }
273     }
274   else
275     {
276       status = GIMP_PDB_CALLING_ERROR;
277     }
278 
279   values[0].data.d_status = status;
280 }
281 
282 static GimpPDBStatusType
send_image(const gchar * filename,gint32 image_ID,gint32 drawable_ID,gint32 run_mode)283 send_image (const gchar *filename,
284             gint32       image_ID,
285             gint32       drawable_ID,
286             gint32       run_mode)
287 {
288   GimpPDBStatusType  status = GIMP_PDB_SUCCESS;
289   gchar             *ext;
290   gchar             *tmpname;
291 #ifndef SENDMAIL /* xdg-email */
292   gchar             *mailcmd[9];
293   gchar             *filepath = NULL;
294   GFile             *tmp_dir  = NULL;
295   GFileEnumerator   *enumerator;
296   gint               i;
297 #else /* SENDMAIL */
298   gchar             *mailcmd[3];
299   GPid               mailpid;
300   FILE              *mailpipe = NULL;
301 #endif
302   GError            *error = NULL;
303 
304   ext = find_extension (filename);
305 
306   if (ext == NULL)
307     return GIMP_PDB_CALLING_ERROR;
308 
309   /* get a temp name with the right extension and save into it. */
310   tmpname = gimp_temp_name (ext + 1);
311 
312   if (! (gimp_file_save (run_mode,
313                          image_ID,
314                          drawable_ID,
315                          tmpname,
316                          tmpname) && valid_file (tmpname)))
317     {
318       goto error;
319     }
320 
321 #ifndef SENDMAIL /* xdg-email */
322   /* From xdg-email doc:
323    * "Some e-mail applications require the file to remain present
324    * after xdg-email returns."
325    * As a consequence, the file cannot be removed at the end of the
326    * function. We actually have no way to ever know *when* the file can
327    * be removed since the caller could leave the email window opened for
328    * hours. Yet we still want to clean sometimes and not have temporary
329    * images piling up.
330    * So I use a known directory that we control under $GIMP_DIRECTORY/tmp/,
331    * and clean it out each time the plugin runs. This means that *if* you
332    * are in the above case (your email client requires the file to stay
333    * alive), * you cannot run twice the plugin at the same time.
334    */
335   tmp_dir = gimp_directory_file ("tmp", PLUG_IN_PROC, NULL);
336 
337   if (g_mkdir_with_parents (gimp_file_get_utf8_name (tmp_dir),
338                             S_IRUSR | S_IWUSR | S_IXUSR) == -1)
339     {
340       g_message ("Temporary directory %s could not be created.",
341                  gimp_file_get_utf8_name (tmp_dir));
342       g_error_free (error);
343       goto error;
344     }
345 
346   enumerator = g_file_enumerate_children (tmp_dir,
347                                           G_FILE_ATTRIBUTE_STANDARD_TYPE,
348                                           G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
349                                           NULL, NULL);
350   if (enumerator)
351     {
352       GFileInfo *info;
353 
354       while ((info = g_file_enumerator_next_file (enumerator,
355                                                   NULL, NULL)))
356         {
357           if (g_file_info_get_file_type (info) == G_FILE_TYPE_REGULAR)
358             {
359               GFile *file = g_file_enumerator_get_child (enumerator, info);
360               g_file_delete (file, NULL, NULL);
361               g_object_unref (file);
362             }
363 
364           g_object_unref (info);
365         }
366 
367       g_object_unref (enumerator);
368     }
369 
370   filepath = g_build_filename (gimp_file_get_utf8_name (tmp_dir),
371                                mail_info.filename, NULL);
372   g_rename (tmpname, filepath);
373 
374   mailcmd[0] = g_strdup ("xdg-email");
375   mailcmd[1] = "--attach";
376   mailcmd[2] = filepath;
377   i = 3;
378   if (strlen (mail_info.subject) > 0)
379     {
380       mailcmd[i++] = "--subject";
381       mailcmd[i++] = mail_info.subject;
382     }
383   if (strlen (mail_info.comment) > 0)
384     {
385       mailcmd[i++] = "--body";
386       mailcmd[i++] = mail_info.comment;
387     }
388   if (strlen (mail_info.receipt) > 0)
389     {
390       mailcmd[i++] = mail_info.receipt;
391     }
392   mailcmd[i] = NULL;
393 
394   if (! g_spawn_async (NULL, mailcmd, NULL,
395                        G_SPAWN_SEARCH_PATH,
396                        NULL, NULL, NULL, &error))
397     {
398       g_message ("%s", error->message);
399       g_error_free (error);
400       goto error;
401     }
402 
403 #else /* SENDMAIL */
404   /* construct the "sendmail user@location" line */
405   if (strlen (SENDMAIL) == 0)
406     mailcmd[0] = g_strdup ("sendmail");
407   else
408     mailcmd[0] = g_build_filename (SENDMAIL, "sendmail", NULL);
409 
410   mailcmd[1] = mail_info.receipt;
411   mailcmd[2] = NULL;
412 
413   /* create a pipe to sendmail */
414   mailpipe = sendmail_pipe (mailcmd, &mailpid);
415 
416   if (mailpipe == NULL)
417     return GIMP_PDB_EXECUTION_ERROR;
418 
419   sendmail_create_headers (mailpipe);
420 
421   fflush (mailpipe);
422 
423   if (! sendmail_to64 (tmpname, mailpipe, &error))
424     {
425       g_message ("%s", error->message);
426       g_error_free (error);
427       goto error;
428     }
429 
430   fprintf (mailpipe, "\n--GUMP-MIME-boundary--\n");
431 #endif
432 
433   goto cleanup;
434 
435 error:
436   /* stop sendmail from doing anything */
437 #ifdef SENDMAIL
438   kill (mailpid, SIGINT);
439 #endif
440   status = GIMP_PDB_EXECUTION_ERROR;
441 
442 cleanup:
443   /* close out the sendmail process */
444 #ifdef SENDMAIL
445   if (mailpipe)
446     {
447       fclose (mailpipe);
448       waitpid (mailpid, NULL, 0);
449       g_spawn_close_pid (mailpid);
450     }
451 
452   /* delete the tmpfile that was generated */
453   g_unlink (tmpname);
454 #else
455   if (tmp_dir)
456     g_object_unref (tmp_dir);
457   if (filepath)
458     g_free (filepath);
459 #endif
460 
461   g_free (mailcmd[0]);
462   g_free (tmpname);
463 
464   return status;
465 }
466 
467 
468 static gboolean
send_dialog(void)469 send_dialog (void)
470 {
471   GtkWidget     *dlg;
472   GtkWidget     *main_vbox;
473   GtkWidget     *entry;
474   GtkWidget     *table;
475 #ifdef SENDMAIL
476   GtkWidget     *scrolled_window;
477   GtkWidget     *text_view;
478   GtkTextBuffer *text_buffer;
479 #endif
480   gchar         *gump_from;
481   gint           row = 0;
482   gboolean       run;
483 
484   gimp_ui_init (PLUG_IN_BINARY, FALSE);
485 
486   /* check gimprc for a preferred "From:" address */
487   gump_from = gimp_gimprc_query ("gump-from");
488 
489   if (gump_from)
490     {
491       g_strlcpy (mail_info.from, gump_from, BUFFER_SIZE);
492       g_free (gump_from);
493     }
494 
495   dlg = gimp_dialog_new (_("Send by Email"), PLUG_IN_ROLE,
496                          NULL, 0,
497                          gimp_standard_help_func, PLUG_IN_PROC,
498 
499                          _("_Cancel"), GTK_RESPONSE_CANCEL,
500                          _("_Send"),   GTK_RESPONSE_OK,
501 
502                          NULL);
503 
504   gtk_dialog_set_alternative_button_order (GTK_DIALOG (dlg),
505                                            GTK_RESPONSE_OK,
506                                            GTK_RESPONSE_CANCEL,
507                                            -1);
508 
509   gimp_window_set_transient (GTK_WINDOW (dlg));
510 
511   main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
512   gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
513   gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))),
514                       main_vbox, TRUE, TRUE, 0);
515   gtk_widget_show (main_vbox);
516 
517   /* table */
518   table = gtk_table_new (5, 2, FALSE);
519   gtk_box_pack_start (GTK_BOX (main_vbox), table, FALSE, FALSE, 0);
520   gtk_widget_show (table);
521 
522   gtk_table_set_row_spacings (GTK_TABLE (table), 6);
523   gtk_table_set_row_spacing (GTK_TABLE (table), 0, 12);
524   gtk_table_set_col_spacings (GTK_TABLE (table), 6);
525 
526   /* Filename entry */
527   entry = gtk_entry_new ();
528   gtk_widget_set_size_request (entry, 200, -1);
529   gtk_entry_set_max_length (GTK_ENTRY (entry), BUFFER_SIZE - 1);
530   gtk_entry_set_text (GTK_ENTRY (entry), mail_info.filename);
531   gimp_table_attach_aligned (GTK_TABLE (table), 0, row++,
532                              _("_Filename:"), 0.0, 0.5,
533                              entry, 1, FALSE);
534   g_signal_connect (entry, "changed",
535                     G_CALLBACK (mail_entry_callback),
536                     mail_info.filename);
537   gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
538 
539 #ifdef SENDMAIL
540   /* To entry */
541   entry = gtk_entry_new ();
542   gtk_widget_set_size_request (entry, 200, -1);
543   gtk_entry_set_max_length (GTK_ENTRY (entry), BUFFER_SIZE - 1);
544   gtk_entry_set_text (GTK_ENTRY (entry), mail_info.receipt);
545   gimp_table_attach_aligned (GTK_TABLE (table), 0, row++,
546                              C_("email-address", "_To:"), 0.0, 0.5,
547                              entry, 1, FALSE);
548   g_signal_connect (entry, "changed",
549                     G_CALLBACK (mail_entry_callback),
550                     mail_info.receipt);
551 
552   gtk_widget_grab_focus (entry);
553 
554   /* From entry */
555   entry = gtk_entry_new ();
556   gtk_widget_set_size_request (entry, 200, -1);
557   gtk_entry_set_max_length (GTK_ENTRY (entry), BUFFER_SIZE - 1);
558   gtk_entry_set_text (GTK_ENTRY (entry), mail_info.from);
559   gimp_table_attach_aligned (GTK_TABLE (table), 0, row++,
560                              C_("email-address", "_From:"), 0.0, 0.5,
561                              entry, 1, FALSE);
562   g_signal_connect (entry, "changed",
563                     G_CALLBACK (mail_entry_callback),
564                     mail_info.from);
565 
566   /* Subject entry */
567   entry = gtk_entry_new ();
568   gtk_widget_set_size_request (entry, 200, -1);
569   gtk_entry_set_max_length (GTK_ENTRY (entry), BUFFER_SIZE - 1);
570   gtk_entry_set_text (GTK_ENTRY (entry), mail_info.subject);
571   gimp_table_attach_aligned (GTK_TABLE (table), 0, row++,
572                              _("S_ubject:"), 0.0, 0.5,
573                              entry, 1, FALSE);
574   g_signal_connect (entry, "changed",
575                     G_CALLBACK (mail_entry_callback),
576                     mail_info.subject);
577 
578   /* Body  */
579   scrolled_window = gtk_scrolled_window_new (NULL, NULL);
580   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
581                                        GTK_SHADOW_IN);
582   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
583                                   GTK_POLICY_AUTOMATIC,
584                                   GTK_POLICY_AUTOMATIC);
585   gtk_box_pack_start (GTK_BOX (main_vbox), scrolled_window, TRUE, TRUE, 0);
586   gtk_widget_show (scrolled_window);
587 
588   text_buffer = gtk_text_buffer_new (NULL);
589 
590   g_signal_connect (text_buffer, "changed",
591                     G_CALLBACK (mesg_body_callback),
592                     NULL);
593 
594   gtk_text_buffer_set_text (text_buffer, mail_info.comment, -1);
595 
596   text_view = gtk_text_view_new_with_buffer (text_buffer);
597   g_object_unref (text_buffer);
598 
599   gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (text_view), GTK_WRAP_WORD);
600   gtk_container_add (GTK_CONTAINER (scrolled_window), text_view);
601   gtk_widget_show (text_view);
602 #endif
603 
604   gtk_widget_show (dlg);
605 
606   run = (gimp_dialog_run (GIMP_DIALOG (dlg)) == GTK_RESPONSE_OK);
607 
608   gtk_widget_destroy (dlg);
609 
610   return run;
611 }
612 
613 static gboolean
valid_file(const gchar * filename)614 valid_file (const gchar *filename)
615 {
616   GStatBuf buf;
617 
618   return g_stat (filename, &buf) == 0 && buf.st_size > 0;
619 }
620 
621 static gchar *
find_extension(const gchar * filename)622 find_extension (const gchar *filename)
623 {
624   gchar *filename_copy;
625   gchar *ext;
626 
627   /* we never free this copy - aren't we evil! */
628   filename_copy = g_strdup (filename);
629 
630   /* find the extension, boy! */
631   ext = strrchr (filename_copy, '.');
632 
633   while (TRUE)
634     {
635       if (!ext || ext[1] == '\0' || strchr (ext, G_DIR_SEPARATOR))
636         {
637           g_message (_("some sort of error with the file extension "
638                        "or lack thereof"));
639 
640           return NULL;
641         }
642 
643       if (0 != g_ascii_strcasecmp (ext, ".gz") &&
644           0 != g_ascii_strcasecmp (ext, ".bz2"))
645         {
646           return ext;
647         }
648       else
649         {
650           /* we found something, loop back, and look again */
651           *ext = 0;
652           ext = strrchr (filename_copy, '.');
653         }
654     }
655 
656   g_free (filename_copy);
657 
658   return ext;
659 }
660 
661 static void
mail_entry_callback(GtkWidget * widget,gchar * data)662 mail_entry_callback (GtkWidget *widget,
663                      gchar     *data)
664 {
665   g_strlcpy (data, gtk_entry_get_text (GTK_ENTRY (widget)), BUFFER_SIZE);
666 }
667 
668 #ifdef SENDMAIL
669 static void
mesg_body_callback(GtkTextBuffer * buffer,gpointer data)670 mesg_body_callback (GtkTextBuffer *buffer,
671                     gpointer       data)
672 {
673   GtkTextIter start_iter;
674   GtkTextIter end_iter;
675 
676   gtk_text_buffer_get_bounds (buffer, &start_iter, &end_iter);
677 
678   g_free (mesg_body);
679   mesg_body = gtk_text_buffer_get_text (buffer, &start_iter, &end_iter, FALSE);
680 }
681 
682 static gchar *
sendmail_content_type(const gchar * filename)683 sendmail_content_type (const gchar *filename)
684 {
685   /* This function returns a MIME Content-type: value based on the
686      filename it is given.  */
687   const gchar *type_mappings[20] =
688   {
689     "gif" , "image/gif",
690     "jpg" , "image/jpeg",
691     "jpeg", "image/jpeg",
692     "tif" , "image/tiff",
693     "tiff", "image/tiff",
694     "png" , "image/png",
695     "g3"  , "image/g3fax",
696     "ps"  , "application/postscript",
697     "eps" , "application/postscript",
698     NULL, NULL
699   };
700 
701   gchar *ext;
702   gint   i;
703 
704   ext = find_extension (filename);
705 
706   if (!ext)
707     {
708       return g_strdup ("application/octet-stream");
709     }
710 
711   i = 0;
712   ext += 1;
713 
714   while (type_mappings[i])
715     {
716       if (g_ascii_strcasecmp (ext, type_mappings[i]) == 0)
717         {
718           return g_strdup (type_mappings[i + 1]);
719         }
720 
721       i += 2;
722     }
723 
724   return g_strdup_printf ("image/x-%s", ext);
725 }
726 
727 static void
sendmail_create_headers(FILE * mailpipe)728 sendmail_create_headers (FILE *mailpipe)
729 {
730   /* create all the mail header stuff. Feel free to add your own */
731   /* It is advisable to leave the X-Mailer header though, as     */
732   /* there is a possibility of a Gimp mail scanner/reader in the  */
733   /* future. It will probabaly need that header.                 */
734 
735   fprintf (mailpipe, "To: %s \n", mail_info.receipt);
736   fprintf (mailpipe, "Subject: %s \n", mail_info.subject);
737   if (strlen (mail_info.from) > 0)
738     fprintf (mailpipe, "From: %s \n", mail_info.from);
739 
740   fprintf (mailpipe, "X-Mailer: GIMP Useless Mail plug-in %s\n", GIMP_VERSION);
741 
742   fprintf (mailpipe, "MIME-Version: 1.0\n");
743   fprintf (mailpipe, "Content-type: multipart/mixed; "
744                      "boundary=GUMP-MIME-boundary\n");
745 
746   fprintf (mailpipe, "\n\n");
747 
748   fprintf (mailpipe, "--GUMP-MIME-boundary\n");
749   fprintf (mailpipe, "Content-type: text/plain; charset=UTF-8\n\n");
750 
751   if (mesg_body)
752     fprintf (mailpipe, "%s", mesg_body);
753 
754   fprintf (mailpipe, "\n\n");
755 
756   {
757     gchar *content = sendmail_content_type (mail_info.filename);
758 
759     fprintf (mailpipe, "--GUMP-MIME-boundary\n");
760     fprintf (mailpipe, "Content-type: %s\n", content);
761     fprintf (mailpipe, "Content-transfer-encoding: base64\n");
762     fprintf (mailpipe, "Content-disposition: attachment; filename=\"%s\"\n",
763              mail_info.filename);
764     fprintf (mailpipe, "Content-description: %s\n\n", mail_info.filename);
765 
766     g_free (content);
767   }
768 }
769 
770 static gboolean
sendmail_to64(const gchar * filename,FILE * outfile,GError ** error)771 sendmail_to64 (const gchar  *filename,
772                FILE         *outfile,
773                GError      **error)
774 {
775   GMappedFile  *infile;
776   const guchar *in;
777   gchar         out[2048];
778   gint          state = 0;
779   gint          save  = 0;
780   gsize         len;
781   gsize         bytes;
782   gsize         c;
783 
784   infile = g_mapped_file_new (filename, FALSE, error);
785   if (! infile)
786     return FALSE;
787 
788   in = (const guchar *) g_mapped_file_get_contents (infile);
789   len = g_mapped_file_get_length (infile);
790 
791   for (c = 0; c < len;)
792     {
793       gsize step = MIN (1024, len - c);
794 
795       bytes = g_base64_encode_step (in + c, step, TRUE, out, &state, &save);
796       fwrite (out, 1, bytes, outfile);
797 
798       c += step;
799     }
800 
801   bytes = g_base64_encode_close (TRUE, out, &state, &save);
802   fwrite (out, 1, bytes, outfile);
803 
804   g_mapped_file_unref (infile);
805 
806   return TRUE;
807 }
808 
809 static FILE *
sendmail_pipe(gchar ** cmd,GPid * pid)810 sendmail_pipe (gchar **cmd,
811                GPid   *pid)
812 {
813   gint    fd;
814   GError *err = NULL;
815 
816   if (! g_spawn_async_with_pipes (NULL, cmd, NULL,
817                                   G_SPAWN_DO_NOT_REAP_CHILD |
818                                   G_SPAWN_SEARCH_PATH,
819                                   NULL, NULL, pid, &fd, NULL, NULL, &err))
820     {
821       g_message (_("Could not start sendmail (%s)"), err->message);
822       g_error_free (err);
823 
824       *pid = -1;
825       return NULL;
826     }
827 
828   return fdopen (fd, "wb");
829 }
830 #endif
831