em-utils.c 58.1 KB
Newer Older
Not Zed's avatar
Not Zed committed
1
/*
2
3
4
5
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) version 3.
Not Zed's avatar
Not Zed committed
6
 *
7
8
9
10
 * 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
 * Lesser General Public License for more details.
Not Zed's avatar
Not Zed committed
11
 *
12
 * You should have received a copy of the GNU Lesser General Public
13
 * License along with the program; if not, see <http://www.gnu.org/licenses/>
Not Zed's avatar
Not Zed committed
14
15
 *
 *
16
17
18
19
 * Authors:
 *		Jeffrey Stedfast <fejj@ximian.com>
 *
 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
Not Zed's avatar
Not Zed committed
20
21
22
23
24
25
26
27
28
29
30
31
32
33
 *
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <time.h>

34
35
36
37
38
39
40
41
42
43
#include <glib/gstdio.h>

#ifdef G_OS_WIN32
/* Work around namespace clobbage in <windows.h> */
#define DATADIR windows_DATADIR
#include <windows.h>
#undef DATADIR
#undef interface
#endif

44
45
#include <libebook/e-book-client.h>
#include <libebook/e-book-query.h>
46

Not Zed's avatar
Not Zed committed
47
#include "em-filter-editor.h"
Not Zed's avatar
Not Zed committed
48

Matthew Barnes's avatar
Matthew Barnes committed
49
#include <glib/gi18n.h>
50

51
52
#include <gio/gio.h>

Not Zed's avatar
Not Zed committed
53
54
55
#include "mail-mt.h"
#include "mail-ops.h"
#include "mail-tools.h"
56
#include "e-mail-tag-editor.h"
Not Zed's avatar
Not Zed committed
57

58
#include <libedataserver/e-data-server-util.h>
59
#include <libedataserver/e-flag.h>
60
#include <libedataserver/e-proxy.h>
61
62
63
#include "e-util/e-util.h"
#include "e-util/e-util-private.h"
#include "e-util/e-mktemp.h"
64
#include "e-util/e-account-utils.h"
65
#include "e-util/e-dialog-utils.h"
66
#include "e-util/e-alert-dialog.h"
67
#include "shell/e-shell.h"
68
#include "widgets/misc/e-attachment.h"
Not Zed's avatar
Not Zed committed
69
70
71
72

#include "em-utils.h"
#include "em-composer-utils.h"
#include "em-format-quote.h"
73
#include "em-format-html-print.h"
74
#include "e-mail-folder-utils.h"
75
#include "e-mail-session.h"
Not Zed's avatar
Not Zed committed
76

77
78
79
80
/* XXX This is a dirty hack on a dirty hack.  We really need
 *     to rework or get rid of the functions that use this. */
extern const gchar *shell_builtin_backend;

81
82
83
84
/* How many is too many? */
/* Used in em_util_ask_open_many() */
#define TOO_MANY 10

85
86
87
88
89
90
/* drag and drop resulting file naming possibilities */
enum {
	DND_USE_SENT_DATE = 1, /* YYYYMMDDhhmmssms_<title> and use email sent date */
	DND_USE_DND_DATE  = 2,  /*  YYYYMMDDhhmmssms_<title> and drag'drop date */
};

Not Zed's avatar
Not Zed committed
91
92
#define d(x)

93
94
95
96
97
98
99
100
101
102
gboolean
em_utils_ask_open_many (GtkWindow *parent,
                        gint how_many)
{
	gchar *string;
	gboolean proceed;

	if (how_many < TOO_MANY)
		return TRUE;

103
104
105
106
107
108
109
110
	string = g_strdup_printf (ngettext (
		/* Translators: This message is shown only for ten or more
		 * messages to be opened.  The %d is replaced with the actual
		 * count of messages. If you need a '%' in your text, then
		 * write it doubled, like '%%'. */
		"Are you sure you want to open %d message at once?",
		"Are you sure you want to open %d messages at once?",
		how_many), how_many);
111
	proceed = em_utils_prompt_user (
112
		parent, "prompt-on-open-many",
113
114
115
116
117
118
		"mail:ask-open-many", string, NULL);
	g_free (string);

	return proceed;
}

Not Zed's avatar
Not Zed committed
119
120
121
/**
 * em_utils_prompt_user:
 * @parent: parent window
122
 * @promptkey: settings key to check if we should prompt the user or not.
123
 * @tag: e_alert tag.
Not Zed's avatar
Not Zed committed
124
125
 *
 * Convenience function to query the user with a Yes/No dialog and a
126
 * "Do not show this dialog again" checkbox. If the user checks that
Not Zed's avatar
Not Zed committed
127
 * checkbox, then @promptkey is set to %FALSE, otherwise it is set to
Not Zed's avatar
Not Zed committed
128
129
130
131
132
 * %TRUE.
 *
 * Returns %TRUE if the user clicks Yes or %FALSE otherwise.
 **/
gboolean
133
134
135
136
em_utils_prompt_user (GtkWindow *parent,
                      const gchar *promptkey,
                      const gchar *tag,
                      ...)
