No issues found
Tool | Failure ID | Location | Function | Message | Data |
---|---|---|---|---|---|
clang-analyzer | no-output-found | evolution-mdn.c | Message(text='Unable to locate XML output from invoke-clang-analyzer') | None | |
clang-analyzer | no-output-found | evolution-mdn.c | Message(text='Unable to locate XML output from invoke-clang-analyzer') | None |
1 /*
2 * evolution-mdn.c
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) version 3.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with the program; if not, see <http://www.gnu.org/licenses/>
16 *
17 */
18
19 #include <config.h>
20 #include <string.h>
21 #include <glib/gi18n-lib.h>
22
23 #include <libebackend/libebackend.h>
24
25 #include <libevolution-utils/e-alert-dialog.h>
26
27 #include <libemail-engine/e-mail-session-utils.h>
28
29 #include <mail/em-utils.h>
30 #include <mail/e-mail-reader.h>
31 #include <mail/mail-send-recv.h>
32 #include <mail/message-list.h>
33 #include <mail/em-composer-utils.h>
34
35 #define MDN_USER_FLAG "receipt-handled"
36
37 #define E_TYPE_MDN (e_mdn_get_type ())
38 #define E_MDN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), E_TYPE_MDN, EMdn))
39 #define E_IS_MDN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), E_TYPE_MDN))
40
41 typedef struct _EMdn EMdn;
42 typedef struct _EMdnClass EMdnClass;
43
44 struct _EMdn {
45 EExtension parent;
46 gpointer alert; /* weak pointer */
47 };
48
49 struct _EMdnClass {
50 EExtensionClass parent_class;
51 };
52
53 typedef struct _MdnContext MdnContext;
54
55 struct _MdnContext {
56 ESource *source;
57 EMailReader *reader;
58 CamelFolder *folder;
59 CamelMessageInfo *info;
60 CamelMimeMessage *message;
61 gchar *notify_to;
62 };
63
64 typedef enum {
65 MDN_ACTION_MODE_MANUAL,
66 MDN_ACTION_MODE_AUTOMATIC
67 } MdnActionMode;
68
69 typedef enum {
70 MDN_SENDING_MODE_MANUAL,
71 MDN_SENDING_MODE_AUTOMATIC
72 } MdnSendingMode;
73
74 /* Module Entry Points */
75 void e_module_load (GTypeModule *type_module);
76 void e_module_unload (GTypeModule *type_module);
77
78 /* Forward Declarations */
79 GType e_mdn_get_type (void);
80
81 G_DEFINE_DYNAMIC_TYPE (EMdn, e_mdn, E_TYPE_EXTENSION)
82
83 static void
84 mdn_context_free (MdnContext *context)
85 {
86 if (context->info != NULL)
87 camel_folder_free_message_info (
88 context->folder, context->info);
89
90 g_object_unref (context->source);
91 g_object_unref (context->reader);
92 g_object_unref (context->folder);
93 g_object_unref (context->message);
94
95 g_free (context->notify_to);
96
97 g_slice_free (MdnContext, context);
98 }
99
100 static void
101 mdn_remove_alert (EMdn *mdn)
102 {
103 g_return_if_fail (E_IS_MDN (mdn));
104
105 if (mdn->alert != NULL)
106 e_alert_response (mdn->alert, GTK_RESPONSE_OK);
107 }
108
109 static void
110 mdn_submit_alert (EMdn *mdn,
111 EMailReader *reader,
112 EAlert *alert)
113 {
114 EPreviewPane *preview_pane;
115
116 g_return_if_fail (E_IS_MDN (mdn));
117
118 mdn_remove_alert (mdn);
119
120 g_return_if_fail (mdn->alert == NULL);
121
122 /* Make sure alerts are shown in the preview pane and not
123 * wherever e_mail_reader_get_alert_sink() might show it. */
124 preview_pane = e_mail_reader_get_preview_pane (reader);
125 e_alert_sink_submit_alert (E_ALERT_SINK (preview_pane), alert);
126
127 mdn->alert = alert;
128 g_object_add_weak_pointer (G_OBJECT (mdn->alert), &mdn->alert);
129 }
130
131 static gchar *
132 mdn_get_notify_to (CamelMimeMessage *message)
133 {
134 CamelMedium *medium;
135 const gchar *address;
136 const gchar *header_name;
137
138 medium = CAMEL_MEDIUM (message);
139 header_name = "Disposition-Notification-To";
140 address = camel_medium_get_header (medium, header_name);
141
142 /* TODO Should probably decode/format the address,
143 * since it could be in RFC 2047 format. */
144 if (address != NULL)
145 while (camel_mime_is_lwsp (*address))
146 address++;
147
148 return g_strdup (address);
149 }
150
151 static gchar *
152 mdn_get_disposition (MdnActionMode action_mode,
153 MdnSendingMode sending_mode)
154 {
155 GString *string;
156
157 string = g_string_sized_new (64);
158
159 switch (action_mode) {
160 case MDN_ACTION_MODE_MANUAL:
161 g_string_append (string, "manual-action");
162 break;
163 case MDN_ACTION_MODE_AUTOMATIC:
164 g_string_append (string, "automatic-action");
165 break;
166 default:
167 g_warn_if_reached ();
168 }
169
170 g_string_append_c (string, '/');
171
172 switch (sending_mode) {
173 case MDN_SENDING_MODE_MANUAL:
174 g_string_append (string, "MDN-sent-manually");
175 break;
176 case MDN_SENDING_MODE_AUTOMATIC:
177 g_string_append (string, "MDN-sent-automatically");
178 break;
179 default:
180 g_warn_if_reached ();
181 }
182
183 g_string_append (string, ";displayed");
184
185 return g_string_free (string, FALSE);
186 }
187
188 static void
189 mdn_receipt_done (EMailSession *session,
190 GAsyncResult *result,
191 gpointer user_data)
192 {
193 GError *error = NULL;
194
195 e_mail_session_append_to_local_folder_finish (
196 session, result, NULL, &error);
197
198 if (error == NULL) {
199 mail_send (session);
200 } else {
201 /* FIXME Poor error handling. */
202 g_warning ("%s: %s", G_STRFUNC, error->message);
203 g_error_free (error);
204 }
205 }
206
207 static void
208 mdn_notify_sender (ESource *identity_source,
209 EMailReader *reader,
210 CamelFolder *folder,
211 CamelMimeMessage *message,
212 CamelMessageInfo *info,
213 const gchar *notify_to,
214 MdnActionMode action_mode,
215 MdnSendingMode sending_mode)
216 {
217 /* See RFC 3798 for a description of message receipts. */
218
219 CamelMimeMessage *receipt;
220 CamelMultipart *body;
221 CamelMimePart *part;
222 CamelMedium *medium;
223 CamelDataWrapper *receipt_text, *receipt_data;
224 CamelContentType *type;
225 CamelInternetAddress *address;
226 CamelStream *stream;
227 CamelMessageInfo *receipt_info;
228 EMailBackend *backend;
229 EMailSession *session;
230 ESourceExtension *extension;
231 const gchar *message_id;
232 const gchar *message_date;
233 const gchar *message_subject;
234 const gchar *extension_name;
235 const gchar *transport_uid;
236 const gchar *self_address;
237 const gchar *sent_folder_uri;
238 gchar *fake_msgid;
239 gchar *hostname;
240 gchar *receipt_subject;
241 gchar *disposition;
242 gchar *recipient;
243 gchar *content;
244 gchar *ua;
245
246 backend = e_mail_reader_get_backend (reader);
247 session = e_mail_backend_get_session (backend);
248
249 /* Tag the message immediately even though we haven't actually sent
250 * the read receipt yet. Not a big deal if we fail to send it, and
251 * we don't want to keep badgering the user about it. */
252 camel_message_info_set_user_flag (info, MDN_USER_FLAG, TRUE);
253
254 medium = CAMEL_MEDIUM (message);
255 message_id = camel_medium_get_header (medium, "Message-ID");
256 message_date = camel_medium_get_header (medium, "Date");
257 message_subject = camel_mime_message_get_subject (message);
258
259 if (message_id == NULL)
260 message_id = "";
261
262 if (message_date == NULL)
263 message_date = "";
264
265 /* Collect information for the receipt. */
266
267 g_return_if_fail (identity_source != NULL);
268
269 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
270 extension = e_source_get_extension (identity_source, extension_name);
271
272 self_address = e_source_mail_identity_get_address (
273 E_SOURCE_MAIL_IDENTITY (extension));
274
275 extension_name = E_SOURCE_EXTENSION_MAIL_SUBMISSION;
276 extension = e_source_get_extension (identity_source, extension_name);
277
278 sent_folder_uri = e_source_mail_submission_get_sent_folder (
279 E_SOURCE_MAIL_SUBMISSION (extension));
280
281 transport_uid = e_source_mail_submission_get_transport_uid (
282 E_SOURCE_MAIL_SUBMISSION (extension));
283
284 /* We use camel_header_msgid_generate() to get a canonical
285 * hostname, then skip the part leading to '@' */
286 fake_msgid = camel_header_msgid_generate ();
287 hostname = strchr (fake_msgid, '@');
288 hostname++;
289
290 /* Create toplevel container. */
291 body = camel_multipart_new ();
292 camel_data_wrapper_set_mime_type (
293 CAMEL_DATA_WRAPPER (body),
294 "multipart/report;"
295 "report-type=\"disposition-notification\"");
296 camel_multipart_set_boundary (body, NULL);
297
298 /* Create textual receipt. */
299
300 receipt_text = camel_data_wrapper_new ();
301
302 type = camel_content_type_new ("text", "plain");
303 camel_content_type_set_param (type, "format", "flowed");
304 camel_content_type_set_param (type, "charset", "UTF-8");
305 camel_data_wrapper_set_mime_type_field (receipt_text, type);
306 camel_content_type_unref (type);
307
308 content = g_strdup_printf (
309 /* Translators: First %s is an email address, second %s
310 * is the subject of the email, third %s is the date. */
311 _("Your message to %s about \"%s\" on %s has been read."),
312 self_address, message_subject, message_date);
313 stream = camel_stream_mem_new ();
314 camel_stream_write_string (stream, content, NULL, NULL);
315 camel_data_wrapper_construct_from_stream_sync (
316 receipt_text, stream, NULL, NULL);
317 g_object_unref (stream);
318 g_free (content);
319
320 part = camel_mime_part_new ();
321 camel_medium_set_content (CAMEL_MEDIUM (part), receipt_text);
322 camel_mime_part_set_encoding (
323 part, CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE);
324 camel_multipart_add_part (body, part);
325 g_object_unref (part);
326
327 g_object_unref (receipt_text);
328
329 /* Create the machine-readable receipt. */
330
331 receipt_data = camel_data_wrapper_new ();
332
333 ua = g_strdup_printf (
334 "%s; %s", hostname, "Evolution "
335 VERSION SUB_VERSION " " VERSION_COMMENT);
336 recipient = g_strdup_printf ("rfc822; %s", self_address);
337 disposition = mdn_get_disposition (action_mode, sending_mode);
338
339 type = camel_content_type_new ("message", "disposition-notification");
340 camel_data_wrapper_set_mime_type_field (receipt_data, type);
341 camel_content_type_unref (type);
342
343 content = g_strdup_printf (
344 "Reporting-UA: %s\n"
345 "Final-Recipient: %s\n"
346 "Original-Message-ID: %s\n"
347 "Disposition: %s\n",
348 ua, recipient, message_id, disposition);
349 stream = camel_stream_mem_new ();
350 camel_stream_write_string (stream, content, NULL, NULL);
351 camel_data_wrapper_construct_from_stream_sync (
352 receipt_data, stream, NULL, NULL);
353 g_object_unref (stream);
354 g_free (content);
355
356 g_free (ua);
357 g_free (recipient);
358 g_free (fake_msgid);
359 g_free (disposition);
360
361 part = camel_mime_part_new ();
362 camel_medium_set_content (CAMEL_MEDIUM (part), receipt_data);
363 camel_mime_part_set_encoding (part, CAMEL_TRANSFER_ENCODING_7BIT);
364 camel_multipart_add_part (body, part);
365 g_object_unref (part);
366
367 g_object_unref (receipt_data);
368
369 /* Finish creating the message. */
370
371 receipt = camel_mime_message_new ();
372 camel_medium_set_content (
373 CAMEL_MEDIUM (receipt), CAMEL_DATA_WRAPPER (body));
374 g_object_unref (body);
375
376 receipt_subject = g_strdup_printf (
377 /* Translators: %s is the subject of the email message. */
378 _("Delivery Notification for \"%s\""), message_subject);
379 camel_mime_message_set_subject (receipt, receipt_subject);
380 g_free (receipt_subject);
381
382 address = camel_internet_address_new ();
383 camel_address_decode (CAMEL_ADDRESS (address), self_address);
384 camel_mime_message_set_from (receipt, address);
385 g_object_unref (address);
386
387 address = camel_internet_address_new ();
388 camel_address_decode (CAMEL_ADDRESS (address), notify_to);
389 camel_mime_message_set_recipients (
390 receipt, CAMEL_RECIPIENT_TYPE_TO, address);
391 g_object_unref (address);
392
393 camel_medium_set_header (
394 CAMEL_MEDIUM (receipt),
395 "Return-Path", "<>");
396 camel_medium_set_header (
397 CAMEL_MEDIUM (receipt),
398 "X-Evolution-Identity",
399 e_source_get_uid (identity_source));
400 camel_medium_set_header (
401 CAMEL_MEDIUM (receipt),
402 "X-Evolution-Transport",
403 transport_uid);
404 camel_medium_set_header (
405 CAMEL_MEDIUM (receipt),
406 "X-Evolution-Fcc",
407 sent_folder_uri);
408
409 /* RFC 3834, Section 5 describes this header. */
410 camel_medium_set_header (
411 CAMEL_MEDIUM (receipt),
412 "Auto-Submitted", "auto-replied");
413
414 /* Send the receipt. */
415 receipt_info = camel_message_info_new (NULL);
416 camel_message_info_set_flags (
417 receipt_info, CAMEL_MESSAGE_SEEN, CAMEL_MESSAGE_SEEN);
418
419 /* FIXME Pass a GCancellable. */
420 e_mail_session_append_to_local_folder (
421 session, E_MAIL_LOCAL_FOLDER_OUTBOX,
422 receipt, receipt_info, G_PRIORITY_DEFAULT,
423 NULL, (GAsyncReadyCallback) mdn_receipt_done,
424 g_object_ref (session));
425
426 camel_message_info_free (receipt_info);
427 }
428
429 static void
430 mdn_notify_action_cb (GtkAction *action,
431 MdnContext *context)
432 {
433 mdn_notify_sender (
434 context->source,
435 context->reader,
436 context->folder,
437 context->message,
438 context->info,
439 context->notify_to,
440 MDN_ACTION_MODE_MANUAL,
441 MDN_SENDING_MODE_MANUAL);
442
443 /* Make sure the newly-added user flag gets saved. */
444 camel_folder_free_message_info (context->folder, context->info);
445 context->info = NULL;
446 }
447
448 static void
449 mdn_mail_reader_changed_cb (EMailReader *reader,
450 EMdn *mdn)
451 {
452 MessageList *message_list;
453
454 g_return_if_fail (E_IS_MAIL_READER (reader));
455
456 message_list = MESSAGE_LIST (e_mail_reader_get_message_list (reader));
457
458 if (!message_list || message_list_selected_count (message_list) != 1)
459 mdn_remove_alert (mdn);
460 }
461
462 static void
463 mdn_message_loaded_cb (EMailReader *reader,
464 const gchar *message_uid,
465 CamelMimeMessage *message,
466 EMdn *mdn)
467 {
468 EAlert *alert;
469 ESource *source;
470 ESourceMDN *extension;
471 ESourceRegistry *registry;
472 EMailBackend *backend;
473 EMailSession *session;
474 CamelFolder *folder;
475 CamelMessageInfo *info;
476 EMdnResponsePolicy response_policy;
477 const gchar *extension_name;
478 gchar *notify_to = NULL;
479
480 backend = e_mail_reader_get_backend (reader);
481 session = e_mail_backend_get_session (backend);
482 registry = e_mail_session_get_registry (session);
483
484 folder = e_mail_reader_get_folder (reader);
485
486 mdn_remove_alert (mdn);
487
488 info = camel_folder_get_message_info (folder, message_uid);
489 if (info == NULL)
490 return;
491
492 if (camel_message_info_user_flag (info, MDN_USER_FLAG)) {
493 alert = e_alert_new ("mdn:sender-notified", NULL);
494 mdn_submit_alert (mdn, reader, alert);
495 g_object_unref (alert);
496 goto exit;
497 }
498
499 notify_to = mdn_get_notify_to (message);
500 if (notify_to == NULL)
501 goto exit;
502
503 /* do not show the notice in special folders */
504 if (em_utils_folder_is_drafts (registry, folder) ||
505 em_utils_folder_is_templates (registry, folder) ||
506 em_utils_folder_is_sent (registry, folder) ||
507 em_utils_folder_is_outbox (registry, folder))
508 goto exit;
509
510 /* This returns a new ESource reference. */
511 source = em_utils_guess_mail_identity_with_recipients (
512 registry, message, folder, message_uid);
513 if (source == NULL)
514 goto exit;
515
516 extension_name = E_SOURCE_EXTENSION_MDN;
517 extension = e_source_get_extension (source, extension_name);
518 response_policy = e_source_mdn_get_response_policy (extension);
519
520 if (response_policy == E_MDN_RESPONSE_POLICY_ASK) {
521 MdnContext *context;
522 GtkAction *action;
523 gchar *tooltip;
524
525 context = g_slice_new0 (MdnContext);
526 context->source = g_object_ref (source);
527 context->reader = g_object_ref (reader);
528 context->folder = g_object_ref (folder);
529 context->message = g_object_ref (message);
530 context->info = camel_message_info_ref (info);
531
532 context->notify_to = notify_to;
533 notify_to = NULL;
534
535 tooltip = g_strdup_printf (
536 _("Send a read receipt to '%s'"),
537 context->notify_to);
538
539 action = gtk_action_new (
540 "notify-sender", /* name doesn't matter */
541 _("_Notify Sender"),
542 tooltip, NULL);
543
544 g_signal_connect_data (
545 action, "activate",
546 G_CALLBACK (mdn_notify_action_cb),
547 context,
548 (GClosureNotify) mdn_context_free,
549 (GConnectFlags) 0);
550
551 alert = e_alert_new ("mdn:notify-sender", NULL);
552 e_alert_add_action (alert, action, GTK_RESPONSE_APPLY);
553 mdn_submit_alert (mdn, reader, alert);
554 g_object_unref (alert);
555
556 g_object_unref (action);
557 g_free (tooltip);
558 }
559
560 g_object_unref (source);
561
562 exit:
563 camel_folder_free_message_info (folder, info);
564 g_free (notify_to);
565 }
566
567 static void
568 mdn_message_seen_cb (EMailReader *reader,
569 const gchar *message_uid,
570 CamelMimeMessage *message)
571 {
572 ESource *source;
573 ESourceMDN *extension;
574 ESourceRegistry *registry;
575 EMailBackend *backend;
576 EMailSession *session;
577 CamelFolder *folder;
578 CamelMessageInfo *info;
579 EMdnResponsePolicy response_policy;
580 const gchar *extension_name;
581 gchar *notify_to = NULL;
582
583 backend = e_mail_reader_get_backend (reader);
584 session = e_mail_backend_get_session (backend);
585 registry = e_mail_session_get_registry (session);
586
587 folder = e_mail_reader_get_folder (reader);
588
589 info = camel_folder_get_message_info (folder, message_uid);
590 if (info == NULL)
591 return;
592
593 if (camel_message_info_user_flag (info, MDN_USER_FLAG))
594 goto exit;
595
596 notify_to = mdn_get_notify_to (message);
597 if (notify_to == NULL)
598 goto exit;
599
600 /* This returns a new ESource reference. */
601 source = em_utils_guess_mail_identity_with_recipients (
602 registry, message, folder, message_uid);
603 if (source == NULL)
604 goto exit;
605
606 extension_name = E_SOURCE_EXTENSION_MDN;
607 extension = e_source_get_extension (source, extension_name);
608 response_policy = e_source_mdn_get_response_policy (extension);
609
610 if (response_policy == E_MDN_RESPONSE_POLICY_ALWAYS)
611 mdn_notify_sender (
612 source, reader, folder,
613 message, info, notify_to,
614 MDN_ACTION_MODE_AUTOMATIC,
615 MDN_SENDING_MODE_AUTOMATIC);
616
617 g_object_unref (source);
618
619 exit:
620 camel_folder_free_message_info (folder, info);
621 g_free (notify_to);
622 }
623
624 static void
625 mdn_constructed (GObject *object)
626 {
627 EExtension *extension;
628 EExtensible *extensible;
629
630 extension = E_EXTENSION (object);
631 extensible = e_extension_get_extensible (extension);
632 g_return_if_fail (E_IS_MAIL_READER (extensible));
633
634 g_signal_connect (
635 extensible, "changed",
636 G_CALLBACK (mdn_mail_reader_changed_cb), extension);
637
638 g_signal_connect (
639 extensible, "message-loaded",
640 G_CALLBACK (mdn_message_loaded_cb), extension);
641
642 g_signal_connect (
643 extensible, "message-seen",
644 G_CALLBACK (mdn_message_seen_cb), NULL);
645
646 /* Chain up to parent's constructed() method. */
647 G_OBJECT_CLASS (e_mdn_parent_class)->constructed (object);
648 }
649
650 static void
651 mdn_dispose (GObject *object)
652 {
653 mdn_remove_alert (E_MDN (object));
654
655 /* Chain up to parent's dispose() method. */
656 G_OBJECT_CLASS (e_mdn_parent_class)->dispose (object);
657 }
658
659 static void
660 e_mdn_class_init (EMdnClass *class)
661 {
662 GObjectClass *object_class;
663 EExtensionClass *extension_class;
664
665 object_class = G_OBJECT_CLASS (class);
666 object_class->constructed = mdn_constructed;
667 object_class->dispose = mdn_dispose;
668
669 extension_class = E_EXTENSION_CLASS (class);
670 extension_class->extensible_type = E_TYPE_MAIL_READER;
671 }
672
673 static void
674 e_mdn_class_finalize (EMdnClass *class)
675 {
676 }
677
678 static void
679 e_mdn_init (EMdn *extension)
680 {
681 }
682
683 G_MODULE_EXPORT void
684 e_module_load (GTypeModule *type_module)
685 {
686 e_mdn_register_type (type_module);
687 }
688
689 G_MODULE_EXPORT void
690 e_module_unload (GTypeModule *type_module)
691 {
692 }