1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #include "modest-mail-operation.h"
31 /* include other impl specific header files */
34 #include <tny-mime-part.h>
35 #include <tny-store-account.h>
36 #include <tny-folder-store.h>
37 #include <tny-folder-store-query.h>
38 #include <glib/gi18n.h>
39 #include "modest-tny-platform-factory.h"
41 /* 'private'/'protected' functions */
42 static void modest_mail_operation_class_init (ModestMailOperationClass *klass);
43 static void modest_mail_operation_init (ModestMailOperation *obj);
44 static void modest_mail_operation_finalize (GObject *obj);
46 #define MODEST_ERROR modest_error_quark ()
48 typedef enum _ModestMailOperationErrorCode ModestMailOperationErrorCode;
49 enum _ModestMailOperationErrorCode {
50 MODEST_MAIL_OPERATION_ERROR_BAD_ACCOUNT,
51 MODEST_MAIL_OPERATION_ERROR_MISSING_PARAMETER,
53 MODEST_MAIL_OPERATION_NUM_ERROR_CODES
56 static void set_error (ModestMailOperation *mail_operation,
57 ModestMailOperationErrorCode error_code,
58 const gchar *fmt, ...);
59 static void status_update_cb (TnyFolder *folder,
63 static void folder_refresh_cb (TnyFolder *folder,
66 static void add_attachments (TnyMsg *msg,
67 const GList *attachments_list);
70 static TnyMimePart * add_body_part (TnyMsg *msg,
72 const gchar *content_type,
73 gboolean has_attachments);
83 typedef struct _ModestMailOperationPrivate ModestMailOperationPrivate;
84 struct _ModestMailOperationPrivate {
86 ModestMailOperationStatus status;
89 #define MODEST_MAIL_OPERATION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
90 MODEST_TYPE_MAIL_OPERATION, \
91 ModestMailOperationPrivate))
93 /* some utility functions */
94 static char * get_content_type(const gchar *s);
95 static gboolean is_ascii(const gchar *s);
98 static GObjectClass *parent_class = NULL;
100 /* uncomment the following if you have defined any signals */
101 /* static guint signals[LAST_SIGNAL] = {0}; */
104 modest_mail_operation_get_type (void)
106 static GType my_type = 0;
108 static const GTypeInfo my_info = {
109 sizeof(ModestMailOperationClass),
110 NULL, /* base init */
111 NULL, /* base finalize */
112 (GClassInitFunc) modest_mail_operation_class_init,
113 NULL, /* class finalize */
114 NULL, /* class data */
115 sizeof(ModestMailOperation),
117 (GInstanceInitFunc) modest_mail_operation_init,
120 my_type = g_type_register_static (G_TYPE_OBJECT,
121 "ModestMailOperation",
128 modest_mail_operation_class_init (ModestMailOperationClass *klass)
130 GObjectClass *gobject_class;
131 gobject_class = (GObjectClass*) klass;
133 parent_class = g_type_class_peek_parent (klass);
134 gobject_class->finalize = modest_mail_operation_finalize;
136 g_type_class_add_private (gobject_class, sizeof(ModestMailOperationPrivate));
138 /* signal definitions go here, e.g.: */
139 /* signals[MY_SIGNAL_1] = */
140 /* g_signal_new ("my_signal_1",....); */
141 /* signals[MY_SIGNAL_2] = */
142 /* g_signal_new ("my_signal_2",....); */
147 modest_mail_operation_init (ModestMailOperation *obj)
149 ModestMailOperationPrivate *priv;
151 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
153 priv->account = NULL;
154 priv->status = MODEST_MAIL_OPERATION_STATUS_INVALID;
159 modest_mail_operation_finalize (GObject *obj)
161 ModestMailOperationPrivate *priv;
163 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
166 g_object_unref (priv->account);
167 priv->account = NULL;
170 g_error_free (priv->error);
174 G_OBJECT_CLASS(parent_class)->finalize (obj);
178 modest_mail_operation_new (TnyAccount *account)
180 ModestMailOperation *mail_operation;
181 ModestMailOperationPrivate *priv;
184 MODEST_MAIL_OPERATION(g_object_new(MODEST_TYPE_MAIL_OPERATION, NULL));
185 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(mail_operation);
187 priv->account = g_object_ref (account);
189 return mail_operation;
194 modest_mail_operation_send_mail (ModestMailOperation *mail_operation,
197 ModestMailOperationPrivate *priv;
198 TnyTransportAccount *transport_account;
200 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(mail_operation);
202 if (!TNY_IS_TRANSPORT_ACCOUNT (priv->account)) {
203 set_error (mail_operation,
204 MODEST_MAIL_OPERATION_ERROR_BAD_ACCOUNT,
205 _("Error trying to send a mail. Use a transport account"));
208 transport_account = TNY_TRANSPORT_ACCOUNT (priv->account);
210 mail_operation = modest_mail_operation_new (NULL);
211 tny_transport_account_send (transport_account, msg);
215 modest_mail_operation_send_new_mail (ModestMailOperation *mail_operation,
220 const gchar *subject,
222 const GList *attachments_list)
226 TnyTransportAccount *transport_account;
227 ModestMailOperationPrivate *priv;
230 g_return_if_fail (mail_operation);
232 /* Check parametters */
234 set_error (mail_operation,
235 MODEST_MAIL_OPERATION_ERROR_MISSING_PARAMETER,
236 _("Error trying to send a mail. You need to set almost one a recipient"));
240 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(mail_operation);
242 if (!TNY_IS_TRANSPORT_ACCOUNT (priv->account)) {
243 set_error (mail_operation,
244 MODEST_MAIL_OPERATION_ERROR_BAD_ACCOUNT,
245 _("Error trying to send a mail. Use a transport account"));
250 transport_account = TNY_TRANSPORT_ACCOUNT (priv->account);
252 new_msg = TNY_MSG (tny_camel_msg_new ());
253 header = TNY_HEADER (tny_camel_header_new ());
255 /* WARNING: set the header before assign values to it */
256 tny_msg_set_header (new_msg, header);
257 tny_header_set_from (TNY_HEADER (header), from);
258 tny_header_set_to (TNY_HEADER (header), to);
259 tny_header_set_cc (TNY_HEADER (header), cc);
260 tny_header_set_bcc (TNY_HEADER (header), bcc);
261 tny_header_set_subject (TNY_HEADER (header), subject);
263 content_type = get_content_type(body);
265 /* Add the body of the new mail */
266 add_body_part (new_msg, body, (const gchar *) content_type,
267 (attachments_list == NULL) ? FALSE : TRUE);
269 /* Add attachments */
270 add_attachments (new_msg, attachments_list);
273 tny_transport_account_send (transport_account, new_msg);
276 g_object_unref (header);
277 g_object_unref (new_msg);
278 g_free(content_type);
282 * modest_mail_operation_create_forward_mail:
283 * @msg: a valid #TnyMsg instance
284 * @forward_type: the type of forwarded message
286 * creates a forwarded message from an existing one
288 * Returns: a new #TnyMsg, or NULL in case of error
291 modest_mail_operation_create_forward_mail (TnyMsg *msg,
293 ModestMailOperationForwardType forward_type)
296 TnyHeader *new_header, *header;
297 TnyStream *attachment_stream;
298 gchar *new_subject, *new_body, *content_type, *quoted;
299 TnyMimePart *text_body_part = NULL;
300 GList *attachments_list;
304 /* Create new objects */
305 new_msg = TNY_MSG (tny_camel_msg_new ());
306 new_header = TNY_HEADER (tny_camel_header_new ());
308 header = tny_msg_get_header (msg);
310 /* Fill the header */
311 tny_msg_set_header (new_msg, new_header);
312 tny_header_set_from (new_header, from);
314 /* Change the subject */
315 new_subject = (gchar *) modest_text_utils_create_forward_subject (tny_header_get_subject(header));
316 tny_header_set_subject (new_header, (const gchar *) new_subject);
317 g_free (new_subject);
319 /* Get body from original msg */
320 new_body = (gchar *) modest_tny_msg_actions_find_body (msg, FALSE);
322 g_object_unref (new_msg);
325 content_type = get_content_type(new_body);
327 /* Create the list of attachments */
328 parts = TNY_LIST (tny_simple_list_new());
329 tny_mime_part_get_parts (TNY_MIME_PART (msg), parts);
330 iter = tny_list_create_iterator (parts);
331 attachments_list = NULL;
333 while (!tny_iterator_is_done(iter)) {
336 part = TNY_MIME_PART (tny_iterator_get_current (iter));
337 if (tny_mime_part_is_attachment (part))
338 attachments_list = g_list_prepend (attachments_list, part);
340 tny_iterator_next (iter);
343 /* Add attachments */
344 add_attachments (new_msg, attachments_list);
346 switch (forward_type) {
347 TnyMimePart *attachment_part;
350 case MODEST_MAIL_OPERATION_FORWARD_TYPE_INLINE:
351 /* Prepend "Original message" text */
352 inlined_text = (gchar *)
353 modest_text_utils_create_inlined_text (tny_header_get_from (header),
354 tny_header_get_date_sent (header),
355 tny_header_get_to (header),
356 tny_header_get_subject (header),
357 (const gchar*) new_body);
359 new_body = inlined_text;
362 add_body_part (new_msg, new_body,
363 (const gchar *) content_type,
364 (tny_list_get_length (parts) > 0) ? TRUE : FALSE);
367 case MODEST_MAIL_OPERATION_FORWARD_TYPE_ATTACHMENT:
368 attachment_part = add_body_part (new_msg, new_body,
369 (const gchar *) content_type, TRUE);
371 /* Set the subject as the name of the attachment */
372 tny_mime_part_set_filename (attachment_part, tny_header_get_subject (header));
378 if (attachments_list) g_list_free (attachments_list);
379 g_object_unref (parts);
380 if (text_body_part) g_free (text_body_part);
381 g_free (content_type);
388 * modest_mail_operation_create_reply_mail:
389 * @msg: a valid #TnyMsg instance
390 * @reply_type: the format of the new message
391 * @reply_mode: the mode of reply, to the sender only, to a mail list or to all
393 * creates a new message to reply to an existing one
395 * Returns: Returns: a new #TnyMsg, or NULL in case of error
398 modest_mail_operation_create_reply_mail (TnyMsg *msg,
400 ModestMailOperationReplyType reply_type,
401 ModestMailOperationReplyMode reply_mode)
404 TnyHeader *new_header, *header;
405 TnyStream *attachment_stream;
406 gchar *new_subject, *new_body, *content_type, *quoted;
408 TnyMimePart *text_body_part;
410 /* Create new objects */
411 new_msg = TNY_MSG (tny_camel_msg_new ());
412 new_header = TNY_HEADER (tny_camel_header_new ());
413 header = tny_msg_get_header (msg);
415 /* Fill the header */
416 tny_msg_set_header (new_msg, new_header);
417 tny_header_set_to (new_header, tny_header_get_from (header));
418 tny_header_set_from (new_header, from);
420 switch (reply_mode) {
421 gchar *new_cc = NULL;
422 const gchar *cc = NULL, *bcc = NULL;
425 case MODEST_MAIL_OPERATION_REPLY_MODE_SENDER:
426 /* Do not fill neither cc nor bcc */
428 case MODEST_MAIL_OPERATION_REPLY_MODE_LIST:
431 case MODEST_MAIL_OPERATION_REPLY_MODE_ALL:
432 /* Concatenate to, cc and bcc */
433 cc = tny_header_get_cc (header);
434 bcc = tny_header_get_bcc (header);
436 tmp = g_string_new (tny_header_get_to (header));
437 if (cc) g_string_append_printf (tmp, ",%s",cc);
438 if (bcc) g_string_append_printf (tmp, ",%s",bcc);
440 /* Remove my own address from the cc list */
442 modest_text_utils_remove_mail_from_mail_list ((const gchar *) tmp->str,
443 (const gchar *) from);
444 /* FIXME: remove also the mails from the new To: */
445 tny_header_set_cc (new_header, new_cc);
448 g_string_free (tmp, TRUE);
453 /* Change the subject */
454 new_subject = (gchar*) modest_text_utils_create_reply_subject (tny_header_get_subject(header));
455 tny_header_set_subject (new_header, (const gchar *) new_subject);
456 g_free (new_subject);
458 /* Get body from original msg */
459 new_body = (gchar*) modest_tny_msg_actions_find_body (msg, FALSE);
461 g_object_unref (new_msg);
464 content_type = get_content_type(new_body);
466 switch (reply_type) {
469 case MODEST_MAIL_OPERATION_REPLY_TYPE_CITE:
470 /* Prepend "Original message" text */
471 cited_text = (gchar *) modest_text_utils_create_cited_text (tny_header_get_from (header),
472 tny_header_get_date_sent (header),
473 (const gchar*) new_body);
475 new_body = cited_text;
477 case MODEST_MAIL_OPERATION_REPLY_TYPE_QUOTE:
478 /* FIXME: replace 80 with a value from ModestConf */
479 quoted = (gchar*) modest_text_utils_quote (new_body,
480 tny_header_get_from (header),
481 tny_header_get_date_sent (header),
488 text_body_part = add_body_part (new_msg, new_body,
489 (const gchar *) content_type, TRUE);
492 /* g_free (text_body_part); */
493 g_free (content_type);
500 modest_mail_operation_update_account (ModestMailOperation *mail_operation)
502 TnyStoreAccount *storage_account;
503 ModestMailOperationPrivate *priv;
505 TnyIterator *ifolders;
506 TnyFolder *cur_folder;
507 TnyFolderStoreQuery *query;
509 g_return_if_fail (mail_operation);
510 g_return_if_fail (MODEST_IS_MAIL_OPERATION (mail_operation));
512 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(mail_operation);
514 /* Check that it is a store account */
515 if (!TNY_IS_STORE_ACCOUNT (priv->account)) {
516 set_error (mail_operation,
517 MODEST_MAIL_OPERATION_ERROR_BAD_ACCOUNT,
518 _("Error trying to update an account. Use a store account"));
521 storage_account = TNY_STORE_ACCOUNT (priv->account);
523 /* Get subscribed folders */
524 folders = TNY_LIST (tny_simple_list_new ());
525 query = tny_folder_store_query_new ();
526 tny_folder_store_query_add_item (query, NULL, TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
527 tny_folder_store_get_folders (TNY_FOLDER_STORE (storage_account),
529 g_object_unref (query);
531 ifolders = tny_list_create_iterator (folders);
533 /* Async refresh folders */
534 for (tny_iterator_first (ifolders);
535 !tny_iterator_is_done (ifolders);
536 tny_iterator_next (ifolders)) {
538 cur_folder = TNY_FOLDER (tny_iterator_get_current (ifolders));
539 tny_folder_refresh_async (cur_folder, folder_refresh_cb,
540 status_update_cb, mail_operation);
543 g_object_unref (ifolders);
546 ModestMailOperationStatus
547 modest_mail_operation_get_status (ModestMailOperation *mail_operation)
549 ModestMailOperationPrivate *priv;
551 /* g_return_val_if_fail (mail_operation, MODEST_MAIL_OPERATION_STATUS_INVALID); */
552 /* g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (mail_operation), */
553 /* MODEST_MAIL_OPERATION_STATUS_INVALID); */
555 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (mail_operation);
560 modest_mail_operation_get_error (ModestMailOperation *mail_operation)
562 ModestMailOperationPrivate *priv;
564 /* g_return_val_if_fail (mail_operation, NULL); */
565 /* g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (mail_operation), NULL); */
567 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (mail_operation);
572 modest_mail_operation_cancel (ModestMailOperation *mail_operation)
578 is_ascii(const gchar *s)
581 if (s[0] & 128 || s[0] < 32)
589 get_content_type(const gchar *s)
593 type = g_string_new("text/plain");
595 if (g_utf8_validate(s, -1, NULL)) {
596 g_string_append(type, "; charset=\"utf-8\"");
598 /* it should be impossible to reach this, but better safe than sorry */
599 g_warning("invalid utf8 in message");
600 g_string_append(type, "; charset=\"latin1\"");
603 return g_string_free(type, FALSE);
607 modest_error_quark (void)
609 static GQuark err_q = 0;
612 err_q = g_quark_from_static_string ("modest-error-quark");
619 set_error (ModestMailOperation *mail_operation,
620 ModestMailOperationErrorCode error_code,
621 const gchar *fmt, ...)
623 ModestMailOperationPrivate *priv;
628 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(mail_operation);
630 va_start (args, fmt);
632 orig = g_strdup_vprintf(fmt, args);
633 error = g_error_new (MODEST_ERROR, error_code, orig);
638 g_object_unref (priv->error);
641 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
645 status_update_cb (TnyFolder *folder, const gchar *what, gint status, gpointer user_data)
647 /* TODO: update main window progress bar */
651 folder_refresh_cb (TnyFolder *folder, gboolean cancelled, gpointer user_data)
654 ModestMailOperation *mail_operation;
655 ModestMailOperationPrivate *priv;
657 mail_operation = MODEST_MAIL_OPERATION (user_data);
658 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(mail_operation);
660 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELLED;
665 add_attachments (TnyMsg *msg, const GList *attachments_list)
668 TnyMimePart *attachment_part, *old_attachment;
669 const gchar *attachment_content_type;
670 const gchar *attachment_filename;
671 TnyStream *attachment_stream;
673 for (pos = (GList *)attachments_list; pos; pos = pos->next) {
675 old_attachment = pos->data;
676 attachment_filename = tny_mime_part_get_filename (old_attachment);
677 attachment_stream = tny_mime_part_get_stream (old_attachment);
678 attachment_part = TNY_MIME_PART (tny_camel_mime_part_new (camel_mime_part_new()));
680 attachment_content_type = tny_mime_part_get_content_type (old_attachment);
682 tny_mime_part_construct_from_stream (attachment_part,
684 attachment_content_type);
685 tny_stream_reset (attachment_stream);
687 tny_mime_part_set_filename (attachment_part, attachment_filename);
689 tny_mime_part_add_part (TNY_MIME_PART (msg), attachment_part);
690 /* g_object_unref (attachment_part); */
695 add_body_part (TnyMsg *msg,
697 const gchar *content_type,
698 gboolean has_attachments)
700 TnyMimePart *text_body_part = NULL;
701 TnyStream *text_body_stream;
703 /* Create the stream */
704 text_body_stream = TNY_STREAM (tny_camel_stream_new
705 (camel_stream_mem_new_with_buffer
706 (body, strlen(body))));
708 /* Create body part if needed */
711 TNY_MIME_PART (tny_camel_mime_part_new (camel_mime_part_new()));
713 text_body_part = TNY_MIME_PART(msg);
715 /* Construct MIME part */
716 tny_stream_reset (text_body_stream);
717 tny_mime_part_construct_from_stream (text_body_part,
720 tny_stream_reset (text_body_stream);
722 /* Add part if needed */
723 if (has_attachments) {
724 tny_mime_part_add_part (TNY_MIME_PART (msg), text_body_part);
725 g_object_unref (G_OBJECT(text_body_part));
729 g_object_unref (text_body_stream);
731 return text_body_part;