Not Zed's avatar
Not Zed committed
137
{
138
139
	GtkWidget *dialog;
	GtkWidget *check = NULL;
140
	GtkWidget *container;
Not Zed's avatar
Not Zed committed
141
	va_list ap;
142
	gint button;
143
	GSettings *settings;
144
	EAlert *alert = NULL;
Not Zed's avatar
Not Zed committed
145

146
	settings = g_settings_new ("org.gnome.evolution.mail");
147

148
149
	if (promptkey && !g_settings_get_boolean (settings, promptkey)) {
		g_object_unref (settings);
Not Zed's avatar
Not Zed committed
150
		return TRUE;
151
	}
152

Matthew Barnes's avatar
Matthew Barnes committed
153
	va_start (ap, tag);
154
	alert = e_alert_new_valist (tag, ap);
Matthew Barnes's avatar
Matthew Barnes committed
155
	va_end (ap);
156

157
	dialog = e_alert_dialog_new (parent, alert);
158
	g_object_unref (alert);
159

160
161
	container = e_alert_dialog_get_content_area (E_ALERT_DIALOG (dialog));

Not Zed's avatar
Not Zed committed
162
	if (promptkey) {
163
164
165
166
		check = gtk_check_button_new_with_mnemonic (
			_("_Do not show this message again"));
		gtk_box_pack_start (
			GTK_BOX (container), check, FALSE, FALSE, 0);
Not Zed's avatar
Not Zed committed
167
168
		gtk_widget_show (check);
	}
169

170
	button = gtk_dialog_run (GTK_DIALOG (dialog));
Not Zed's avatar
Not Zed committed
171
	if (promptkey)
Matthew Barnes's avatar
Matthew Barnes committed
172
173
174
175
		g_settings_set_boolean (
			settings, promptkey,
			!gtk_toggle_button_get_active (
				GTK_TOGGLE_BUTTON (check)));
Not Zed's avatar
Not Zed committed
176

177
	gtk_widget_destroy (dialog);
178

179
	g_object_unref (settings);
180

Not Zed's avatar
Not Zed committed
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
	return button == GTK_RESPONSE_YES;
}

/**
 * em_utils_uids_copy:
 * @uids: array of uids
 *
 * Duplicates the array of uids held by @uids into a new
 * GPtrArray. Use em_utils_uids_free() to free the resultant uid
 * array.
 *
 * Returns a duplicate copy of @uids.
 **/
GPtrArray *
em_utils_uids_copy (GPtrArray *uids)
{
	GPtrArray *copy;
198
	gint i;
199

Not Zed's avatar
Not Zed committed
200
201
	copy = g_ptr_array_new ();
	g_ptr_array_set_size (copy, uids->len);
202

Not Zed's avatar
Not Zed committed
203
204
	for (i = 0; i < uids->len; i++)
		copy->pdata[i] = g_strdup (uids->pdata[i]);
205

Not Zed's avatar
Not Zed committed
206
207
208
209
210
211
212
213
214
215
216
217
	return copy;
}

/**
 * em_utils_uids_free:
 * @uids: array of uids
 *
 * Frees the array of uids pointed to by @uids back to the system.
 **/
void
em_utils_uids_free (GPtrArray *uids)
{
218
	gint i;
219

Not Zed's avatar
Not Zed committed
220
221
	for (i = 0; i < uids->len; i++)
		g_free (uids->pdata[i]);
222

Not Zed's avatar
Not Zed committed
223
224
225
	g_ptr_array_free (uids, TRUE);
}

226
/* Editing Filters/Search Folders... */
Not Zed's avatar
Not Zed committed
227
228
229
230

static GtkWidget *filter_editor = NULL;

static void
231
232
233
em_filter_editor_response (GtkWidget *dialog,
                           gint button,
                           gpointer user_data)
Not Zed's avatar
Not Zed committed
234
{
Not Zed's avatar
Not Zed committed
235
	EMFilterContext *fc;
236

237
	if (button == GTK_RESPONSE_OK) {
238
		const gchar *config_dir;
239
		gchar *user;
240

241
		config_dir = mail_session_get_config_dir ();
Not Zed's avatar
Not Zed committed
242
		fc = g_object_get_data ((GObject *) dialog, "context");
243
		user = g_build_filename (config_dir, "filters.xml", NULL);
244
		e_rule_context_save ((ERuleContext *) fc, user);
Not Zed's avatar
Not Zed committed
245
246
		g_free (user);
	}
247

Not Zed's avatar
Not Zed committed
248
	gtk_widget_destroy (dialog);
249

Not Zed's avatar
Not Zed committed
250
251
252
	filter_editor = NULL;
}

Not Zed's avatar
Not Zed committed
253
254
255
static EMFilterSource em_filter_source_element_names[] = {
	{ "incoming", },
	{ "outgoing", },
256
	{ NULL }
Not Zed's avatar
Not Zed committed
257
258
259
260
};

/**
 * em_utils_edit_filters:
261
262
263
 * @session: an #EMailSession
 * @alert_sink: an #EAlertSink
 * @parent_window: a parent #GtkWindow
Not Zed's avatar
Not Zed committed
264
265
266
267
268
269
 *
 * Opens or raises the filters editor dialog so that the user may edit
 * his/her filters. If @parent is non-NULL, then the dialog will be
 * created as a child window of @parent's toplevel window.
 **/
void
270
271
272
em_utils_edit_filters (EMailSession *session,
                       EAlertSink *alert_sink,
                       GtkWindow *parent_window)
Not Zed's avatar
Not Zed committed
273
{
274
	const gchar *config_dir;
275
	gchar *user, *system;
Not Zed's avatar
Not Zed committed
276
	EMFilterContext *fc;
277

278
279
	g_return_if_fail (E_IS_MAIL_SESSION (session));
	g_return_if_fail (E_IS_ALERT_SINK (alert_sink));
280

Not Zed's avatar
Not Zed committed
281
	if (filter_editor) {
282
		gtk_window_present (GTK_WINDOW (filter_editor));
Not Zed's avatar
Not Zed committed
283
284
		return;
	}
285

286
	config_dir = mail_session_get_config_dir ();
287

288
	fc = em_filter_context_new (session);
289
	user = g_build_filename (config_dir, "filters.xml", NULL);
290
	system = g_build_filename (EVOLUTION_PRIVDATADIR, "filtertypes.xml", NULL);
291
	e_rule_context_load ((ERuleContext *) fc, system, user);
Not Zed's avatar
Not Zed committed
292
	g_free (user);
293
	g_free (system);
294

295
	if (((ERuleContext *) fc)->error) {
296
297
298
		e_alert_submit (
			alert_sink,
			"mail:filter-load-error",
Matthew Barnes's avatar
Matthew Barnes committed
299
			((ERuleContext *) fc)->error, NULL);
Not Zed's avatar
Not Zed committed
300
301
		return;
	}
Not Zed's avatar
Not Zed committed
302
303
304
305
306
307

	if (em_filter_source_element_names[0].name == NULL) {
		em_filter_source_element_names[0].name = _("Incoming");
		em_filter_source_element_names[1].name = _("Outgoing");
	}

308
309
	filter_editor = (GtkWidget *) em_filter_editor_new (
		fc, em_filter_source_element_names);
310
311

	if (GTK_IS_WINDOW (parent_window))
312
		gtk_window_set_transient_for (
313
			GTK_WINDOW (filter_editor), parent_window);
314

315
316
317
318
319
320
321
322
	gtk_window_set_title (
		GTK_WINDOW (filter_editor), _("Message Filters"));
	g_object_set_data_full (
		G_OBJECT (filter_editor), "context", fc,
		(GDestroyNotify) g_object_unref);
	g_signal_connect (
		filter_editor, "response",
		G_CALLBACK (em_filter_editor_response), NULL);
Not Zed's avatar
Not Zed committed
323
324
325
	gtk_widget_show (GTK_WIDGET (filter_editor));
}

