mail.c 26.6 KB
Newer Older
Elliot Lee's avatar
Elliot Lee committed
1 2
/* The GIMP -- an image manipulation program
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3
 * Copyright (C) 1997 Daniel Risacher
Elliot Lee's avatar
Elliot Lee committed
4 5 6 7 8 9 10 11 12 13 14 15 16
 *
 * 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
17
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Elliot Lee's avatar
Elliot Lee committed
18 19 20
 */

/*
21 22 23
 *   GUMP - Gimp Useless Mail Plugin (or Gump Useless Mail Plugin if
 *   you prefer) version about .85 I would say... give or take a few
 *   decimal points
24 25 26 27 28 29 30 31 32
 *
 *
 *   by Adrian Likins <adrian@gimp.org>
 *      MIME encapsulation by Reagan Blundell <reagan@emails.net>
 *
 *
 *
 *   Based heavily on gz.c by Daniel Risacher
 *
33 34
 *     Lets you choose to send a image to the mail from the file save
 *     as dialog.  images are piped to uuencode and then to mail...
35 36
 *
 *
37 38 39 40
 *   This works fine for .99.10. I havent actually tried it in
 *   combination with the gz plugin, but it works with all other file
 *   types. I will eventually get around to making sure it works with
 *   gz.
41 42
 *
 *  To use: 1) image->File->mail image
43 44 45 46
 *          2) when the mail dialog popups up, fill it out. Only to:
 *             and filename are required note: the filename needs to a
 *             type that the image can be saved as. otherwise you will
 *             just send an empty message.
47 48 49
 *          3) click ok and it should be on its way
 *
 *
50 51 52 53
 * NOTE: You probabaly need sendmail installed. If your sendmail is in
 *       an odd spot you can change the #define below. If you use
 *       qmail or other MTA's, and this works after changing the
 *       MAILER, let me know how well or what changes were needed.
54
 *
55 56
 * NOTE: Uuencode is needed. If it is in the path, it should work fine
 *       as is. Other- wise just change the UUENCODE.
57 58
 *
 *
59
 * TODO: 1) the aforementioned abilty to specify the
60 61 62 63 64
 *           uuencode filename                         *done*
 *       2) someway to do this without tmp files
 *              * wont happen anytime soon*
 *       3) MIME? *done*
 *       4) a pointlessly snazzier dialog
65
 *       5) make sure it works with gz
66
 *               * works for .xcfgz but not .xcf.gz *
67
 *       6) add an option to choose if mail get
68 69 70
 *          uuencode or not (or MIME'ed for that matter)
 *       7) realtime preview
 *       8) better entry for comments    *done*
71
 *       9) list of frequently used addreses
72 73
 *      10) openGL compliance
 *      11) better handling of filesave errors
74
 *
75
 *
76 77
 * As always: The utility of this plugin is left as an exercise for
 * the reader
78
 *
Elliot Lee's avatar
Elliot Lee committed
79 80
 */

81 82
#include "config.h"

83 84 85
#include <string.h>
#include <errno.h>

86
#include <sys/types.h>
Elliot Lee's avatar
Elliot Lee committed
87 88 89
#include <sys/param.h>
#include <sys/wait.h>
#include <sys/stat.h>
90 91

#ifdef HAVE_UNISTD_H
Elliot Lee's avatar
Elliot Lee committed
92
#include <unistd.h>
93
#endif
94

95
#include <glib/gstdio.h>
96

97 98
#include <libgimp/gimp.h>
#include <libgimp/gimpui.h>
Elliot Lee's avatar
Elliot Lee committed
99

100
#include "libgimp/stdplugins-intl.h"
Elliot Lee's avatar
Elliot Lee committed
101

102

103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
/* GdkPixbuf RGBA C-Source image dump 1-byte-run-length-encoded */

