[evolution-mapi] Fast-transfer for calendars with fallback to slow fetch
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-mapi] Fast-transfer for calendars with fallback to slow fetch
- Date: Fri, 18 Nov 2011 17:06:59 +0000 (UTC)
commit 00bc632874d5b67ed3f63d3d458ebcce6ffda447
Author: Milan Crha <mcrha redhat com>
Date: Fri Nov 18 18:06:12 2011 +0100
Fast-transfer for calendars with fallback to slow fetch
src/calendar/e-cal-backend-mapi.c | 965 ++++++++++++-------------------
src/camel/camel-mapi-folder.c | 39 +--
src/libexchangemapi/e-mapi-cal-utils.c | 6 +-
src/libexchangemapi/e-mapi-connection.c | 531 +++++++++++++++++-
src/libexchangemapi/e-mapi-connection.h | 13 +-
src/libexchangemapi/e-mapi-mail-utils.c | 17 +-
src/libexchangemapi/e-mapi-utils.c | 186 ++++++
src/libexchangemapi/e-mapi-utils.h | 27 +-
8 files changed, 1125 insertions(+), 659 deletions(-)
---
diff --git a/src/calendar/e-cal-backend-mapi.c b/src/calendar/e-cal-backend-mapi.c
index 505fbed..7293eb9 100644
--- a/src/calendar/e-cal-backend-mapi.c
+++ b/src/calendar/e-cal-backend-mapi.c
@@ -55,9 +55,6 @@
#define EDC_ERROR(_code) e_data_cal_create_error (_code, NULL)
#define EDC_ERROR_EX(_code, _msg) e_data_cal_create_error (_code, _msg)
-#define SERVER_UTC_TIME "server_utc_time"
-#define CACHE_MARKER "populated"
-
G_DEFINE_TYPE (ECalBackendMAPI, e_cal_backend_mapi, E_TYPE_CAL_BACKEND)
typedef struct {
@@ -73,7 +70,7 @@ struct _ECalBackendMAPIPrivate {
mapi_id_t fid;
uint32_t olFolder;
gchar *profile;
- EMapiConnection *conn;
+ EMapiConnection *conn;
/* These fields are entirely for access rights */
gchar *owner_name;
@@ -87,7 +84,6 @@ struct _ECalBackendMAPIPrivate {
gboolean read_only;
gchar *uri;
gboolean mode_changed;
- gboolean populating_cache; /* whether in populate_cache */
GMutex *updating_mutex;
/* timeout handler for syncing sendoptions */
@@ -97,6 +93,10 @@ struct _ECalBackendMAPIPrivate {
guint timeout_id;
GThread *dthread;
SyncDelta *dlock;
+
+ time_t last_refresh;
+ gint last_obj_total;
+ GCancellable *cancellable;
};
#define CACHE_REFRESH_INTERVAL 600000
@@ -138,6 +138,23 @@ mapi_error_to_edc_error (GError **perror, const GError *mapi_error, EDataCalCall
/* **** UTILITY FUNCTIONS **** */
+static void
+get_comp_mid (icalcomponent *icalcomp, mapi_id_t *mid)
+{
+ gchar *x_mid;
+
+ g_return_if_fail (icalcomp != NULL);
+ g_return_if_fail (mid != NULL);
+
+ x_mid = e_mapi_cal_utils_get_icomp_x_prop (icalcomp, "X-EVOLUTION-MAPI-MID");
+ if (x_mid) {
+ e_mapi_util_mapi_id_from_string (x_mid, mid);
+ g_free (x_mid);
+ } else {
+ e_mapi_util_mapi_id_from_string (icalcomponent_get_uid (icalcomp), mid);
+ }
+}
+
static const gchar *
ecbm_get_owner_name (ECalBackendMAPI *cbmapi)
{
@@ -333,12 +350,16 @@ view_progress_cb (EDataCalView *view, gpointer user_data)
}
static void
-notify_view_progress (ECalBackendMAPI *cbmapi, guint64 index, guint64 total)
+notify_view_progress (ECalBackendMAPI *cbmapi, guint index, guint total)
{
struct EMAPIProgressData pd = { 0 };
gchar *progress_string;
- pd.percent = ((gfloat) index/total) * 100;
+ if (total > 0)
+ pd.percent = index * 100 / total;
+ else
+ pd.percent = -1;
+
if (pd.percent > 100)
pd.percent = 99;
@@ -399,457 +420,403 @@ put_component_to_store (ECalBackendMAPI *cbmapi,
e_cal_backend_store_put_component_with_time_range (priv->store, comp, time_start, time_end);
}
-static gboolean
-mapi_cal_get_changes_cb (FetchItemsCallbackData *item_data,
- gpointer data,
- GCancellable *cancellable,
- GError **perror)
-{
- struct mapi_SPropValue_array *array = item_data->properties;
- const mapi_id_t mid = item_data->mid;
- GSList *streams = item_data->streams;
- GSList *recipients = item_data->recipients;
- GSList *attachments = item_data->attachments;
- GSList *detached = NULL, *d_i = NULL;
- ECalBackendMAPI *cbmapi = data;
- ECalBackendMAPIPrivate *priv = cbmapi->priv;
- icalcomponent_kind kind;
- gchar *tmp = NULL;
- ECalComponent *cache_comp = NULL;
- const gchar *cache_dir;
- const bool *recurring;
-
- kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbmapi));
- cache_dir = e_cal_backend_get_cache_dir (E_CAL_BACKEND (cbmapi));
-
- recurring = e_mapi_util_find_array_namedid (array, item_data->conn, item_data->fid, PidLidTaskFRecurring);
- if (recurring && *recurring) {
- g_warning ("Encountered a recurring task.");
- e_mapi_util_free_stream_list (&streams);
- e_mapi_util_free_recipient_list (&recipients);
- e_mapi_util_free_attachment_list (&attachments);
- return TRUE;
- }
-
- tmp = e_mapi_util_mapi_id_to_string (mid);
- cache_comp = e_cal_backend_store_get_component (priv->store, tmp, NULL);
-
- if (cache_comp == NULL) {
- ECalComponent *comp = e_mapi_cal_util_mapi_props_to_comp (item_data->conn, item_data->fid, kind, tmp, array,
- streams, recipients, attachments,
- cache_dir, icaltimezone_get_utc_timezone (), FALSE, &detached);
-
- detached = g_slist_prepend (detached, comp);
+#define notify_error(_mmapi_backend, _merror, _mformat) \
+ G_STMT_START { \
+ notify_error_ex (_mmapi_backend, \
+ &(_merror), \
+ _mformat, \
+ (_merror) ? (_merror)->message : _("Unknown error")); \
+ } G_STMT_END
- for (d_i = detached; d_i; d_i = g_slist_next (d_i)) {
- comp = d_i->data;
+static void notify_error_ex (ECalBackendMAPI *mapi_backend, GError **perror, const gchar *format, ...) G_GNUC_PRINTF (3, 4);
- if (E_IS_CAL_COMPONENT (comp)) {
- gchar *comp_str;
+static void
+notify_error_ex (ECalBackendMAPI *mapi_backend, GError **perror, const gchar *format, ...)
+{
+ gchar *msg;
+ va_list args;
- e_cal_component_commit_sequence (comp);
- put_component_to_store (cbmapi, comp);
+ g_return_if_fail (mapi_backend != NULL);
+ g_return_if_fail (format != NULL);
- comp_str = e_cal_component_get_as_string (comp);
- e_cal_backend_notify_object_created (E_CAL_BACKEND (cbmapi), comp_str);
- g_free (comp_str);
- }
+ if (perror && (
+ g_error_matches (*perror, G_IO_ERROR, G_IO_ERROR_CANCELLED) ||
+ g_error_matches (*perror, E_MAPI_ERROR, MAPI_E_USER_CANCEL)))
+ return;
- g_object_unref (comp);
- }
- g_slist_free (detached);
- } else {
- struct timeval t;
+ va_start (args, format);
+ msg = g_strdup_vprintf (format, args);
+ va_end (args);
- if (get_mapi_SPropValue_array_date_timeval (&t, array, PR_LAST_MODIFICATION_TIME) == MAPI_E_SUCCESS) {
- struct icaltimetype itt, *cache_comp_lm = NULL;
+ e_cal_backend_notify_error (E_CAL_BACKEND (mapi_backend), msg);
+ g_free (msg);
- itt = icaltime_from_timet_with_zone (t.tv_sec, 0, 0);
- icaltime_set_timezone (&itt, icaltimezone_get_utc_timezone ());
+ g_clear_error (perror);
+}
- e_cal_component_get_last_modified (cache_comp, &cache_comp_lm);
- if (!cache_comp_lm || (icaltime_compare (itt, *cache_comp_lm) != 0)) {
- ECalComponent *comp;
- gchar *cache_comp_str = NULL, *modif_comp_str = NULL;
+static void
+free_component_slist (gpointer ptr)
+{
+ g_slist_free_full (ptr, g_object_unref);
+}
- e_cal_component_commit_sequence (cache_comp);
- cache_comp_str = e_cal_component_get_as_string (cache_comp);
+static void
+drop_removed_comps_cb (gpointer pmid, gpointer slist, gpointer pcbmapi)
+{
+ ECalBackendMAPI *cbmapi = pcbmapi;
+ ECalBackend *backend;
+ GSList *iter;
- comp = e_mapi_cal_util_mapi_props_to_comp (item_data->conn, item_data->fid, kind, tmp, array,
- streams, recipients, attachments,
- cache_dir, icaltimezone_get_utc_timezone (), FALSE, NULL);
+ g_return_if_fail (pcbmapi != NULL);
- e_cal_component_commit_sequence (comp);
- modif_comp_str = e_cal_component_get_as_string (comp);
+ backend = E_CAL_BACKEND (cbmapi);
+ g_return_if_fail (backend != NULL);
- put_component_to_store (cbmapi, comp);
- e_cal_backend_notify_object_modified (E_CAL_BACKEND (cbmapi), cache_comp_str, modif_comp_str);
+ for (iter = slist; iter; iter = iter->next) {
+ ECalComponent *comp = iter->data;
+ ECalComponentId *id;
- g_object_unref (comp);
- g_free (cache_comp_str);
- g_free (modif_comp_str);
- }
- g_object_unref (cache_comp);
- g_free (cache_comp_lm);
+ if (!comp) {
+ g_debug ("%s: NULL component in list", G_STRFUNC);
+ continue;
}
- }
-
- g_free (tmp);
- e_mapi_util_free_stream_list (&streams);
- e_mapi_util_free_recipient_list (&recipients);
- e_mapi_util_free_attachment_list (&attachments);
-
- notify_view_progress (cbmapi, item_data->index, item_data->total);
- return TRUE;
-}
+ id = e_cal_component_get_id (comp);
+ if (!id) {
+ g_debug ("%s: Failed to get component's ID", G_STRFUNC);
+ continue;
+ }
-static void
-copy_strings_in_slist (GSList *slist)
-{
- GSList *l;
+ if (e_cal_backend_store_remove_component (cbmapi->priv->store, id->uid, id->rid)) {
+ e_cal_backend_notify_component_removed (backend, id, e_cal_component_get_icalcomponent (comp), NULL);
+ }
- for (l = slist; l; l = l->next) {
- l->data = g_strdup (l->data);
+ e_cal_component_free_id (id);
}
}
-struct deleted_items_data {
- ECalBackendMAPI *cbmapi;
- GSList *cache_ids;
- GSList *unknown_mids; /* MIDs of items not in the cache */
+struct ListCalendarObjectsData
+{
+ GSList *changed_mids; /* newly allocated mapi_id_t *; these will be fetched again */
+ GHashTable *known_comps; /* reffed ECalComponent-s from the cache; those left will be removed;
+ key is 'mapi_id_t *', the mid;
+ value is GSList of the component and its detached instances
+ */
+ time_t latest_modified;
};
static gboolean
-handle_deleted_items_cb (FetchItemsCallbackData *item_data,
- gpointer data,
- GCancellable *cancellable,
- GError **perror)
+list_calendar_objects_cb (EMapiConnection *conn,
+ mapi_id_t fid,
+ TALLOC_CTX *mem_ctx,
+ const ListObjectsData *object_data,
+ guint32 obj_index,
+ guint32 obj_total,
+ gpointer user_data,
+ GCancellable *cancellable,
+ GError **perror)
{
- const mapi_id_t mid = item_data->mid;
- struct deleted_items_data *did = data;
- gchar *tmp = NULL;
- GSList *cache_comp_uid = NULL;
- gboolean need_refetch = FALSE;
+ struct ListCalendarObjectsData *lco = user_data;
+ GSList *slist;
+ gboolean need_update = FALSE;
- g_return_val_if_fail (did != NULL, FALSE);
+ g_return_val_if_fail (object_data != NULL, FALSE);
+ g_return_val_if_fail (lco != NULL, FALSE);
- tmp = e_mapi_util_mapi_id_to_string (mid);
- cache_comp_uid = g_slist_find_custom (did->cache_ids, tmp, (GCompareFunc) (g_ascii_strcasecmp));
- if (cache_comp_uid != NULL) {
- ECalBackendMAPIPrivate *priv = did->cbmapi->priv;
- ECalComponent *comp;
+ if (object_data->msg_class &&
+ g_ascii_strcasecmp (object_data->msg_class, "IPM.Note") == 0) {
+ return TRUE;
+ }
- comp = e_cal_backend_store_get_component (priv->store, cache_comp_uid->data, NULL);
- if (comp) {
- struct icaltimetype *last_mod = NULL;
- struct timeval t;
-
- e_cal_component_get_last_modified (comp, &last_mod);
- if (!last_mod) {
- need_refetch = TRUE;
- } else if (get_mapi_SPropValue_array_date_timeval (&t, item_data->properties, PR_LAST_MODIFICATION_TIME) == MAPI_E_SUCCESS
- && icaltime_compare (icaltime_from_timet_with_zone (t.tv_sec, 0, icaltimezone_get_utc_timezone ()), *last_mod) != 0) {
- need_refetch = TRUE;
- }
+ if (lco->latest_modified < object_data->last_modified)
+ lco->latest_modified = object_data->last_modified;
- if (last_mod)
- e_cal_component_free_icaltimetype (last_mod);
+ slist = g_hash_table_lookup (lco->known_comps, &object_data->mid);
+ if (!slist) {
+ /* it's a new component on the server */
+ need_update = TRUE;
+ } else {
+ /* known component, which might change */
+ ECalComponent *comp = slist->data;
+ struct icaltimetype *last_mod = NULL;
- g_object_unref (comp);
+ /* pretty bad, but do not avoid fetching of other objects */
+ g_return_val_if_fail (comp != NULL, TRUE);
+
+ e_cal_component_get_last_modified (comp, &last_mod);
+
+ if (!last_mod ||
+ icaltime_compare (icaltime_from_timet_with_zone (object_data->last_modified, 0, icaltimezone_get_utc_timezone ()), *last_mod) != 0) {
+ need_update = TRUE;
}
- g_free (cache_comp_uid->data);
- did->cache_ids = g_slist_remove_link (did->cache_ids, cache_comp_uid);
- } else {
- /* fetch it, as it is not in the cache */
- need_refetch = TRUE;
+ if (last_mod)
+ e_cal_component_free_icaltimetype (last_mod);
+
+ g_hash_table_remove (lco->known_comps, &object_data->mid);
}
- if (need_refetch) {
- mapi_id_t *nmid = g_new (mapi_id_t, 1);
+ if (need_update) {
+ mapi_id_t *pmid;
- *nmid = mid;
- did->unknown_mids = g_slist_prepend (did->unknown_mids, nmid);
+ pmid = g_new0 (mapi_id_t, 1);
+ *pmid = object_data->mid;
+
+ lco->changed_mids = g_slist_prepend (lco->changed_mids, pmid);
}
- g_free (tmp);
return TRUE;
}
static gboolean
-mapi_cal_get_idlist (EMapiConnection *conn,
- mapi_id_t fid,
- TALLOC_CTX *mem_ctx,
- struct SPropTagArray *props,
- gpointer data,
- GCancellable *cancellable,
- GError **perror)
-{
- static const uint32_t cal_IDList[] = {
- PR_FID,
- PR_MID,
- PR_LAST_MODIFICATION_TIME
- };
-
- g_return_val_if_fail (props != NULL, FALSE);
+transfer_calendar_objects_cb (EMapiConnection *conn,
+ TALLOC_CTX *mem_ctx,
+ /* const */ EMapiObject *object,
+ guint32 obj_index,
+ guint32 obj_total,
+ gpointer user_data,
+ GCancellable *cancellable,
+ GError **perror)
+{
+ ECalBackendMAPI *cbmapi = user_data;
+ ECalBackend *backend;
+ ECalComponent *comp;
+ const mapi_id_t *pmid;
+ gchar *use_uid;
+ GSList *comps = NULL, *iter;
- return e_mapi_utils_add_props_to_props_array (mem_ctx, props, cal_IDList, G_N_ELEMENTS (cal_IDList));
-}
+ g_return_val_if_fail (cbmapi != NULL, FALSE);
+ g_return_val_if_fail (object != NULL, FALSE);
-static gboolean
-ecbm_build_last_modification_restriction (EMapiConnection *conn,
- mapi_id_t fid,
- TALLOC_CTX *mem_ctx,
- struct mapi_SRestriction **restrictions,
- gpointer user_data,
- GCancellable *cancellable,
- GError **perror)
-{
- icaltimetype *itt_cache = user_data;
+ backend = E_CAL_BACKEND (cbmapi);
+ g_return_val_if_fail (backend != NULL, FALSE);
- g_return_val_if_fail (restrictions != NULL, FALSE);
+ pmid = e_mapi_util_find_array_propval (&object->properties, PidTagMid);
+ if (pmid)
+ use_uid = e_mapi_util_mapi_id_to_string (*pmid);
+ else
+ use_uid = e_cal_component_gen_uid ();
- if (itt_cache && !icaltime_is_null_time (*itt_cache)) {
- struct mapi_SRestriction *restriction;
- struct SPropValue sprop;
- struct timeval t;
+ comp = e_mapi_cal_util_object_to_comp (conn, object,
+ e_cal_backend_get_kind (E_CAL_BACKEND (cbmapi)), FALSE,
+ e_cal_backend_get_cache_dir (E_CAL_BACKEND (cbmapi)),
+ use_uid, &comps);
- restriction = talloc_zero (mem_ctx, struct mapi_SRestriction);
- g_return_val_if_fail (restriction != NULL, FALSE);
+ g_free (use_uid);
- restriction->rt = RES_PROPERTY;
- restriction->res.resProperty.relop = RELOP_GE;
- restriction->res.resProperty.ulPropTag = PR_LAST_MODIFICATION_TIME;
+ if (comp)
+ comps = g_slist_prepend (comps, comp);
- t.tv_sec = icaltime_as_timet_with_zone (*itt_cache, icaltimezone_get_utc_timezone ());
- t.tv_usec = 0;
- set_SPropValue_proptag_date_timeval (&sprop, PR_LAST_MODIFICATION_TIME, &t);
- cast_mapi_SPropValue (mem_ctx, &(restriction->res.resProperty.lpProp), &sprop);
+ for (iter = comps; iter; iter = iter->next) {
+ ECalComponentId *id;
+ ECalComponent *old_comp;
- *restrictions = restriction;
- }
+ comp = iter->data;
+ if (!comp)
+ continue;
- return TRUE;
-}
+ e_cal_component_commit_sequence (comp);
-static gboolean
-ecbm_build_restriction_unknown_mids (EMapiConnection *conn,
- mapi_id_t fid,
- TALLOC_CTX *mem_ctx,
- struct mapi_SRestriction **restrictions,
- gpointer user_data,
- GCancellable *cancellable,
- GError **perror)
-{
- GSList *unknown_mids = user_data, *iter;
- gint ii;
+ id = e_cal_component_get_id (comp);
+ if (!id) {
+ g_debug ("%s: Failed to get component's ID", G_STRFUNC);
+ continue;
+ }
- g_return_val_if_fail (restrictions != NULL, FALSE);
+ old_comp = e_cal_backend_store_get_component (cbmapi->priv->store, id->uid, id->rid);
+ if (old_comp) {
+ mapi_id_t old_mid, new_mid;
- if (unknown_mids) {
- struct mapi_SRestriction *restriction;
- struct mapi_SRestriction_or *or_res;
+ get_comp_mid (e_cal_component_get_icalcomponent (old_comp), &old_mid);
+ get_comp_mid (e_cal_component_get_icalcomponent (comp), &new_mid);
- or_res = talloc_zero_array (mem_ctx, struct mapi_SRestriction_or, g_slist_length (unknown_mids));
- g_return_val_if_fail (or_res != NULL, FALSE);
+ if (new_mid && old_mid && new_mid != old_mid) {
+ use_uid = e_mapi_util_mapi_id_to_string (new_mid);
+ e_cal_component_set_uid (comp, use_uid);
+ g_free (use_uid);
- for (ii = 0, iter = unknown_mids; iter; ii++, iter = iter->next) {
- mapi_id_t *pmid = iter->data;
+ e_cal_component_free_id (id);
+ id = e_cal_component_get_id (comp);
+ if (!id) {
+ g_debug ("%s: Failed to re-get component's ID", G_STRFUNC);
+ continue;
+ }
- or_res[ii].rt = RES_PROPERTY;
- or_res[ii].res.resProperty.relop = RELOP_EQ;
- or_res[ii].res.resProperty.ulPropTag = PR_MID;
- or_res[ii].res.resProperty.lpProp.ulPropTag = PR_MID;
- or_res[ii].res.resProperty.lpProp.value.dbl = *pmid;
+ old_comp = e_cal_backend_store_get_component (cbmapi->priv->store, id->uid, id->rid);
+ }
}
- restriction = talloc_zero (mem_ctx, struct mapi_SRestriction);
- g_return_val_if_fail (restriction != NULL, FALSE);
+ put_component_to_store (cbmapi, comp);
- restriction->rt = RES_OR;
- restriction->res.resOr.cRes = g_slist_length (unknown_mids);
- restriction->res.resOr.res = or_res;
+ if (old_comp) {
+ e_cal_backend_notify_component_modified (backend, e_cal_component_get_icalcomponent (old_comp), e_cal_component_get_icalcomponent (comp));
+ g_object_unref (old_comp);
+ } else {
+ e_cal_backend_notify_component_created (E_CAL_BACKEND (cbmapi), e_cal_component_get_icalcomponent (comp));
+ }
- *restrictions = restriction;
+ e_cal_component_free_id (id);
}
+ g_slist_free_full (comps, g_object_unref);
+
+ notify_view_progress (cbmapi, obj_index, obj_total);
+
return TRUE;
}
-/* Simple workflow for fetching deltas:
- * Poke cache for server_utc_time -> if exists, fetch all items modified after that time,
- * note current time before fetching and update cache with the same after fetching.
- * If server_utc_time does not exist OR is invalid, fetch all items
- * (we anyway process the results only if last_modified has changed).
- */
+static void
+copy_to_known_comps (gpointer key, gpointer value, gpointer user_data)
+{
+ mapi_id_t *pmid = key, *pmidcopy;
+ GSList *comps = value;
+ GHashTable *known_comps = user_data;
+
+ g_return_if_fail (pmid != NULL);
+ g_return_if_fail (known_comps != NULL);
+
+ pmidcopy = g_new0 (mapi_id_t, 1);
+ *pmidcopy = *pmid;
+
+ /* stealing 'comps' pointer here */
+ g_hash_table_insert (known_comps, pmidcopy, comps);
+}
static gboolean
-get_deltas (gpointer handle)
+update_local_cache (ECalBackendMAPI *cbmapi, GCancellable *cancellable)
{
- ECalBackendMAPI *cbmapi;
ECalBackendMAPIPrivate *priv;
- icalcomponent_kind kind;
- icaltimetype itt_current, itt_cache = icaltime_null_time();
- time_t current_time;
- struct tm tm;
- gchar *time_string = NULL;
- gchar t_str [26];
- const gchar *serv_time;
- gboolean use_restriction = TRUE;
- GSList *ls = NULL;
- struct deleted_items_data did;
+ EMapiConnection *conn;
ESource *source = NULL;
- guint32 options= MAPI_OPTIONS_FETCH_ALL;
- gboolean is_public = FALSE;
+ struct ListCalendarObjectsData lco;
+ GSList *iter, *components;
+ mapi_object_t obj_folder;
+ gboolean success;
GError *mapi_error = NULL;
+ GHashTable *comps_by_mids;
+ gboolean partial_update;
+ struct FolderBasicPropertiesData fbp;
- if (!handle)
- return FALSE;
-
- cbmapi = (ECalBackendMAPI *) handle;
- priv= cbmapi->priv;
- kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbmapi));
+ priv = cbmapi->priv;
source = e_backend_get_source (E_BACKEND (cbmapi));
if (!e_backend_get_online (E_BACKEND (cbmapi)))
return FALSE;
g_mutex_lock (priv->updating_mutex);
- serv_time = e_cal_backend_store_get_key_value (priv->store, SERVER_UTC_TIME);
- if (serv_time)
- itt_cache = icaltime_from_string (serv_time);
-
- itt_current = icaltime_current_time_with_zone (icaltimezone_get_utc_timezone ());
- current_time = icaltime_as_timet_with_zone (itt_current, icaltimezone_get_utc_timezone ());
- gmtime_r (¤t_time, &tm);
- strftime (t_str, 26, "%Y-%m-%dT%H:%M:%SZ", &tm);
+ conn = g_object_ref (priv->conn);
if (g_strcmp0 (e_source_get_property (source, "public"), "yes") == 0) {
- options |= MAPI_OPTIONS_USE_PFSTORE;
- is_public = TRUE;
- use_restriction = FALSE;
- }
-
- if (!e_mapi_connection_fetch_items (priv->conn, priv->fid, use_restriction ? ecbm_build_last_modification_restriction : NULL, &itt_cache, NULL,
- is_public ? NULL : e_mapi_cal_utils_get_props_cb, GINT_TO_POINTER (kind),
- mapi_cal_get_changes_cb, cbmapi,
- options, NULL, &mapi_error)) {
- if (mapi_error) {
- gchar *msg = g_strdup_printf (_("Failed to fetch changes from a server: %s"), mapi_error->message);
- e_cal_backend_notify_error (E_CAL_BACKEND (cbmapi), msg);
- g_free (msg);
- g_error_free (mapi_error);
- } else {
- e_cal_backend_notify_error (E_CAL_BACKEND (cbmapi), _("Failed to fetch changes from a server"));
- }
+ success = e_mapi_connection_open_public_folder (conn, priv->fid, &obj_folder, cancellable, &mapi_error);
+ } else {
+ success = e_mapi_connection_open_personal_folder (conn, priv->fid, &obj_folder, cancellable, &mapi_error);
+ }
- g_mutex_unlock (priv->updating_mutex);
- return FALSE;
+ if (!success) {
+ notify_error (cbmapi, mapi_error, _("Failed to open folder: %s"));
+ goto cleanup;
}
- notify_view_completed (cbmapi);
+ success = e_mapi_connection_get_folder_properties (conn, &obj_folder, NULL, NULL,
+ e_mapi_utils_get_folder_basic_properties_cb, &fbp,
+ cancellable, &mapi_error);
+ if (!success) {
+ notify_error (cbmapi, mapi_error, _("Failed to get folder properties: %s"));
+ e_mapi_connection_close_folder (conn, &obj_folder, NULL, NULL);
+ goto cleanup;
+ }
- time_string = g_strdup (t_str);
- e_cal_backend_store_put_key_value (priv->store, SERVER_UTC_TIME, time_string);
- g_free (time_string);
+ comps_by_mids = g_hash_table_new_full (g_int64_hash, g_int64_equal, g_free, NULL);
- /* handle deleted items here by going over the entire cache and
- * checking for deleted items.*/
+ components = e_cal_backend_store_get_components (priv->store);
+ for (iter = components; iter; iter = iter->next) {
+ ECalComponent *comp = iter->data;
+ mapi_id_t mid, *pmid;
+ GSList *comps;
- /* e_cal_backend_cache_get_keys returns a list of all the keys.
- * The items in the list are pointers to internal data,
- * so should not be freed, only the list should. */
- did.cbmapi = cbmapi;
- did.cache_ids = e_cal_backend_store_get_component_ids (priv->store);
- did.unknown_mids = NULL;
- copy_strings_in_slist (did.cache_ids);
- options = 0;
+ if (!comp)
+ continue;
- if (g_strcmp0 (e_source_get_property (source, "public"), "yes") == 0)
- options = MAPI_OPTIONS_USE_PFSTORE;
+ get_comp_mid (e_cal_component_get_icalcomponent (comp), &mid);
- if (!e_mapi_connection_fetch_items (priv->conn, priv->fid, NULL, NULL, NULL,
- mapi_cal_get_idlist, NULL,
- handle_deleted_items_cb, &did,
- options, NULL, &mapi_error)) {
- if (mapi_error) {
- gchar *msg = g_strdup_printf (_("Failed to fetch changes from a server: %s"), mapi_error->message);
- e_cal_backend_notify_error (E_CAL_BACKEND (cbmapi), msg);
- g_free (msg);
- g_error_free (mapi_error);
- } else {
- e_cal_backend_notify_error (E_CAL_BACKEND (cbmapi), _("Failed to fetch changes from a server"));
- }
+ pmid = g_new0 (mapi_id_t, 1);
+ *pmid = mid;
- g_slist_foreach (did.cache_ids, (GFunc) g_free, NULL);
- g_slist_free (did.cache_ids);
- g_mutex_unlock (priv->updating_mutex);
- return FALSE;
+ comps = g_slist_prepend (g_hash_table_lookup (comps_by_mids, pmid), comp);
+ g_hash_table_insert (comps_by_mids, pmid, comps);
}
+ /* doesn't call unref, because the hash table holds the components */
+ g_slist_free (components);
- options = MAPI_OPTIONS_FETCH_ALL;
- e_cal_backend_store_freeze_changes (priv->store);
- for (ls = did.cache_ids; ls; ls = g_slist_next (ls)) {
- ECalComponent *comp = NULL;
- icalcomponent *icalcomp = NULL;
+ lco.changed_mids = NULL;
+ lco.known_comps = g_hash_table_new_full (g_int64_hash, g_int64_equal, g_free, free_component_slist);
+ lco.latest_modified = priv->last_refresh;
- comp = e_cal_backend_store_get_component (priv->store, (const gchar *) ls->data, NULL);
+ g_hash_table_foreach (comps_by_mids, copy_to_known_comps, lco.known_comps);
+ g_hash_table_destroy (comps_by_mids);
+ comps_by_mids = NULL;
- if (!comp)
- continue;
+ partial_update = priv->last_refresh > 0 && fbp.obj_total == priv->last_obj_total;
+ success = e_mapi_connection_list_objects (conn, &obj_folder,
+ partial_update ? e_mapi_utils_build_last_modify_restriction : NULL, &priv->last_refresh,
+ list_calendar_objects_cb, &lco,
+ cancellable, &mapi_error);
+ if (!success) {
+ notify_error (cbmapi, mapi_error, _("Failed to list objects: %s"));
- icalcomp = e_cal_component_get_icalcomponent (comp);
- if (kind == icalcomponent_isa (icalcomp)) {
- gchar *comp_str = NULL;
- ECalComponentId *id = e_cal_component_get_id (comp);
+ e_mapi_connection_close_folder (conn, &obj_folder, NULL, NULL);
- comp_str = e_cal_component_get_as_string (comp);
- e_cal_backend_notify_object_removed (E_CAL_BACKEND (cbmapi),
- id, comp_str, NULL);
- e_cal_backend_store_remove_component (priv->store, (const gchar *) id->uid, id->rid);
+ g_slist_free_full (lco.changed_mids, g_free);
+ g_hash_table_destroy (lco.known_comps);
- e_cal_component_free_id (id);
- g_free (comp_str);
- }
- g_object_unref (comp);
+ goto cleanup;
}
- e_cal_backend_store_thaw_changes (priv->store);
- g_slist_foreach (did.cache_ids, (GFunc) g_free, NULL);
- g_slist_free (did.cache_ids);
+ e_cal_backend_store_freeze_changes (priv->store);
- if (did.unknown_mids) {
- if (g_strcmp0 (e_source_get_property (source, "public"), "yes") == 0) {
- options |= MAPI_OPTIONS_USE_PFSTORE;
- is_public = TRUE;
- }
+ if (!partial_update)
+ g_hash_table_foreach (lco.known_comps, drop_removed_comps_cb, cbmapi);
+ g_hash_table_destroy (lco.known_comps);
+ lco.known_comps = NULL;
- if (!e_mapi_connection_fetch_items (priv->conn, priv->fid, ecbm_build_restriction_unknown_mids, did.unknown_mids, NULL,
- is_public ? NULL : e_mapi_cal_utils_get_props_cb, GINT_TO_POINTER (kind),
- mapi_cal_get_changes_cb, cbmapi,
- options, NULL, &mapi_error)) {
- g_slist_foreach (did.unknown_mids, (GFunc) g_free, NULL);
- g_slist_free (did.unknown_mids);
-
- if (mapi_error) {
- gchar *msg = g_strdup_printf (_("Failed to fetch changes from a server: %s"), mapi_error->message);
- e_cal_backend_notify_error (E_CAL_BACKEND (cbmapi), msg);
- g_free (msg);
- g_error_free (mapi_error);
- } else {
- e_cal_backend_notify_error (E_CAL_BACKEND (cbmapi), _("Failed to fetch changes from a server"));
- }
+ if (lco.changed_mids) {
+ success = e_mapi_connection_transfer_objects (conn, &obj_folder,
+ lco.changed_mids,
+ transfer_calendar_objects_cb, cbmapi,
+ cancellable, &mapi_error);
+
+ e_cal_backend_store_thaw_changes (priv->store);
+
+ if (!success) {
+ notify_error (cbmapi, mapi_error, _("Failed to transfer objects: %s"));
+
+ e_mapi_connection_close_folder (conn, &obj_folder, NULL, NULL);
- g_mutex_unlock (priv->updating_mutex);
- return FALSE;
+ g_slist_free_full (lco.changed_mids, g_free);
+
+ goto cleanup;
}
- g_slist_foreach (did.unknown_mids, (GFunc) g_free, NULL);
- g_slist_free (did.unknown_mids);
+
+ g_slist_free_full (lco.changed_mids, g_free);
+ } else {
+ e_cal_backend_store_thaw_changes (priv->store);
+ }
+
+ priv->last_obj_total = fbp.obj_total;
+ priv->last_refresh = lco.latest_modified;
+
+ success = e_mapi_connection_close_folder (conn, &obj_folder, cancellable, &mapi_error);
+ if (!success) {
+ notify_error (cbmapi, mapi_error, _("Failed to close folder: %s"));
+
+ goto cleanup;
}
+ cleanup:
+ g_object_unref (conn);
g_mutex_unlock (priv->updating_mutex);
- return TRUE;
+
+ return success;
}
static void
@@ -874,7 +841,7 @@ ecbm_get_object (ECalBackend *backend, EDataCal *cal, GCancellable *cancellable,
* also not on the server to prevent for a race condition where we
* might otherwise mistakenly generate a new UID */
g_mutex_unlock (priv->mutex);
- get_deltas (cbmapi);
+ update_local_cache (cbmapi, cancellable);
g_mutex_lock (priv->mutex);
comp = e_cal_backend_store_get_component (priv->store, uid, rid);
}
@@ -913,8 +880,6 @@ ecbm_get_object_list (ECalBackend *backend, EDataCal *cal, GCancellable *cancell
g_mutex_lock (priv->mutex);
-// d(g_message (G_STRLOC ": Getting object list (%s)", sexp));
-
if (!strcmp (sexp, "#t"))
search_needed = FALSE;
@@ -978,24 +943,28 @@ delta_thread (gpointer data)
{
ECalBackendMAPI *cbmapi;
ECalBackendMAPIPrivate *priv;
+ GCancellable *cancellable;
GTimeVal timeout;
cbmapi = (ECalBackendMAPI *)(data);
g_return_val_if_fail (E_IS_CAL_BACKEND_MAPI (cbmapi), NULL);
priv = cbmapi->priv;
+ cancellable = g_object_ref (priv->cancellable);
timeout.tv_sec = 0;
timeout.tv_usec = 0;
- while (TRUE) {
- gboolean succeeded = get_deltas (cbmapi);
+ while (!g_cancellable_is_cancelled (cancellable)) {
+ update_local_cache (cbmapi, cancellable);
g_mutex_lock (priv->dlock->mutex);
- if (!succeeded || priv->dlock->exit)
+ if (priv->dlock->exit)
break;
+ notify_view_completed (cbmapi);
+
g_get_current_time (&timeout);
g_time_val_add (&timeout, get_cache_refresh_interval () * 1000);
g_cond_timed_wait (priv->dlock->cond, priv->dlock->mutex, &timeout);
@@ -1006,24 +975,28 @@ delta_thread (gpointer data)
g_mutex_unlock (priv->dlock->mutex);
}
+ g_object_unref (cancellable);
g_mutex_unlock (priv->dlock->mutex);
priv->dthread = NULL;
+
return NULL;
}
-static gboolean
-fetch_deltas (ECalBackendMAPI *cbmapi)
+static void
+run_delta_thread (ECalBackendMAPI *cbmapi)
{
ECalBackendMAPIPrivate *priv;
GError *error = NULL;
- g_return_val_if_fail (E_IS_CAL_BACKEND_MAPI (cbmapi), FALSE);
+ g_return_if_fail (E_IS_CAL_BACKEND_MAPI (cbmapi));
priv = cbmapi->priv;
/* If the thread is already running just return back */
- if (priv->dthread)
- return FALSE;
+ if (priv->dthread) {
+ g_cond_signal (priv->dlock->cond);
+ return;
+ }
if (!priv->dlock) {
priv->dlock = g_new0 (SyncDelta, 1);
@@ -1037,207 +1010,12 @@ fetch_deltas (ECalBackendMAPI *cbmapi)
g_warning (G_STRLOC ": %s", error->message);
g_error_free (error);
}
-
- return TRUE;
-}
-
-static gboolean
-start_fetch_deltas (gpointer data)
-{
- ECalBackendMAPI *cbmapi;
- ECalBackendMAPIPrivate *priv;
-
- cbmapi = (ECalBackendMAPI *)(data);
- g_return_val_if_fail (E_IS_CAL_BACKEND_MAPI (cbmapi), FALSE);
-
- priv = cbmapi->priv;
-
- fetch_deltas (cbmapi);
-
- priv->timeout_id = 0;
-
- return FALSE;
-}
-
-static gboolean
-mapi_cal_cache_create_cb (FetchItemsCallbackData *item_data,
- gpointer data,
- GCancellable *cancellable,
- GError **perror)
-{
- struct mapi_SPropValue_array *properties = item_data->properties;
- const mapi_id_t mid = item_data->mid;
- GSList *streams = item_data->streams;
- GSList *recipients = item_data->recipients;
- GSList *attachments = item_data->attachments;
- GSList *detached = NULL, *d_i = NULL;
- ECalBackendMAPI *cbmapi = E_CAL_BACKEND_MAPI (data);
- icalcomponent_kind kind;
- ECalComponent *comp = NULL;
- gchar *tmp = NULL;
- const bool *recurring = NULL;
- const gchar *cache_dir;
-
- kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbmapi));
- cache_dir = e_cal_backend_get_cache_dir (E_CAL_BACKEND (cbmapi));
-
-// e_mapi_debug_property_dump (properties);
-
- switch (kind) {
- case ICAL_VTODO_COMPONENT:
- /* FIXME: Evolution does not support recurring tasks */
- recurring = e_mapi_util_find_array_namedid (properties, item_data->conn, item_data->fid, PidLidTaskFRecurring);
- if (recurring && *recurring) {
- g_warning ("Encountered a recurring task.");
- e_mapi_util_free_stream_list (&streams);
- e_mapi_util_free_recipient_list (&recipients);
- e_mapi_util_free_attachment_list (&attachments);
- return TRUE;
- }
- break;
- case ICAL_VEVENT_COMPONENT :
- case ICAL_VJOURNAL_COMPONENT:
- break;
- default:
- return FALSE;
- }
-
- tmp = e_mapi_util_mapi_id_to_string (mid);
- comp = e_mapi_cal_util_mapi_props_to_comp (item_data->conn, item_data->fid, kind, tmp, properties,
- streams, recipients, attachments,
- cache_dir, icaltimezone_get_utc_timezone (), FALSE, &detached);
- g_free (tmp);
-
- detached = g_slist_prepend (detached, comp);
- for (d_i = detached; d_i; d_i = g_slist_next (d_i)) {
- comp = d_i->data;
-
- if (E_IS_CAL_COMPONENT (comp)) {
- gchar *comp_str;
-
- e_cal_component_commit_sequence (comp);
- put_component_to_store (cbmapi, comp);
-
- comp_str = e_cal_component_get_as_string (comp);
- e_cal_backend_notify_object_created (E_CAL_BACKEND (cbmapi), comp_str);
- g_free (comp_str);
- }
-
- g_object_unref (comp);
- }
- g_slist_free (detached);
-
- e_mapi_util_free_stream_list (&streams);
- e_mapi_util_free_recipient_list (&recipients);
- e_mapi_util_free_attachment_list (&attachments);
-
- notify_view_progress (cbmapi, item_data->index, item_data->total);
-
- return TRUE;
-}
-
-static gboolean
-populate_cache (ECalBackendMAPI *cbmapi, GError **perror)
-{
- ECalBackendMAPIPrivate *priv;
- ESource *source = NULL;
- icalcomponent_kind kind;
- icaltimetype itt_current;
- time_t current_time;
- struct tm tm;
- gchar *time_string = NULL;
- gchar t_str [26];
- guint32 options= MAPI_OPTIONS_FETCH_ALL;
- gboolean is_public = FALSE;
- GError *mapi_error = NULL;
-
- priv = cbmapi->priv;
-
- g_mutex_lock (priv->mutex);
- if (priv->populating_cache) {
- g_mutex_unlock (priv->mutex);
- return TRUE; /* Success */
- }
- priv->populating_cache = TRUE;
- g_mutex_unlock (priv->mutex);
-
- source = e_backend_get_source (E_BACKEND (cbmapi));
- kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbmapi));
-
- itt_current = icaltime_current_time_with_zone (icaltimezone_get_utc_timezone ());
- current_time = icaltime_as_timet_with_zone (itt_current, icaltimezone_get_utc_timezone ());
- gmtime_r (¤t_time, &tm);
- strftime (t_str, 26, "%Y-%m-%dT%H:%M:%SZ", &tm);
-
- if (g_strcmp0 (e_source_get_property (source, "public"), "yes") == 0) {
- options |= MAPI_OPTIONS_USE_PFSTORE;
- is_public = TRUE;
- }
-
- if (!e_mapi_connection_fetch_items (priv->conn, priv->fid, NULL, NULL, NULL,
- is_public ? NULL : e_mapi_cal_utils_get_props_cb, GINT_TO_POINTER (kind),
- mapi_cal_cache_create_cb, cbmapi,
- options, NULL, &mapi_error)) {
- e_cal_backend_store_thaw_changes (priv->store);
- g_mutex_lock (priv->mutex);
- priv->populating_cache = FALSE;
- g_mutex_unlock (priv->mutex);
-
- mapi_error_to_edc_error (perror, mapi_error, OtherError, _("Failed to fetch items from a server"));
- if (mapi_error)
- g_error_free (mapi_error);
-
- return FALSE;
- }
-
- notify_view_completed (cbmapi);
-
- time_string = g_strdup (t_str);
- e_cal_backend_store_put_key_value (priv->store, SERVER_UTC_TIME, time_string);
-
- g_free (time_string);
-
- e_cal_backend_store_put_key_value (priv->store, CACHE_MARKER, "1");
-
- g_mutex_lock (priv->mutex);
- priv->populating_cache = FALSE;
- g_mutex_unlock (priv->mutex);
-
- return TRUE;
-}
-
-static gpointer
-cache_init (ECalBackendMAPI *cbmapi)
-{
- ECalBackendMAPIPrivate *priv = cbmapi->priv;
-
- if (!e_cal_backend_store_get_key_value (priv->store, CACHE_MARKER)) {
- /* Populate the cache for the first time.*/
- if (!populate_cache (cbmapi, NULL)) {
- g_warning (G_STRLOC ": Could not populate the cache");
- /*FIXME why dont we do a notify here */
- return NULL;
- } else {
- /* Set delta fetch timeout */
- priv->timeout_id = g_timeout_add (get_cache_refresh_interval (), start_fetch_deltas, (gpointer) cbmapi);
-
- return NULL;
- }
- }
-
- g_mutex_lock (priv->mutex);
- fetch_deltas (cbmapi);
- g_mutex_unlock (priv->mutex);
-
- return NULL;
}
static void
ecbm_connect (ECalBackendMAPI *cbmapi, GError **perror)
{
ECalBackendMAPIPrivate *priv;
- GThread *thread;
- GError *error = NULL;
priv = cbmapi->priv;
@@ -1252,27 +1030,16 @@ ecbm_connect (ECalBackendMAPI *cbmapi, GError **perror)
}
/* We have established a connection */
- if (priv->store && priv->fid && e_cal_backend_store_put_key_value (priv->store, CACHE_MARKER, "1")) {
+ if (priv->store && priv->fid) {
e_cal_backend_notify_online (E_CAL_BACKEND (cbmapi), TRUE);
if (priv->mode_changed && !priv->dthread) {
priv->mode_changed = FALSE;
- fetch_deltas (cbmapi);
+ run_delta_thread (cbmapi);
}
-
- /* FIXME: put server UTC time in cache */
- return /* Success */;
}
priv->mode_changed = FALSE;
-
- /* spawn a new thread for caching the calendar items */
- thread = g_thread_create ((GThreadFunc) cache_init, cbmapi, FALSE, &error);
- if (!thread) {
- g_warning (G_STRLOC ": %s", error->message);
- g_error_free (error);
- g_propagate_error (perror, EDC_ERROR_EX (OtherError, _("Could not create thread for populating cache")));
- }
}
static void
@@ -1504,23 +1271,6 @@ capture_req_props (FetchItemsCallbackData *item_data,
return TRUE;
}
-static void
-get_comp_mid (icalcomponent *icalcomp, mapi_id_t *mid)
-{
- gchar *x_mid;
-
- g_return_if_fail (icalcomp != NULL);
- g_return_if_fail (mid != NULL);
-
- x_mid = e_mapi_cal_utils_get_icomp_x_prop (icalcomp, "X-EVOLUTION-MAPI-MID");
- if (x_mid) {
- e_mapi_util_mapi_id_from_string (x_mid, mid);
- g_free (x_mid);
- } else {
- e_mapi_util_mapi_id_from_string (icalcomponent_get_uid (icalcomp), mid);
- }
-}
-
static gboolean
ecbm_build_global_id_restriction (EMapiConnection *conn,
mapi_id_t fid,
@@ -1760,8 +1510,7 @@ ecbm_create_object (ECalBackend *backend, EDataCal *cal, GCancellable *cancellab
return;
}
- if (!fetch_deltas(cbmapi))
- g_cond_signal (priv->dlock->cond);
+ run_delta_thread (cbmapi);
g_object_unref (comp);
e_mapi_util_free_recipient_list (&recipients);
@@ -1917,7 +1666,7 @@ ecbm_modify_object (ECalBackend *backend, EDataCal *cal, GCancellable *cancellab
/* check if the object exists */
cache_comp = e_cal_backend_store_get_component (priv->store, uid, rid);
if (!cache_comp) {
- get_deltas (cbmapi);
+ update_local_cache (cbmapi, cancellable);
cache_comp = e_cal_backend_store_get_component (priv->store, uid, rid);
}
@@ -2582,6 +2331,9 @@ ecbm_notify_online_cb (ECalBackend *backend, GParamSpec *pspec)
}
} else {
priv->read_only = TRUE;
+
+ g_object_unref (priv->conn);
+ priv->conn = NULL;
}
e_cal_backend_notify_readonly (backend, priv->read_only);
@@ -3408,6 +3160,28 @@ ecbm_op_get_free_busy (ECalBackend *backend, EDataCal *cal, guint32 opid, GCance
}
static void
+ecbm_dispose (GObject *object)
+{
+ ECalBackendMAPI *cbmapi;
+ ECalBackendMAPIPrivate *priv;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (E_IS_CAL_BACKEND_MAPI (object));
+
+ cbmapi = E_CAL_BACKEND_MAPI (object);
+ priv = cbmapi->priv;
+
+ if (priv && priv->cancellable) {
+ g_cancellable_cancel (priv->cancellable);
+ g_object_unref (priv->cancellable);
+ priv->cancellable = NULL;
+ }
+
+ if (G_OBJECT_CLASS (e_cal_backend_mapi_parent_class)->dispose)
+ (* G_OBJECT_CLASS (e_cal_backend_mapi_parent_class)->dispose) (object);
+}
+
+static void
ecbm_finalize (GObject *object)
{
ECalBackendMAPI *cbmapi;
@@ -3514,6 +3288,7 @@ e_cal_backend_mapi_class_init (ECalBackendMAPIClass *class)
backend_class = (ECalBackendClass *) class;
object_class->finalize = ecbm_finalize;
+ object_class->dispose = ecbm_dispose;
/* functions done asynchronously */
backend_class->get_backend_property = ecbm_op_get_backend_property;
@@ -3552,8 +3327,10 @@ e_cal_backend_mapi_init (ECalBackendMAPI *cbmapi)
/* create the mutex for thread safety */
priv->mutex = g_mutex_new ();
priv->updating_mutex = g_mutex_new ();
- priv->populating_cache = FALSE;
priv->op_queue = e_mapi_operation_queue_new ((EMapiOperationQueueFunc) ecbm_operation_cb, cbmapi);
+ priv->last_refresh = -1;
+ priv->last_obj_total = -1;
+ priv->cancellable = g_cancellable_new ();
cbmapi->priv = priv;
diff --git a/src/camel/camel-mapi-folder.c b/src/camel/camel-mapi-folder.c
index 2d45a56..c5c5e0a 100644
--- a/src/camel/camel-mapi-folder.c
+++ b/src/camel/camel-mapi-folder.c
@@ -419,43 +419,6 @@ add_message_to_cache (CamelMapiFolder *mapi_folder, const gchar *uid, CamelMimeM
camel_folder_summary_unlock (folder->summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
}
-static gboolean
-build_last_modify_restriction (EMapiConnection *conn,
- mapi_id_t fid,
- TALLOC_CTX *mem_ctx,
- struct mapi_SRestriction **restrictions,
- gpointer user_data,
- GCancellable *cancellable,
- GError **perror)
-{
- const time_t *latest_last_modify = user_data;
- struct mapi_SRestriction *restriction = NULL;
-
- g_return_val_if_fail (restrictions != NULL, FALSE);
-
- if (latest_last_modify && *latest_last_modify > 0) {
- struct SPropValue sprop;
- struct timeval t;
-
- restriction = talloc_zero (mem_ctx, struct mapi_SRestriction);
- g_return_val_if_fail (restriction != NULL, FALSE);
-
- restriction->rt = RES_PROPERTY;
- restriction->res.resProperty.relop = RELOP_GT;
- restriction->res.resProperty.ulPropTag = PR_LAST_MODIFICATION_TIME;
-
- t.tv_sec = *latest_last_modify;
- t.tv_usec = 0;
-
- set_SPropValue_proptag_date_timeval (&sprop, PR_LAST_MODIFICATION_TIME, &t);
- cast_mapi_SPropValue (mem_ctx, &(restriction->res.resProperty.lpProp), &sprop);
- }
-
- *restrictions = restriction;
-
- return TRUE;
-}
-
struct GatherChangedObjectsData
{
CamelFolderSummary *summary;
@@ -924,7 +887,7 @@ camel_mapi_folder_fetch_summary (CamelFolder *folder, GCancellable *cancellable,
if (status) {
status = e_mapi_connection_list_objects (conn, &obj_folder,
- full_download ? NULL : build_last_modify_restriction, &mapi_summary->latest_last_modify,
+ full_download ? NULL : e_mapi_utils_build_last_modify_restriction, &mapi_summary->latest_last_modify,
gather_changed_objects_to_slist, &gco, cancellable, mapi_error);
}
diff --git a/src/libexchangemapi/e-mapi-cal-utils.c b/src/libexchangemapi/e-mapi-cal-utils.c
index 79f884b..c231075 100644
--- a/src/libexchangemapi/e-mapi-cal-utils.c
+++ b/src/libexchangemapi/e-mapi-cal-utils.c
@@ -2067,9 +2067,9 @@ static gboolean
emcu_check_id_exists_cb (EMapiConnection *conn,
mapi_id_t fid,
TALLOC_CTX *mem_ctx,
- const ListObjectsData *item_data,
- guint32 item_index,
- guint32 items_total,
+ const ListObjectsData *object_data,
+ guint32 obj_index,
+ guint32 obj_total,
gpointer user_data,
GCancellable *cancellable,
GError **perror)
diff --git a/src/libexchangemapi/e-mapi-connection.c b/src/libexchangemapi/e-mapi-connection.c
index a02692f..efc4840 100644
--- a/src/libexchangemapi/e-mapi-connection.c
+++ b/src/libexchangemapi/e-mapi-connection.c
@@ -40,6 +40,8 @@
#include "e-mapi-utils.h"
#include "e-mapi-mail-utils.h"
#include "e-mapi-fast-transfer.h"
+#include "e-mapi-openchange.h"
+
#include <param.h>
static void register_connection (EMapiConnection *conn);
@@ -1711,7 +1713,7 @@ e_mapi_connection_get_folder_properties (EMapiConnection *conn,
if (g_cancellable_set_error_if_cancelled (cancellable, perror))
goto cleanup;
- spropTagArray = set_SPropTagArray (mem_ctx, 1, PR_FID);
+ spropTagArray = set_SPropTagArray (mem_ctx, 3, PidTagFolderId, PidTagLastModificationTime, PidTagContentCount);
if (brp_cb) {
if (!brp_cb (conn, mapi_object_get_id (obj_folder), mem_ctx, spropTagArray, brp_cb_user_data, cancellable, perror)) {
goto cleanup;
@@ -1809,7 +1811,7 @@ e_mapi_connection_get_folder_properties (EMapiConnection *conn,
}
}
} else {
- ms = GetPropsAll (obj_folder, MAPI_PROPS_SKIP_NAMEDID_CHECK | MAPI_UNICODE, properties);
+ ms = GetPropsAll (obj_folder, MAPI_UNICODE, properties);
if (ms != MAPI_E_SUCCESS) {
make_mapi_error (perror, "GetPropsAll", ms);
goto cleanup;
@@ -1904,6 +1906,7 @@ list_objects_internal_cb (EMapiConnection *conn,
struct ListObjectsInternalData *loi_data = user_data;
ListObjectsData lod;
const mapi_id_t *pmid;
+ const gchar *msg_class;
const uint32_t *pmsg_flags;
struct SPropValue *last_modified;
struct timeval t;
@@ -1913,11 +1916,13 @@ list_objects_internal_cb (EMapiConnection *conn,
e_return_val_mapi_error_if_fail (mem_ctx != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
e_return_val_mapi_error_if_fail (srow != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
- pmid = get_SPropValue_SRow_data (srow, PR_MID);
- pmsg_flags = get_SPropValue_SRow_data (srow, PR_MESSAGE_FLAGS);
- last_modified = get_SPropValue_SRow (srow, PR_LAST_MODIFICATION_TIME);
+ pmid = get_SPropValue_SRow_data (srow, PidTagMid);
+ msg_class = get_SPropValue_SRow_data (srow, PidTagMessageClass);
+ pmsg_flags = get_SPropValue_SRow_data (srow, PidTagMessageFlags);
+ last_modified = get_SPropValue_SRow (srow, PidTagLastModificationTime);
lod.mid = pmid ? *pmid : 0;
+ lod.msg_class = msg_class;
lod.msg_flags = pmsg_flags ? *pmsg_flags : 0;
if (last_modified && get_mapi_SPropValue_date_timeval (&t, *last_modified) == MAPI_E_SUCCESS)
@@ -2136,10 +2141,11 @@ e_mapi_connection_list_objects (EMapiConnection *conn,
goto cleanup;
}
- propTagArray = set_SPropTagArray (mem_ctx, 0x3,
- PR_MID,
- PR_MESSAGE_FLAGS,
- PR_LAST_MODIFICATION_TIME);
+ propTagArray = set_SPropTagArray (mem_ctx, 0x4,
+ PidTagMid,
+ PidTagMessageClass,
+ PidTagMessageFlags,
+ PidTagLastModificationTime);
/* Set primary columns to be fetched */
ms = SetColumns (&obj_table, propTagArray);
@@ -2357,6 +2363,7 @@ struct EnsureAdditionalPropertiesData
TransferObjectCB cb;
gpointer cb_user_data;
mapi_object_t *obj_folder;
+ guint32 downloaded;
};
static gboolean
@@ -2457,9 +2464,503 @@ ensure_additional_properties_cb (EMapiConnection *conn,
}
}
+ eap->downloaded++;
+
return eap->cb (conn, mem_ctx, object, obj_index, obj_total, eap->cb_user_data, cancellable, perror);
}
+static enum MAPISTATUS
+fetch_object_property_as_stream (EMapiConnection *conn,
+ TALLOC_CTX *mem_ctx,
+ mapi_object_t *obj_message,
+ uint32_t proptag,
+ struct SBinary_short *bin,
+ GCancellable *cancellable,
+ GError **perror)
+{
+ enum MAPISTATUS ms;
+ mapi_object_t obj_stream;
+ uint32_t buf_size, max_read;
+ uint16_t off_data, cn_read;
+ gboolean done = FALSE;
+
+ g_return_val_if_fail (conn != NULL, MAPI_E_INVALID_PARAMETER);
+ g_return_val_if_fail (mem_ctx != NULL, MAPI_E_INVALID_PARAMETER);
+ g_return_val_if_fail (obj_message != NULL, MAPI_E_INVALID_PARAMETER);
+ g_return_val_if_fail (bin != NULL, MAPI_E_INVALID_PARAMETER);
+
+ mapi_object_init (&obj_stream);
+
+ ms = OpenStream (obj_message, proptag, STREAM_ACCESS_READ, &obj_stream);
+ if (ms != MAPI_E_SUCCESS) {
+ make_mapi_error (perror, "OpenStream", ms);
+ goto cleanup;
+ }
+
+ bin->cb = 0;
+
+ ms = GetStreamSize (&obj_stream, &buf_size);
+ if (ms != MAPI_E_SUCCESS) {
+ make_mapi_error (perror, "GetStreamSize", ms);
+ goto cleanup;
+ }
+
+ bin->cb = buf_size;
+ bin->lpb = talloc_size (mem_ctx, bin->cb + 1);
+ if (!bin->lpb || !bin->cb)
+ goto cleanup;
+
+ /* determine max_read first, to read by chunks as long as possible */
+ off_data = 0;
+ max_read = buf_size > STREAM_MAX_READ_SIZE ? STREAM_MAX_READ_SIZE : buf_size;
+ do {
+ ms = ReadStream (&obj_stream, (bin->lpb) + off_data, max_read, &cn_read);
+ if (ms == MAPI_E_SUCCESS) {
+ if (cn_read == 0) {
+ done = TRUE;
+ } else {
+ off_data += cn_read;
+ if (off_data >= buf_size)
+ done = TRUE;
+ }
+ break;
+ }
+
+ if (ms == 0x2c80)
+ max_read = max_read >> 1;
+ else
+ max_read = STREAM_MAX_READ_SIZE_DF;
+
+ if (max_read < STREAM_MAX_READ_SIZE_DF)
+ max_read = STREAM_MAX_READ_SIZE_DF;
+ } while (ms == 0x2c80); /* an error when max_read is too large? */
+
+ while (!done) {
+ ms = ReadStream (&obj_stream, bin->lpb + off_data, max_read, &cn_read);
+ if (ms != MAPI_E_SUCCESS) {
+ make_mapi_error (perror, "ReadStream", ms);
+ done = TRUE;
+ } else if (cn_read == 0) {
+ done = TRUE;
+ } else {
+ off_data += cn_read;
+ if (off_data >= buf_size)
+ done = TRUE;
+ }
+ }
+
+ cleanup:
+ mapi_object_release (&obj_stream);
+
+ return ms;
+}
+
+static enum MAPISTATUS
+e_mapi_connection_fetch_object_internal (EMapiConnection *conn,
+ TALLOC_CTX *mem_ctx,
+ mapi_object_t *obj_message,
+ struct EnsureAdditionalPropertiesData *eap,
+ EMapiObject **out_object,
+ GCancellable *cancellable,
+ GError **perror);
+
+struct FetchObjectAttachmentData
+{
+ mapi_object_t *obj_message;
+ struct EnsureAdditionalPropertiesData *eap;
+ EMapiObject *object; /* to add attachments to */
+};
+
+static gboolean
+fetch_object_attachment_cb (EMapiConnection *conn,
+ mapi_id_t fid,
+ TALLOC_CTX *mem_ctx,
+ struct SRow *srow,
+ guint32 row_index,
+ guint32 rows_total,
+ gpointer user_data,
+ GCancellable *cancellable,
+ GError **perror)
+{
+ enum MAPISTATUS ms;
+ struct FetchObjectAttachmentData *foa = user_data;
+ EMapiAttachment *attachment = NULL;
+ mapi_object_t obj_attach;
+ const uint32_t *attach_num, *attach_method;
+
+ g_return_val_if_fail (conn != NULL, FALSE);
+ g_return_val_if_fail (mem_ctx != NULL, FALSE);
+ g_return_val_if_fail (srow != NULL, FALSE);
+ g_return_val_if_fail (user_data != NULL, FALSE);
+ g_return_val_if_fail (foa->obj_message != NULL, FALSE);
+ g_return_val_if_fail (foa->object != NULL, FALSE);
+
+ mapi_object_init (&obj_attach);
+
+ attach_num = e_mapi_util_find_row_propval (srow, PidTagAttachNumber);
+ if (!attach_num)
+ return FALSE;
+
+ ms = OpenAttach (foa->obj_message, *attach_num, &obj_attach);
+ if (ms != MAPI_E_SUCCESS) {
+ make_mapi_error (perror, "OpenAttach", ms);
+ goto cleanup;
+ }
+
+ attachment = e_mapi_attachment_new (foa->object);
+
+ ms = GetPropsAll (&obj_attach, MAPI_UNICODE, &attachment->properties);
+ if (ms != MAPI_E_SUCCESS) {
+ make_mapi_error (perror, "Attachment::GetPropsAll", ms);
+ goto cleanup;
+ }
+
+ attach_method = e_mapi_util_find_row_propval (srow, PidTagAttachMethod);
+ if (attach_method && *attach_method == ATTACH_BY_VALUE) {
+ if (!e_mapi_util_find_array_propval (&attachment->properties, PidTagAttachDataBinary)) {
+ struct SBinary_short bin;
+
+ ms = fetch_object_property_as_stream (conn, mem_ctx, &obj_attach, PidTagAttachDataBinary, &bin, cancellable, perror);
+ if (ms != MAPI_E_SUCCESS) {
+ make_mapi_error (perror, "Attachment::fetch PidTagAttachDataBinary", ms);
+ goto cleanup;
+ }
+
+ attachment->properties.cValues++;
+ attachment->properties.lpProps = talloc_realloc (mem_ctx,
+ attachment->properties.lpProps,
+ struct mapi_SPropValue,
+ attachment->properties.cValues + 1);
+ attachment->properties.lpProps[attachment->properties.cValues - 1].ulPropTag = PidTagAttachDataBinary;
+ attachment->properties.lpProps[attachment->properties.cValues - 1].value.bin = bin;
+ attachment->properties.lpProps[attachment->properties.cValues].ulPropTag = 0;
+ }
+ } else if (attach_method && *attach_method == ATTACH_EMBEDDED_MSG) {
+ mapi_object_t obj_emb_msg;
+
+ mapi_object_init (&obj_emb_msg);
+
+ if (OpenEmbeddedMessage (&obj_attach, &obj_emb_msg, MAPI_READONLY) == MAPI_E_SUCCESS) {
+ e_mapi_connection_fetch_object_internal (conn, mem_ctx, &obj_emb_msg, foa->eap, &attachment->embedded_object, cancellable, perror);
+ }
+
+ mapi_object_release (&obj_emb_msg);
+ }
+
+ cleanup:
+ mapi_object_release (&obj_attach);
+
+ if (ms == MAPI_E_SUCCESS) {
+ if (!foa->object->attachments) {
+ foa->object->attachments = attachment;
+ } else {
+ EMapiAttachment *attach = foa->object->attachments;
+ while (attach->next)
+ attach = attach->next;
+ attach->next = attachment;
+ }
+ } else {
+ e_mapi_attachment_free (attachment);
+ }
+
+ return ms == MAPI_E_SUCCESS;
+}
+
+static enum MAPISTATUS
+e_mapi_connection_fetch_object_internal (EMapiConnection *conn,
+ TALLOC_CTX *mem_ctx,
+ mapi_object_t *obj_message,
+ struct EnsureAdditionalPropertiesData *eap,
+ EMapiObject **out_object,
+ GCancellable *cancellable,
+ GError **perror)
+{
+ enum MAPISTATUS ms;
+ EMapiObject *object;
+ uint16_t ui16, uj16, np_count = 0, *np_propID = NULL;
+ uint32_t ui32;
+ struct MAPINAMEID *np_nameid = NULL;
+ const bool *has_attachments;
+ struct SPropTagArray recipient_proptags;
+ struct SRowSet recipient_rows;
+ mapi_object_t attach_table;
+
+ g_return_val_if_fail (conn != NULL, MAPI_E_INVALID_PARAMETER);
+ g_return_val_if_fail (mem_ctx != NULL, MAPI_E_INVALID_PARAMETER);
+ g_return_val_if_fail (obj_message != NULL, MAPI_E_INVALID_PARAMETER);
+ g_return_val_if_fail (eap != NULL, MAPI_E_INVALID_PARAMETER);
+ g_return_val_if_fail (out_object != NULL, MAPI_E_INVALID_PARAMETER);
+
+ mapi_object_init (&attach_table);
+
+ object = e_mapi_object_new (mem_ctx);
+
+ ms = GetPropsAll (obj_message, MAPI_UNICODE, &object->properties);
+ if (ms != MAPI_E_SUCCESS) {
+ make_mapi_error (perror, "GetPropsAll", ms);
+ goto cleanup;
+ }
+
+ /* to transform named ids to their PidLid or PidName tags, like the fast-transfer does */
+ ms = QueryNamedProperties (obj_message, 0, NULL, &np_count, &np_propID, &np_nameid);
+ if (ms != MAPI_E_SUCCESS) {
+ make_mapi_error (perror, "QueryNamedProperties", ms);
+ goto cleanup;
+ }
+
+ if (np_count && np_propID && np_nameid) {
+ for (ui16 = 0; ui16 < np_count; ui16++) {
+ uint32_t proptag = np_propID[ui16];
+
+ for (uj16 = 0; uj16 < object->properties.cValues; uj16++) {
+ if (object->properties.lpProps[uj16].ulPropTag == proptag) {
+ uint32_t lid = MAPI_E_RESERVED;
+ char *guid;
+
+ guid = GUID_string (mem_ctx, &(np_nameid[ui16].lpguid));
+
+ if (np_nameid[ui16].ulKind == MNID_ID) {
+ if (e_mapi_nameid_lid_lookup_canonical (np_nameid[ui16].kind.lid, guid, &lid) != MAPI_E_SUCCESS)
+ lid = MAPI_E_RESERVED;
+ } else if (np_nameid[ui16].ulKind == MNID_STRING) {
+ if (e_mapi_nameid_string_lookup_canonical (np_nameid[ui16].kind.lpwstr.Name, guid, &lid) != MAPI_E_SUCCESS)
+ lid = MAPI_E_RESERVED;
+ }
+
+ talloc_free (guid);
+
+ if (lid != MAPI_E_RESERVED && (lid & 0xFFFF) == (proptag & 0xFFFF)) {
+ object->properties.lpProps[uj16].ulPropTag = lid;
+ }
+
+ break;
+ }
+ }
+ }
+ }
+
+ talloc_free (np_propID);
+ talloc_free (np_nameid);
+
+ /* ensure certain properties */
+ if (!e_mapi_util_find_array_propval (&object->properties, PidTagHtml)) {
+ uint8_t best_body = 0;
+
+ if (GetBestBody (obj_message, &best_body) == MAPI_E_SUCCESS && best_body == olEditorHTML) {
+ struct SBinary_short bin;
+
+ ms = fetch_object_property_as_stream (conn, mem_ctx, obj_message, PidTagHtml, &bin, cancellable, perror);
+ if (ms != MAPI_E_SUCCESS) {
+ make_mapi_error (perror, "Object::fetch PidTagHtml", ms);
+ goto cleanup;
+ }
+
+ object->properties.cValues++;
+ object->properties.lpProps = talloc_realloc (mem_ctx,
+ object->properties.lpProps,
+ struct mapi_SPropValue,
+ object->properties.cValues + 1);
+ object->properties.lpProps[object->properties.cValues - 1].ulPropTag = PidTagHtml;
+ object->properties.lpProps[object->properties.cValues - 1].value.bin = bin;
+ object->properties.lpProps[object->properties.cValues].ulPropTag = 0;
+ }
+ }
+
+ if (!e_mapi_util_find_array_propval (&object->properties, PidTagBody)) {
+ struct SBinary_short bin;
+
+ if (fetch_object_property_as_stream (conn, mem_ctx, obj_message, PidTagBody, &bin, cancellable, NULL) == MAPI_E_SUCCESS) {
+ object->properties.cValues++;
+ object->properties.lpProps = talloc_realloc (mem_ctx,
+ object->properties.lpProps,
+ struct mapi_SPropValue,
+ object->properties.cValues + 1);
+ object->properties.lpProps[object->properties.cValues - 1].ulPropTag = PidTagBody;
+ if (bin.cb > 0 && bin.lpb[bin.cb - 1] == 0)
+ object->properties.lpProps[object->properties.cValues - 1].value.lpszW = (const char *) talloc_steal (object, bin.lpb);
+ else
+ object->properties.lpProps[object->properties.cValues - 1].value.lpszW = talloc_strndup (object, (char *) bin.lpb, bin.cb);
+ object->properties.lpProps[object->properties.cValues].ulPropTag = 0;
+ }
+ }
+
+ if (!e_mapi_util_find_array_propval (&object->properties, PidNameContentClass)) {
+ uint32_t prop = PidNameContentClass;
+
+ prop = e_mapi_connection_resolve_named_prop (conn, mapi_object_get_id (eap->obj_folder), prop, cancellable, NULL);
+ if (prop != MAPI_E_RESERVED) {
+ struct SPropTagArray *tags;
+ struct SPropValue *lpProps = NULL;
+ uint32_t prop_count = 0;
+
+ tags = set_SPropTagArray (mem_ctx, 1, prop);
+
+ if (GetProps (obj_message, MAPI_PROPS_SKIP_NAMEDID_CHECK | MAPI_UNICODE, tags, &lpProps, &prop_count) == MAPI_E_SUCCESS && lpProps) {
+ if (lpProps[0].ulPropTag == prop) {
+ object->properties.cValues++;
+ object->properties.lpProps = talloc_realloc (mem_ctx,
+ object->properties.lpProps,
+ struct mapi_SPropValue,
+ object->properties.cValues + 1);
+ object->properties.lpProps[object->properties.cValues - 1].ulPropTag = PidNameContentClass;
+ object->properties.lpProps[object->properties.cValues - 1].value.lpszW = talloc_strdup (object, lpProps[0].value.lpszW);
+ object->properties.lpProps[object->properties.cValues].ulPropTag = 0;
+ }
+ }
+
+ talloc_free (tags);
+ talloc_free (lpProps);
+ }
+ }
+
+ /* fetch attachments */
+ has_attachments = e_mapi_util_find_array_propval (&object->properties, PidTagHasAttachments);
+ if (has_attachments && *has_attachments) {
+ struct SPropTagArray *attach_columns;
+ struct FetchObjectAttachmentData foa;
+
+ ms = GetAttachmentTable (obj_message, &attach_table);
+ if (ms != MAPI_E_SUCCESS) {
+ make_mapi_error (perror, "GetAttachmentTable", ms);
+ goto cleanup;
+ }
+
+ attach_columns = set_SPropTagArray (mem_ctx, 1, PidTagAttachNumber);
+ ms = SetColumns (&attach_table, attach_columns);
+ if (ms != MAPI_E_SUCCESS) {
+ make_mapi_error (perror, "AttachTable::SetColumns", ms);
+ talloc_free (attach_columns);
+ goto cleanup;
+ }
+ talloc_free (attach_columns);
+
+ foa.obj_message = obj_message;
+ foa.eap = eap;
+ foa.object = object;
+
+ ms = foreach_tablerow (conn, mapi_object_get_id (eap->obj_folder), mem_ctx, &attach_table, fetch_object_attachment_cb, &foa, cancellable, perror);
+ if (ms != MAPI_E_SUCCESS) {
+ make_mapi_error (perror, "AttachTable::foreach_tablerow", ms);
+ goto cleanup;
+ }
+ }
+
+ /* get recipients */
+ ms = GetRecipientTable (obj_message, &recipient_rows, &recipient_proptags);
+ if (ms != MAPI_E_SUCCESS) {
+ make_mapi_error (perror, "GetRecipientTable", ms);
+ goto cleanup;
+ }
+
+ if (recipient_rows.cRows > 0) {
+ uint32_t uj32, uk32;
+ EMapiRecipient *first_recipient = NULL;
+
+ for (ui32 = 0; ui32 < recipient_rows.cRows; ui32++) {
+ struct SRow *row = &recipient_rows.aRow[ui32];
+ EMapiRecipient *recipient;
+
+ recipient = e_mapi_recipient_new (object);
+ recipient->properties.cValues = row->cValues;
+ recipient->properties.lpProps = talloc_zero_array (recipient, struct mapi_SPropValue, recipient->properties.cValues + 1);
+
+ for (uj32 = 0, uk32 = 0; uj32 < row->cValues; uj32++, uk32++) {
+ if (may_skip_property (row->lpProps[uj32].ulPropTag) ||
+ !e_mapi_utils_copy_to_mapi_SPropValue (recipient, &recipient->properties.lpProps[uk32], &row->lpProps[uj32])) {
+ uk32--;
+ recipient->properties.cValues--;
+ recipient->properties.lpProps[recipient->properties.cValues].ulPropTag = 0;
+ }
+ }
+
+ recipient->properties.lpProps[recipient->properties.cValues].ulPropTag = 0;
+ }
+
+ object->recipients = first_recipient;
+ }
+
+ cleanup:
+ mapi_object_release (&attach_table);
+
+ if (ms == MAPI_E_SUCCESS) {
+ *out_object = object;
+ } else {
+ *out_object = NULL;
+ e_mapi_object_free (object);
+ }
+
+ return ms;
+}
+
+static enum MAPISTATUS
+e_mapi_connection_fetch_objects_internal (EMapiConnection *conn,
+ TALLOC_CTX *mem_ctx,
+ mapi_id_array_t *ids,
+ struct EnsureAdditionalPropertiesData *eap,
+ GCancellable *cancellable,
+ GError **perror)
+{
+ enum MAPISTATUS ms;
+ guint32 idx;
+ mapi_container_list_t *element;
+
+ g_return_val_if_fail (conn != NULL, MAPI_E_INVALID_PARAMETER);
+ g_return_val_if_fail (mem_ctx != NULL, MAPI_E_INVALID_PARAMETER);
+ g_return_val_if_fail (ids != NULL, MAPI_E_INVALID_PARAMETER);
+ g_return_val_if_fail (eap != NULL, MAPI_E_INVALID_PARAMETER);
+ g_return_val_if_fail (eap->obj_folder != NULL, MAPI_E_INVALID_PARAMETER);
+ g_return_val_if_fail (eap->downloaded < ids->count, MAPI_E_INVALID_PARAMETER);
+
+ for (idx = 0, element = ids->lpContainerList; idx < ids->count && idx < eap->downloaded && element; idx++) {
+ element = element->next;
+ }
+
+ g_return_val_if_fail (idx < ids->count, MAPI_E_INVALID_PARAMETER);
+
+ ms = MAPI_E_SUCCESS;
+ while (element && ms == MAPI_E_SUCCESS) {
+ mapi_object_t obj_message;
+ EMapiObject *object = NULL;
+ GError *local_error = NULL;
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
+ ms = MAPI_E_USER_CANCEL;
+ break;
+ }
+
+ mapi_object_init (&obj_message);
+
+ ms = OpenMessage (eap->obj_folder, mapi_object_get_id (eap->obj_folder), element->id, &obj_message, 0 /* read-only */);
+ if (ms != MAPI_E_SUCCESS) {
+ make_mapi_error (perror, "OpenMessage", ms);
+ mapi_object_release (&obj_message);
+ break;
+ }
+
+ /* silently skip broken objects */
+ ms = e_mapi_connection_fetch_object_internal (conn, mem_ctx, &obj_message, eap, &object, cancellable, &local_error);
+ if (ms == MAPI_E_SUCCESS) {
+ if (!eap->cb (conn, mem_ctx, object, eap->downloaded, ids->count, eap->cb_user_data, cancellable, perror)) {
+ ms = MAPI_E_USER_CANCEL;
+ make_mapi_error (perror, "Object processing", ms);
+ }
+ } else {
+ e_mapi_debug_print ("%s: Skipping object %016" G_GINT64_MODIFIER "X because its fetch failed: %s",
+ G_STRFUNC, element->id, local_error ? local_error->message : mapi_get_errstr (ms));
+ ms = MAPI_E_SUCCESS;
+ }
+
+ e_mapi_object_free (object);
+ mapi_object_release (&obj_message);
+
+ eap->downloaded++;
+
+ element = element->next;
+ }
+
+ return ms;
+}
+
/* deals with named IDs transparently, thus it's OK to check with PidLid and PidName constants only */
gboolean
e_mapi_connection_transfer_objects (EMapiConnection *conn,
@@ -2506,8 +3007,20 @@ e_mapi_connection_transfer_objects (EMapiConnection *conn,
eap.cb = cb;
eap.cb_user_data = cb_user_data;
eap.obj_folder = obj_folder;
+ eap.downloaded = 0;
ms = e_mapi_fast_transfer_objects (conn, mem_ctx, obj_folder, &ids, ensure_additional_properties_cb, &eap, cancellable, perror);
+ if (ms == MAPI_E_CALL_FAILED) {
+ /* err, fallback to slow transfer, probably FXGetBuffer failed;
+ see http://tracker.openchange.org/issues/378
+ */
+
+ g_clear_error (perror);
+
+ e_mapi_debug_print ("%s: Failed to fast-transfer, fallback to slow fetch from %d of %d objects\n", G_STRFUNC, eap.downloaded, ids.count);
+
+ ms = e_mapi_connection_fetch_objects_internal (conn, mem_ctx, &ids, &eap, cancellable, perror);
+ }
mapi_id_array_release (&ids);
diff --git a/src/libexchangemapi/e-mapi-connection.h b/src/libexchangemapi/e-mapi-connection.h
index 46ce521..eb7cfdd 100644
--- a/src/libexchangemapi/e-mapi-connection.h
+++ b/src/libexchangemapi/e-mapi-connection.h
@@ -143,9 +143,10 @@ typedef struct {
} ResolveNamedIDsData;
typedef struct {
- mapi_id_t mid; /* message ID, from PR_MID */
- uint32_t msg_flags; /* MAPI MSGFLAG_* bit OR, from PR_MESSAGE_FLAGS */
- time_t last_modified; /* PR_LAST_MODIFICATION_TIME as UTC */
+ mapi_id_t mid; /* message ID, from PidTagMid */
+ const gchar *msg_class; /* PidTagMessageClass */
+ uint32_t msg_flags; /* MAPI MSGFLAG_* bit OR, from PidTagMessageFlags */
+ time_t last_modified; /* PidTagLastModificationTime as UTC */
} ListObjectsData;
struct _EMapiObject;
@@ -225,9 +226,9 @@ typedef gboolean (*BuildRestrictionsCB) (EMapiConnection *conn,
typedef gboolean (*ListObjectsCB) (EMapiConnection *conn,
mapi_id_t fid,
TALLOC_CTX *mem_ctx,
- const ListObjectsData *item_data,
- guint32 item_index,
- guint32 items_total,
+ const ListObjectsData *object_data,
+ guint32 obj_index,
+ guint32 obj_total,
gpointer user_data,
GCancellable *cancellable,
GError **perror);
diff --git a/src/libexchangemapi/e-mapi-mail-utils.c b/src/libexchangemapi/e-mapi-mail-utils.c
index 2f12040..34181c1 100644
--- a/src/libexchangemapi/e-mapi-mail-utils.c
+++ b/src/libexchangemapi/e-mapi-mail-utils.c
@@ -992,6 +992,7 @@ e_mapi_mail_utils_decode_email_address (EMapiConnection *conn,
{
gint ii;
const gchar *cname = NULL, *cemail = NULL;
+ const gchar *addr_type, *email_addr;
g_return_if_fail (conn != NULL);
g_return_if_fail (properties != NULL);
@@ -1007,18 +1008,18 @@ e_mapi_mail_utils_decode_email_address (EMapiConnection *conn,
cname = e_mapi_util_find_array_propval (properties, name_proptags[ii]);
}
+ addr_type = e_mapi_util_find_array_propval (properties, email_type_proptag);
+ email_addr = e_mapi_util_find_array_propval (properties, email_proptag);
+
+ if (addr_type && g_ascii_strcasecmp (addr_type, "SMTP") == 0)
+ cemail = email_addr;
+
for (ii = 0; ii < smtp_proptags_len && !cemail; ii++) {
cemail = e_mapi_util_find_array_propval (properties, smtp_proptags[ii]);
}
- if (!cemail) {
- const gchar *addr_type = e_mapi_util_find_array_propval (properties, email_type_proptag);
- const gchar *email_addr = e_mapi_util_find_array_propval (properties, email_proptag);
-
- if (addr_type && g_ascii_strcasecmp (addr_type, "EX") == 0 && email_addr)
- *email = e_mapi_connection_ex_to_smtp (conn, email_addr, name, NULL, NULL);
- else if (addr_type && g_ascii_strcasecmp (addr_type, "SMTP") == 0)
- cemail = email_addr;
+ if (!cemail && addr_type && g_ascii_strcasecmp (addr_type, "EX") == 0 && email_addr) {
+ *email = e_mapi_connection_ex_to_smtp (conn, email_addr, name, NULL, NULL);
}
if (!*email) {
diff --git a/src/libexchangemapi/e-mapi-utils.c b/src/libexchangemapi/e-mapi-utils.c
index 95318dc..58ac349 100644
--- a/src/libexchangemapi/e-mapi-utils.c
+++ b/src/libexchangemapi/e-mapi-utils.c
@@ -1318,3 +1318,189 @@ e_mapi_utils_ensure_utf8_string (uint32_t proptag,
return TRUE;
}
+
+/* takes pointer to a time_t and populates restrictions
+ with a restriction on PidTagLastModificationTime
+*/
+gboolean
+e_mapi_utils_build_last_modify_restriction (EMapiConnection *conn,
+ mapi_id_t fid,
+ TALLOC_CTX *mem_ctx,
+ struct mapi_SRestriction **restrictions,
+ gpointer user_data,
+ GCancellable *cancellable,
+ GError **perror)
+{
+ const time_t *latest_last_modify = user_data;
+ struct mapi_SRestriction *restriction = NULL;
+
+ g_return_val_if_fail (restrictions != NULL, FALSE);
+
+ if (latest_last_modify && *latest_last_modify > 0) {
+ struct SPropValue sprop;
+ struct timeval t;
+
+ restriction = talloc_zero (mem_ctx, struct mapi_SRestriction);
+ g_return_val_if_fail (restriction != NULL, FALSE);
+
+ restriction->rt = RES_PROPERTY;
+ restriction->res.resProperty.relop = RELOP_GT;
+ restriction->res.resProperty.ulPropTag = PidTagLastModificationTime;
+
+ t.tv_sec = *latest_last_modify;
+ t.tv_usec = 0;
+
+ set_SPropValue_proptag_date_timeval (&sprop, PidTagLastModificationTime, &t);
+ cast_mapi_SPropValue (mem_ctx, &(restriction->res.resProperty.lpProp), &sprop);
+ }
+
+ *restrictions = restriction;
+
+ return TRUE;
+}
+
+gboolean
+e_mapi_utils_get_folder_basic_properties_cb (EMapiConnection *conn,
+ mapi_id_t fid,
+ TALLOC_CTX *mem_ctx,
+ /* const */ struct mapi_SPropValue_array *properties,
+ gpointer user_data,
+ GCancellable *cancellable,
+ GError **perror)
+{
+ struct FolderBasicPropertiesData *fbp = user_data;
+ const mapi_id_t *pfid;
+ const struct FILETIME *plast_modified;
+ const uint32_t *pcontent_count;
+
+ g_return_val_if_fail (properties != NULL, FALSE);
+ g_return_val_if_fail (user_data != NULL, FALSE);
+
+ pfid = e_mapi_util_find_array_propval (properties, PidTagFolderId);
+ plast_modified = e_mapi_util_find_array_propval (properties, PidTagLastModificationTime);
+ pcontent_count = e_mapi_util_find_array_propval (properties, PidTagContentCount);
+
+ if (pfid)
+ fbp->fid = *pfid;
+ else
+ fbp->fid = 0;
+
+ if (pcontent_count)
+ fbp->obj_total = *pcontent_count;
+ else
+ fbp->obj_total = 0;
+
+ if (plast_modified)
+ fbp->last_modified = e_mapi_util_filetime_to_time_t (plast_modified);
+ else
+ fbp->last_modified = 0;
+
+ return TRUE;
+}
+
+gboolean
+e_mapi_utils_copy_to_mapi_SPropValue (TALLOC_CTX *mem_ctx,
+ struct mapi_SPropValue *mapi_sprop,
+ struct SPropValue *sprop)
+{
+ mapi_sprop->ulPropTag = sprop->ulPropTag;
+
+ switch (sprop->ulPropTag & 0xFFFF) {
+ case PT_BOOLEAN:
+ mapi_sprop->value.b = sprop->value.b;
+ return TRUE;
+ case PT_I2:
+ mapi_sprop->value.i = sprop->value.i;
+ return TRUE;
+ case PT_LONG:
+ mapi_sprop->value.l = sprop->value.l;
+ return TRUE;
+ case PT_DOUBLE:
+ memcpy (&mapi_sprop->value.dbl, (uint8_t *) &sprop->value.dbl, 8);
+ return TRUE;
+ case PT_I8:
+ mapi_sprop->value.d = sprop->value.d;
+ return TRUE;
+ case PT_STRING8:
+ mapi_sprop->value.lpszA = talloc_strdup (mem_ctx, sprop->value.lpszA);
+ return TRUE;
+ case PT_UNICODE:
+ mapi_sprop->value.lpszW = talloc_strdup (mem_ctx, sprop->value.lpszW);
+ return TRUE;
+ case PT_SYSTIME:
+ mapi_sprop->value.ft.dwLowDateTime = sprop->value.ft.dwLowDateTime;
+ mapi_sprop->value.ft.dwHighDateTime = sprop->value.ft.dwHighDateTime;
+ return TRUE;
+ case PT_BINARY:
+ mapi_sprop->value.bin.cb = sprop->value.bin.cb;
+ mapi_sprop->value.bin.lpb = talloc_memdup (mem_ctx, sprop->value.bin.lpb, sprop->value.bin.cb);
+ return TRUE;
+ case PT_ERROR:
+ mapi_sprop->value.err = sprop->value.err;
+ return TRUE;
+ case PT_CLSID:
+ {
+ DATA_BLOB b;
+
+ b.data = sprop->value.lpguid->ab;
+ b.length = 16;
+
+ GUID_from_ndr_blob (&b, &mapi_sprop->value.lpguid);
+
+ return TRUE;
+ }
+ case PT_SVREID:
+ mapi_sprop->value.bin.cb = sprop->value.bin.cb;
+ mapi_sprop->value.bin.lpb = talloc_memdup (mem_ctx, sprop->value.bin.lpb, sprop->value.bin.cb);
+ return TRUE;
+ case PT_MV_STRING8:
+ {
+ uint32_t i;
+
+ mapi_sprop->value.MVszA.cValues = sprop->value.MVszA.cValues;
+ mapi_sprop->value.MVszA.strings = talloc_array (mem_ctx, struct mapi_LPSTR, mapi_sprop->value.MVszA.cValues);
+ for (i = 0; i < mapi_sprop->value.MVszA.cValues; i++) {
+ mapi_sprop->value.MVszA.strings[i].lppszA = talloc_strdup (mem_ctx, sprop->value.MVszA.lppszA[i]);
+ }
+ return TRUE;
+ }
+ case PT_MV_UNICODE:
+ {
+ uint32_t i;
+
+ mapi_sprop->value.MVszW.cValues = sprop->value.MVszW.cValues;
+ mapi_sprop->value.MVszW.strings = talloc_array (mem_ctx, struct mapi_LPWSTR, mapi_sprop->value.MVszW.cValues);
+ for (i = 0; i < mapi_sprop->value.MVszW.cValues; i++) {
+ mapi_sprop->value.MVszW.strings[i].lppszW = talloc_strdup (mem_ctx, sprop->value.MVszW.lppszW[i]);
+ }
+ return TRUE;
+ }
+ case PT_MV_BINARY:
+ {
+ uint32_t i;
+
+ mapi_sprop->value.MVbin.cValues = sprop->value.MVbin.cValues;
+ mapi_sprop->value.MVbin.bin = talloc_array (mem_ctx, struct SBinary_short, mapi_sprop->value.MVbin.cValues);
+ for (i = 0; i < mapi_sprop->value.MVbin.cValues; i++) {
+ mapi_sprop->value.MVbin.bin[i].cb = sprop->value.MVbin.lpbin[i].cb;
+ mapi_sprop->value.MVbin.bin[i].lpb = talloc_memdup (mem_ctx, sprop->value.MVbin.lpbin[i].lpb, sprop->value.MVbin.lpbin[i].cb);
+ }
+ return TRUE;
+ }
+ case PT_MV_LONG:
+ {
+ uint32_t i;
+
+ mapi_sprop->value.MVl.cValues = sprop->value.MVl.cValues;
+ mapi_sprop->value.MVl.lpl = talloc_array (mem_ctx, uint32_t, mapi_sprop->value.MVl.cValues);
+ for (i = 0; i < mapi_sprop->value.MVl.cValues; i++) {
+ mapi_sprop->value.MVl.lpl[i] = sprop->value.MVl.lpl[i];
+ }
+ return TRUE;
+ }
+ default:
+ break;
+ }
+
+ return FALSE;
+}
diff --git a/src/libexchangemapi/e-mapi-utils.h b/src/libexchangemapi/e-mapi-utils.h
index 83fd432..79fd661 100644
--- a/src/libexchangemapi/e-mapi-utils.h
+++ b/src/libexchangemapi/e-mapi-utils.h
@@ -108,7 +108,32 @@ void e_mapi_util_time_t_to_filetime (const time_t tt, struct FILETIME *filetime)
void e_mapi_utils_global_lock (void);
void e_mapi_utils_global_unlock (void);
-gboolean e_mapi_utils_create_mapi_context (struct mapi_context **mapi_ctx, GError **perror);
+gboolean e_mapi_utils_create_mapi_context (struct mapi_context **mapi_ctx,
+ GError **perror);
void e_mapi_utils_destroy_mapi_context (struct mapi_context *mapi_ctx);
+gboolean e_mapi_utils_build_last_modify_restriction (EMapiConnection *conn,
+ mapi_id_t fid,
+ TALLOC_CTX *mem_ctx,
+ struct mapi_SRestriction **restrictions,
+ gpointer user_data,
+ GCancellable *cancellable,
+ GError **perror);
+struct FolderBasicPropertiesData
+{
+ mapi_id_t fid;
+ time_t last_modified;
+ guint32 obj_total;
+};
+
+gboolean e_mapi_utils_get_folder_basic_properties_cb (EMapiConnection *conn,
+ mapi_id_t fid,
+ TALLOC_CTX *mem_ctx,
+ /* const */ struct mapi_SPropValue_array *properties,
+ gpointer user_data, /* struct FolderBasicPropertiesData * */
+ GCancellable *cancellable,
+ GError **perror);
+gboolean e_mapi_utils_copy_to_mapi_SPropValue (TALLOC_CTX *mem_ctx,
+ struct mapi_SPropValue *mapi_sprop,
+ struct SPropValue *sprop);
#endif
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]