326
327
/*
 * Picked this from e-d-s/libedataserver/e-data.
328
329
 * But it allows more characters to occur in filenames, especially
 * when saving attachment.
330
331
332
333
334
335
336
 */
void
em_filename_make_safe (gchar *string)
{
	gchar *p, *ts;
	gunichar c;
#ifdef G_OS_WIN32
337
	const gchar *unsafe_chars = "/\":*?<>|\\#";
338
#else
339
	const gchar *unsafe_chars = "/#";
340
341
#endif

342
343
344
	g_return_if_fail (string != NULL);
	p = string;

Matthew Barnes's avatar
Matthew Barnes committed
345
	while (p && *p) {
346
347
348
349
350
351
352
		c = g_utf8_get_char (p);
		ts = p;
		p = g_utf8_next_char (p);
		/* I wonder what this code is supposed to actually
		 * achieve, and whether it does that as currently
		 * written?
		 */
Matthew Barnes's avatar
Matthew Barnes committed
353
		if (!g_unichar_isprint (c) || ( c < 0xff && strchr (unsafe_chars, c&0xff ))) {
354
			while (ts < p)
355
356
357
358
359
				*ts++ = '_';
		}
	}
}

360
/* ********************************************************************** */
Not Zed's avatar
Not Zed committed
361
362
363
364
/* Flag-for-Followup... */

/**
 * em_utils_flag_for_followup:
Matthew Barnes's avatar
Matthew Barnes committed
365
 * @reader: an #EMailReader
Not Zed's avatar
Not Zed committed
366
367
368
369
370
371
372
 * @folder: folder containing messages to flag
 * @uids: uids of messages to flag
 *
 * Open the Flag-for-Followup editor for the messages specified by
 * @folder and @uids.
 **/
void
Matthew Barnes's avatar
Matthew Barnes committed
373
374
375
em_utils_flag_for_followup (EMailReader *reader,
                            CamelFolder *folder,
                            GPtrArray *uids)
Not Zed's avatar
Not Zed committed
376
{
377
	EShell *shell;
378
	EMailBackend *backend;
379
380
	EShellSettings *shell_settings;
	EShellBackend *shell_backend;
381
	EMFormatHTML *formatter;
382
	GtkWidget *editor;
383
	GtkWindow *window;
Matthew Barnes's avatar
Matthew Barnes committed
384
	CamelTag *tags;
385
	gint i;
386

Matthew Barnes's avatar
Matthew Barnes committed
387
	g_return_if_fail (E_IS_MAIL_READER (reader));
Not Zed's avatar
Not Zed committed
388
389
	g_return_if_fail (CAMEL_IS_FOLDER (folder));
	g_return_if_fail (uids != NULL);
390

391
	window = e_mail_reader_get_window (reader);
392
393
394
	backend = e_mail_reader_get_backend (reader);

	editor = e_mail_tag_editor_new ();
395
	gtk_window_set_transient_for (GTK_WINDOW (editor), window);
396

397
	shell_backend = E_SHELL_BACKEND (backend);
398
399
400
401
	shell = e_shell_backend_get_shell (shell_backend);
	shell_settings = e_shell_get_shell_settings (shell);

	/* These settings come from the calendar module. */
402
	g_object_bind_property (
403
		shell_settings, "cal-use-24-hour-format",
404
405
406
		editor, "use-24-hour-format",
		G_BINDING_SYNC_CREATE);
	g_object_bind_property (
407
		shell_settings, "cal-week-start-day",
408
409
		editor, "week-start-day",
		G_BINDING_SYNC_CREATE);
410

Not Zed's avatar
Not Zed committed
411
412
	for (i = 0; i < uids->len; i++) {
		CamelMessageInfo *info;
413

Not Zed's avatar
Not Zed committed
414
		info = camel_folder_get_message_info (folder, uids->pdata[i]);
Matthew Barnes's avatar
Matthew Barnes committed
415
416
417
418

		if (info == NULL)
			continue;

419
420
		e_mail_tag_editor_add_message (
			E_MAIL_TAG_EDITOR (editor),
Matthew Barnes's avatar
Matthew Barnes committed
421
422
423
			camel_message_info_from (info),
			camel_message_info_subject (info));

424
		camel_folder_free_message_info (folder, info);
Not Zed's avatar
Not Zed committed
425
	}
426

Not Zed's avatar
Not Zed committed
427
428
429
	/* special-case... */
	if (uids->len == 1) {
		CamelMessageInfo *info;
Matthew Barnes's avatar
Matthew Barnes committed
430
		const gchar *message_uid;
431

Matthew Barnes's avatar
Matthew Barnes committed
432
433
		message_uid = g_ptr_array_index (uids, 0);
		info = camel_folder_get_message_info (folder, message_uid);
Not Zed's avatar
Not Zed committed
434
		if (info) {
Matthew Barnes's avatar
Matthew Barnes committed
435
			tags = (CamelTag *) camel_message_info_user_tags (info);
436
437

			if (tags)
438
439
				e_mail_tag_editor_set_tag_list (
					E_MAIL_TAG_EDITOR (editor), tags);
440
			camel_folder_free_message_info (folder, info);
Not Zed's avatar
Not Zed committed
441
442
		}
	}
443

Matthew Barnes's avatar
Matthew Barnes committed
444
445
446
	if (gtk_dialog_run (GTK_DIALOG (editor)) != GTK_RESPONSE_OK)
		goto exit;

447
	tags = e_mail_tag_editor_get_tag_list (E_MAIL_TAG_EDITOR (editor));
Matthew Barnes's avatar
Matthew Barnes committed
448
449
450
451
452
453
454
455
	if (tags == NULL)
		goto exit;

	camel_folder_freeze (folder);
	for (i = 0; i < uids->len; i++) {
		CamelMessageInfo *info;
		CamelTag *iter;

Matthew Barnes's avatar
Matthew Barnes committed
456
		info = camel_folder_get_message_info (folder, uids->pdata[i]);
Matthew Barnes's avatar
Matthew Barnes committed
457
458
459
460
461
462
463
464

		if (info == NULL)
			continue;

		for (iter = tags; iter != NULL; iter = iter->next)
			camel_message_info_set_user_tag (
				info, iter->name, iter->value);

465
		camel_folder_free_message_info (folder, info);
Matthew Barnes's avatar
Matthew Barnes committed
466
467
468
469
470
	}

	camel_folder_thaw (folder);
	camel_tag_list_free (&tags);

471
	formatter = e_mail_reader_get_formatter (reader);
472
	em_format_queue_redraw (EM_FORMAT (formatter));
Matthew Barnes's avatar
Matthew Barnes committed
473
474
475
476

exit:
	/* XXX We shouldn't be freeing this. */
	em_utils_uids_free (uids);
477

Matthew Barnes's avatar
Matthew Barnes committed
478
	gtk_widget_destroy (GTK_WIDGET (editor));
Not Zed's avatar
Not Zed committed
479
480
481
482
483
484
485
486
487
488
489
490
}