#ifdef __SUNPRO_C
#pragma align 4 (mail_icon)
#endif
#ifdef __GNUC__
static const guint8 mail_icon[] __attribute__ ((__aligned__ (4))) =
#else
static const guint8 mail_icon[] =
#endif
{ ""
  /* Pixbuf magic (0x47646b50) */
  "GdkP"
  /* length: header (24) + pixel_data (473) */
  "\0\0\1\361"
  /* pixdata_type (0x2010002) */
  "\2\1\0\2"
  /* rowstride (64) */
  "\0\0\0@"
  /* width (16) */
  "\0\0\0\20"
  /* height (16) */
  "\0\0\0\20"
  /* pixel_data: */
  "\261\0\0\0\0\3\0\0\0e\0\0\0\377\40\40\40\377\211\0\0\0\377\1\0\0\0e\203"
  "\0\0\0\0\2\0\0\0\377ttt\377\211\365\365\365\377\2DDD\377\0\0\0\377\203"
  "\0\0\0\0\15\0\0\0\377\365\365\365\377ggg\377\361\361\361\377\343\343"
  "\343\377\365\365\365\377\343\343\343\377\363\363\363\377\343\343\343"
  "\377\330\330\330\377VVV\377\270\270\270\377\0\0\0\377\203\0\0\0\0\15"
  "\0\0\0\377\365\365\365\377\343\343\343\377hhh\377\363\363\363\377\345"
  "\345\345\377\362\362\362\377\343\343\343\377\326\326\326\377YYY\377\326"
  "\326\326\377\260\260\260\377\0\0\0\377\203\0\0\0\0\2\0\0\0\377\365\365"
  "\365\377\202\343\343\343\377\11eee\377\363\363\363\377\343\343\343\377"
  "\333\333\333\377YYY\377\343\343\343\377\323\323\323\377\260\260\260\377"
  "\0\0\0\377\203\0\0\0\0\15\0\0\0\377\365\365\365\377\343\343\343\377\336"
  "\336\336\377\177\177\177\377YYY\377\365\365\365\377YYY\377nnn\377\330"
  "\330\330\377\306\306\306\377\257\257\257\377\0\0\0\377\203\0\0\0\0\4"
  "\0\0\0\377\365\365\365\377\340\340\340\377yyy\377\202\343\343\343\377"
  "\1YYY\377\202\333\333\333\377\4eee\377\266\266\266\377\257\257\257\377"
  "\0\0\0\377\203\0\0\0\0\15\0\0\0\377\365\365\365\377{{{\377\343\343\343"
  "\377\323\323\323\377\334\334\334\377\325\325\325\377\333\333\333\377"
  "\324\324\324\377\270\270\270\377JJJ\377\265\265\265\377\0\0\0\377\203"
  "\0\0\0\0\3\0\0\0\377ZZZ\377\252\252\252\377\203\253\253\253\377\1\252"
  "\252\252\377\202\254\254\254\377\4\255\255\255\377\262\262\262\377DD"
  "D\377\0\0\0\377\203\0\0\0\0\1\0\0\0e\213\0\0\0\377\1\0\0\0e\262\0\0\0"
  "\0"};


#ifndef SENDMAIL
#define SENDMAIL "/usr/lib/sendmail"
#endif

#ifndef UUENCODE
#define UUENCODE "uuencode"
#endif

159 160 161 162 163 164 165 166
enum
{
  ENCAPSULATION_UUENCODE,
  ENCAPSULATION_MIME
};

#define BUFFER_SIZE 256

167 168 169
#define PLUG_IN_NAME  "plug_in_mail_image"
#define HELP_ID       "plug-in-mail-image"

170 171 172 173 174 175 176 177
typedef struct
{
  gchar receipt[BUFFER_SIZE];
  gchar subject[BUFFER_SIZE];
  gchar comment[BUFFER_SIZE];
  gchar from[BUFFER_SIZE];
  gchar filename[BUFFER_SIZE];
  gint  encapsulation;
178
} m_info;
179

180

181
static void   query (void);
182 183 184 185 186 187
static void   run   (const gchar      *name,
		     gint              nparams,
		     const GimpParam  *param,
		     gint             *nreturn_vals,
		     GimpParam       **return_vals);

188 189 190 191
static GimpPDBStatusType save_image   (const gchar *filename,
                                       gint32       image_ID,
                                       gint32       drawable_ID,
                                       gint32       run_mode);
192

193 194 195 196 197
static gboolean  save_dialog          (void);
static void      mail_entry_callback  (GtkWidget     *widget,
                                       gchar         *data);
static void      mesg_body_callback   (GtkTextBuffer *buffer,
                                       gpointer       data);
198

199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
static gboolean  valid_file     (const gchar *filename);
static void      create_headers (FILE        *mailpipe);
static gchar    *find_extension (const gchar *filename);
static gint      to64           (FILE        *infile,
                                 FILE        *outfile);
static void      output64chunk  (gint         c1,
                                 gint         c2,
                                 gint         c3,
                                 gint         pads,
                                 FILE        *outfile);

static gint    sane_dup2      (gint    fd1,
                               gint    fd2);
static FILE   *sendmail_pipe  (gchar **cmd,
                               gint   *pid);

Elliot Lee's avatar
Elliot Lee committed
215

Sven Neumann's avatar
Sven Neumann committed
216
GimpPlugInInfo PLUG_IN_INFO =
Elliot Lee's avatar
Elliot Lee committed
217
{
218 219 220 221
  NULL,  /* init_proc  */
  NULL,  /* quit_proc  */
  query, /* query_proc */
  run,   /* run_proc   */
Elliot Lee's avatar
Elliot Lee committed
222 223
};

224 225
static m_info mail_info =
{
226
  "", "", "", "", "",
Elliot Lee's avatar
Elliot Lee committed
227 228 229 230
  ENCAPSULATION_MIME,  /* Change this to ENCAPSULATION_UUENCODE
			  if you prefer that as the default */
};

231
static gchar * mesg_body = NULL;
232

Elliot Lee's avatar
Elliot Lee committed
233 234 235 236

MAIN ()

