/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
/* camel-ews-utils.c
*
* Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU Lesser General Public
* License as published by the Free Software Foundation.
*
* 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 Lesser General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "evolution-ews-config.h"
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <glib/gi18n-lib.h>
#include <glib/gstdio.h>
#include <libemail-engine/libemail-engine.h>
#include "server/camel-ews-settings.h"
#include "server/e-ews-camel-common.h"
#include "server/e-ews-item-change.h"
#include "server/e-ews-message.h"
#include "camel-ews-utils.h"
#define SUBFOLDER_DIR_NAME "subfolders"
#define SUBFOLDER_DIR_NAME_LEN 10
#define EWS_MAPI_MSGFLAG_RN_PENDING 0x100
CamelFolderInfo *
camel_ews_utils_build_folder_info (CamelEwsStore *store,
const gchar *fid)
{
CamelEwsStoreSummary *ews_summary = store->summary;
CamelFolderInfo *fi;
gchar *folder_name;
fi = camel_folder_info_new ();
fi->full_name = camel_ews_store_summary_get_folder_full_name (
ews_summary, fid, NULL);
if (!fi->full_name) {
camel_folder_info_free (fi);
g_warn_if_reached ();
return NULL;
}
folder_name = camel_ews_store_summary_get_folder_name (ews_summary, fid, NULL);
fi->display_name = e_ews_folder_utils_unescape_name (folder_name);
fi->flags = camel_ews_store_summary_get_folder_flags (ews_summary, fid, NULL);
fi->unread = camel_ews_store_summary_get_folder_unread (ews_summary, fid, NULL);
fi->total = camel_ews_store_summary_get_folder_total (ews_summary, fid, NULL);
g_free (folder_name);
if (!(fi->flags & CAMEL_FOLDER_TYPE_MASK)) {
switch (camel_ews_store_summary_get_folder_type (ews_summary, fid, NULL)) {
case E_EWS_FOLDER_TYPE_CALENDAR:
fi->flags |= CAMEL_FOLDER_TYPE_EVENTS;
break;
case E_EWS_FOLDER_TYPE_CONTACTS:
fi->flags |= CAMEL_FOLDER_TYPE_CONTACTS;
break;
case E_EWS_FOLDER_TYPE_TASKS:
fi->flags |= CAMEL_FOLDER_TYPE_TASKS;
break;
case E_EWS_FOLDER_TYPE_MEMOS:
fi->flags |= CAMEL_FOLDER_TYPE_MEMOS;
break;
default:
break;
}
}
return fi;
}
static void
add_folder_to_summary (CamelEwsStore *store,
EEwsFolder *folder);
static void
sync_deleted_folders (CamelEwsStore *store,
GSList *deleted_folders)
{
CamelEwsStoreSummary *ews_summary = store->summary;
GSList *l;
for (l = deleted_folders; l != NULL; l = g_slist_next (l)) {
const gchar *fid = l->data;
EEwsFolderType ftype;
CamelFolderInfo *fi;
GError *error = NULL;
if (!camel_ews_store_summary_has_folder (ews_summary, fid))
continue;
ftype = camel_ews_store_summary_get_folder_type (
ews_summary, fid, NULL);
if (ftype == E_EWS_FOLDER_TYPE_MAILBOX) {
fi = camel_ews_utils_build_folder_info (store, fid);
if (!camel_ews_store_summary_remove_folder (ews_summary, fid, &error)) {
if (error != NULL) {
g_warning ("%s: %s", G_STRFUNC, error->message);
g_clear_error (&error);
}
continue;
}
camel_subscribable_folder_unsubscribed (CAMEL_SUBSCRIBABLE (store), fi);
camel_store_folder_deleted (CAMEL_STORE (store), fi);
}
}
}
static gboolean
ews_utils_rename_folder (CamelEwsStore *store,
EEwsFolderType ftype,
const gchar *fid,
const gchar *changekey,
const gchar *pfid,
const gchar *display_name,
const gchar *old_fname,
GError **error)
{
CamelEwsStoreSummary *ews_summary = store->summary;
CamelFolderInfo *fi;
camel_ews_store_summary_set_change_key (ews_summary, fid, changekey);
if (display_name)
camel_ews_store_summary_set_folder_name (
ews_summary, fid, display_name);
if (pfid)
camel_ews_store_summary_set_parent_folder_id (
ews_summary, fid, pfid);
if (ftype == E_EWS_FOLDER_TYPE_MAILBOX) {
fi = camel_ews_utils_build_folder_info (store, fid);
camel_store_folder_renamed (CAMEL_STORE (store), old_fname, fi);
}
return TRUE;
}
static void
sync_updated_folders (CamelEwsStore *store,
GSList *updated_folders)
{
CamelEwsStoreSummary *ews_summary = store->summary;
GSList *l;
for (l = updated_folders; l != NULL; l = g_slist_next (l)) {
EEwsFolder *ews_folder = (EEwsFolder *) l->data;
EEwsFolderType ftype;
gchar *folder_name;
gchar *display_name;
const EwsFolderId *fid, *pfid;
ftype = e_ews_folder_get_folder_type (ews_folder);
if (ftype != E_EWS_FOLDER_TYPE_MAILBOX)
continue;
fid = e_ews_folder_get_id (ews_folder);
folder_name = camel_ews_store_summary_get_folder_full_name (
ews_summary, fid->id, NULL);
if (!folder_name) {
/* in case the folder is not in the local store summary,
just add it as a new folder */
add_folder_to_summary (store, ews_folder);
continue;
}
pfid = e_ews_folder_get_parent_id (ews_folder);
display_name = g_strdup (e_ews_folder_get_escaped_name (ews_folder));
/* If the folder is moved or renamed (which are separate
* operations in Exchange, unfortunately, then the name
* or parent folder will change. Handle both... */
if (pfid || display_name) {
GError *error = NULL;
gchar *new_fname = NULL;
if (pfid) {
gchar *pfname;
/* If the display name wasn't changed, its basename is still
* the same as it was before... */
if (!display_name)
display_name = camel_ews_store_summary_get_folder_name (
ews_summary, fid->id, NULL);
if (!display_name)
goto done;
pfname = camel_ews_store_summary_get_folder_full_name (
ews_summary, pfid->id, NULL);
/* If the lookup failed, it'll be because the new parent folder
* is the message folder root. */
if (pfname) {
new_fname = g_strconcat (
pfname, "/", display_name, NULL);
g_free (pfname);
} else
new_fname = g_strdup (display_name);
} else {
/* Parent folder not changed; just basename */
const gchar *last_slash;
/* Append new display_name to old parent directory name... */
last_slash = g_strrstr (folder_name, "/");
if (last_slash)
new_fname = g_strdup_printf (
"%.*s/%s",
(gint)(last_slash - folder_name),
folder_name, display_name);
else /* ...unless it was a child of the root folder */
new_fname = g_strdup (display_name);
}
if (strcmp (new_fname, folder_name))
ews_utils_rename_folder (
store, ftype,
fid->id, fid->change_key,
pfid ? pfid->id : NULL,
display_name, folder_name, &error);
g_free (new_fname);
g_clear_error (&error);
}
done:
g_free (folder_name);
g_free (display_name);
}
}
/* FIXME get the real folder ids of the system folders using
* by fetching them using distinguished folder ids once */
static void
add_folder_to_summary (CamelEwsStore *store,
EEwsFolder *folder)
{
CamelEwsStoreSummary *ews_summary = store->summary;
const EwsFolderId *pfid, *fid;
const gchar *dname;
gint64 unread, total;
EEwsFolderType ftype;
fid = e_ews_folder_get_id (folder);
pfid = e_ews_folder_get_parent_id (folder);
dname = e_ews_folder_get_escaped_name (folder);
total = e_ews_folder_get_total_count (folder);
unread = e_ews_folder_get_unread_count (folder);
ftype = e_ews_folder_get_folder_type (folder);
camel_ews_store_summary_new_folder (
ews_summary, fid->id,
pfid ? pfid->id : NULL, fid->change_key,
dname, ftype, 0, total,
e_ews_folder_get_foreign (folder),
FALSE);
camel_ews_store_summary_set_folder_unread (
ews_summary, fid->id, unread);
}
static void
sync_created_folders (CamelEwsStore *ews_store,
GSList *created_folders,
GSList **created_folder_ids)
{
GSList *l;
for (l = created_folders; l != NULL; l = g_slist_next (l)) {
EEwsFolder *folder = (EEwsFolder *) l->data;
EEwsFolderType ftype;
CamelFolderInfo *fi;
const EwsFolderId *fid;
ftype = e_ews_folder_get_folder_type (folder);
if (ftype != E_EWS_FOLDER_TYPE_MAILBOX)
continue;
fid = e_ews_folder_get_id (folder);
/* FIXME: Sort folders so that a child is always added *after*
* its parent. But since the old code was already completely
* broken and would just go into an endless loop if the server
* didn't return the folders in the 'right' order for that,
* let's worry about that in a later commit. */
add_folder_to_summary (ews_store, folder);
if (created_folder_ids) {
*created_folder_ids = g_slist_append (*created_folder_ids, g_strdup (fid->id));
} else {
fi = camel_ews_utils_build_folder_info (
ews_store, fid->id);
camel_store_folder_created (
CAMEL_STORE (ews_store), fi);
camel_subscribable_folder_subscribed (
CAMEL_SUBSCRIBABLE (ews_store), fi);
camel_folder_info_free (fi);
}
}
}
void
ews_utils_sync_folders (CamelEwsStore *ews_store,
GSList *created_folders,
GSList *deleted_folders,
GSList *updated_folders,
GSList **created_folder_ids)
{
GError *error = NULL;
sync_deleted_folders (ews_store, deleted_folders);
sync_updated_folders (ews_store, updated_folders);
sync_created_folders (ews_store, created_folders, created_folder_ids);
camel_ews_store_summary_save (ews_store->summary, &error);
if (error != NULL) {
g_print (
"Error while saving store summary %s \n",
error->message);
g_clear_error (&error);
}
}
void
camel_ews_utils_sync_deleted_items (CamelEwsFolder *ews_folder,
GSList *items_deleted,
CamelFolderChangeInfo *change_info)
{
CamelStore *store;
CamelFolder *folder;
const gchar *full_name;
CamelEwsStore *ews_store;
GSList *l;
GList *items_deleted_list = NULL;
folder = CAMEL_FOLDER (ews_folder);
full_name = camel_folder_get_full_name (folder);
store = camel_folder_get_parent_store (folder);
ews_store = CAMEL_EWS_STORE (store);
for (l = items_deleted; l != NULL; l = g_slist_next (l)) {
const gchar *id = l->data;
items_deleted_list = g_list_prepend (
items_deleted_list, (gpointer) id);
camel_folder_summary_remove_uid (camel_folder_get_folder_summary (folder), id);
camel_folder_change_info_remove_uid (change_info, id);
}
items_deleted_list = g_list_reverse (items_deleted_list);
camel_db_delete_uids (
camel_store_get_db (CAMEL_STORE (ews_store)),
full_name, items_deleted_list, NULL);
g_list_free (items_deleted_list);
g_slist_foreach (items_deleted, (GFunc) g_free, NULL);
g_slist_free (items_deleted);
}
static const gchar *
ews_utils_rename_label (const gchar *cat,
gboolean from_cat)
{
gint i;
/* this is a mapping from Exchange/Outlook categories to
* evolution labels based on the standard colours */
const gchar *labels[] = {
"Red Category", "$Labelimportant",
"Orange Category", "$Labelwork",
"Green Category", "$Labelpersonal",
"Blue Category", "$Labeltodo",
"Purple Category", "$Labellater",
NULL, NULL
};
if (!cat || !*cat)
return "";
for (i = 0; labels[i]; i += 2) {
if (from_cat) {
if (!g_ascii_strcasecmp (cat, labels[i]))
return labels[i + 1];
} else {
if (!g_ascii_strcasecmp (cat, labels[i + 1]))
return labels[i];
}
}
return cat;
}
static gboolean
ews_utils_is_system_user_flag (const gchar *name)
{
if (!name)
return FALSE;
return g_str_equal (name, "receipt-handled") ||
g_str_equal (name, "$has-cal");
}
/* free with g_slist_free_full (flags, g_free);
the lists' members are values for the String xml element. */
GSList *
ews_utils_gather_server_user_flags (ESoapMessage *msg,
CamelMessageInfo *mi)
{
GSList *out_user_flags = NULL;
const CamelNamedFlags *user_flags;
guint ii, len;
camel_message_info_property_lock (mi);
user_flags = camel_message_info_get_user_flags (mi);
len = camel_named_flags_get_length (user_flags);
/* transfer camel flags to become the categories as an XML
* array of strings */
for (ii = 0; ii < len; ii++) {
const gchar *n = ews_utils_rename_label (camel_named_flags_get (user_flags, ii), FALSE);
if (*n == '\0')
continue;
/* Skip evolution-defined flags which are not supposed to
be categories on an Exchange server */
if (ews_utils_is_system_user_flag (n))
continue;
if (strchr (n, '_')) {
GString *str = g_string_sized_new (strlen (n));
while (*n) {
if (*n == '_') {
if (n[1] == '_')
g_string_append_c (str, '_');
else
g_string_append_c (str, ' ');
} else {
g_string_append_c (str, *n);
}
n++;
}
out_user_flags = g_slist_prepend (out_user_flags, g_string_free (str, FALSE));
} else {
out_user_flags = g_slist_prepend (out_user_flags, g_strdup (n));
}
}
camel_message_info_property_unlock (mi);
return g_slist_reverse (out_user_flags);
}
static void
ews_utils_merge_server_user_flags (EEwsItem *item,
CamelMessageInfo *mi)
{
CamelFolderSummary *summary;
GSList *list = NULL;
const GSList *p;
const CamelNamedFlags *user_flags;
guint ii, len;
summary = camel_message_info_ref_summary (mi);
if (summary)
camel_folder_summary_lock (summary);
camel_message_info_property_lock (mi);
camel_message_info_freeze_notifications (mi);
user_flags = camel_message_info_get_user_flags (mi);
len = camel_named_flags_get_length (user_flags);
/* transfer camel flags to a list */
for (ii = 0; ii < len; ii++) {
const gchar *name = camel_named_flags_get (user_flags, ii);
if (!ews_utils_is_system_user_flag (name))
list = g_slist_prepend (list, (gchar *) name);
}
for (p = list; p; p = p->next) {
/* remove custom user flags */
camel_message_info_set_user_flag (mi, p->data, FALSE);
}
g_slist_free (list);
/* now transfer over all the categories */
for (p = e_ews_item_get_categories (item); p; p = p->next) {
const gchar *flag = ews_utils_rename_label (p->data, 1);
gchar *underscored = NULL;
if (!flag || !*flag)
continue;
if (strchr (flag, ' ')) {
GString *str;
str = g_string_sized_new (strlen (flag) + 16);
while (*flag) {
if (*flag == '_')
g_string_append_c (str, '_');
g_string_append_c (str, *flag == ' ' ? '_' : *flag);
flag++;
}
underscored = g_string_free (str, FALSE);
flag = underscored;
}
camel_message_info_set_user_flag (mi, flag, TRUE);
g_free (underscored);
}
camel_message_info_thaw_notifications (mi);
camel_message_info_property_unlock (mi);
if (summary)
camel_folder_summary_unlock (summary);
g_clear_object (&summary);
}
static guint32
ews_utils_get_server_flags (EEwsItem *item)
{
gboolean flag;
EwsImportance importance;
guint32 server_flags = 0, msg_flags;
e_ews_item_is_read (item, &flag);
if (flag)
server_flags |= CAMEL_MESSAGE_SEEN;
else
server_flags &= ~CAMEL_MESSAGE_SEEN;
e_ews_item_is_forwarded (item, &flag);
if (flag)
server_flags |= CAMEL_MESSAGE_FORWARDED;
else
server_flags &= ~CAMEL_MESSAGE_FORWARDED;
e_ews_item_is_answered (item, &flag);
if (flag)
server_flags |= CAMEL_MESSAGE_ANSWERED;
else
server_flags &= ~CAMEL_MESSAGE_ANSWERED;
importance = e_ews_item_get_importance (item);
if (importance == EWS_ITEM_HIGH)
server_flags |= CAMEL_MESSAGE_FLAGGED;
msg_flags = e_ews_item_get_message_flags (item);
if ((msg_flags & EWS_MAPI_MSGFLAG_RN_PENDING) != 0)
server_flags |= CAMEL_EWS_MESSAGE_MSGFLAG_RN_PENDING;
/* TODO Update replied flags */
return server_flags;
}
static gchar *
form_email_string_from_mb (EEwsConnection *cnc,
const EwsMailbox *mb,
GCancellable *cancellable)
{
if (mb) {
GString *str;
const gchar *email = NULL;
if (g_strcmp0 (mb->routing_type, "EX") == 0)
email = e_ews_item_util_strip_ex_address (mb->email);
str = g_string_new ("");
if (mb->name && mb->name[0]) {
g_string_append (str, mb->name);
g_string_append (str, " ");
}
if (mb->email || email) {
g_string_append (str, "<");
g_string_append (str, email ? email : mb->email);
g_string_append (str, ">");
}
return g_string_free (str, FALSE);
} else
return NULL;
}
static gchar *
form_recipient_list (EEwsConnection *cnc,
const GSList *recipients,
GCancellable *cancellable)
{
const GSList *l;
GString *str = NULL;
if (!recipients)
return NULL;
for (l = recipients; l != NULL; l = g_slist_next (l)) {
EwsMailbox *mb = (EwsMailbox *) l->data;
gchar *mb_str = form_email_string_from_mb (cnc, mb, cancellable);
if (!str)
str = g_string_new ("");
else
str = g_string_append (str, ", ");
str = g_string_append (str, mb_str);
g_free (mb_str);
}
return g_string_free (str, FALSE);
}
static guint8 *
get_md5_digest (const guchar *str)
{
guint8 *digest;
gsize length;
GChecksum *checksum;
length = g_checksum_type_get_length (G_CHECKSUM_MD5);
digest = g_malloc0 (length);
checksum = g_checksum_new (G_CHECKSUM_MD5);
g_checksum_update (checksum, str, -1);
g_checksum_get_digest (checksum, digest, &length);
g_checksum_free (checksum);
return digest;
}
static void
ews_set_threading_data (CamelMessageInfo *mi,
EEwsItem *item)
{
const gchar *references_str, *inreplyto_str;
const gchar *message_id;
GSList *refs, *irt, *link;
guint8 *digest;
gchar *msgid;
CamelSummaryMessageID tmp_msgid;
GArray *references;
/* set message id */
message_id = e_ews_item_get_msg_id (item);
msgid = camel_header_msgid_decode (message_id);
if (msgid) {
digest = get_md5_digest ((const guchar *) msgid);
memcpy (tmp_msgid.id.hash, digest, sizeof (tmp_msgid.id.hash));
g_free (digest);
g_free (msgid);
camel_message_info_set_message_id (mi, tmp_msgid.id.id);
}
/* Process References: header */
references_str = e_ews_item_get_references (item);
refs = camel_header_references_decode (references_str);
/* Prepend In-Reply-To: contents to References: for summary info */
inreplyto_str = e_ews_item_get_in_replyto (item);
irt = camel_header_references_decode (inreplyto_str);
if (irt) {
refs = g_slist_concat (irt, refs);
}
if (!refs)
return;
references = g_array_sized_new (FALSE, FALSE, sizeof (guint64), g_slist_length (refs));
for (link = refs; link; link = g_slist_next (link)) {
digest = get_md5_digest ((const guchar *) link->data);
memcpy (tmp_msgid.id.hash, digest, sizeof (tmp_msgid.id.hash));
g_free (digest);
g_array_append_val (references, tmp_msgid.id.id);
}
g_slist_free_full (refs, g_free);
camel_message_info_take_references (mi, references);
}
static gboolean
camel_ews_utils_update_follow_up_flags (EEwsItem *item,
CamelMessageInfo *info)
{
gboolean changed = FALSE, found;
time_t completed_tt, dueby_tt;
const gchar *followup_name;
gint flag_status;
/* PidTagFlagStatus */
found = FALSE;
flag_status = e_ews_item_get_extended_property_as_int (item, NULL, 0x1090, &found);
if (!found)
flag_status = 0;
/* PidTagFlagCompleteTime */
found = FALSE;
completed_tt = e_ews_item_get_extended_property_as_time (item, NULL, 0x1091, &found);
if (!found)
completed_tt = (time_t) 0;
/* PidLidFlagRequest */
found = FALSE;
followup_name = e_ews_item_get_extended_property_as_string (item, "Common", 0x8530, &found);
if (!found)
followup_name = NULL;
/* PidLidTaskDueDate */
found = FALSE;
dueby_tt = e_ews_item_get_extended_property_as_time (item, "Task", 0x8105, &found);
if (!found)
dueby_tt = (time_t) 0;
if (flag_status == 1) {
/* complete */
if (!camel_message_info_get_user_tag (info, "follow-up"))
changed = camel_message_info_set_user_tag (info, "follow-up", followup_name ? followup_name : "follow-up") || changed;
if (completed_tt != (time_t) 0) {
gchar *text = camel_header_format_date (completed_tt, 0);
changed = camel_message_info_set_user_tag (info, "completed-on", text) || changed;
g_free (text);
} else {
changed = camel_message_info_set_user_tag (info, "completed-on", NULL) || changed;
}
} else if (flag_status == 2) {
/* follow-up */
changed = camel_message_info_set_user_tag (info, "follow-up", followup_name ? followup_name : "follow-up") || changed;
changed = camel_message_info_set_user_tag (info, "completed-on", NULL) || changed;
if (dueby_tt != (time_t) 0) {
gchar *text = camel_header_format_date (dueby_tt, 0);
changed = camel_message_info_set_user_tag (info, "due-by", text) || changed;
g_free (text);
} else {
changed = camel_message_info_set_user_tag (info, "due-by", NULL) || changed;
}
} else {
changed = camel_message_info_set_user_tag (info, "follow-up", NULL) || changed;
changed = camel_message_info_set_user_tag (info, "completed-on", NULL) || changed;
changed = camel_message_info_set_user_tag (info, "due-by", NULL) || changed;
}
return changed;
}
static gboolean
camel_ews_utils_update_read_receipt_flags (EEwsItem *item,
CamelMessageInfo *info,
guint32 server_flags,
gboolean requests_read_receipt)
{
gboolean changed = FALSE;
/* PidTagReadReceiptRequested */
if ((requests_read_receipt || e_ews_item_get_extended_property_as_boolean (item, NULL, 0x0029, NULL)) &&
(server_flags & CAMEL_EWS_MESSAGE_MSGFLAG_RN_PENDING) == 0) {
changed = camel_message_info_set_user_flag (info, "receipt-handled", TRUE) || changed;
}
return changed;
}
void
camel_ews_utils_sync_updated_items (CamelEwsFolder *ews_folder,
GSList *items_updated,
CamelFolderChangeInfo *change_info)
{
CamelFolder *folder;
CamelFolderSummary *folder_summary;
GSList *l;
folder = CAMEL_FOLDER (ews_folder);
folder_summary = camel_folder_get_folder_summary (folder);
for (l = items_updated; l != NULL; l = g_slist_next (l)) {
EEwsItem *item = (EEwsItem *) l->data;
const EwsId *id;
CamelMessageInfo *mi;
if (e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_ERROR) {
g_object_unref (item);
continue;
}
id = e_ews_item_get_id (item);
if (!id) {
g_warning ("%s: Missing ItemId for item type %d (subject:%s)", G_STRFUNC, e_ews_item_get_item_type (item),
e_ews_item_get_subject (item) ? e_ews_item_get_subject (item) : "???");
g_object_unref (item);
continue;
}
mi = camel_folder_summary_get (folder_summary, id->id);
if (mi) {
guint32 server_flags;
gboolean changed, was_changed;
camel_message_info_freeze_notifications (mi);
was_changed = camel_message_info_get_folder_flagged (mi);
server_flags = ews_utils_get_server_flags (item);
ews_utils_merge_server_user_flags (item, mi);
changed = camel_ews_update_message_info_flags (folder_summary, mi, server_flags, NULL);
changed = camel_ews_utils_update_follow_up_flags (item, mi) || changed;
changed = camel_ews_utils_update_read_receipt_flags (item, mi, server_flags, FALSE) || changed;
if (changed)
camel_folder_change_info_change_uid (change_info, id->id);
camel_ews_message_info_set_change_key (CAMEL_EWS_MESSAGE_INFO (mi), id->change_key);
if (!was_changed) {
/* do not save to the server what was just read, when did not change locally before */
camel_message_info_set_folder_flagged (mi, FALSE);
}
camel_message_info_thaw_notifications (mi);
g_clear_object (&mi);
g_object_unref (item);
continue;
}
g_object_unref (item);
}
g_slist_free (items_updated);
}
CamelMessageInfo * /* (transfer full) */
camel_ews_utils_item_to_message_info (CamelEwsFolder *ews_folder,
EEwsConnection *cnc,
EEwsItem *item,
GCancellable *cancellable)
{
CamelFolderSummary *folder_summary;
CamelMessageInfo *mi = NULL;
const EwsId *id;
const EwsMailbox *from;
gchar *tmp;
EEwsItemType item_type;
const gchar *msg_headers;
gboolean has_attachments, found_property, message_requests_read_receipt = FALSE;
guint32 server_flags;
g_return_val_if_fail (CAMEL_IS_EWS_FOLDER (ews_folder), NULL);
if (!item || e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_ERROR)
return NULL;
id = e_ews_item_get_id (item);
if (!id)
return NULL;
folder_summary = camel_folder_get_folder_summary (CAMEL_FOLDER (ews_folder));
/* PidTagTransportMessageHeaders */
found_property = FALSE;
msg_headers = e_ews_item_get_extended_property_as_string (item, NULL, 0x007D, &found_property);
if (!found_property)
msg_headers = NULL;
if (msg_headers && *msg_headers) {
CamelMimePart *part = camel_mime_part_new ();
CamelStream *stream;
CamelMimeParser *parser;
stream = camel_stream_mem_new_with_buffer (msg_headers, strlen (msg_headers));
parser = camel_mime_parser_new ();
camel_mime_parser_init_with_stream (parser, stream, NULL);
camel_mime_parser_scan_from (parser, FALSE);
g_object_unref (stream);
if (camel_mime_part_construct_from_parser_sync (part, parser, NULL, NULL)) {
mi = camel_folder_summary_info_new_from_headers (folder_summary, camel_medium_get_headers (CAMEL_MEDIUM (part)));
if (camel_medium_get_header (CAMEL_MEDIUM (part), "Disposition-Notification-To"))
message_requests_read_receipt = TRUE;
}
g_object_unref (parser);
g_object_unref (part);
}
if (!mi)
mi = camel_message_info_new (folder_summary);
camel_message_info_set_abort_notifications (mi, TRUE);
item_type = e_ews_item_get_item_type (item);
if (item_type == E_EWS_ITEM_TYPE_EVENT ||
item_type == E_EWS_ITEM_TYPE_MEETING_MESSAGE ||
item_type == E_EWS_ITEM_TYPE_MEETING_REQUEST ||
item_type == E_EWS_ITEM_TYPE_MEETING_RESPONSE ||
item_type == E_EWS_ITEM_TYPE_MEETING_RESPONSE)
camel_message_info_set_user_flag (mi, "$has_cal", TRUE);
camel_message_info_set_uid (mi, id->id);
camel_message_info_set_size (mi, e_ews_item_get_size (item));
camel_message_info_set_subject (mi, e_ews_item_get_subject (item));
camel_ews_message_info_set_item_type (CAMEL_EWS_MESSAGE_INFO (mi), item_type);
camel_ews_message_info_set_change_key (CAMEL_EWS_MESSAGE_INFO (mi), id->change_key);
camel_message_info_set_date_sent (mi, e_ews_item_get_date_sent (item));
camel_message_info_set_date_received (mi, e_ews_item_get_date_received (item));
from = e_ews_item_get_from (item);
if (!from)
from = e_ews_item_get_sender (item);
tmp = form_email_string_from_mb (cnc, from, cancellable);
camel_message_info_set_from (mi, tmp);
g_free (tmp);
tmp = form_recipient_list (cnc, e_ews_item_get_to_recipients (item), cancellable);
camel_message_info_set_to (mi, tmp);
g_free (tmp);
tmp = form_recipient_list (cnc, e_ews_item_get_cc_recipients (item), cancellable);
camel_message_info_set_cc (mi, tmp);
g_free (tmp);
e_ews_item_has_attachments (item, &has_attachments);
if (has_attachments)
camel_message_info_set_flags (mi, CAMEL_MESSAGE_ATTACHMENTS, CAMEL_MESSAGE_ATTACHMENTS);
ews_set_threading_data (mi, item);
server_flags = ews_utils_get_server_flags (item);
ews_utils_merge_server_user_flags (item, mi);
camel_message_info_set_flags (mi, server_flags, server_flags);
camel_ews_message_info_set_server_flags (CAMEL_EWS_MESSAGE_INFO (mi), server_flags);
camel_ews_utils_update_follow_up_flags (item, mi);
camel_ews_utils_update_read_receipt_flags (item, mi, server_flags, message_requests_read_receipt);
camel_message_info_set_abort_notifications (mi, FALSE);
return mi;
}
void
camel_ews_utils_sync_created_items (CamelEwsFolder *ews_folder,
EEwsConnection *cnc,
GSList *items_created,
CamelFolderChangeInfo *change_info,
GCancellable *cancellable)
{
CamelFolder *folder;
CamelFolderSummary *folder_summary;
GSList *l;
if (!items_created)
return;
folder = CAMEL_FOLDER (ews_folder);
folder_summary = camel_folder_get_folder_summary (folder);
for (l = items_created; l != NULL; l = g_slist_next (l)) {
EEwsItem *item = (EEwsItem *) l->data;
CamelMessageInfo *mi;
const EwsId *id;
if (!item)
continue;
if (e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_ERROR) {
g_object_unref (item);
continue;
}
id = e_ews_item_get_id (item);
if (!id) {
g_warning ("%s: Missing ItemId for item type %d (subject:%s)", G_STRFUNC, e_ews_item_get_item_type (item),
e_ews_item_get_subject (item) ? e_ews_item_get_subject (item) : "???");
g_object_unref (item);
continue;
}
mi = camel_folder_summary_get (folder_summary, id->id);
if (mi) {
g_clear_object (&mi);
g_object_unref (item);
continue;
}
mi = camel_ews_utils_item_to_message_info (ews_folder, cnc, item, cancellable);
if (!mi) {
g_warn_if_reached ();
g_object_unref (item);
continue;
}
camel_folder_summary_add (folder_summary, mi, FALSE);
/* camel_folder_summary_add() sets folder_flagged flag
* on the message info, but this is a fresh item downloaded
* from the server, thus unset it, to avoid resync up to the server
* on folder leave/store
*/
camel_message_info_set_folder_flagged (mi, FALSE);
camel_folder_change_info_add_uid (change_info, id->id);
camel_folder_change_info_recent_uid (change_info, id->id);
g_object_unref (mi);
g_object_unref (item);
}
g_slist_free (items_created);
}
gchar *
camel_ews_utils_get_host_name (CamelSettings *settings)
{
CamelURL *url;
gchar *host = NULL, *hosturl;
g_return_val_if_fail (settings != NULL, NULL);
hosturl = camel_ews_settings_dup_hosturl (CAMEL_EWS_SETTINGS (settings));
url = camel_url_new (hosturl, NULL);
if (url) {
host = g_strdup (url->host);
camel_url_free (url);
}
if (!host || !*host) {
g_free (host);
host = camel_network_settings_dup_host (CAMEL_NETWORK_SETTINGS (settings));
}
g_free (hosturl);
return host;
}
void
ews_utils_update_followup_flags (ESoapMessage *msg,
CamelMessageInfo *mi)
{
const gchar *followup, *completed, *dueby;
time_t completed_tt = (time_t) 0 , dueby_tt = (time_t) 0;
g_return_if_fail (msg != NULL);
g_return_if_fail (mi != NULL);
followup = camel_message_info_get_user_tag (mi, "follow-up");
completed = camel_message_info_get_user_tag (mi, "completed-on");
dueby = camel_message_info_get_user_tag (mi, "due-by");
if (followup && !*followup)
followup = NULL;
if (completed && *completed)
completed_tt = camel_header_decode_date (completed, NULL);
if (dueby && *dueby)
dueby_tt = camel_header_decode_date (dueby, NULL);
if (followup) {
time_t now_tt = time (NULL);
/* PidTagFlagStatus */
e_ews_message_add_set_item_field_extended_tag_int (msg, NULL, "Message", 0x1090,
completed_tt != (time_t) 0 ? 0x01 /* followupComplete */: 0x02 /* followupFlagged */);
/* PidLidFlagRequest */
e_ews_message_add_set_item_field_extended_distinguished_tag_string (msg, NULL, "Message", "Common", 0x8530, followup);
/* PidTagToDoItemFlags */
e_ews_message_add_set_item_field_extended_tag_int (msg, NULL, "Message", 0x0e2b, 1);
if (completed_tt == (time_t) 0 && dueby_tt == (time_t) 0) {
/* PidLidTaskStatus */
e_ews_message_add_set_item_field_extended_distinguished_tag_int (msg, NULL, "Message", "Task", 0x8101, 0);
/* PidLidPercentComplete */
e_ews_message_add_set_item_field_extended_distinguished_tag_double (msg, NULL, "Message", "Task", 0x8102, 0.0);
/* PidLidTaskStartDate */
e_ews_message_add_set_item_field_extended_distinguished_tag_time (msg, NULL, "Message", "Task", 0x8104, now_tt);
/* PidLidTaskDueDate */
e_ews_message_add_set_item_field_extended_distinguished_tag_time (msg, NULL, "Message", "Task", 0x8105, now_tt);
/* PidLidTaskComplete */
e_ews_message_add_set_item_field_extended_distinguished_tag_boolean (msg, NULL, "Message", "Task", 0x811c, FALSE);
}
} else {
/* PidTagFlagStatus */
e_ews_message_add_delete_item_field_extended_tag (msg, 0x1090, E_EWS_MESSAGE_DATA_TYPE_INT);
/* PidTagFlagCompleteTime */
e_ews_message_add_delete_item_field_extended_tag (msg, 0x1091, E_EWS_MESSAGE_DATA_TYPE_TIME);
/* PidTagToDoItemFlags */
e_ews_message_add_delete_item_field_extended_tag (msg, 0x0e2b, E_EWS_MESSAGE_DATA_TYPE_INT);
/* PidTagFollowupIcon */
e_ews_message_add_delete_item_field_extended_tag (msg, 0x1095, E_EWS_MESSAGE_DATA_TYPE_INT);
/* PidLidFlagRequest */
e_ews_message_add_delete_item_field_extended_distinguished_tag (msg, "Common", 0x8530, E_EWS_MESSAGE_DATA_TYPE_STRING);
/* PidLidFlagString */
e_ews_message_add_delete_item_field_extended_distinguished_tag (msg, "Common", 0x85c0, E_EWS_MESSAGE_DATA_TYPE_INT);
/* PidLidTaskStatus */
e_ews_message_add_delete_item_field_extended_distinguished_tag (msg, "Task", 0x8101, E_EWS_MESSAGE_DATA_TYPE_INT);
/* PidLidPercentComplete */
e_ews_message_add_delete_item_field_extended_distinguished_tag (msg, "Task", 0x8102, E_EWS_MESSAGE_DATA_TYPE_DOUBLE);
/* PidLidTaskStartDate */
e_ews_message_add_delete_item_field_extended_distinguished_tag (msg, "Task", 0x8104, E_EWS_MESSAGE_DATA_TYPE_TIME);
/* PidLidTaskDueDate */
e_ews_message_add_delete_item_field_extended_distinguished_tag (msg, "Task", 0x8105, E_EWS_MESSAGE_DATA_TYPE_TIME);
/* PidLidTaskDateCompleted */
e_ews_message_add_delete_item_field_extended_distinguished_tag (msg, "Task", 0x810f, E_EWS_MESSAGE_DATA_TYPE_TIME);
/* PidLidTaskComplete */
e_ews_message_add_delete_item_field_extended_distinguished_tag (msg, "Task", 0x811c, E_EWS_MESSAGE_DATA_TYPE_BOOLEAN);
}
if (followup && completed_tt != (time_t) 0) {
/* minute precision */
completed_tt = completed_tt - (completed_tt % 60);
/* PidTagFlagCompleteTime */
e_ews_message_add_set_item_field_extended_tag_time (msg, NULL, "Message", 0x1091, completed_tt);
/* PidTagFollowupIcon */
e_ews_message_add_delete_item_field_extended_tag (msg, 0x1095, E_EWS_MESSAGE_DATA_TYPE_INT);
/* PidLidTaskDateCompleted */
e_ews_message_add_set_item_field_extended_distinguished_tag_time (msg, NULL, "Message", "Task", 0x810f, completed_tt);
/* PidLidTaskStatus */
e_ews_message_add_set_item_field_extended_distinguished_tag_int (msg, NULL, "Message", "Task", 0x8101, 2);
/* PidLidPercentComplete */
e_ews_message_add_set_item_field_extended_distinguished_tag_double (msg, NULL, "Message", "Task", 0x8102, 1.0);
/* PidLidTaskComplete */
e_ews_message_add_set_item_field_extended_distinguished_tag_boolean (msg, NULL, "Message", "Task", 0x811c, TRUE);
}
if (followup && dueby_tt != (time_t) 0 && completed_tt == (time_t) 0) {
time_t now_tt = time (NULL);
if (now_tt > dueby_tt)
now_tt = dueby_tt - 1;
/* PidLidTaskStatus */
e_ews_message_add_set_item_field_extended_distinguished_tag_int (msg, NULL, "Message", "Task", 0x8101, 0);
/* PidLidPercentComplete */
e_ews_message_add_set_item_field_extended_distinguished_tag_double (msg, NULL, "Message", "Task", 0x8102, 0.0);
/* PidLidTaskStartDate */
e_ews_message_add_set_item_field_extended_distinguished_tag_time (msg, NULL, "Message", "Task", 0x8104, now_tt);
/* PidLidTaskDueDate */
e_ews_message_add_set_item_field_extended_distinguished_tag_time (msg, NULL, "Message", "Task", 0x8105, dueby_tt);
/* PidLidTaskComplete */
e_ews_message_add_set_item_field_extended_distinguished_tag_boolean (msg, NULL, "Message", "Task", 0x811c, FALSE);
}
}
gboolean
camel_ews_utils_delete_folders_from_summary_recursive (CamelEwsStore *ews_store,
CamelFolderInfo *folder_info,
gboolean send_signals,
GError **error)
{
gboolean success = TRUE;
while (folder_info != NULL) {
gchar *fid;
if (folder_info->child != NULL) {
success = camel_ews_utils_delete_folders_from_summary_recursive (
ews_store, folder_info->child, send_signals, error);
if (!success)
break;
}
fid = camel_ews_store_summary_get_folder_id_from_name (ews_store->summary, folder_info->full_name);
success = camel_ews_store_summary_remove_folder (ews_store->summary, fid, error);
g_free (fid);
if (!success)
break;
if (send_signals) {
camel_subscribable_folder_unsubscribed (CAMEL_SUBSCRIBABLE (ews_store), folder_info);
camel_store_folder_deleted (CAMEL_STORE (ews_store), folder_info);
}
folder_info = folder_info->next;
}
return success;
}
/* Unref with g_object_unref() when done with it */
ESource *
camel_ews_utils_ref_corresponding_source (CamelService *service,
GCancellable *cancellable)
{
ESourceRegistry *registry = NULL;
CamelSession *session;
ESource *source = NULL;
g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
session = camel_service_ref_session (service);
if (E_IS_MAIL_SESSION (session)) {
registry = e_mail_session_get_registry (E_MAIL_SESSION (session));
if (registry)
g_object_ref (registry);
}
g_clear_object (&session);
if (!registry)
registry = e_source_registry_new_sync (cancellable, NULL);
if (registry) {
source = e_source_registry_ref_source (registry, camel_service_get_uid (service));
while (source && e_source_get_parent (source) &&
!e_source_has_extension (source, E_SOURCE_EXTENSION_COLLECTION)) {
ESource *parent;
parent = e_source_registry_ref_source (registry, e_source_get_parent (source));
if (!parent)
break;
g_clear_object (&source);
source = parent;
}
}
g_clear_object (®istry);
return source;
}