/**
 * em_utils_flag_for_followup_clear:
 * @parent: parent window
 * @folder: folder containing messages to unflag
 * @uids: uids of messages to unflag
 *
 * Clears the Flag-for-Followup flag on the messages referenced by
 * @folder and @uids.
 **/
void
491
492
493
em_utils_flag_for_followup_clear (GtkWindow *parent,
                                  CamelFolder *folder,
                                  GPtrArray *uids)
Not Zed's avatar
Not Zed committed
494
{
495
	gint i;
496

497
	g_return_if_fail (GTK_IS_WINDOW (parent));
Not Zed's avatar
Not Zed committed
498
499
	g_return_if_fail (CAMEL_IS_FOLDER (folder));
	g_return_if_fail (uids != NULL);
500

Not Zed's avatar
Not Zed committed
501
502
	camel_folder_freeze (folder);
	for (i = 0; i < uids->len; i++) {
Matthew Barnes's avatar
Matthew Barnes committed
503
		CamelMessageInfo *mi = camel_folder_get_message_info (folder, uids->pdata[i]);
Not Zed's avatar
Not Zed committed
504
505
506
507
508

		if (mi) {
			camel_message_info_set_user_tag(mi, "follow-up", NULL);
			camel_message_info_set_user_tag(mi, "due-by", NULL);
			camel_message_info_set_user_tag(mi, "completed-on", NULL);
509
			camel_folder_free_message_info (folder, mi);
Not Zed's avatar
Not Zed committed
510
		}
Not Zed's avatar
Not Zed committed
511
	}
Matthew Barnes's avatar
Matthew Barnes committed
512

Not Zed's avatar
Not Zed committed
513
	camel_folder_thaw (folder);
514

Not Zed's avatar
Not Zed committed
515
516
517
518
519
520
521
522
523
524
525
526
527
528
	em_utils_uids_free (uids);
}

/**
 * em_utils_flag_for_followup_completed:
 * @parent: parent window
 * @folder: folder containing messages to 'complete'
 * @uids: uids of messages to 'complete'
 *
 * Sets the completed state (and date/time) for each message
 * referenced by @folder and @uids that is marked for
 * Flag-for-Followup.
 **/
void
529
530
531
em_utils_flag_for_followup_completed (GtkWindow *parent,
                                      CamelFolder *folder,
                                      GPtrArray *uids)
Not Zed's avatar
Not Zed committed
532
{
533
534
	gchar *now;
	gint i;
535

536
	g_return_if_fail (GTK_IS_WINDOW (parent));
Not Zed's avatar
Not Zed committed
537
538
	g_return_if_fail (CAMEL_IS_FOLDER (folder));
	g_return_if_fail (uids != NULL);
539

540
	now = camel_header_format_date (time (NULL), 0);
541

Not Zed's avatar
Not Zed committed
542
543
	camel_folder_freeze (folder);
	for (i = 0; i < uids->len; i++) {
544
		const gchar *tag;
Matthew Barnes's avatar
Matthew Barnes committed
545
		CamelMessageInfo *mi = camel_folder_get_message_info (folder, uids->pdata[i]);
Not Zed's avatar
Not Zed committed
546
547
548
549
550

		if (mi) {
			tag = camel_message_info_user_tag(mi, "follow-up");
			if (tag && tag[0])
				camel_message_info_set_user_tag(mi, "completed-on", now);
551
			camel_folder_free_message_info (folder, mi);
Not Zed's avatar
Not Zed committed
552
		}
Not Zed's avatar
Not Zed committed
553
	}
Matthew Barnes's avatar
Matthew Barnes committed
554

Not Zed's avatar
Not Zed committed
555
	camel_folder_thaw (folder);
556

Not Zed's avatar
Not Zed committed
557
	g_free (now);
558

Not Zed's avatar
Not Zed committed
559
560
561
	em_utils_uids_free (uids);
}