static void
237
query (void)
Elliot Lee's avatar
Elliot Lee committed
238
{
Sven Neumann's avatar
Sven Neumann committed
239
  static GimpParamDef args[] =
Elliot Lee's avatar
Elliot Lee committed
240
  {
Sven Neumann's avatar
Sven Neumann committed
241 242 243 244 245 246 247 248 249
    { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
    { GIMP_PDB_IMAGE, "image", "Input image" },
    { GIMP_PDB_DRAWABLE, "drawable", "Drawable to save" },
    { GIMP_PDB_STRING, "filename", "The name of the file to save the image in" },
    { GIMP_PDB_STRING, "receipt", "The email address to send to" },
    { GIMP_PDB_STRING, "from", "The email address for the From: field" },
    { GIMP_PDB_STRING, "subject", "The subject" },
    { GIMP_PDB_STRING, "comment", "The Comment" },
    { GIMP_PDB_INT32,  "encapsulation", "Uuencode, MIME" }
Elliot Lee's avatar
Elliot Lee committed
250
  };
251

252
  gimp_install_procedure (PLUG_IN_NAME,
Marc Lehmann's avatar
Marc Lehmann committed
253 254
			  "pipe files to uuencode then mail them",
			  "You need to have uuencode and mail installed",
Elliot Lee's avatar
Elliot Lee committed
255
			  "Adrian Likins, Reagan Blundell",
256 257
			  "Adrian Likins, Reagan Blundell, Daniel Risacher, "
                          "Spencer Kimball and Peter Mattis",
Elliot Lee's avatar
Elliot Lee committed
258
			  "1995-1997",
259
			  N_("_Mail Image..."),
Elliot Lee's avatar
Elliot Lee committed
260
			  "RGB*, GRAY*, INDEXED*",
Sven Neumann's avatar
Sven Neumann committed
261
			  GIMP_PLUGIN,
262
			  G_N_ELEMENTS (args), 0,
263
			  args, NULL);
264

265 266 267
  gimp_plugin_menu_register (PLUG_IN_NAME, "<Image>/File/Send");
  gimp_plugin_icon_register (PLUG_IN_NAME,
                             GIMP_ICON_TYPE_INLINE_PIXBUF, mail_icon);
Elliot Lee's avatar
Elliot Lee committed
268 269 270
}

static void
271 272 273 274 275
run (const gchar      *name,
     gint              nparams,
     const GimpParam  *param,
     gint             *nreturn_vals,
     GimpParam       **return_vals)
Elliot Lee's avatar
Elliot Lee committed
276
{
277
  static GimpParam   values[2];
278
  GimpRunMode        run_mode;
279
  GimpPDBStatusType  status = GIMP_PDB_SUCCESS;
280 281
  gint32             image_ID;
  gint32             drawable_ID;
Elliot Lee's avatar
Elliot Lee committed
282

283
  INIT_I18N ();
Elliot Lee's avatar
Elliot Lee committed
284

285 286
  run_mode    = param[0].data.d_int32;
  image_ID    = param[1].data.d_image;
Elliot Lee's avatar
Elliot Lee committed
287 288
  drawable_ID = param[2].data.d_drawable;

289 290
  *nreturn_vals = 1;
  *return_vals  = values;
291

Sven Neumann's avatar
Sven Neumann committed
292 293
  values[0].type          = GIMP_PDB_STATUS;
  values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
Elliot Lee's avatar
Elliot Lee committed
294

295
  if (strcmp (name, PLUG_IN_NAME) == 0)
Elliot Lee's avatar
Elliot Lee committed
296 297 298
    {
      switch (run_mode)
	{
Sven Neumann's avatar
Sven Neumann committed
299
	case GIMP_RUN_INTERACTIVE:
300
	  gimp_get_data (PLUG_IN_NAME, &mail_info);
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315
          if (! strlen (mail_info.filename))
            {
              gchar *filename = gimp_image_get_filename (image_ID);

              if (filename)
                {
                  gchar *basename = g_path_get_basename (filename);

                  g_strlcpy (mail_info.filename, basename, BUFFER_SIZE);
                  g_free (basename);
                  g_free (filename);
                }
            }

	  if (! save_dialog ())
Sven Neumann's avatar
Sven Neumann committed
316
	    status = GIMP_PDB_CANCEL;
Elliot Lee's avatar
Elliot Lee committed
317
	  break;
318

Sven Neumann's avatar
Sven Neumann committed
319
	case GIMP_RUN_NONINTERACTIVE:
Elliot Lee's avatar
Elliot Lee committed
320
	  /*  Make sure all the arguments are there!  */
321
	  if (nparams != 9)
322
	    {
Sven Neumann's avatar
Sven Neumann committed
323
	      status = GIMP_PDB_CALLING_ERROR;
324 325
	    }
	  else
Elliot Lee's avatar
Elliot Lee committed
326
	    {
327 328 329 330 331
	      g_strlcpy (mail_info.filename, param[3].data.d_string, BUFFER_SIZE);
	      g_strlcpy (mail_info.receipt, param[4].data.d_string, BUFFER_SIZE);
	      g_strlcpy (mail_info.from, param[5].data.d_string, BUFFER_SIZE);
	      g_strlcpy (mail_info.subject, param[6].data.d_string, BUFFER_SIZE);
	      g_strlcpy (mail_info.comment, param[7].data.d_string, BUFFER_SIZE);
332
	      mail_info.encapsulation = param[8].data.d_int32;
Elliot Lee's avatar
Elliot Lee committed
333 334
	    }
	  break;
335

Sven Neumann's avatar
Sven Neumann committed
336
	case GIMP_RUN_WITH_LAST_VALS:
337
	  gimp_get_data (PLUG_IN_NAME, &mail_info);
Elliot Lee's avatar
Elliot Lee committed
338
	  break;
339

Elliot Lee's avatar
Elliot Lee committed
340 341 342 343
	default:
	  break;
	}

Sven Neumann's avatar
Sven Neumann committed
344
      if (status == GIMP_PDB_SUCCESS)
Elliot Lee's avatar
Elliot Lee committed
345
	{
346 347 348 349 350
	  status = save_image (mail_info.filename,
			       image_ID,
			       drawable_ID,
			       run_mode);

Sven Neumann's avatar
Sven Neumann committed
351
	  if (status == GIMP_PDB_SUCCESS)
352
	    gimp_set_data (PLUG_IN_NAME, &mail_info, sizeof (m_info));
Elliot Lee's avatar
Elliot Lee committed
353 354 355
	}
    }
  else
356
    {
Sven Neumann's avatar
Sven Neumann committed
357
      status = GIMP_PDB_CALLING_ERROR;
358 359 360
    }

  values[0].data.d_status = status;
Elliot Lee's avatar
Elliot Lee committed
361 362
}

Sven Neumann's avatar
Sven Neumann committed
363
static GimpPDBStatusType
364 365 366 367
save_image (const gchar *filename,
	    gint32       image_ID,
	    gint32       drawable_ID,
	    gint32       run_mode)
Elliot Lee's avatar
Elliot Lee committed
368
{
369 370 371 372 373 374 375 376 377 378 379 380 381 382
  GimpPDBStatusType  status = GIMP_PDB_SUCCESS;
  gchar             *ext;
  gchar             *tmpname;
  gchar             *mailcmd[3];
  GPid               mailpid;
  pid_t              uupid;
  gint               wpid;
  gint               process_status;
  FILE              *mailpipe;
  FILE              *infile;

  ext = find_extension (filename);

  if (ext == NULL)
Sven Neumann's avatar
Sven Neumann committed
383
    return GIMP_PDB_CALLING_ERROR;
Elliot Lee's avatar
Elliot Lee committed
384 385

  /* get a temp name with the right extension and save into it. */
386
  tmpname = gimp_temp_name (ext + 1);
Elliot Lee's avatar
Elliot Lee committed
387 388

  /* construct the "sendmail user@location" line */
389 390 391
  mailcmd[0] = SENDMAIL;
  mailcmd[1] = mail_info.receipt;
  mailcmd[2] = NULL;
Elliot Lee's avatar
Elliot Lee committed
392 393

  /* create a pipe to sendmail */
394 395 396 397
  mailpipe = sendmail_pipe (mailcmd, &mailpid);

  if (mailpipe == NULL)
    return GIMP_PDB_EXECUTION_ERROR;
398

Elliot Lee's avatar
Elliot Lee committed
399
  create_headers (mailpipe);
400

401
  /* This is necessary to make the comments and headers work
402
   * correctly. Not real sure why.
403
   */
404
  fflush (mailpipe);
Elliot Lee's avatar
Elliot Lee committed
405

406 407 408
  if (! (gimp_file_save (run_mode,
			 image_ID,
			 drawable_ID,
409
			 tmpname,
410
			 tmpname) && valid_file (tmpname)) )
Elliot Lee's avatar
Elliot Lee committed
411
    {
412
      goto error;
Elliot Lee's avatar
Elliot Lee committed
413 414
    }

415 416
  if (mail_info.encapsulation == ENCAPSULATION_UUENCODE)
    {
Elliot Lee's avatar
Elliot Lee committed
417
      /* fork off a uuencode process */
418
      if ((uupid = fork ()) < 0)
419
	{
420
	  g_message ("fork() failed: %s", g_strerror (errno));
421
	  goto error;
422
	}
423
      else if (uupid == 0)
424
	{
425
	  if (sane_dup2 (fileno (mailpipe), fileno (stdout)) == -1)
426
	    {
427
	      g_message ("dup2() failed: %s", g_strerror (errno));
428 429 430
	    }

	  execlp (UUENCODE, UUENCODE, tmpname, filename, NULL);
431

432
	  /* What are we doing here? exec must have failed */
433
	  g_message ("exec failed: uuencode: %s", g_strerror (errno));
434 435
	  _exit (127);
	}
Elliot Lee's avatar
Elliot Lee committed
436
      else
437
        {
438
	  wpid = waitpid (uupid, &process_status, 0);
439

440 441 442
	  if ((wpid < 0)
	      || !WIFEXITED (process_status)
	      || (WEXITSTATUS (process_status) != 0))
443
	    {
444
	      g_message ("mail didn't work or something on file '%s'",
445
                         gimp_filename_to_utf8 (tmpname));
446
	      goto error;
447 448 449 450
	    }
	}
    }
  else
451 452
    {
      /* This must be MIME stuff. Base64 away... */
453
      infile = g_fopen (tmpname, "r");
454
      to64 (infile, mailpipe);
455

Elliot Lee's avatar
Elliot Lee committed
456
      /* close off mime */
457
      if (mail_info.encapsulation == ENCAPSULATION_MIME)
458
        fprintf (mailpipe, "\n--GUMP-MIME-boundary--\n");
459 460
    }

461 462 463 464 465 466 467 468 469 470 471 472
  goto cleanup;

error:
  /* stop sendmail from doing anything */
  kill (mailpid, SIGINT);
  status = GIMP_PDB_EXECUTION_ERROR;

cleanup:
  /* close out the sendmail process */
  fclose (mailpipe);
  waitpid (mailpid, NULL, 0);

Elliot Lee's avatar
Elliot Lee committed
473
  /* delete the tmpfile that was generated */
474
  g_unlink (tmpname);
475 476
  g_free (tmpname);

477
  return status;
Elliot Lee's avatar
Elliot Lee committed
478 479 480
}


481
static gboolean
482
save_dialog (void)
Elliot Lee's avatar
Elliot Lee committed
483
{
484
  GtkWidget     *dlg;
485
  GtkWidget     *main_vbox;
486 487 488 489 490 491 492
  GtkWidget     *entry;
  GtkWidget     *table;
  GtkWidget     *label;
  GtkWidget     *vbox;
  GtkWidget     *scrolled_window;
  GtkWidget     *text_view;
  GtkTextBuffer *text_buffer;
493
  gchar         *gump_from;
494
  gint           row = 0;
495
  gboolean       run;
Elliot Lee's avatar
Elliot Lee committed
496

497
  gimp_ui_init ("mail", FALSE);
Elliot Lee's avatar
Elliot Lee committed
498

499
  /* check gimprc for a preferred "From:" address */
500
  gump_from = gimp_gimprc_query ("gump-from");
501

502 503
  if (gump_from)
    {
504
      g_strlcpy (mail_info.from, gump_from, BUFFER_SIZE);
505 506
      g_free (gump_from);
    }
507

508
  dlg = gimp_dialog_new (_("Send as Mail"), "mail",
509
                         NULL, 0,
510
			 gimp_standard_help_func, HELP_ID,
511

512 513
			 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
			 GTK_STOCK_OK,     GTK_RESPONSE_OK,
514 515 516

			 NULL);

517
  gtk_dialog_set_alternative_button_order (GTK_DIALOG (dlg),
518 519 520
                                           GTK_RESPONSE_OK,
                                           GTK_RESPONSE_CANCEL,
                                           -1);
521

522 523 524 525 526 527
  main_vbox = gtk_vbox_new (FALSE, 12);
  gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox),
                      main_vbox, TRUE, TRUE, 0);
  gtk_widget_show (main_vbox);