562
563
564
/* This kind of sucks, because for various reasons most callers need to run
 * synchronously in the gui thread, however this could take a long, blocking
 * time to run. */
Matthew Barnes's avatar
Matthew Barnes committed
565
static gint
566
567
568
em_utils_write_messages_to_stream (CamelFolder *folder,
                                   GPtrArray *uids,
                                   CamelStream *stream)
Not Zed's avatar
Not Zed committed
569
{
Matthew Barnes's avatar
Matthew Barnes committed
570
571
	CamelStream *filtered_stream;
	CamelMimeFilter *from_filter;
572
	gint i, res = 0;
Not Zed's avatar
Not Zed committed
573

Matthew Barnes's avatar
Matthew Barnes committed
574
	from_filter = camel_mime_filter_from_new ();
Matthew Barnes's avatar
Matthew Barnes committed
575
576
577
	filtered_stream = camel_stream_filter_new (stream);
	camel_stream_filter_add (
		CAMEL_STREAM_FILTER (filtered_stream), from_filter);
Matthew Barnes's avatar
Matthew Barnes committed
578
	g_object_unref (from_filter);
579

580
	for (i = 0; i < uids->len; i++) {
Not Zed's avatar
Not Zed committed
581
		CamelMimeMessage *message;
582
		gchar *from;
Not Zed's avatar
Not Zed committed
583

Matthew Barnes's avatar
Matthew Barnes committed
584
585
		/* FIXME camel_folder_get_message_sync() may block. */
		message = camel_folder_get_message_sync (
Matthew Barnes's avatar
Matthew Barnes committed
586
			folder, uids->pdata[i], NULL, NULL);
Not Zed's avatar
Not Zed committed
587
588
589
590
591
		if (message == NULL) {
			res = -1;
			break;
		}

592
593
		/* We need to flush after each stream write since we are
		 * writing to the same stream. */
Matthew Barnes's avatar
Matthew Barnes committed
594
		from = camel_mime_message_build_mbox_from (message);
Not Zed's avatar
Not Zed committed
595

Matthew Barnes's avatar
Matthew Barnes committed
596
597
		if (camel_stream_write_string (stream, from, NULL, NULL) == -1
		    || camel_stream_flush (stream, NULL, NULL) == -1
598
599
600
601
602
		    || camel_data_wrapper_write_to_stream_sync (
			(CamelDataWrapper *) message, (CamelStream *)
			filtered_stream, NULL, NULL) == -1
		    || camel_stream_flush (
			(CamelStream *) filtered_stream, NULL, NULL) == -1)
Not Zed's avatar
Not Zed committed
603
			res = -1;
604

Matthew Barnes's avatar
Matthew Barnes committed
605
		g_free (from);
Matthew Barnes's avatar
Matthew Barnes committed
606
		g_object_unref (message);
Not Zed's avatar
Not Zed committed
607
608
609
610
611

		if (res == -1)
			break;
	}

Matthew Barnes's avatar
Matthew Barnes committed
612
	g_object_unref (filtered_stream);
Not Zed's avatar
Not Zed committed
613
614
615
616

	return res;
}

617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
static gboolean
em_utils_print_messages_to_file	(CamelFolder *folder, 
				 const gchar * uid, 
				 const gchar *filename)
{
	EMFormatHTMLPrint *efhp;
	CamelMimeMessage *message;

	message = camel_folder_get_message_sync (folder, uid, NULL, NULL);
	if (message == NULL)
		return FALSE;

	efhp = em_format_html_print_new (NULL, GTK_PRINT_OPERATION_ACTION_EXPORT);
	efhp->export_filename = g_strdup (filename);
	efhp->async = FALSE;

	em_format_html_print_message (efhp, message, folder, uid);

	g_object_unref (efhp);
	g_object_unref (message);

	return TRUE;
}

641
642
643
/* This kind of sucks, because for various reasons most callers need to run
 * synchronously in the gui thread, however this could take a long, blocking
 * time to run. */
Matthew Barnes's avatar
Matthew Barnes committed
644
static gint
645
646
em_utils_read_messages_from_stream (CamelFolder *folder,
                                    CamelStream *stream)
Not Zed's avatar
Not Zed committed
647
{
Matthew Barnes's avatar
Matthew Barnes committed
648
	CamelMimeParser *mp = camel_mime_parser_new ();
649
	gboolean success = TRUE;
Not Zed's avatar
Not Zed committed
650

Matthew Barnes's avatar
Matthew Barnes committed
651
652
	camel_mime_parser_scan_from (mp, TRUE);
	camel_mime_parser_init_with_stream (mp, stream, NULL);
Not Zed's avatar
Not Zed committed
653

Matthew Barnes's avatar
Matthew Barnes committed
654
	while (camel_mime_parser_step (mp, NULL, NULL) == CAMEL_MIME_PARSER_STATE_FROM) {
Not Zed's avatar
Not Zed committed
655
		CamelMimeMessage *msg;
656
		gboolean success;
Not Zed's avatar
Not Zed committed
657
658

		/* NB: de-from filter, once written */
Matthew Barnes's avatar
Matthew Barnes committed
659
		msg = camel_mime_message_new ();
Matthew Barnes's avatar
Matthew Barnes committed
660
		if (!camel_mime_part_construct_from_parser_sync (
Matthew Barnes's avatar
Matthew Barnes committed
661
			(CamelMimePart *) msg, mp, NULL, NULL)) {
Matthew Barnes's avatar
Matthew Barnes committed
662
			g_object_unref (msg);
Not Zed's avatar
Not Zed committed
663
664
			break;
		}
665

Matthew Barnes's avatar
Matthew Barnes committed
666
667
		/* FIXME camel_folder_append_message_sync() may block. */
		success = camel_folder_append_message_sync (
Matthew Barnes's avatar
Matthew Barnes committed
668
			folder, msg, NULL, NULL, NULL, NULL);
Matthew Barnes's avatar
Matthew Barnes committed
669
		g_object_unref (msg);
670

671
		if (!success)
Not Zed's avatar
Not Zed committed
672
			break;
673

Matthew Barnes's avatar
Matthew Barnes committed
674
		camel_mime_parser_step (mp, NULL, NULL);
Not Zed's avatar
Not Zed committed
675
	}
676

Matthew Barnes's avatar
Matthew Barnes committed
677
	g_object_unref (mp);
Not Zed's avatar
Not Zed committed
678

679
	return success ? 0 : -1;
Not Zed's avatar
Not Zed committed
680
681
682
683
684
685
686
}

/**
 * em_utils_selection_set_mailbox:
 * @data: selection data
 * @folder: folder containign messages to copy into the selection
 * @uids: uids of the messages to copy into the selection
687
 *
Not Zed's avatar
Not Zed committed
688
689
690
691
692
 * Creates a mailbox-format selection.
 * Warning: Could be BIG!
 * Warning: This could block the ui for an extended period.
 **/
void
Matthew Barnes's avatar
Matthew Barnes committed
693
694
695
em_utils_selection_set_mailbox (GtkSelectionData *data,
                                CamelFolder *folder,
                                GPtrArray *uids)
Not Zed's avatar
Not Zed committed
696
{
Matthew Barnes's avatar
Matthew Barnes committed
697
	GByteArray *byte_array;
Not Zed's avatar
Not Zed committed
698
	CamelStream *stream;
699
700
701
	GdkAtom target;

	target = gtk_selection_data_get_target (data);
Not Zed's avatar
Not Zed committed
702

Matthew Barnes's avatar
Matthew Barnes committed
703
704
705
706
	byte_array = g_byte_array_new ();
	stream = camel_stream_mem_new_with_byte_array (byte_array);

	if (em_utils_write_messages_to_stream (folder, uids, stream) == 0)
Matthew Barnes's avatar
Matthew Barnes committed
707
		gtk_selection_data_set (
708
			data, target, 8,
Matthew Barnes's avatar
Matthew Barnes committed
709
			byte_array->data, byte_array->len);
Not Zed's avatar
Not Zed committed
710

Matthew Barnes's avatar
Matthew Barnes committed
711
	g_object_unref (stream);
Not Zed's avatar
Not Zed committed
712
713
714
715
}

/**
 * em_utils_selection_get_mailbox:
716
 * @selection_data: selection data
717
718
 * @folder:
 *
Not Zed's avatar
Not Zed committed
719
720
721
722
723
724
 * Receive a mailbox selection/dnd
 * Warning: Could be BIG!
 * Warning: This could block the ui for an extended period.
 * FIXME: Exceptions?
 **/
void
725
726
em_utils_selection_get_mailbox (GtkSelectionData *selection_data,
                                CamelFolder *folder)
Not Zed's avatar
Not Zed committed
727
728
{
	CamelStream *stream;
729
730
731
732
733
	const guchar *data;
	gint length;

	data = gtk_selection_data_get_data (selection_data);
	length = gtk_selection_data_get_length (selection_data);
Not Zed's avatar
Not Zed committed
734

735
	if (data == NULL || length == -1)
Not Zed's avatar
Not Zed committed
736
737
738
739
		return;

	/* TODO: a stream mem with read-only access to existing data? */
	/* NB: Although copying would let us run this async ... which it should */
740
741
	stream = (CamelStream *)
		camel_stream_mem_new_with_buffer ((gchar *) data, length);
Matthew Barnes's avatar
Matthew Barnes committed
742
	em_utils_read_messages_from_stream (folder, stream);
Matthew Barnes's avatar
Matthew Barnes committed
743
	g_object_unref (stream);
Not Zed's avatar
Not Zed committed
744
745
}

Not Zed's avatar
Not Zed committed
746
747
/**
 * em_utils_selection_get_message:
748
 * @selection_data:
749
750
 * @folder:
 *
Not Zed's avatar
Not Zed committed
751
752
753
 * get a message/rfc822 data.
 **/
void
754
755
em_utils_selection_get_message (GtkSelectionData *selection_data,
                                CamelFolder *folder)
Not Zed's avatar
Not Zed committed
756
757
758
{
	CamelStream *stream;
	CamelMimeMessage *msg;
759
760
761
762
763
	const guchar *data;
	gint length;

	data = gtk_selection_data_get_data (selection_data);
	length = gtk_selection_data_get_length (selection_data);
Not Zed's avatar
Not Zed committed
764

765
	if (data == NULL || length == -1)
Not Zed's avatar
Not Zed committed
766
767
		return;

768
	stream = (CamelStream *)
Matthew Barnes's avatar
Matthew Barnes committed
769
		camel_stream_mem_new_with_buffer ((gchar *) data, length);
Matthew Barnes's avatar
Matthew Barnes committed
770
	msg = camel_mime_message_new ();
Matthew Barnes's avatar
Matthew Barnes committed
771
	if (camel_data_wrapper_construct_from_stream_sync (
Matthew Barnes's avatar
Matthew Barnes committed
772
		(CamelDataWrapper *) msg, stream, NULL, NULL))
Matthew Barnes's avatar
Matthew Barnes committed
773
774
		/* FIXME camel_folder_append_message_sync() may block. */
		camel_folder_append_message_sync (
Matthew Barnes's avatar
Matthew Barnes committed
775
			folder, msg, NULL, NULL, NULL, NULL);
Matthew Barnes's avatar
Matthew Barnes committed
776
777
	g_object_unref (msg);
	g_object_unref (stream);
Not Zed's avatar
Not Zed committed
778
779
}

Not Zed's avatar
Not Zed committed
780
781
/**
 * em_utils_selection_set_uidlist:
782
 * @selection_data: selection data
783
 * @folder:
784
785
 * @uids:
 *
786
 * Sets a "x-uid-list" format selection data.
Not Zed's avatar
Not Zed committed
787
788
 **/