Elliot Lee's avatar
Elliot Lee committed
528
  /* table */
529 530
  table = gtk_table_new (5, 2, FALSE);
  gtk_box_pack_start (GTK_BOX (main_vbox), table, FALSE, FALSE, 0);
Elliot Lee's avatar
Elliot Lee committed
531 532
  gtk_widget_show (table);

533
  gtk_table_set_row_spacings (GTK_TABLE (table), 6);
534
  gtk_table_set_row_spacing (GTK_TABLE (table), 2, 12);
535
  gtk_table_set_col_spacings (GTK_TABLE (table), 6);
Elliot Lee's avatar
Elliot Lee committed
536

537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571
  /* Filename entry */
  entry = gtk_entry_new ();
  gtk_widget_set_size_request (entry, 200, -1);
  gtk_entry_set_max_length (GTK_ENTRY (entry), BUFFER_SIZE - 1);
  gtk_entry_set_text (GTK_ENTRY (entry), mail_info.filename);
  gimp_table_attach_aligned (GTK_TABLE (table), 0, row++,
			     _("_Filename:"), 0.0, 0.5,
			     entry, 1, FALSE);
  g_signal_connect (entry, "changed",
                    G_CALLBACK (mail_entry_callback),
                    mail_info.filename);

  /* Encapsulation label */
  label = gtk_label_new (_("Encapsulation:"));
  gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.2);
  gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row + 1,
		    GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
  gtk_widget_show (label);

  /* Encapsulation radiobuttons */
  vbox = gimp_int_radio_group_new (FALSE, NULL,
				   G_CALLBACK (gimp_radio_button_update),
				   &mail_info.encapsulation,
				   mail_info.encapsulation,

				   _("_MIME"),     ENCAPSULATION_MIME,     NULL,
				   _("_Uuencode"), ENCAPSULATION_UUENCODE, NULL,

				   NULL);
  gtk_table_attach (GTK_TABLE (table), vbox, 1, 2, row, row + 2,
		    GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
  gtk_widget_show (vbox);
  row += 2;

  /* To entry */
Elliot Lee's avatar
Elliot Lee committed
572
  entry = gtk_entry_new ();
573
  gtk_widget_set_size_request (entry, 200, -1);
574 575
  gtk_entry_set_max_length (GTK_ENTRY (entry), BUFFER_SIZE - 1);
  gtk_entry_set_text (GTK_ENTRY (entry), mail_info.receipt);
576
  gimp_table_attach_aligned (GTK_TABLE (table), 0, row++,
577
			     _("_Recipient:"), 0.0, 0.5,
578
			     entry, 1, FALSE);
579
  g_signal_connect (entry, "changed",
580
                    G_CALLBACK (mail_entry_callback),
581
                    mail_info.receipt);
582

583 584
  gtk_widget_grab_focus (entry);

Sven Neumann's avatar
Sven Neumann committed
585
  /* From entry */
586
  entry = gtk_entry_new ();
587
  gtk_widget_set_size_request (entry, 200, -1);
588 589
  gtk_entry_set_max_length (GTK_ENTRY (entry), BUFFER_SIZE - 1);
  gtk_entry_set_text (GTK_ENTRY (entry), mail_info.from);
590
  gimp_table_attach_aligned (GTK_TABLE (table), 0, row++,
591
			     _("_Sender:"), 0.0, 0.5,
592
			     entry, 1, FALSE);
593
  g_signal_connect (entry, "changed",
594
                    G_CALLBACK (mail_entry_callback),
595
                    mail_info.from);
Elliot Lee's avatar
Elliot Lee committed
596 597 598

  /* Subject entry */
  entry = gtk_entry_new ();
599
  gtk_widget_set_size_request (entry, 200, -1);
600 601
  gtk_entry_set_max_length (GTK_ENTRY (entry), BUFFER_SIZE - 1);
  gtk_entry_set_text (GTK_ENTRY (entry), mail_info.subject);
602
  gimp_table_attach_aligned (GTK_TABLE (table), 0, row++,
603
			     _("S_ubject:"), 0.0, 0.5,
604
			     entry, 1, FALSE);
605
  g_signal_connect (entry, "changed",
606
                    G_CALLBACK (mail_entry_callback),
607
                    mail_info.subject);
Elliot Lee's avatar
Elliot Lee committed
608

Sven Neumann's avatar
Sven Neumann committed
609
  /* Comment entry */
Elliot Lee's avatar
Elliot Lee committed
610
  entry = gtk_entry_new ();
611
  gtk_widget_set_size_request (entry, 200, -1);
612 613
  gtk_entry_set_max_length (GTK_ENTRY (entry), BUFFER_SIZE - 1);
  gtk_entry_set_text (GTK_ENTRY (entry), mail_info.comment);
614
  gimp_table_attach_aligned (GTK_TABLE (table), 0, row++,
615
			     _("Comm_ent:"), 0.0, 0.5,
616
			     entry, 1, FALSE);
617
  g_signal_connect (entry, "changed",
618
                    G_CALLBACK (mail_entry_callback),
619
                    mail_info.comment);
Elliot Lee's avatar
Elliot Lee committed
620 621


622
  /* Body  */
623
  scrolled_window = gtk_scrolled_window_new (NULL, NULL);
624 625
  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
                                       GTK_SHADOW_IN);
626 627 628
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
                                  GTK_POLICY_AUTOMATIC,
                                  GTK_POLICY_AUTOMATIC);
629
  gtk_box_pack_start (GTK_BOX (main_vbox), scrolled_window, TRUE, TRUE, 0);
630 631 632 633 634
  gtk_widget_show (scrolled_window);

  text_buffer = gtk_text_buffer_new (NULL);

  text_view = gtk_text_view_new_with_buffer (text_buffer);
635 636
  g_object_unref (text_buffer);

637 638 639 640
  gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (text_view), GTK_WRAP_WORD);
  gtk_container_add (GTK_CONTAINER (scrolled_window), text_view);
  gtk_widget_show (text_view);

641
  g_signal_connect (text_buffer, "changed",
642 643
                    G_CALLBACK (mesg_body_callback),
		    NULL);
644

Elliot Lee's avatar
Elliot Lee committed
645
  gtk_widget_show (dlg);
646

647
  run = (gimp_dialog_run (GIMP_DIALOG (dlg)) == GTK_RESPONSE_OK);
Elliot Lee's avatar
Elliot Lee committed
648

649 650 651
  gtk_widget_destroy (dlg);

  return run;
Elliot Lee's avatar
Elliot Lee committed
652 653
}

654
static gboolean
655
valid_file (const gchar *filename)
Elliot Lee's avatar
Elliot Lee committed
656 657 658 659
{
  int stat_res;
  struct stat buf;

660
  stat_res = g_stat (filename, &buf);
Elliot Lee's avatar
Elliot Lee committed
661 662

  if ((0 == stat_res) && (buf.st_size > 0))
663
    return TRUE;
Elliot Lee's avatar
Elliot Lee committed
664
  else
665
    return FALSE;
Elliot Lee's avatar
Elliot Lee committed
666 667
}