void
789
em_utils_selection_set_uidlist (GtkSelectionData *selection_data,
790
                                CamelFolder *folder,
791
                                GPtrArray *uids)
Not Zed's avatar
Not Zed committed
792
{
Matthew Barnes's avatar
Matthew Barnes committed
793
	GByteArray *array = g_byte_array_new ();
794
	GdkAtom target;
795
	gchar *folder_uri;
796
	gint i;
Not Zed's avatar
Not Zed committed
797
798

	/* format: "uri\0uid1\0uid2\0uid3\0...\0uidn\0" */
799

800
801
802
803
	folder_uri = e_mail_folder_uri_from_folder (folder);

	g_byte_array_append (
		array, (guchar *) folder_uri, strlen (folder_uri) + 1);
Not Zed's avatar
Not Zed committed
804

805
806
	for (i = 0; i < uids->len; i++)
		g_byte_array_append (array, uids->pdata[i], strlen (uids->pdata[i]) + 1);
807

808
809
810
	target = gtk_selection_data_get_target (selection_data);
	gtk_selection_data_set (
		selection_data, target, 8, array->data, array->len);
Matthew Barnes's avatar
Matthew Barnes committed
811
	g_byte_array_free (array, TRUE);
812
813

	g_free (folder_uri);
Not Zed's avatar
Not Zed committed
814
815
816
817
818
}

/**
 * em_utils_selection_get_uidlist:
 * @data: selection data
819
 * @session: an #EMailSession
Not Zed's avatar
Not Zed committed
820
 * @move: do we delete the messages.
821
 *
Not Zed's avatar
Not Zed committed
822
 * Convert a uid list into a copy/move operation.
823
 *
Not Zed's avatar
Not Zed committed
824
 * Warning: Could take some time to run.
Not Zed's avatar
Not Zed committed
825
 **/
Not Zed's avatar
Not Zed committed
826
void
827
em_utils_selection_get_uidlist (GtkSelectionData *selection_data,
828
                                EMailSession *session,
829
830
                                CamelFolder *dest,
                                gint move,
Matthew Barnes's avatar
Matthew Barnes committed
831
                                GCancellable *cancellable,
832
                                GError **error)
Not Zed's avatar
Not Zed committed
833
834
{
	/* format: "uri\0uid1\0uid2\0uid3\0...\0uidn" */
835
	gchar *inptr, *inend;
Not Zed's avatar
Not Zed committed
836
	GPtrArray *uids;
Not Zed's avatar
Not Zed committed
837
	CamelFolder *folder;
838
839
840
841
	const guchar *data;
	gint length;

	g_return_if_fail (selection_data != NULL);
842
	g_return_if_fail (E_IS_MAIL_SESSION (session));
Not Zed's avatar
Not Zed committed
843

844
845
846
847
	data = gtk_selection_data_get_data (selection_data);
	length = gtk_selection_data_get_length (selection_data);

	if (data == NULL || length == -1)
Not Zed's avatar
Not Zed committed
848
		return;
849

Matthew Barnes's avatar
Matthew Barnes committed
850
	uids = g_ptr_array_new ();
Not Zed's avatar
Not Zed committed
851

852
853
	inptr = (gchar *) data;
	inend = (gchar *) (data + length);
Not Zed's avatar
Not Zed committed
854
	while (inptr < inend) {
855
		gchar *start = inptr;
Not Zed's avatar
Not Zed committed
856
857
858
859

		while (inptr < inend && *inptr)
			inptr++;

860
		if (start > (gchar *) data)
861
			g_ptr_array_add (uids, g_strndup (start, inptr - start));
Not Zed's avatar
Not Zed committed
862
863
864
865
866

		inptr++;
	}

	if (uids->len == 0) {
Matthew Barnes's avatar
Matthew Barnes committed
867
		g_ptr_array_free (uids, TRUE);
Not Zed's avatar
Not Zed committed
868
		return;
Not Zed's avatar
Not Zed committed
869
870
	}

871
872
873
	/* FIXME e_mail_session_uri_to_folder_sync() may block. */
	folder = e_mail_session_uri_to_folder_sync (
		session, (gchar *) data, 0, cancellable, error);
Not Zed's avatar
Not Zed committed
874
	if (folder) {
Matthew Barnes's avatar
Matthew Barnes committed
875
876
877
		/* FIXME camel_folder_transfer_messages_to_sync() may block. */
		camel_folder_transfer_messages_to_sync (
			folder, uids, dest, move, NULL, cancellable, error);
Matthew Barnes's avatar
Matthew Barnes committed
878
		g_object_unref (folder);
Not Zed's avatar
Not Zed committed
879
880
	}

Matthew Barnes's avatar
Matthew Barnes committed
881
	em_utils_uids_free (uids);
Not Zed's avatar
Not Zed committed
882
883
}

884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
static gchar *
em_utils_build_export_filename	(CamelFolder *folder, 
				 const gchar * uid, 
				 const gchar * exporttype, 
				 gint exportname, 
				 const gchar * tmpdir)
{
	CamelMessageInfo *info;
	gchar *file, *tmpfile;
	struct tm  *ts;
	gchar datetmp[15];

	/* Try to get the drop filename from the message or folder */
	info = camel_folder_get_message_info(folder, uid);
	if (info) {
		if (camel_message_info_subject (info)) {
			time_t reftime;
			reftime = camel_message_info_date_sent (info);
			if (exportname==DND_USE_DND_DATE) {
				reftime = time(NULL);
			}

			ts = localtime(&reftime);
			strftime(datetmp, 15, "%Y%m%d%H%M%S", ts);

			if (g_ascii_strcasecmp (exporttype, "pdf")==0)
				file = g_strdup_printf ("%s_%s.pdf", datetmp, camel_message_info_subject (info));
			else
				file = g_strdup_printf ("%s_%s", datetmp, camel_message_info_subject(info));

		}
		camel_folder_free_message_info(folder, info);
	} else {
		time_t reftime;
		reftime = time(NULL);
		ts = localtime(&reftime);
		strftime(datetmp, 15, "%Y%m%d%H%M%S", ts);
		if (g_ascii_strcasecmp (exporttype, "pdf")==0)
			file = g_strdup_printf ("%s_Untitled Message.pdf", datetmp);
		else
			file = g_strdup_printf ("%s_Untitled Message", datetmp);

	}

	e_filename_make_safe(file);

	tmpfile = g_build_filename(tmpdir, file, NULL);

	g_free(file);

	return tmpfile;
}

Not Zed's avatar
Not Zed committed
937
938
/**
 * em_utils_selection_set_urilist:
939
940
941
942
 * @data:
 * @folder:
 * @uids:
 *
Not Zed's avatar
Not Zed committed
943
944
945
946
947
 * Set the selection data @data to a uri which points to a file, which is
 * a berkely mailbox format mailbox.  The file is automatically cleaned
 * up when the application quits.
 **/
void
948
949
950
em_utils_selection_set_urilist (GtkSelectionData *data,
                                CamelFolder *folder,
                                GPtrArray *uids)
Not Zed's avatar
Not Zed committed
951
{
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
 	gchar *tmpdir;
	gchar *uri;
 	gint fd;
	GConfClient *client;
	gchar *exporttype;
	gint exportname;
 
 	tmpdir = e_mkdtemp("drag-n-drop-XXXXXX");
 	if (tmpdir == NULL)
 		return;
 
	client = gconf_client_get_default ();
	exporttype = gconf_client_get_string (
		client, "/apps/evolution/mail/save_file_format", NULL);
	if (exporttype == NULL)
		exporttype = g_strdup ("mbox");
	exportname = gconf_client_get_int (
		client, "/apps/evolution/mail/save_name_format", NULL);

	if (g_ascii_strcasecmp (exporttype, "mbox")==0) {
		gchar * file = NULL;
		CamelStream *fstream;

		if(uids->len>1) {
			gchar * tmp = g_strdup_printf(_("Messages from %s"), camel_folder_get_display_name (folder));
			e_filename_make_safe(tmp);
			file = g_build_filename(tmpdir, tmp, NULL);
			g_free(tmp);
		} else {
			file = em_utils_build_export_filename(folder, uids->pdata[0], exporttype, exportname, tmpdir);
 		}
		
		g_free(tmpdir);
		fd = g_open(file, O_WRONLY | O_CREAT | O_EXCL | O_BINARY, 0666);
		if (fd == -1) {
			g_free(file);
			g_free(exporttype);
			return;
 		}
 
		uri = g_filename_to_uri(file, NULL, NULL);
		fstream = camel_stream_fs_new_with_fd(fd);
		if (fstream) {
			if (em_utils_write_messages_to_stream(folder, uids, fstream) == 0) {
				GdkAtom type;
				/* terminate with \r\n to be compliant with the spec */
				gchar *uri_crlf = g_strconcat(uri, "\r\n", NULL);

				type = gtk_selection_data_get_target (data);
				gtk_selection_data_set(data, type, 8, (guchar *) uri_crlf, strlen(uri_crlf));
				g_free(uri_crlf);
			}
			g_object_unref (fstream);
		} else
			close(fd);
 
		g_free(exporttype);
		g_free(file);
		g_free(uri);
	} else if(g_ascii_strcasecmp (exporttype, "pdf")==0) {
		gchar ** filenames, **uris;
		gint i, uris_count=0;

		filenames = g_new(gchar *, uids->len);
		uris = g_new(gchar *, uids->len + 1);
		for(i=0; i<uids->len; i++) {
			filenames[i] = em_utils_build_export_filename(folder, uids->pdata[i], exporttype, exportname, tmpdir);
			/* validity test */
			fd = g_open(filenames[i], O_WRONLY | O_CREAT | O_EXCL | O_BINARY, 0666);
			if (fd == -1) {
				gint j;
				for(j=0; j<=i; j++) {
					g_free(filenames[j]);
				}
				g_free(filenames);
				g_free(uris);
				g_free(tmpdir);
				g_free(exporttype);
				return;
			}
			close(fd);

			/* export */
			if (em_utils_print_messages_to_file (folder, uids->pdata[i], filenames[i])) {
				/* terminate with \r\n to be compliant with the spec */
				uri = g_filename_to_uri(filenames[i], NULL, NULL);
				uris[uris_count++] = g_strconcat(uri, "\r\n", NULL);
				g_free(uri);
			}
 		}
 
		uris[uris_count] = NULL;
		gtk_selection_data_set_uris(data, uris);
 
		g_free(tmpdir);
		for(i=0; i<uids->len; i++) {
			g_free(filenames[i]);
 		}
		g_free(filenames);
		for(i=0; i<uris_count; i++) {
			g_free(uris[i]);
Not Zed's avatar
Not Zed committed
1053
		}
1054
1055
1056
1057
1058
1059
		g_free(uris);
		g_free(exporttype);
 
	} else {
		g_free(tmpdir);
		g_free(exporttype);
Not Zed's avatar
Not Zed committed
1060
	}
Not Zed's avatar
Not Zed committed
1061
1062
}

Not Zed's avatar
Not Zed committed
1063
1064
/**
 * em_utils_selection_set_urilist:
1065
1066
1067
1068
 * @data:
 * @folder:
 * @uids:
 *
Not Zed's avatar
Not Zed committed
1069
1070
1071
1072
1073
 * Get the selection data @data from a uri list which points to a
 * file, which is a berkely mailbox format mailbox.  The file is
 * automatically cleaned up when the application quits.
 **/
void
1074
1075
em_utils_selection_get_urilist (GtkSelectionData *selection_data,
                                CamelFolder *folder)
Not Zed's avatar
Not Zed committed
1076
1077
1078
{
	CamelStream *stream;
	CamelURL *url;
1079
	gint fd, i, res = 0;
Matthew Barnes's avatar