668
static gchar *
669
find_content_type (const gchar *filename)
Elliot Lee's avatar
Elliot Lee committed
670
{
671 672
  /* This function returns a MIME Content-type: value based on the
     filename it is given.  */
673
  const gchar *type_mappings[20] =
674 675 676 677 678 679 680 681
  {
    "gif" , "image/gif",
    "jpg" , "image/jpeg",
    "jpeg", "image/jpeg",
    "tif" , "image/tiff",
    "tiff", "image/tiff",
    "png" , "image/png",
    "g3"  , "image/g3fax",
682 683
    "ps"  , "application/postscript",
    "eps" , "application/postscript",
684 685 686 687
    NULL, NULL
  };

  gchar *ext;
688
  gint   i;
689 690

  ext = find_extension (filename);
691

692 693
  if (!ext)
    {
694
      return g_strdup ("application/octet-stream");
Elliot Lee's avatar
Elliot Lee committed
695
    }
696

697 698 699
  i = 0;
  ext += 1;

700 701
  while (type_mappings[i])
    {
702
      if (strcmp (ext, type_mappings[i]) == 0)
703
	{
704
	  return g_strdup (type_mappings[i + 1]);
Elliot Lee's avatar
Elliot Lee committed
705
	}
706

707
      i += 2;
Elliot Lee's avatar
Elliot Lee committed
708 709
    }

710
  return g_strdup_printf ("image/x-%s", ext);
Elliot Lee's avatar
Elliot Lee committed
711 712
}

713
static gchar *
714
find_extension (const gchar *filename)
Elliot Lee's avatar
Elliot Lee committed
715
{
716 717 718
  gchar *filename_copy;
  gchar *ext;

Elliot Lee's avatar
Elliot Lee committed
719
  /* we never free this copy - aren't we evil! */
720
  filename_copy = g_strdup (filename);
Elliot Lee's avatar
Elliot Lee committed
721 722 723 724

  /* find the extension, boy! */
  ext = strrchr (filename_copy, '.');

725
  while (TRUE)
Elliot Lee's avatar
Elliot Lee committed
726 727 728
    {
      if (!ext || ext[1] == 0 || strchr (ext, '/'))
	{
729 730
	  g_message (_("some sort of error with the file extension "
                       "or lack thereof"));
731

Elliot Lee's avatar
Elliot Lee committed
732 733
	  return NULL;
	}
734

Elliot Lee's avatar
Elliot Lee committed
735
      if (0 != strcmp(ext,".gz"))
736
	{
Elliot Lee's avatar
Elliot Lee committed
737 738 739 740 741 742 743 744 745
	  return ext;
	}
      else
	{
	  /* we found somehting, loop back, and look again */
	  *ext = 0;
	  ext = strrchr (filename_copy, '.');
	}
    }
746

Elliot Lee's avatar
Elliot Lee committed
747 748 749 750
  return ext;
}

static void
751
mail_entry_callback (GtkWidget *widget,
752
		     gchar     *data)
Elliot Lee's avatar
Elliot Lee committed
753
{
754
  g_strlcpy (data, gtk_entry_get_text (GTK_ENTRY (widget)), BUFFER_SIZE);
Elliot Lee's avatar
Elliot Lee committed
755 756
}

757
static void
758 759
mesg_body_callback (GtkTextBuffer *buffer,
		    gpointer       data)
760
{
761 762
  GtkTextIter start_iter;
  GtkTextIter end_iter;
763

764
  gtk_text_buffer_get_bounds (buffer, &start_iter, &end_iter);
765 766

  g_free (mesg_body);
767
  mesg_body = gtk_text_buffer_get_text (buffer, &start_iter, &end_iter, FALSE);
768
}
769

Elliot Lee's avatar
Elliot Lee committed
770
static void
771
create_headers (FILE *mailpipe)
Elliot Lee's avatar
Elliot Lee committed
772 773 774 775 776 777 778 779
{
  /* create all the mail header stuff. Feel free to add your own */
  /* It is advisable to leave the X-Mailer header though, as     */
  /* there is a possibilty of a Gimp mail scanner/reader in the  */
  /* future. It will probabaly need that header.                 */

  fprintf (mailpipe, "To: %s \n", mail_info.receipt);
  fprintf (mailpipe, "Subject: %s \n", mail_info.subject);
780 781 782
  if (strlen (mail_info.from) > 0)
    fprintf (mailpipe, "From: %s \n", mail_info.from);

783
  fprintf (mailpipe, "X-Mailer: GIMP Useless Mail Program %s\n", GIMP_VERSION);
784

785 786
  if (mail_info.encapsulation == ENCAPSULATION_MIME )
    {
Elliot Lee's avatar
Elliot Lee committed
787
      fprintf (mailpipe, "MIME-Version: 1.0\n");
788 789
      fprintf (mailpipe, "Content-type: multipart/mixed; "
                         "boundary=GUMP-MIME-boundary\n");
790
    }
791

Elliot Lee's avatar
Elliot Lee committed
792
  fprintf (mailpipe, "\n\n");
793

794 795
  if (mail_info.encapsulation == ENCAPSULATION_MIME )
    {
Elliot Lee's avatar
Elliot Lee committed
796
      fprintf (mailpipe, "--GUMP-MIME-boundary\n");
797
      fprintf (mailpipe, "Content-type: text/plain; charset=UTF-8\n\n");
798
    }
799

800 801 802 803 804
  if (strlen (mail_info.comment) > 0)
    {
      fprintf (mailpipe, mail_info.comment);
      fprintf (mailpipe, "\n\n");
    }
805

806
  if (mesg_body)
807
    fprintf (mailpipe, mesg_body);
808

809
  fprintf (mailpipe, "\n\n");
810

811 812
  if (mail_info.encapsulation == ENCAPSULATION_MIME )
    {
813 814
      gchar *content = find_content_type (mail_info.filename);

Elliot Lee's avatar
Elliot Lee committed
815
      fprintf (mailpipe, "--GUMP-MIME-boundary\n");
816
      fprintf (mailpipe, "Content-type: %s\n", content);
Elliot Lee's avatar
Elliot Lee committed
817
      fprintf (mailpipe, "Content-transfer-encoding: base64\n");
818 819
      fprintf (mailpipe, "Content-disposition: attachment; filename=\"%s\"\n",
               mail_info.filename);
820
      fprintf (mailpipe, "Content-description: %s\n\n", mail_info.filename);
821 822

      g_free (content);
823
    }
Elliot Lee's avatar
Elliot Lee committed
824 825 826 827
}

/*
 * The following code taken from codes.c in the mpack-1.5 distribution
828 829
 * by Carnegie Mellon University.
 *
Elliot Lee's avatar
Elliot Lee committed
830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845
 *
 * (C) Copyright 1993,1994 by Carnegie Mellon University
 * All Rights Reserved.
 *
 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 */
/*
Copyright (c) 1991 Bell Communications Research, Inc. (Bellcore)

846 847 848 849 850 851 852 853 854
Permission to use, copy, modify, and distribute this material
for any purpose and without fee is hereby granted, provided
that the above copyright notice and this permission notice
appear in all copies, and that the name of Bellcore not be
used in advertising or publicity pertaining to this
material without the specific, prior written permission
of an authorized representative of Bellcore.  BELLCORE
MAKES NO REPRESENTATIONS ABOUT THE ACCURACY OR SUITABILITY
OF THIS MATERIAL FOR ANY PURPOSE.  IT IS PROVIDED "AS IS",
Elliot Lee's avatar
Elliot Lee committed
855 856 857
WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.  */


858 859
static gchar basis_64[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
Elliot Lee's avatar
Elliot Lee committed
860

861 862
static gint
to64 (FILE *infile,
863
      FILE *outfile)
Elliot Lee's avatar
Elliot Lee committed
864
{
865
  gint c1, c2, c3, ct = 0, written = 0;
866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883

  while ((c1 = getc (infile)) != EOF)
    {
      c2 = getc (infile);
      if (c2 == EOF)
	{
	  output64chunk (c1, 0, 0, 2, outfile);
        }
      else
	{
	  c3 = getc (infile);
	  if (c3 == EOF)
	    {
	      output64chunk (c1, c2, 0, 1, outfile);
            }
	  else
	    {
	      output64chunk (c1, c2, c3, 0, outfile);
Elliot Lee's avatar
Elliot Lee committed
884 885
            }
        }
886 887 888 889 890 891
      ct += 4;
      if (ct > 71)
	{
	  putc ('\n', outfile);
	  written += 73;
	  ct = 0;
Elliot Lee's avatar
Elliot Lee committed
892 893
        }
    }
894 895 896 897
  if (ct)
    {
      putc ('\n', outfile);
      ct++;
Elliot Lee's avatar
Elliot Lee committed
898
    }
899 900

  return written + ct;
Elliot Lee's avatar
Elliot Lee committed
901 902 903
}

static void
904 905 906 907 908
output64chunk (gint  c1,
	       gint  c2,
	       gint  c3,
	       gint  pads,
	       FILE *outfile)
Elliot Lee's avatar
Elliot Lee committed
909
{
910
  putc (basis_64[c1 >> 2], outfile);
911
  putc (basis_64[((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4)], outfile);
912

913 914 915 916 917 918 919 920 921 922 923 924 925 926
  if (pads == 2)
    {
      putc ('=', outfile);
      putc ('=', outfile);
    }
  else if (pads)
    {
      putc (basis_64[((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6)], outfile);
      putc ('=', outfile);
    }
  else
    {
      putc (basis_64[((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6)], outfile);
      putc (basis_64[c3 & 0x3F], outfile);
Elliot Lee's avatar
Elliot Lee committed
927 928
    }
}
929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963

static gint
sane_dup2 (gint fd1,
           gint fd2)
{
  gint ret;

retry:
  ret = dup2 (fd1, fd2);

  if (ret < 0 && errno == EINTR)
    goto retry;
  
  return ret;
}

static FILE *
sendmail_pipe (gchar **cmd,
               gint   *pid)
{
  gint    fd;
  GError *err = NULL;

  if (! g_spawn_async_with_pipes (NULL, cmd, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
	                          NULL, NULL, pid, &fd, NULL, NULL, &err))
    {
      g_message (_("Could not start sendmail (%s)"), err->message);
      g_error_free (err);

      *pid = -1;
      return NULL;
    }

  return fdopen (fd, "w");
}