"Fossies" - the Fresh Open Source Software Archive

Member "evolution-mapi-3.46.1/src/libexchangemapi/e-mapi-connection.c" (2 Dec 2022, 227031 Bytes) of package /linux/misc/evolution-mapi-3.46.1.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "e-mapi-connection.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 3.44.0_vs_3.44.1.

    1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
    2 /*
    3  * This program is free software; you can redistribute it and/or
    4  * modify it under the terms of the GNU Lesser General Public
    5  * License as published by the Free Software Foundation; either
    6  * version 2 of the License, or (at your option) version 3.
    7  *
    8  * This program is distributed in the hope that it will be useful,
    9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   11  * Lesser General Public License for more details.
   12  *
   13  * You should have received a copy of the GNU Lesser General Public
   14  * License along with the program; if not, see <http://www.gnu.org/licenses/>
   15  *
   16  *
   17  * Authors:
   18  *    Srinivasa Ragavan <sragavan@novell.com>
   19  *    Suman Manjunath <msuman@novell.com>
   20  *
   21  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
   22  *
   23  */
   24 
   25 #include "evolution-mapi-config.h"
   26 
   27 #include <glib/gi18n-lib.h>
   28 #include <camel/camel.h>
   29 #include <libedataserver/libedataserver.h>
   30 
   31 #include <tevent.h>
   32 
   33 #include "e-mapi-connection.h"
   34 #include "e-mapi-folder.h"
   35 #include "e-mapi-utils.h"
   36 #include "e-mapi-book-utils.h"
   37 #include "e-mapi-mail-utils.h"
   38 #include "e-mapi-fast-transfer.h"
   39 
   40 /* how many bytes can be written within one property with SetProps() call;
   41    if its size exceeds this limit, it's converted into an EMapiStreamedProp */
   42 #define MAX_PROPERTY_WRITE_SIZE 2048
   43 
   44 /* how may contacts in one chunk can GAL ask to fetch */
   45 #define MAX_GAL_CHUNK 50
   46 
   47 static void register_connection (EMapiConnection *conn);
   48 static void unregister_connection (EMapiConnection *conn);
   49 static gboolean mapi_profile_create (struct mapi_context *mapi_ctx, const EMapiProfileData *empd, mapi_profile_callback_t callback, gconstpointer data, GCancellable *cancellable, GError **perror, gboolean use_locking);
   50 static struct mapi_session *mapi_profile_load (ESourceRegistry *registry, struct mapi_context *mapi_ctx, const gchar *profname, const ENamedParameters *credentials, GCancellable *cancellable, GError **perror);
   51 
   52 /* GObject foo - begin */
   53 
   54 /* These three macros require 'priv' variable of type EMapiConnectionPrivate */
   55 #define LOCK(_cclb,_err,_ret) G_STMT_START {                        \
   56     e_mapi_debug_print ("%s: %s: lock(session & global)", G_STRLOC, G_STRFUNC); \
   57     if (!e_mapi_cancellable_rec_mutex_lock (&priv->session_lock, _cclb, _err)) {    \
   58         e_mapi_debug_print ("   %s: %s: cancelled before got session lock)", G_STRLOC, G_STRFUNC); \
   59         return _ret;                                \
   60     }                                       \
   61     if (!e_mapi_utils_global_lock (_cclb, _err)) {                  \
   62         e_mapi_cancellable_rec_mutex_unlock (&priv->session_lock);      \
   63         e_mapi_debug_print ("   %s: %s: cancelled before got global lock)", G_STRLOC, G_STRFUNC); \
   64         return _ret;                                \
   65     }                                       \
   66     } G_STMT_END
   67 
   68 #define LOCK_VOID(_cclb,_err) G_STMT_START {                        \
   69     e_mapi_debug_print ("%s: %s: lock(session & global)", G_STRLOC, G_STRFUNC); \
   70     if (!e_mapi_cancellable_rec_mutex_lock (&priv->session_lock, _cclb, _err)) {    \
   71         e_mapi_debug_print ("   %s: %s: cancelled before got session lock)", G_STRLOC, G_STRFUNC); \
   72         return;                                 \
   73     }                                       \
   74     if (!e_mapi_utils_global_lock (_cclb, _err)) {                  \
   75         e_mapi_cancellable_rec_mutex_unlock (&priv->session_lock);      \
   76         e_mapi_debug_print ("   %s: %s: cancelled before got global lock)", G_STRLOC, G_STRFUNC); \
   77         return;                                 \
   78     }                                       \
   79     } G_STMT_END
   80 
   81 #define UNLOCK() G_STMT_START {                             \
   82     e_mapi_debug_print ("%s: %s: unlock(session & global)", G_STRLOC, G_STRFUNC);   \
   83     e_mapi_utils_global_unlock ();                          \
   84     e_mapi_cancellable_rec_mutex_unlock (&priv->session_lock);          \
   85     } G_STMT_END
   86 
   87 #define e_return_val_mapi_error_if_fail(expr, _code, _val)              \
   88     G_STMT_START {                                  \
   89         if (G_LIKELY(expr)) {                           \
   90         } else {                                \
   91             g_log (G_LOG_DOMAIN,                        \
   92                 G_LOG_LEVEL_CRITICAL,                   \
   93                 "file %s: line %d (%s): assertion `%s' failed",     \
   94                 __FILE__, __LINE__, G_STRFUNC, #expr);          \
   95             if (perror)                         \
   96                 g_set_error (perror, E_MAPI_ERROR, (_code),     \
   97                     "file %s: line %d (%s): assertion `%s' failed", \
   98                     __FILE__, __LINE__, G_STRFUNC, #expr);      \
   99             return (_val);                          \
  100         }                                   \
  101     } G_STMT_END
  102 
  103 /* Create the EDataCal error quark */
  104 GQuark
  105 e_mapi_error_quark (void)
  106 {
  107     static GQuark quark = 0;
  108     if (!quark)
  109         quark = g_quark_from_static_string ("e_mapi_error");
  110     return quark;
  111 }
  112 
  113 void
  114 make_mapi_error (GError **perror, const gchar *context, enum MAPISTATUS mapi_status)
  115 {
  116     const gchar *error_msg = NULL, *status_name;
  117     gchar *to_free = NULL;
  118     GQuark error_domain;
  119     gint error_code;
  120     GError *error;
  121 
  122     if (!perror)
  123         return;
  124 
  125     /* do not overwrite already set error */
  126     if (*perror != NULL)
  127         return;
  128 
  129     switch (mapi_status) {
  130     case MAPI_E_SUCCESS:
  131         return;
  132     #define err(_code, _str)        \
  133         case _code:         \
  134             error_msg = _str;   \
  135             break
  136 
  137     err (MAPI_E_LOGON_FAILED,           _("Failed to login into the server"));
  138     err (MAPI_E_SESSION_LIMIT,          _("Cannot create more sessions, session limit was reached"));
  139     err (MAPI_E_USER_CANCEL,            _("User cancelled operation"));
  140     err (MAPI_E_UNABLE_TO_ABORT,            _("Unable to abort"));
  141     err (ecRpcFailed,               _("Network error"));
  142     err (MAPI_E_DISK_ERROR,             _("Disk error"));
  143     err (MAPI_E_PASSWORD_CHANGE_REQUIRED,       _("Password change required"));
  144     err (MAPI_E_PASSWORD_EXPIRED,           _("Password expired"));
  145     err (MAPI_E_INVALID_WORKSTATION_ACCOUNT,    _("Invalid workstation account"));
  146     err (MAPI_E_INVALID_ACCESS_TIME,        _("Invalid access time"));
  147     err (MAPI_E_ACCOUNT_DISABLED,           _("Account is disabled"));
  148     err (MAPI_E_END_OF_SESSION,         _("End of session"));
  149     err (MAPI_E_NOT_INITIALIZED,            _("MAPI is not initialized or connected"));
  150     err (MAPI_E_NO_ACCESS,              _("Permission denied"));
  151     err (ecShutoffQuotaExceeded,            _("Mailbox quota exceeded"));
  152 
  153     #undef err
  154 
  155     default:
  156         status_name = mapi_get_errstr (mapi_status);
  157         if (!status_name)
  158             status_name = "";
  159         to_free = g_strdup_printf (_("MAPI error %s (0x%x) occurred"), status_name, mapi_status);
  160         error_msg = to_free;
  161     }
  162 
  163     g_return_if_fail (error_msg != NULL);
  164 
  165     error_domain = E_MAPI_ERROR;
  166     error_code = mapi_status;
  167 
  168     if (mapi_status == MAPI_E_USER_CANCEL) {
  169         error_domain = G_IO_ERROR;
  170         error_code = G_IO_ERROR_CANCELLED;
  171     }
  172 
  173     if (context && *context) {
  174         /* Translators: The first '%s' is replaced with an error context,
  175            aka where the error occurred, the second '%s' is replaced with
  176            the error message. */
  177         error = g_error_new (error_domain, error_code, C_("EXCHANGEMAPI_ERROR", "%s: %s"), context, error_msg);
  178     } else {
  179         error = g_error_new_literal (error_domain, error_code, error_msg);
  180     }
  181 
  182     g_free (to_free);
  183 
  184     g_propagate_error (perror, error);
  185 }
  186 
  187 struct _EMapiConnectionPrivate {
  188     ESourceRegistry *registry;
  189 
  190     struct mapi_context *mapi_ctx;
  191     struct mapi_session *session;
  192     EMapiCancellableRecMutex session_lock;
  193 
  194     gchar *profile;         /* profile name, where the session is connected to */
  195     mapi_object_t msg_store;    /* valid only when session != NULL */
  196 
  197     gboolean has_public_store;  /* whether is 'public_store' filled */
  198     mapi_object_t public_store;
  199 
  200     GHashTable *foreign_stores; /* username (gchar *) => msg_store (mapi_object_t *); opened foreign stores */
  201 
  202     GSList *folders;        /* list of ExchangeMapiFolder pointers */
  203     GRecMutex folders_lock;     /* lock for 'folders' variable */
  204 
  205     GHashTable *named_ids;      /* cache of named ids; key is a folder ID, value is a hash table
  206                        of named_id to prop_id in that respective folder */
  207 
  208     GHashTable *known_notifications;/* mapi_id_t * -> uint32_t for Unsubscribe call */
  209     GThread *notification_thread;
  210     EFlag *notification_flag;
  211     enum MAPISTATUS register_notification_result; /* MAPI_E_RESERVED if not called yet */
  212     gint notification_poll_seconds; /* delay between polls, in seconds */
  213 };
  214 
  215 G_DEFINE_TYPE_WITH_PRIVATE (EMapiConnection, e_mapi_connection, G_TYPE_OBJECT)
  216 
  217 enum {
  218     SERVER_NOTIFICATION,
  219     LAST_SIGNAL
  220 };
  221 
  222 static guint signals[LAST_SIGNAL];
  223 
  224 static gboolean
  225 stop_notification (EMapiConnectionPrivate *priv,
  226            uint32_t conn_id,
  227            GCancellable *cancellable,
  228            GError **perror)
  229 {
  230     enum MAPISTATUS ms;
  231 
  232     e_return_val_mapi_error_if_fail (priv != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
  233     e_return_val_mapi_error_if_fail (priv->session != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
  234 
  235     LOCK (cancellable, perror, FALSE);
  236 
  237     ms = Unsubscribe (priv->session, conn_id);
  238     if (ms != MAPI_E_SUCCESS)
  239         make_mapi_error (perror, "Unsubscribe", ms);
  240 
  241     UNLOCK ();
  242 
  243     return ms == MAPI_E_SUCCESS;
  244 }
  245 
  246 static void
  247 call_stop_notification (gpointer key,
  248             gpointer value,
  249             gpointer user_data)
  250 {
  251     stop_notification (user_data, GPOINTER_TO_UINT (value), NULL, NULL);
  252 }
  253 
  254 static void
  255 stop_all_notifications (EMapiConnectionPrivate *priv)
  256 {
  257     g_return_if_fail (priv != NULL);
  258 
  259     if (!priv->notification_thread)
  260         return;
  261 
  262     LOCK_VOID (NULL, NULL);
  263     if (priv->session)
  264         g_hash_table_foreach (priv->known_notifications, call_stop_notification, priv);
  265     g_hash_table_remove_all (priv->known_notifications);
  266     e_flag_set (priv->notification_flag);
  267     UNLOCK ();
  268 
  269     g_thread_join (priv->notification_thread);
  270     priv->notification_thread = NULL;
  271 }
  272 
  273 static void
  274 release_foreign_stores_cb (gpointer pusername, gpointer pmsg_store, gpointer user_data)
  275 {
  276     mapi_object_t *msg_store = pmsg_store;
  277 
  278     g_return_if_fail (msg_store != NULL);
  279 
  280     mapi_object_release (msg_store);
  281     talloc_free (msg_store);
  282 }
  283 
  284 /* should have session_lock locked already, when calling this function */
  285 static void
  286 disconnect (EMapiConnectionPrivate *priv,
  287         gboolean clean)
  288 {
  289     g_return_if_fail (priv != NULL);
  290 
  291     if (!priv->session)
  292         return;
  293 
  294     g_rec_mutex_lock (&priv->folders_lock);
  295     if (priv->folders)
  296         e_mapi_folder_free_list (priv->folders);
  297     priv->folders = NULL;
  298     g_rec_mutex_unlock (&priv->folders_lock);
  299 
  300     if (priv->has_public_store)
  301         mapi_object_release (&priv->public_store);
  302 
  303     g_hash_table_foreach (priv->foreign_stores, release_foreign_stores_cb, NULL);
  304     g_hash_table_remove_all (priv->foreign_stores); 
  305 
  306     if (clean) {
  307         Logoff (&priv->msg_store);
  308         /* it's released by the Logoff() call
  309         mapi_object_release (&priv->msg_store); */
  310     }
  311 
  312     if (priv->named_ids)
  313         g_hash_table_remove_all (priv->named_ids);
  314 
  315     priv->session = NULL;
  316     priv->has_public_store = FALSE;
  317 }
  318 
  319 /* should have session_lock locked already, when calling this function */
  320 static gboolean
  321 ensure_public_store (EMapiConnectionPrivate *priv,
  322              GError **perror)
  323 {
  324     e_return_val_mapi_error_if_fail (priv != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
  325 
  326     if (!priv->session)
  327         return FALSE;
  328 
  329     if (!priv->has_public_store) {
  330         enum MAPISTATUS ms;
  331 
  332         mapi_object_init (&priv->public_store);
  333 
  334         ms = OpenPublicFolder (priv->session, &priv->public_store);
  335         if (ms == MAPI_E_SUCCESS) {
  336             priv->has_public_store = TRUE;
  337         } else {
  338             make_mapi_error (perror, "OpenPublicFolder", ms);
  339         }
  340     }
  341 
  342     return priv->has_public_store;
  343 }
  344 
  345 /* should have session_lock locked already, when calling this function */
  346 static gboolean
  347 ensure_foreign_store (EMapiConnectionPrivate *priv,
  348               const gchar *username,
  349               mapi_object_t **pmsg_store,
  350               GError **perror)
  351 {
  352     enum MAPISTATUS ms;
  353     mapi_object_t *msg_store;
  354 
  355     e_return_val_mapi_error_if_fail (priv != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
  356     e_return_val_mapi_error_if_fail (username != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
  357     e_return_val_mapi_error_if_fail (pmsg_store != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
  358 
  359     *pmsg_store = NULL;
  360 
  361     if (!priv->session)
  362         return FALSE;
  363 
  364     msg_store = g_hash_table_lookup (priv->foreign_stores, username);
  365     if (msg_store) {
  366         *pmsg_store = msg_store;
  367         return TRUE;
  368     }
  369 
  370     msg_store = talloc_zero (priv->session, mapi_object_t);
  371     mapi_object_init (msg_store);
  372 
  373     ms = OpenUserMailbox (priv->session, username, msg_store);
  374     if (ms != MAPI_E_SUCCESS) {
  375         make_mapi_error (perror, "OpenUserMailbox", ms);
  376 
  377         mapi_object_release (msg_store);
  378         talloc_free (msg_store);
  379 
  380         return FALSE;
  381     }
  382 
  383     g_hash_table_insert (priv->foreign_stores, g_strdup (username), msg_store);
  384 
  385     *pmsg_store = msg_store;
  386 
  387     return TRUE;
  388 }
  389 
  390 static void
  391 e_mapi_connection_dispose (GObject *object)
  392 {
  393     EMapiConnection *conn = E_MAPI_CONNECTION (object);
  394 
  395     unregister_connection (conn);
  396 
  397     if (conn->priv) {
  398         stop_all_notifications (conn->priv);
  399     }
  400 
  401     G_OBJECT_CLASS (e_mapi_connection_parent_class)->dispose (object);
  402 }
  403 
  404 static void
  405 e_mapi_connection_finalize (GObject *object)
  406 {
  407     EMapiConnection *conn;
  408     EMapiConnectionPrivate *priv;
  409 
  410     conn = E_MAPI_CONNECTION (object);
  411     priv = conn->priv;
  412 
  413     if (priv) {
  414         LOCK_VOID (NULL, NULL);
  415 
  416         disconnect (priv, TRUE && e_mapi_connection_connected (conn));
  417 
  418         g_clear_pointer (&priv->profile, g_free);
  419         g_clear_pointer (&priv->named_ids, g_hash_table_destroy);
  420         g_clear_pointer (&priv->foreign_stores, g_hash_table_destroy);
  421         g_clear_pointer (&priv->mapi_ctx, e_mapi_utils_destroy_mapi_context);
  422         g_clear_pointer (&priv->known_notifications, g_hash_table_destroy);
  423         g_clear_pointer (&priv->notification_flag, e_flag_free);
  424         g_clear_object (&priv->registry);
  425 
  426         UNLOCK ();
  427 
  428         e_mapi_cancellable_rec_mutex_clear (&priv->session_lock);
  429         g_rec_mutex_clear (&priv->folders_lock);
  430     }
  431 
  432     G_OBJECT_CLASS (e_mapi_connection_parent_class)->finalize (object);
  433 }
  434 
  435 static void
  436 e_mapi_connection_class_init (EMapiConnectionClass *klass)
  437 {
  438     GObjectClass *object_class;
  439 
  440     object_class = G_OBJECT_CLASS (klass);
  441     object_class->dispose = e_mapi_connection_dispose;
  442     object_class->finalize = e_mapi_connection_finalize;
  443 
  444     signals[SERVER_NOTIFICATION] = g_signal_new (
  445         "server-notification",
  446         G_OBJECT_CLASS_TYPE (object_class),
  447         G_SIGNAL_RUN_FIRST | G_SIGNAL_DETAILED | G_SIGNAL_ACTION,
  448         0, NULL, NULL,
  449         g_cclosure_marshal_VOID__UINT_POINTER,
  450         G_TYPE_NONE, 2,
  451         G_TYPE_UINT, G_TYPE_POINTER);
  452 }
  453 
  454 static void
  455 e_mapi_connection_init (EMapiConnection *conn)
  456 {
  457     conn->priv = e_mapi_connection_get_instance_private (conn);
  458     g_return_if_fail (conn->priv != NULL);
  459 
  460     e_mapi_cancellable_rec_mutex_init (&conn->priv->session_lock);
  461     g_rec_mutex_init (&conn->priv->folders_lock);
  462 
  463     conn->priv->session = NULL;
  464     conn->priv->profile = NULL;
  465     conn->priv->has_public_store = FALSE;
  466     conn->priv->folders = NULL;
  467 
  468     conn->priv->foreign_stores = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
  469     conn->priv->named_ids = g_hash_table_new_full (g_int64_hash, g_int64_equal, g_free, (GDestroyNotify) g_hash_table_destroy);
  470 
  471     conn->priv->known_notifications = g_hash_table_new_full (g_int64_hash, g_int64_equal, g_free, NULL);
  472     conn->priv->notification_thread = NULL;
  473     conn->priv->notification_flag = e_flag_new ();
  474     conn->priv->register_notification_result = MAPI_E_RESERVED;
  475     conn->priv->notification_poll_seconds = 60;
  476 
  477     if (g_getenv ("MAPI_SERVER_POLL")) {
  478         conn->priv->notification_poll_seconds = atoi (g_getenv ("MAPI_SERVER_POLL"));
  479         if (conn->priv->notification_poll_seconds < 1)
  480             conn->priv->notification_poll_seconds = 60;
  481     }
  482 
  483     register_connection (conn);
  484 }
  485 
  486 /* GObject foo - end */
  487 
  488 /* tracking alive connections - begin  */
  489 
  490 static GSList *known_connections = NULL;
  491 G_LOCK_DEFINE_STATIC (known_connections);
  492 
  493 static void
  494 register_connection (EMapiConnection *conn)
  495 {
  496     g_return_if_fail (conn != NULL);
  497     g_return_if_fail (E_MAPI_IS_CONNECTION (conn));
  498 
  499     G_LOCK (known_connections);
  500     /* append to prefer older connections when searching with e_mapi_connection_find() */
  501     known_connections = g_slist_append (known_connections, conn);
  502     G_UNLOCK (known_connections);
  503 }
  504 
  505 static void
  506 unregister_connection (EMapiConnection *conn)
  507 {
  508     g_return_if_fail (conn != NULL);
  509     g_return_if_fail (E_MAPI_IS_CONNECTION (conn));
  510 
  511     G_LOCK (known_connections);
  512     if (!g_slist_find (known_connections, conn)) {
  513         G_UNLOCK (known_connections);
  514         return;
  515     }
  516 
  517     known_connections = g_slist_remove (known_connections, conn);
  518     G_UNLOCK (known_connections);
  519 }
  520 
  521 /* Tries to find a connection associated with the 'profile'.
  522    If there are more, then the first created is returned.
  523    Note if it doesn't return NULL, then the returned pointer
  524    should be g_object_unref-ed, when done with it.
  525 */
  526 EMapiConnection *
  527 e_mapi_connection_find (const gchar *profile)
  528 {
  529     GSList *l;
  530     EMapiConnection *res = NULL;
  531 
  532     g_return_val_if_fail (profile != NULL, NULL);
  533 
  534     G_LOCK (known_connections);
  535     for (l = known_connections; l != NULL && res == NULL; l = l->next) {
  536         EMapiConnection *conn = E_MAPI_CONNECTION (l->data);
  537         EMapiConnectionPrivate *priv = conn->priv;
  538 
  539         if (priv && priv->profile && g_str_equal (profile, priv->profile) &&
  540             e_mapi_connection_connected (conn))
  541             res = conn;
  542     }
  543 
  544     if (res)
  545         g_object_ref (res);
  546 
  547     G_UNLOCK (known_connections);
  548 
  549     return res;
  550 }
  551 
  552 /* tracking alive connections - end  */
  553 
  554 /* Specifies READ/WRITE sizes to be used while handling normal streams */
  555 #define STREAM_MAX_READ_SIZE    0x8000
  556 #define STREAM_MAX_READ_SIZE_DF 0x1000
  557 #define STREAM_MAX_WRITE_SIZE   0x1000
  558 
  559 #define CHECK_CORRECT_CONN_AND_GET_PRIV(_conn, _val)                            \
  560     EMapiConnectionPrivate *priv;                                   \
  561                                                     \
  562     e_return_val_mapi_error_if_fail (_conn != NULL, MAPI_E_INVALID_PARAMETER, _val);        \
  563     e_return_val_mapi_error_if_fail (E_MAPI_IS_CONNECTION (_conn), MAPI_E_INVALID_PARAMETER, _val); \
  564                                                     \
  565     priv = (_conn)->priv;                                       \
  566     e_return_val_mapi_error_if_fail (priv != NULL, MAPI_E_INVALID_PARAMETER, _val);
  567 
  568 /* Creates a new connection object and connects to a server as defined in 'profile' */
  569 EMapiConnection *
  570 e_mapi_connection_new (ESourceRegistry *registry,
  571                const gchar *profile,
  572                const ENamedParameters *credentials,
  573                GCancellable *cancellable,
  574                GError **perror)
  575 {
  576     EMapiConnection *conn;
  577     EMapiConnectionPrivate *priv;
  578     struct mapi_context *mapi_ctx = NULL;
  579     struct mapi_session *session;
  580     enum MAPISTATUS ms;
  581 
  582     e_return_val_mapi_error_if_fail (profile != NULL, MAPI_E_INVALID_PARAMETER, NULL);
  583 
  584     if (!e_mapi_utils_create_mapi_context (&mapi_ctx, perror))
  585         return NULL;
  586 
  587     session = mapi_profile_load (registry, mapi_ctx, profile, credentials, cancellable, perror);
  588     if (!session) {
  589         e_mapi_utils_destroy_mapi_context (mapi_ctx);
  590         return NULL;
  591     }
  592 
  593     conn = g_object_new (E_MAPI_TYPE_CONNECTION, NULL);
  594     priv = conn->priv;
  595     e_return_val_mapi_error_if_fail (priv != NULL, MAPI_E_INVALID_PARAMETER, conn);
  596 
  597     LOCK (cancellable, perror, NULL);
  598     mapi_object_init (&priv->msg_store);
  599     priv->registry = registry ? g_object_ref (registry) : NULL;
  600     priv->mapi_ctx = mapi_ctx;
  601     priv->session = session;
  602 
  603     /* Open the message store and keep it opened for all the life-time for this connection */
  604     ms = OpenMsgStore (priv->session, &priv->msg_store);
  605     if (ms != MAPI_E_SUCCESS) {
  606         make_mapi_error (perror, "OpenMsgStore", ms);
  607 
  608         /* how to close and free session without store? */
  609         priv->session = NULL;
  610 
  611         UNLOCK ();
  612         g_object_unref (conn);
  613         return NULL;
  614     }
  615 
  616     if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
  617         UNLOCK ();
  618         g_object_unref (conn);
  619         return NULL;
  620     }
  621 
  622     priv->profile = g_strdup (profile);
  623     priv->has_public_store = FALSE;
  624 
  625     UNLOCK ();
  626 
  627     e_mapi_debug_print ("%s: %s: Connected ", G_STRLOC, G_STRFUNC);
  628 
  629     return conn;
  630 }
  631 
  632 gboolean
  633 e_mapi_connection_disconnect (EMapiConnection *conn,
  634                   gboolean clean,
  635                   GCancellable *cancellable,
  636                   GError **perror)
  637 {
  638     gboolean res = FALSE;
  639 
  640     CHECK_CORRECT_CONN_AND_GET_PRIV (conn, FALSE);
  641 
  642     LOCK (cancellable, perror, FALSE);
  643 
  644     res = priv->session != NULL;
  645     disconnect (priv, clean && e_mapi_connection_connected (conn));
  646 
  647     UNLOCK ();
  648 
  649     return res;
  650 }
  651 
  652 gboolean
  653 e_mapi_connection_reconnect (EMapiConnection *conn,
  654                  const ENamedParameters *credentials,
  655                  GCancellable *cancellable,
  656                  GError **perror)
  657 {
  658     enum MAPISTATUS ms;
  659 
  660     CHECK_CORRECT_CONN_AND_GET_PRIV (conn, FALSE);
  661 
  662     e_return_val_mapi_error_if_fail (priv->profile != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
  663 
  664     LOCK (cancellable, perror, FALSE);
  665     if (priv->session)
  666         e_mapi_connection_disconnect (conn, FALSE, cancellable, perror);
  667 
  668     priv->session = mapi_profile_load (priv->registry, priv->mapi_ctx, priv->profile, credentials, cancellable, perror);
  669     if (!priv->session) {
  670         e_mapi_debug_print ("%s: %s: Login failed ", G_STRLOC, G_STRFUNC);
  671         UNLOCK ();
  672         return FALSE;
  673     }
  674 
  675     mapi_object_init (&priv->msg_store);
  676 
  677     /* Open the message store and keep it opened for all the life-time for this connection */
  678     ms = OpenMsgStore (priv->session, &priv->msg_store);
  679     if (ms != MAPI_E_SUCCESS) {
  680         make_mapi_error (perror, "OpenMsgStore", ms);
  681 
  682         /* how to close and free session without store? */
  683         priv->session = NULL;
  684 
  685         UNLOCK ();
  686         return FALSE;
  687     }
  688 
  689     priv->has_public_store = FALSE;
  690 
  691     if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
  692         UNLOCK ();
  693         return FALSE;
  694     }
  695 
  696     UNLOCK ();
  697 
  698     e_mapi_debug_print ("%s: %s: Connected ", G_STRLOC, G_STRFUNC);
  699 
  700     return priv->session != NULL;
  701 }
  702 
  703 static gboolean
  704 can_reach_mapi_server (const gchar *server_address,
  705                GCancellable *cancellable,
  706                GError **perror)
  707 {
  708     GNetworkMonitor *network_monitor;
  709     GSocketConnectable *connectable;
  710     GError *local_error = NULL;
  711     gboolean reachable;
  712 
  713     g_return_val_if_fail (server_address != NULL, FALSE);
  714 
  715     network_monitor = e_network_monitor_get_default ();
  716     connectable = g_network_address_new (server_address, 135);
  717     reachable = g_network_monitor_can_reach (network_monitor, connectable, cancellable, &local_error);
  718     g_object_unref (connectable);
  719 
  720     if (!reachable) {
  721         if (local_error)
  722             g_propagate_error (perror, local_error);
  723         else
  724             g_set_error (perror, G_IO_ERROR, G_IO_ERROR_HOST_UNREACHABLE, _("Server “%s” cannot be reached"), server_address);
  725     }
  726 
  727     return reachable;
  728 }
  729 
  730 gboolean
  731 e_mapi_connection_connected (EMapiConnection *conn)
  732 {
  733     /* to have this used in the below macros */
  734     GError **perror = NULL;
  735     gboolean res;
  736 
  737     CHECK_CORRECT_CONN_AND_GET_PRIV (conn, FALSE);
  738 
  739     LOCK (NULL, NULL, FALSE);
  740 
  741     res = priv->session != NULL;
  742     if (res) {
  743         struct mapi_profile *profile;
  744 
  745         profile = talloc_zero (priv->mapi_ctx, struct mapi_profile);
  746         if (MAPI_E_SUCCESS == OpenProfile (priv->mapi_ctx, profile, priv->profile, NULL)) {
  747             res = can_reach_mapi_server (profile->server, NULL, perror);
  748             ShutDown (profile);
  749         }
  750 
  751         talloc_free (profile);
  752     }
  753 
  754     UNLOCK ();
  755 
  756     return res;
  757 }
  758 
  759 static gboolean
  760 may_skip_property (uint32_t proptag)
  761 {
  762     /* skip all "strange" properties */
  763     gboolean skip = TRUE;
  764 
  765     switch (proptag & 0xFFFF) {
  766     case PT_BOOLEAN:
  767     case PT_I2:
  768     case PT_LONG:
  769     case PT_DOUBLE:
  770     case PT_I8:
  771     case PT_STRING8:
  772     case PT_UNICODE:
  773     case PT_SYSTIME:
  774     case PT_BINARY:
  775     case PT_ERROR:
  776     case PT_CLSID:
  777     case PT_SVREID:
  778     case PT_MV_STRING8:
  779     case PT_MV_UNICODE:
  780     case PT_MV_BINARY:
  781     case PT_MV_LONG:
  782         skip = FALSE;
  783         break;
  784     default:
  785         break;
  786     }
  787 
  788     return skip;
  789 }
  790 
  791 gboolean
  792 e_mapi_connection_test_foreign_folder (EMapiConnection *conn,
  793                        const gchar *username,
  794                        const gchar *folder_name,
  795                        mapi_id_t *fid, /* out */
  796                        GCancellable *cancellable,
  797                        GError **perror)
  798 {
  799     enum MAPISTATUS ms;
  800     mapi_id_t foreign_fid = 0;
  801     mapi_object_t obj_store, obj_folder;
  802 
  803     CHECK_CORRECT_CONN_AND_GET_PRIV (conn, FALSE);
  804     e_return_val_mapi_error_if_fail (priv->session != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
  805     e_return_val_mapi_error_if_fail (username != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
  806     e_return_val_mapi_error_if_fail (folder_name != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
  807     e_return_val_mapi_error_if_fail (fid != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
  808 
  809     LOCK (cancellable, perror, FALSE);
  810 
  811     mapi_object_init (&obj_store);
  812     mapi_object_init (&obj_folder);
  813 
  814     ms = OpenUserMailbox (priv->session, username, &obj_store);
  815     if (ms != MAPI_E_SUCCESS) {
  816         make_mapi_error (perror, "OpenUserMailbox", ms);
  817         goto cleanup;
  818     }
  819 
  820     if (folder_name[0] == '0' && folder_name[1] == 'x' && e_mapi_util_mapi_id_from_string (folder_name + 2, &foreign_fid)) {
  821         ms = OpenFolder (&obj_store, foreign_fid, &obj_folder);
  822         if (ms != MAPI_E_SUCCESS) {
  823             make_mapi_error (perror, "OpenFolder", ms);
  824             goto cleanup;
  825         }
  826     } else {
  827         uint32_t def_folder_id = 0;
  828 
  829         /* intentionally not localized strings */
  830         if (g_ascii_strcasecmp (folder_name, "Inbox") == 0) {
  831             def_folder_id = olFolderInbox;
  832         } else if (g_ascii_strcasecmp (folder_name, "DeletedItems") == 0) {
  833             def_folder_id = olFolderDeletedItems;
  834         } else if (g_ascii_strcasecmp (folder_name, "Outbox") == 0) {
  835             def_folder_id = olFolderOutbox;
  836         } else if (g_ascii_strcasecmp (folder_name, "SentMail") == 0) {
  837             def_folder_id = olFolderSentMail;
  838         } else if (g_ascii_strcasecmp (folder_name, "Calendar") == 0) {
  839             def_folder_id = olFolderCalendar;
  840         } else if (g_ascii_strcasecmp (folder_name, "Contacts") == 0) {
  841             def_folder_id = olFolderContacts;
  842         } else if (g_ascii_strcasecmp (folder_name, "Notes") == 0) {
  843             def_folder_id = olFolderNotes;
  844         } else if (g_ascii_strcasecmp (folder_name, "Tasks") == 0) {
  845             def_folder_id = olFolderTasks;
  846         } else if (g_ascii_strcasecmp (folder_name, "Drafts") == 0) {
  847             def_folder_id = olFolderDrafts;
  848         } else if (g_ascii_strcasecmp (folder_name, "Junk") == 0) {
  849             def_folder_id = olFolderJunk;
  850         } else if (!e_mapi_util_mapi_id_from_string (folder_name, &foreign_fid)) {
  851             ms = MAPI_E_CALL_FAILED;
  852             g_propagate_error (perror, g_error_new (E_MAPI_ERROR, ms, _("Folder name “%s” is not a known default folder name, nor folder ID."), folder_name));
  853             goto cleanup;
  854         }
  855 
  856         if (def_folder_id != 0) {
  857             ms = GetDefaultFolder (&obj_store, &foreign_fid, def_folder_id);
  858             if (ms != MAPI_E_SUCCESS) {
  859                 make_mapi_error (perror, "GetDefaultFolder", ms);
  860                 goto cleanup;
  861             }
  862         }
  863 
  864         ms = OpenFolder (&obj_store, foreign_fid, &obj_folder);
  865         if (ms != MAPI_E_SUCCESS) {
  866             make_mapi_error (perror, "OpenFolder", ms);
  867             goto cleanup;
  868         }
  869     }
  870 
  871     *fid = mapi_object_get_id (&obj_folder);
  872 
  873  cleanup:
  874     mapi_object_release (&obj_folder);
  875     mapi_object_release (&obj_store);
  876 
  877     UNLOCK ();
  878 
  879     return ms == MAPI_E_SUCCESS;
  880 }
  881 
  882 gboolean
  883 e_mapi_connection_get_public_folder (EMapiConnection *conn,
  884                      mapi_object_t *obj_folder,
  885                      GCancellable *cancellable,
  886                      GError **perror)
  887 {
  888     enum MAPISTATUS ms;
  889 
  890     CHECK_CORRECT_CONN_AND_GET_PRIV (conn, FALSE);
  891     e_return_val_mapi_error_if_fail (priv->session != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
  892 
  893     LOCK (cancellable, perror, FALSE);
  894 
  895     mapi_object_init (obj_folder);
  896 
  897     ms = OpenPublicFolder (priv->session, obj_folder);
  898     if (ms != MAPI_E_SUCCESS) {
  899         make_mapi_error (perror, "OpenPublicFolder", ms);
  900     }
  901 
  902     UNLOCK ();
  903 
  904     return ms == MAPI_E_SUCCESS;
  905 }
  906 
  907 gboolean
  908 e_mapi_connection_peek_store (EMapiConnection *conn,
  909                   gboolean public_store,
  910                   const gchar *foreign_username,
  911                   mapi_object_t **obj_store, /* out */
  912                   GCancellable *cancellable,
  913                   GError **perror)
  914 {
  915     CHECK_CORRECT_CONN_AND_GET_PRIV (conn, FALSE);
  916     e_return_val_mapi_error_if_fail (priv->session != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
  917     if (public_store)
  918         e_return_val_mapi_error_if_fail (foreign_username == NULL, MAPI_E_INVALID_PARAMETER, FALSE);
  919     if (foreign_username)
  920         e_return_val_mapi_error_if_fail (!public_store, MAPI_E_INVALID_PARAMETER, FALSE);
  921     e_return_val_mapi_error_if_fail (obj_store != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
  922 
  923     LOCK (cancellable, perror, FALSE);
  924 
  925     if (public_store) {
  926         if (!ensure_public_store (priv, perror)) {
  927             UNLOCK ();
  928             return FALSE;
  929         }
  930 
  931         if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
  932             UNLOCK ();
  933             return FALSE;
  934         }
  935 
  936         *obj_store = &priv->public_store;
  937 
  938         UNLOCK ();
  939 
  940         return TRUE;
  941     }
  942 
  943     if (foreign_username) {
  944         if (!ensure_foreign_store (priv, foreign_username, obj_store, perror)) {
  945             UNLOCK ();
  946             return FALSE;
  947         }
  948 
  949         UNLOCK ();
  950         return TRUE;
  951     }
  952 
  953     *obj_store = &priv->msg_store;
  954 
  955     UNLOCK ();
  956 
  957     return TRUE;
  958 }
  959 
  960 /* sets quotas and current_size to -1 when not available, but still can return TRUE */
  961 gboolean
  962 e_mapi_connection_get_store_quotas (EMapiConnection *conn,
  963                     mapi_object_t *obj_store, /* can be NULL, for mailbox store */
  964                     uint64_t *current_size, /* out */
  965                     uint64_t *receive_quota, /* out */
  966                     uint64_t *send_quota, /* out */
  967                     GCancellable *cancellable,
  968                     GError **perror)
  969 {
  970     enum MAPISTATUS ms = MAPI_E_RESERVED;
  971     TALLOC_CTX *mem_ctx;
  972     struct SPropTagArray *spropTagArray = NULL;
  973     struct SPropValue *lpProps = NULL;
  974     mapi_object_t *use_store;
  975 
  976     CHECK_CORRECT_CONN_AND_GET_PRIV (conn, FALSE);
  977     e_return_val_mapi_error_if_fail (priv->session != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
  978     e_return_val_mapi_error_if_fail (current_size != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
  979     e_return_val_mapi_error_if_fail (receive_quota != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
  980     e_return_val_mapi_error_if_fail (send_quota != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
  981 
  982     LOCK (cancellable, perror, FALSE);
  983     use_store = obj_store;
  984     if (!use_store)
  985         use_store = &priv->msg_store;
  986 
  987     *current_size = -1;
  988     *receive_quota = -1;
  989     *send_quota = -1;
  990 
  991     mem_ctx = talloc_new (priv->session);
  992 
  993     if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
  994         ms = MAPI_E_USER_CANCEL;
  995         goto cleanup;
  996     }
  997 
  998     spropTagArray = set_SPropTagArray (mem_ctx, 4,
  999         PidTagMessageSize,
 1000         PidTagMessageSizeExtended,
 1001         PidTagProhibitReceiveQuota,
 1002         PidTagProhibitSendQuota);
 1003 
 1004     if (spropTagArray && spropTagArray->cValues) {
 1005         uint32_t prop_count = 0;
 1006         const uint32_t *pmessage_size, *preceive_quota, *psend_quota;
 1007         const uint64_t *pmessage_size_ex;
 1008 
 1009         ms = GetProps (use_store, MAPI_PROPS_SKIP_NAMEDID_CHECK | MAPI_UNICODE, spropTagArray, &lpProps, &prop_count);
 1010         if (ms != MAPI_E_SUCCESS) {
 1011             make_mapi_error (perror, "GetProps", ms);
 1012             goto cleanup;
 1013         } else if (!lpProps) {
 1014             ms = MAPI_E_CALL_FAILED;
 1015             make_mapi_error (perror, "GetProps", ms);
 1016             goto cleanup;
 1017         }
 1018 
 1019         if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
 1020             ms = MAPI_E_USER_CANCEL;
 1021             goto cleanup;
 1022         }
 1023 
 1024         pmessage_size = e_mapi_util_find_SPropVal_array_propval (lpProps, PidTagMessageSize);
 1025         pmessage_size_ex = e_mapi_util_find_SPropVal_array_propval (lpProps, PidTagMessageSizeExtended);
 1026         preceive_quota = e_mapi_util_find_SPropVal_array_propval (lpProps, PidTagProhibitReceiveQuota);
 1027         psend_quota = e_mapi_util_find_SPropVal_array_propval (lpProps, PidTagProhibitSendQuota);
 1028 
 1029         if (pmessage_size && *pmessage_size != -1)
 1030             *current_size = *pmessage_size;
 1031         else if (pmessage_size_ex && *pmessage_size_ex)
 1032             *current_size = *pmessage_size_ex;
 1033 
 1034         if (*current_size != -1) {
 1035             if (preceive_quota && *preceive_quota != -1) {
 1036                 *receive_quota = *preceive_quota;
 1037                 *receive_quota *= 1024;
 1038             }
 1039 
 1040             if (psend_quota && *psend_quota != -1) {
 1041                 *send_quota = *psend_quota;
 1042                 *send_quota *= 1024;
 1043             }
 1044         }
 1045     } else {
 1046         ms = MAPI_E_NOT_ENOUGH_RESOURCES;
 1047         make_mapi_error (perror, "set_SPropTagArray", ms);
 1048     }
 1049 
 1050  cleanup:
 1051     talloc_free (spropTagArray);
 1052     talloc_free (lpProps);
 1053     talloc_free (mem_ctx);
 1054     UNLOCK();
 1055 
 1056     return ms == MAPI_E_SUCCESS;
 1057 }
 1058 
 1059 gboolean
 1060 e_mapi_connection_open_default_folder (EMapiConnection *conn,
 1061                        uint32_t olFolderIdentifier,
 1062                        mapi_object_t *obj_folder,
 1063                        GCancellable *cancellable,
 1064                        GError **perror)
 1065 {
 1066     enum MAPISTATUS ms;
 1067     mapi_id_t fid = 0;
 1068     gboolean res;
 1069 
 1070     CHECK_CORRECT_CONN_AND_GET_PRIV (conn, FALSE);
 1071     e_return_val_mapi_error_if_fail (obj_folder != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 1072 
 1073     mapi_object_init (obj_folder);
 1074 
 1075     LOCK (cancellable, perror, FALSE);
 1076 
 1077     ms = GetDefaultFolder (&priv->msg_store, &fid, olFolderIdentifier);
 1078     if (ms != MAPI_E_SUCCESS) {
 1079         make_mapi_error (perror, "GetDefaultFolder", ms);
 1080         UNLOCK ();
 1081         return FALSE;
 1082     }
 1083 
 1084     if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
 1085         UNLOCK ();
 1086         return FALSE;
 1087     }
 1088 
 1089     res = e_mapi_connection_open_personal_folder (conn, fid, obj_folder, cancellable, perror);
 1090 
 1091     UNLOCK ();
 1092 
 1093     return res;
 1094 }
 1095 
 1096 gboolean
 1097 e_mapi_connection_open_personal_folder (EMapiConnection *conn,
 1098                     mapi_id_t fid,
 1099                     mapi_object_t *obj_folder,
 1100                     GCancellable *cancellable,
 1101                     GError **perror)
 1102 {
 1103     enum MAPISTATUS ms;
 1104 
 1105     CHECK_CORRECT_CONN_AND_GET_PRIV (conn, FALSE);
 1106     e_return_val_mapi_error_if_fail (obj_folder != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 1107 
 1108     mapi_object_init (obj_folder);
 1109 
 1110     LOCK (cancellable, perror, FALSE);
 1111 
 1112     ms = OpenFolder (&priv->msg_store, fid, obj_folder);
 1113     if (ms != MAPI_E_SUCCESS)
 1114         make_mapi_error (perror, "OpenFolder", ms);
 1115 
 1116     UNLOCK ();
 1117 
 1118     return ms == MAPI_E_SUCCESS;
 1119 }
 1120 
 1121 gboolean
 1122 e_mapi_connection_open_public_folder (EMapiConnection *conn,
 1123                       mapi_id_t fid,
 1124                       mapi_object_t *obj_folder,
 1125                       GCancellable *cancellable,
 1126                       GError **perror)
 1127 {
 1128     enum MAPISTATUS ms;
 1129 
 1130     CHECK_CORRECT_CONN_AND_GET_PRIV (conn, FALSE);
 1131     e_return_val_mapi_error_if_fail (obj_folder != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 1132 
 1133     mapi_object_init (obj_folder);
 1134 
 1135     LOCK (cancellable, perror, FALSE);
 1136 
 1137     if (!ensure_public_store (priv, perror)) {
 1138         UNLOCK ();
 1139         return FALSE;
 1140     }
 1141 
 1142     if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
 1143         UNLOCK ();
 1144         return FALSE;
 1145     }
 1146 
 1147     ms = OpenFolder (&priv->public_store, fid, obj_folder);
 1148     if (ms != MAPI_E_SUCCESS)
 1149         make_mapi_error (perror, "OpenFolder", ms);
 1150 
 1151     UNLOCK ();
 1152 
 1153     return ms == MAPI_E_SUCCESS;
 1154 }
 1155 
 1156 gboolean
 1157 e_mapi_connection_open_foreign_folder (EMapiConnection *conn,
 1158                        const gchar *username,
 1159                        mapi_id_t fid,
 1160                        mapi_object_t *obj_folder, /* out */
 1161                        GCancellable *cancellable,
 1162                        GError **perror)
 1163 {
 1164     enum MAPISTATUS ms;
 1165     mapi_object_t *msg_store = NULL;
 1166 
 1167     CHECK_CORRECT_CONN_AND_GET_PRIV (conn, FALSE);
 1168     e_return_val_mapi_error_if_fail (username != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 1169     e_return_val_mapi_error_if_fail (obj_folder != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 1170 
 1171     mapi_object_init (obj_folder);
 1172 
 1173     LOCK (cancellable, perror, FALSE);
 1174 
 1175     if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
 1176         UNLOCK ();
 1177         return FALSE;
 1178     }
 1179 
 1180     if (!ensure_foreign_store (conn->priv, username, &msg_store, perror)) {
 1181         ms = MAPI_E_CALL_FAILED;
 1182         if (perror && !*perror)
 1183             g_propagate_error (perror, g_error_new (E_MAPI_ERROR, ms, _("Failed to open store for user “%s”"), username));
 1184     } else {
 1185         ms = MAPI_E_SUCCESS;
 1186     }
 1187 
 1188     if (ms == MAPI_E_SUCCESS) {
 1189         ms = OpenFolder (msg_store, fid, obj_folder);
 1190         if (ms == MAPI_E_NOT_FOUND)
 1191             g_propagate_error (perror, g_error_new (E_MAPI_ERROR, ms, _("Folder of user “%s” not found"), username));
 1192         else if (ms != MAPI_E_SUCCESS)
 1193             make_mapi_error (perror, "OpenFolder", ms);
 1194     }
 1195 
 1196     UNLOCK ();
 1197 
 1198     return ms == MAPI_E_SUCCESS;
 1199 }
 1200 
 1201 gboolean
 1202 e_mapi_connection_close_folder (EMapiConnection *conn,
 1203                 mapi_object_t *obj_folder,
 1204                 GCancellable *cancellable,
 1205                 GError **perror)
 1206 {
 1207     gboolean was_cancelled = FALSE;
 1208 
 1209     CHECK_CORRECT_CONN_AND_GET_PRIV (conn, FALSE);
 1210     e_return_val_mapi_error_if_fail (obj_folder != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 1211 
 1212     /* kinda bad thing to do, but nothing better;
 1213        if the open_folder succeeded, then it's always good to free resources,
 1214        even when the operation on the folder was cancelled; and as it's good
 1215        to be cancellable, then this is better than passing NULL to LOCK().
 1216     */
 1217     if (cancellable) {
 1218         was_cancelled = g_cancellable_is_cancelled (cancellable);
 1219         if (was_cancelled)
 1220             g_cancellable_reset (cancellable);
 1221     }
 1222 
 1223     LOCK (cancellable, perror, FALSE);
 1224 
 1225     mapi_object_release (obj_folder);
 1226 
 1227     /* this can invoke 'cancelled' again, but no big deal for evo-mapi */
 1228     if (was_cancelled)
 1229         g_cancellable_cancel (cancellable);
 1230 
 1231     UNLOCK ();
 1232 
 1233     return TRUE;
 1234 }
 1235 
 1236 static void
 1237 maybe_add_named_id_tag (uint32_t proptag,
 1238             EResolveNamedIDsData **named_ids_list,
 1239             guint *named_ids_len)
 1240 {
 1241     g_return_if_fail (named_ids_list != NULL);
 1242     g_return_if_fail (named_ids_len != NULL);
 1243 
 1244     if (get_namedid_name (proptag)) {
 1245         if (!*named_ids_list) {
 1246             *named_ids_list = g_new0 (EResolveNamedIDsData, 1);
 1247             *named_ids_len = 0;
 1248         } else {
 1249             *named_ids_list = g_renew (EResolveNamedIDsData, *named_ids_list, *named_ids_len + 1);
 1250         }
 1251 
 1252         (*named_ids_list)[*named_ids_len].pidlid_propid = proptag;
 1253         (*named_ids_list)[*named_ids_len].propid = MAPI_E_RESERVED;
 1254         (*named_ids_len) += 1;
 1255     }
 1256 }
 1257 
 1258 /* free returned pointer with g_hash_table_destroy */
 1259 static GHashTable *
 1260 prepare_maybe_replace_hash (const EResolveNamedIDsData *named_ids_list,
 1261                 guint named_ids_len,
 1262                 gboolean to_server_ids)
 1263 {
 1264     GHashTable *res;
 1265     gint ii;
 1266 
 1267     if (!named_ids_list || !named_ids_len)
 1268         return NULL;
 1269 
 1270     res = g_hash_table_new (g_direct_hash, g_direct_equal);
 1271 
 1272     for (ii = 0; ii < named_ids_len; ii++) {
 1273         uint32_t search_tag = named_ids_list[ii].pidlid_propid;
 1274         uint32_t replace_with = named_ids_list[ii].propid;
 1275 
 1276         if (!to_server_ids) {
 1277             uint32_t ui32;
 1278 
 1279             ui32 = search_tag;
 1280             search_tag = replace_with;
 1281             replace_with = ui32;
 1282         }
 1283 
 1284         g_hash_table_insert (res, GUINT_TO_POINTER (search_tag), GUINT_TO_POINTER (replace_with));
 1285 
 1286         search_tag = (search_tag & ~0xFFFF) | PT_ERROR;
 1287         replace_with = (replace_with & ~0xFFFF) | PT_ERROR;
 1288 
 1289         g_hash_table_insert (res, GUINT_TO_POINTER (search_tag), GUINT_TO_POINTER (replace_with));
 1290     }
 1291 
 1292     return res;
 1293 }
 1294 
 1295 static void
 1296 maybe_replace_named_id_tag (uint32_t *pproptag,
 1297                 GHashTable *replace_hash)
 1298 {
 1299     gpointer key, value;
 1300 
 1301     g_return_if_fail (pproptag != NULL);
 1302 
 1303     if (!replace_hash)
 1304         return;
 1305 
 1306     if (g_hash_table_lookup_extended (replace_hash, GUINT_TO_POINTER (*pproptag), &key, &value))
 1307         *pproptag = GPOINTER_TO_UINT (value);
 1308 }
 1309 
 1310 /* deals with named IDs transparently, if not using NULL bpr_cb, thus it's OK to check with PidLid and PidName constants only */
 1311 gboolean
 1312 e_mapi_connection_get_folder_properties (EMapiConnection *conn,
 1313                      mapi_object_t *obj_folder,
 1314                      BuildReadPropsCB brp_cb,
 1315                      gpointer brp_cb_user_data,
 1316                      GetPropertiesCB cb,
 1317                      gpointer cb_user_data,
 1318                      GCancellable *cancellable,
 1319                      GError **perror)
 1320 {
 1321     enum MAPISTATUS ms;
 1322     TALLOC_CTX *mem_ctx;
 1323     struct SPropTagArray *spropTagArray = NULL;
 1324     struct mapi_SPropValue_array *properties = NULL;
 1325     struct SPropValue *lpProps = NULL;
 1326     gboolean res = FALSE;
 1327 
 1328     CHECK_CORRECT_CONN_AND_GET_PRIV (conn, FALSE);
 1329     e_return_val_mapi_error_if_fail (priv->session != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 1330     e_return_val_mapi_error_if_fail (obj_folder != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 1331     e_return_val_mapi_error_if_fail (cb != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 1332 
 1333     LOCK (cancellable, perror, FALSE);
 1334     mem_ctx = talloc_new (priv->session);
 1335 
 1336     if (g_cancellable_set_error_if_cancelled (cancellable, perror))
 1337         goto cleanup;
 1338 
 1339     spropTagArray = set_SPropTagArray (mem_ctx, 3, PidTagFolderId, PidTagLastModificationTime, PidTagContentCount);
 1340     if (brp_cb) {
 1341         if (!brp_cb (conn, mem_ctx, spropTagArray, brp_cb_user_data, cancellable, perror)) {
 1342             goto cleanup;
 1343         }
 1344     } else {
 1345         talloc_free (spropTagArray);
 1346         spropTagArray = NULL;
 1347     }
 1348 
 1349     properties = talloc_zero (mem_ctx, struct mapi_SPropValue_array);
 1350     if (spropTagArray && spropTagArray->cValues) {
 1351         uint32_t prop_count = 0, k, ll;
 1352         EResolveNamedIDsData *named_ids_list = NULL;
 1353         guint named_ids_len = 0;
 1354         GHashTable *replace_hash = NULL;
 1355 
 1356         for (k = 0; k < spropTagArray->cValues; k++) {
 1357             uint32_t proptag = spropTagArray->aulPropTag[k];
 1358 
 1359             if (may_skip_property (proptag)) {
 1360                 const gchar *name = get_proptag_name (proptag);
 1361                 if (!name)
 1362                     name = "";
 1363 
 1364                 g_debug ("%s: Cannot fetch property 0x%08x %s", G_STRFUNC, proptag, name);
 1365             } else {
 1366                 maybe_add_named_id_tag (proptag, &named_ids_list, &named_ids_len);
 1367             }
 1368         }
 1369 
 1370         if (named_ids_list) {
 1371             if (!e_mapi_connection_resolve_named_props (conn, obj_folder, named_ids_list, named_ids_len, cancellable, perror)) {
 1372                 g_free (named_ids_list);
 1373                 goto cleanup;
 1374             }
 1375 
 1376             if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
 1377                 g_free (named_ids_list);
 1378                 goto cleanup;
 1379             }
 1380 
 1381             replace_hash = prepare_maybe_replace_hash (named_ids_list, named_ids_len, TRUE);
 1382             if (replace_hash) {
 1383                 for (k = 0; k < spropTagArray->cValues; k++) {
 1384                     uint32_t proptag = spropTagArray->aulPropTag[k];
 1385 
 1386                     maybe_replace_named_id_tag (&proptag, replace_hash);
 1387 
 1388                     spropTagArray->aulPropTag[k] = proptag;
 1389                 }
 1390                 g_hash_table_destroy (replace_hash);
 1391                 replace_hash = NULL;
 1392             }
 1393         }
 1394 
 1395         ms = GetProps (obj_folder, MAPI_PROPS_SKIP_NAMEDID_CHECK | MAPI_UNICODE, spropTagArray, &lpProps, &prop_count);
 1396         if (ms != MAPI_E_SUCCESS) {
 1397             make_mapi_error (perror, "GetProps", ms);
 1398             g_free (named_ids_list);
 1399             goto cleanup;
 1400         } else if (!lpProps) {
 1401             ms = MAPI_E_CALL_FAILED;
 1402             make_mapi_error (perror, "GetProps", ms);
 1403             g_free (named_ids_list);
 1404             goto cleanup;
 1405         }
 1406 
 1407         if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
 1408             g_free (named_ids_list);
 1409             goto cleanup;
 1410         }
 1411 
 1412         if (named_ids_list)
 1413             replace_hash = prepare_maybe_replace_hash (named_ids_list, named_ids_len, FALSE);
 1414 
 1415         /* Conversion from SPropValue to mapi_SPropValue. (no padding here) */
 1416         properties->cValues = prop_count;
 1417         properties->lpProps = talloc_zero_array (mem_ctx, struct mapi_SPropValue, prop_count + 1);
 1418         for (k = 0, ll = 0; k < prop_count; k++, ll++) {
 1419             if (may_skip_property (lpProps[k].ulPropTag)) {
 1420                 ll--;
 1421                 properties->cValues--;
 1422             } else {
 1423                 uint32_t proptag = lpProps[k].ulPropTag;
 1424 
 1425                 maybe_replace_named_id_tag (&proptag, replace_hash);
 1426                 lpProps[k].ulPropTag = proptag;
 1427 
 1428                 cast_mapi_SPropValue (mem_ctx, &properties->lpProps[ll], &lpProps[k]);
 1429             }
 1430         }
 1431 
 1432         g_free (named_ids_list);
 1433         if (replace_hash)
 1434             g_hash_table_destroy (replace_hash);
 1435     } else {
 1436         ms = GetPropsAll (obj_folder, MAPI_UNICODE, properties);
 1437         if (ms != MAPI_E_SUCCESS) {
 1438             make_mapi_error (perror, "GetPropsAll", ms);
 1439             goto cleanup;
 1440         }
 1441 
 1442         if (properties)
 1443             properties->lpProps = talloc_steal (properties, properties->lpProps);
 1444     }
 1445 
 1446     if (g_cancellable_set_error_if_cancelled (cancellable, perror))
 1447         goto cleanup;
 1448 
 1449     res = cb (conn, mem_ctx, properties, cb_user_data, cancellable, perror);
 1450 
 1451  cleanup:
 1452     talloc_free (spropTagArray);
 1453     talloc_free (properties);
 1454     talloc_free (lpProps);
 1455     talloc_free (mem_ctx);
 1456     UNLOCK();
 1457 
 1458     return res;
 1459 }
 1460 
 1461 typedef gboolean (*ForeachTableRowCB)   (EMapiConnection *conn,
 1462                      TALLOC_CTX *mem_ctx,
 1463                      struct SRow *srow,
 1464                      guint32 row_index,
 1465                      guint32 rows_total,
 1466                      gpointer user_data,
 1467                      GCancellable *cancellable,
 1468                      GError **perror);
 1469 
 1470 static enum MAPISTATUS
 1471 foreach_tablerow (EMapiConnection *conn,
 1472           TALLOC_CTX *mem_ctx,
 1473           mapi_object_t *obj_table,
 1474           ForeachTableRowCB cb,
 1475           gpointer user_data,
 1476           GCancellable *cancellable,
 1477           GError **perror)
 1478 {
 1479     enum MAPISTATUS ms;
 1480     struct SRowSet SRowSet;
 1481     uint32_t count, i, cursor_pos = 0;
 1482 
 1483     CHECK_CORRECT_CONN_AND_GET_PRIV (conn, FALSE);
 1484     e_return_val_mapi_error_if_fail (priv->session != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 1485     e_return_val_mapi_error_if_fail (mem_ctx != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 1486     e_return_val_mapi_error_if_fail (obj_table != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 1487     e_return_val_mapi_error_if_fail (cb != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 1488 
 1489     do {
 1490         /* Number of items in the container */
 1491         ms = QueryPosition (obj_table, &cursor_pos, &count);
 1492         if (ms != MAPI_E_SUCCESS) {
 1493             make_mapi_error (perror, "QueryPosition", ms);
 1494             break;
 1495         }
 1496 
 1497         if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
 1498             ms = MAPI_E_USER_CANCEL;
 1499             break;
 1500         }
 1501 
 1502         if (!count)
 1503             break;
 1504 
 1505         /* Fill the table columns with data from the rows */
 1506         ms = QueryRows (obj_table, count, TBL_ADVANCE,
 1507             #ifdef HAVE_QUERYROWS_FORWARDREAD
 1508             TBL_FORWARD_READ,
 1509             #endif
 1510             &SRowSet);
 1511         if (ms != MAPI_E_SUCCESS) {
 1512             make_mapi_error (perror, "QueryRows", ms);
 1513             break;
 1514         }
 1515 
 1516         if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
 1517             ms = MAPI_E_USER_CANCEL;
 1518             break;
 1519         }
 1520 
 1521         for (i = 0; i < SRowSet.cRows && ms == MAPI_E_SUCCESS; i++) {
 1522             if (!cb (conn, mem_ctx, &SRowSet.aRow[i], cursor_pos + i + 1, count, user_data, cancellable, perror))
 1523                 ms = MAPI_E_RESERVED;
 1524             else if (g_cancellable_set_error_if_cancelled (cancellable, perror))
 1525                 ms = MAPI_E_USER_CANCEL;
 1526         }
 1527     } while (cursor_pos < count && ms == MAPI_E_SUCCESS);
 1528 
 1529     return ms;
 1530 }
 1531 
 1532 static gboolean
 1533 gather_folder_permissions_cb (EMapiConnection *conn,
 1534                   TALLOC_CTX *mem_ctx,
 1535                   struct SRow *srow,
 1536                   guint32 row_index,
 1537                   guint32 rows_total,
 1538                   gpointer user_data,
 1539                   GCancellable *cancellable,
 1540                   GError **perror)
 1541 {
 1542     GSList **entries = user_data;
 1543     const gchar *username;
 1544     const struct Binary_r *pentry_id;
 1545     const uint64_t *pid;
 1546     const uint32_t *prights;
 1547 
 1548     g_return_val_if_fail (srow != NULL, FALSE);
 1549     g_return_val_if_fail (entries != NULL, FALSE);
 1550 
 1551     username = e_mapi_util_find_row_propval (srow, PidTagMemberName);
 1552     pid = e_mapi_util_find_row_propval (srow, PidTagMemberId);
 1553     pentry_id = e_mapi_util_find_row_propval (srow, PidTagEntryId);
 1554     prights = e_mapi_util_find_row_propval (srow, PidTagMemberRights);
 1555 
 1556     if (prights && pid) {
 1557         EMapiPermissionEntry *pem;
 1558         struct SBinary_short entry_id;
 1559 
 1560         entry_id.cb = pentry_id ? pentry_id->cb : 0;
 1561         entry_id.lpb = pentry_id ? pentry_id->lpb : NULL;
 1562 
 1563         pem = e_mapi_permission_entry_new (username, pentry_id ? &entry_id : NULL, *pid, *prights);
 1564         g_return_val_if_fail (pem != NULL, FALSE);
 1565 
 1566         *entries = g_slist_prepend (*entries, pem);
 1567     } else {
 1568         g_debug ("%s: Skipping [%d/%d] (%s) No rights or member ID set", G_STRFUNC, row_index, rows_total, username ? username : "no member name");
 1569     }
 1570 
 1571     return TRUE;
 1572 }
 1573 
 1574 gboolean
 1575 e_mapi_connection_get_permissions (EMapiConnection *conn,
 1576                    mapi_object_t *obj_folder,
 1577                    gboolean with_freebusy,
 1578                    GSList **entries, /* EMapiPermissionEntry */
 1579                    GCancellable *cancellable,
 1580                    GError **perror)
 1581 {
 1582     enum MAPISTATUS ms = MAPI_E_RESERVED;
 1583     struct SPropTagArray *propTagArray;
 1584     mapi_object_t obj_table;
 1585     TALLOC_CTX *mem_ctx;
 1586 
 1587     CHECK_CORRECT_CONN_AND_GET_PRIV (conn, FALSE);
 1588     e_return_val_mapi_error_if_fail (priv->session != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 1589     e_return_val_mapi_error_if_fail (obj_folder != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 1590     e_return_val_mapi_error_if_fail (entries != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 1591 
 1592     LOCK (cancellable, perror, FALSE);
 1593     mem_ctx = talloc_new (priv->session);
 1594     mapi_object_init (&obj_table);
 1595 
 1596     if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
 1597         ms = MAPI_E_USER_CANCEL;
 1598         goto cleanup;
 1599     }
 1600 
 1601     ms = GetPermissionsTable (obj_folder, with_freebusy ? IncludeFreeBusy : 0, &obj_table);
 1602     if (ms != MAPI_E_SUCCESS) {
 1603         make_mapi_error (perror, "GetPermissionsTable", ms);
 1604         goto cleanup;
 1605     }
 1606 
 1607     propTagArray = set_SPropTagArray (mem_ctx, 4,
 1608                       PidTagMemberId,
 1609                       PidTagEntryId,
 1610                       PidTagMemberName,
 1611                       PidTagMemberRights);
 1612 
 1613     /* Set primary columns to be fetched */
 1614     ms = SetColumns (&obj_table, propTagArray);
 1615     if (ms != MAPI_E_SUCCESS) {
 1616         make_mapi_error (perror, "SetColumns", ms);
 1617         goto cleanup;
 1618     }
 1619 
 1620     if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
 1621         ms = MAPI_E_USER_CANCEL;
 1622         goto cleanup;
 1623     }
 1624 
 1625     *entries = NULL;
 1626 
 1627     ms = foreach_tablerow (conn, mem_ctx, &obj_table, gather_folder_permissions_cb, entries, cancellable, perror);
 1628     if (ms == MAPI_E_SUCCESS) {
 1629         *entries = g_slist_reverse (*entries);
 1630     } else {
 1631         g_slist_free_full (*entries, (GDestroyNotify) e_mapi_permission_entry_free);
 1632         *entries = NULL;
 1633     }
 1634 
 1635  cleanup:
 1636     mapi_object_release (&obj_table);
 1637     talloc_free (mem_ctx);
 1638     UNLOCK();
 1639 
 1640     return ms == MAPI_E_SUCCESS;
 1641 }
 1642 
 1643 gboolean
 1644 e_mapi_connection_set_permissions (EMapiConnection *conn,
 1645                    mapi_object_t *obj_folder,
 1646                    gboolean with_freebusy,
 1647                    const GSList *entries, /* EMapiPermissionEntry */
 1648                    GCancellable *cancellable,
 1649                    GError **perror)
 1650 {
 1651     enum MAPISTATUS ms = MAPI_E_RESERVED;
 1652     struct mapi_PermissionsData *rows = NULL;
 1653     GSList *current_entries = NULL;
 1654     TALLOC_CTX *mem_ctx;
 1655 
 1656     CHECK_CORRECT_CONN_AND_GET_PRIV (conn, FALSE);
 1657     e_return_val_mapi_error_if_fail (priv->session != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 1658     e_return_val_mapi_error_if_fail (obj_folder != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 1659 
 1660     LOCK (cancellable, perror, FALSE);
 1661     mem_ctx = talloc_new (priv->session);
 1662 
 1663     rows = talloc_zero (mem_ctx, struct mapi_PermissionsData);
 1664     if (!rows) {
 1665         ms = MAPI_E_NOT_ENOUGH_RESOURCES;
 1666         make_mapi_error (perror, "talloc_zero", ms);
 1667         goto cleanup;
 1668     }
 1669 
 1670     if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
 1671         ms = MAPI_E_USER_CANCEL;
 1672         goto cleanup;
 1673     }
 1674 
 1675     if (!e_mapi_connection_get_permissions (conn, obj_folder, with_freebusy, &current_entries, cancellable, perror)) {
 1676         ms = MAPI_E_CALL_FAILED;
 1677         make_mapi_error (perror, "e_mapi_connection_get_permissions", ms);
 1678         goto cleanup;
 1679     }
 1680 
 1681     if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
 1682         ms = MAPI_E_USER_CANCEL;
 1683         goto cleanup;
 1684     }
 1685 
 1686     rows->ModifyCount = g_slist_length ((GSList *) entries) + g_slist_length (current_entries);
 1687     if (rows->ModifyCount > 0) {
 1688         const GSList *iter, *citer;
 1689         GSList *removed_entries = g_slist_copy (current_entries);
 1690         gint row_index = 0;
 1691 
 1692         rows->PermissionsData = talloc_array (rows, struct PermissionData, rows->ModifyCount);
 1693         if (!rows->PermissionsData) {
 1694             ms = MAPI_E_NOT_ENOUGH_RESOURCES;
 1695             make_mapi_error (perror, "talloc_zero", ms);
 1696             g_slist_free (removed_entries);
 1697             goto cleanup;
 1698         }
 1699 
 1700         for (iter = entries; iter; iter = iter->next) {
 1701             const EMapiPermissionEntry *pem = iter->data, *cpem = NULL;
 1702 
 1703             if (!pem) {
 1704                 ms = MAPI_E_INVALID_PARAMETER;
 1705                 make_mapi_error (perror, "entries::data", ms);
 1706                 g_slist_free (removed_entries);
 1707                 goto cleanup;
 1708             }
 1709 
 1710             for (citer = current_entries; citer; citer = citer->next) {
 1711                 cpem = citer->data;
 1712 
 1713                 if (cpem && ((cpem->entry_id.cb == pem->entry_id.cb && cpem->member_id == pem->member_id) ||
 1714                    (cpem->entry_id.cb > 0 && e_mapi_util_recip_entryid_equal (&cpem->entry_id, &pem->entry_id)))) {
 1715                     removed_entries = g_slist_remove (removed_entries, cpem);
 1716                     break;
 1717                 }
 1718 
 1719                 cpem = NULL;
 1720             }
 1721 
 1722             if (cpem == NULL) {
 1723                 rows->PermissionsData[row_index].PermissionDataFlags = ROW_ADD;
 1724                 rows->PermissionsData[row_index].lpProps.cValues = 2;
 1725                 rows->PermissionsData[row_index].lpProps.lpProps = talloc_zero_array (rows, struct mapi_SPropValue, 3);
 1726                 if (!rows->PermissionsData[row_index].lpProps.lpProps) {
 1727                     ms = MAPI_E_NOT_ENOUGH_RESOURCES;
 1728                     make_mapi_error (perror, "talloc_zero", ms);
 1729                     g_slist_free (removed_entries);
 1730                     goto cleanup;
 1731                 }
 1732 
 1733                 rows->PermissionsData[row_index].lpProps.lpProps[0].ulPropTag = PidTagEntryId;
 1734                 rows->PermissionsData[row_index].lpProps.lpProps[0].value.bin.cb = pem->entry_id.cb;
 1735                 rows->PermissionsData[row_index].lpProps.lpProps[0].value.bin.lpb = pem->entry_id.lpb;
 1736 
 1737                 rows->PermissionsData[row_index].lpProps.lpProps[1].ulPropTag = PidTagMemberRights;
 1738                 rows->PermissionsData[row_index].lpProps.lpProps[1].value.l = pem->member_rights &
 1739                     ~(with_freebusy ? 0 : (E_MAPI_PERMISSION_BIT_FREE_BUSY_DETAILED | E_MAPI_PERMISSION_BIT_FREE_BUSY_SIMPLE));
 1740 
 1741                 row_index++;
 1742             } else if (cpem->member_rights != pem->member_rights) {
 1743                 rows->PermissionsData[row_index].PermissionDataFlags = ROW_MODIFY;
 1744                 rows->PermissionsData[row_index].lpProps.cValues = 2;
 1745                 rows->PermissionsData[row_index].lpProps.lpProps = talloc_zero_array (rows, struct mapi_SPropValue, 3);
 1746                 if (!rows->PermissionsData[row_index].lpProps.lpProps) {
 1747                     ms = MAPI_E_NOT_ENOUGH_RESOURCES;
 1748                     make_mapi_error (perror, "talloc_zero", ms);
 1749                     g_slist_free (removed_entries);
 1750                     goto cleanup;
 1751                 }
 1752 
 1753                 rows->PermissionsData[row_index].lpProps.lpProps[0].ulPropTag = PidTagMemberId;
 1754                 rows->PermissionsData[row_index].lpProps.lpProps[0].value.d = pem->member_id;
 1755 
 1756                 rows->PermissionsData[row_index].lpProps.lpProps[1].ulPropTag = PidTagMemberRights;
 1757                 rows->PermissionsData[row_index].lpProps.lpProps[1].value.l = pem->member_rights &
 1758                     ~(with_freebusy ? 0 : (E_MAPI_PERMISSION_BIT_FREE_BUSY_DETAILED | E_MAPI_PERMISSION_BIT_FREE_BUSY_SIMPLE));
 1759 
 1760                 row_index++;
 1761             }
 1762         }
 1763 
 1764         for (citer = removed_entries; citer; citer = citer->next) {
 1765             const EMapiPermissionEntry *cpem = citer->data;
 1766 
 1767             if (cpem) {
 1768                 rows->PermissionsData[row_index].PermissionDataFlags = ROW_REMOVE;
 1769                 rows->PermissionsData[row_index].lpProps.cValues = 1;
 1770                 rows->PermissionsData[row_index].lpProps.lpProps = talloc_zero_array (rows, struct mapi_SPropValue, 2);
 1771                 if (!rows->PermissionsData[row_index].lpProps.lpProps) {
 1772                     ms = MAPI_E_NOT_ENOUGH_RESOURCES;
 1773                     make_mapi_error (perror, "talloc_zero", ms);
 1774                     g_slist_free (removed_entries);
 1775                     goto cleanup;
 1776                 }
 1777 
 1778                 rows->PermissionsData[row_index].lpProps.lpProps[0].ulPropTag = PidTagMemberId;
 1779                 rows->PermissionsData[row_index].lpProps.lpProps[0].value.d = cpem->member_id;
 1780 
 1781                 row_index++;
 1782             }
 1783         }
 1784 
 1785         rows->ModifyCount = row_index;
 1786 
 1787         g_slist_free (removed_entries);
 1788     }
 1789 
 1790     if (rows->ModifyCount > 0) {
 1791         ms = ModifyPermissions (obj_folder, with_freebusy ? ModifyPerms_IncludeFreeBusy : 0, rows);
 1792         if (ms == MAPI_E_INVALID_PARAMETER && with_freebusy) {
 1793             gint ii;
 1794 
 1795             for (ii = 0; ii < rows->ModifyCount; ii++) {
 1796                 if (rows->PermissionsData[ii].PermissionDataFlags == ROW_ADD) {
 1797                     rows->PermissionsData[ii].lpProps.lpProps[1].value.l &=
 1798                         ~(E_MAPI_PERMISSION_BIT_FREE_BUSY_DETAILED | E_MAPI_PERMISSION_BIT_FREE_BUSY_SIMPLE);
 1799                 } else if (rows->PermissionsData[ii].PermissionDataFlags == ROW_MODIFY) {
 1800                     rows->PermissionsData[ii].lpProps.lpProps[1].value.l &=
 1801                         ~(E_MAPI_PERMISSION_BIT_FREE_BUSY_DETAILED | E_MAPI_PERMISSION_BIT_FREE_BUSY_SIMPLE);
 1802                 }
 1803             }
 1804 
 1805             /* older servers (up to 8.0.360.0) can have issue setting Free/Busy flags,
 1806                thus try to set permissions without modifying these;
 1807                similar error can be also thrown when setting Free/Busy flags in rights,
 1808                but does not use ModifyPerms_IncludeFreeBusy flag
 1809             */
 1810             ms = ModifyPermissions (obj_folder, 0, rows);
 1811         }
 1812 
 1813         if (ms != MAPI_E_SUCCESS) {
 1814             make_mapi_error (perror, "ModifyPermissions", ms);
 1815             goto cleanup;
 1816         }
 1817     }
 1818 
 1819  cleanup:
 1820     g_slist_free_full (current_entries, (GDestroyNotify) e_mapi_permission_entry_free);
 1821     talloc_free (rows);
 1822     talloc_free (mem_ctx);
 1823     UNLOCK();
 1824 
 1825     return ms == MAPI_E_SUCCESS;
 1826 }
 1827 
 1828 struct ListObjectsInternalData
 1829 {
 1830     ListObjectsCB cb;
 1831     gpointer user_data;
 1832 };
 1833 
 1834 static gboolean
 1835 list_objects_internal_cb (EMapiConnection *conn,
 1836               TALLOC_CTX *mem_ctx,
 1837               struct SRow *srow,
 1838               guint32 row_index,
 1839               guint32 rows_total,
 1840               gpointer user_data,
 1841               GCancellable *cancellable,
 1842               GError **perror)
 1843 {
 1844     struct ListObjectsInternalData *loi_data = user_data;
 1845     ListObjectsData lod = { 0 };
 1846     const mapi_id_t *pmid;
 1847     const gchar *msg_class;
 1848     const uint32_t *pmsg_flags;
 1849     const struct FILETIME *last_modified;
 1850 
 1851     CHECK_CORRECT_CONN_AND_GET_PRIV (conn, FALSE);
 1852     e_return_val_mapi_error_if_fail (priv->session != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 1853     e_return_val_mapi_error_if_fail (mem_ctx != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 1854     e_return_val_mapi_error_if_fail (srow != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 1855 
 1856     pmid = e_mapi_util_find_row_propval (srow, PidTagMid);
 1857     msg_class = e_mapi_util_find_row_propval (srow, PidTagMessageClass);
 1858     pmsg_flags = e_mapi_util_find_row_propval (srow, PidTagMessageFlags);
 1859     last_modified = e_mapi_util_find_row_propval (srow, PidTagLastModificationTime);
 1860 
 1861     lod.mid = pmid ? *pmid : 0;
 1862     lod.msg_class = msg_class;
 1863     lod.msg_flags = pmsg_flags ? *pmsg_flags : 0;
 1864     lod.last_modified = last_modified ? e_mapi_util_filetime_to_time_t (last_modified) : 0;
 1865 
 1866     return loi_data->cb (conn, mem_ctx, &lod, row_index, rows_total, loi_data->user_data, cancellable, perror);
 1867 }
 1868 
 1869 static void
 1870 gather_mapi_SRestriction_named_ids (struct mapi_SRestriction *restriction,
 1871                     EResolveNamedIDsData **named_ids_list,
 1872                     guint *named_ids_len)
 1873 {
 1874     guint i;
 1875 
 1876     g_return_if_fail (restriction != NULL);
 1877     g_return_if_fail (named_ids_list != NULL);
 1878     g_return_if_fail (named_ids_len != NULL);
 1879 
 1880     switch (restriction->rt) {
 1881     case RES_AND:
 1882         for (i = 0; i < restriction->res.resAnd.cRes; i++) {
 1883             gather_mapi_SRestriction_named_ids ((struct mapi_SRestriction *) &(restriction->res.resAnd.res[i]), named_ids_list, named_ids_len);
 1884         }
 1885         break;
 1886     case RES_OR:
 1887         for (i = 0; i < restriction->res.resOr.cRes; i++) {
 1888             gather_mapi_SRestriction_named_ids ((struct mapi_SRestriction *) &(restriction->res.resOr.res[i]), named_ids_list, named_ids_len);
 1889         }
 1890         break;
 1891     #ifdef HAVE_RES_NOT_SUPPORTED
 1892     case RES_NOT:
 1893         gather_mapi_SRestriction_named_ids ((struct mapi_SRestriction *) restriction->res.resNot.res, named_ids_list, named_ids_len);
 1894         break;
 1895     #endif
 1896     case RES_CONTENT:
 1897         maybe_add_named_id_tag (restriction->res.resContent.ulPropTag, named_ids_list, named_ids_len);
 1898         maybe_add_named_id_tag (restriction->res.resContent.lpProp.ulPropTag, named_ids_list, named_ids_len);
 1899         break;
 1900     case RES_PROPERTY:
 1901         maybe_add_named_id_tag (restriction->res.resProperty.ulPropTag, named_ids_list, named_ids_len);
 1902         maybe_add_named_id_tag (restriction->res.resProperty.lpProp.ulPropTag, named_ids_list, named_ids_len);
 1903         break;
 1904     case RES_COMPAREPROPS:
 1905         maybe_add_named_id_tag (restriction->res.resCompareProps.ulPropTag1, named_ids_list, named_ids_len);
 1906         maybe_add_named_id_tag (restriction->res.resCompareProps.ulPropTag2, named_ids_list, named_ids_len);
 1907         break;
 1908     case RES_BITMASK:
 1909         maybe_add_named_id_tag (restriction->res.resBitmask.ulPropTag, named_ids_list, named_ids_len);
 1910         break;
 1911     case RES_SIZE:
 1912         maybe_add_named_id_tag (restriction->res.resSize.ulPropTag, named_ids_list, named_ids_len);
 1913         break;
 1914     case RES_EXIST:
 1915         maybe_add_named_id_tag (restriction->res.resExist.ulPropTag, named_ids_list, named_ids_len);
 1916         break;
 1917     }
 1918 }
 1919 
 1920 static void
 1921 replace_mapi_SRestriction_named_ids (struct mapi_SRestriction *restriction,
 1922                      GHashTable *replace_hash)
 1923 {
 1924     guint i;
 1925     uint32_t proptag;
 1926 
 1927     g_return_if_fail (restriction != NULL);
 1928 
 1929     #define check_proptag(x) {                      \
 1930             proptag = x;                        \
 1931             maybe_replace_named_id_tag (&proptag, replace_hash);    \
 1932             x = proptag;                        \
 1933         }
 1934 
 1935     switch (restriction->rt) {
 1936     case RES_AND:
 1937         for (i = 0; i < restriction->res.resAnd.cRes; i++) {
 1938             replace_mapi_SRestriction_named_ids ((struct mapi_SRestriction *) &(restriction->res.resAnd.res[i]), replace_hash);
 1939         }
 1940         break;
 1941     case RES_OR:
 1942         for (i = 0; i < restriction->res.resOr.cRes; i++) {
 1943             replace_mapi_SRestriction_named_ids ((struct mapi_SRestriction *) &(restriction->res.resOr.res[i]), replace_hash);
 1944         }
 1945         break;
 1946     #ifdef HAVE_RES_NOT_SUPPORTED
 1947     case RES_NOT:
 1948         replace_mapi_SRestriction_named_ids (restriction->res.resNot.res, replace_hash);
 1949         break;
 1950     #endif
 1951     case RES_CONTENT:
 1952         check_proptag (restriction->res.resContent.ulPropTag);
 1953         check_proptag (restriction->res.resContent.lpProp.ulPropTag);
 1954         break;
 1955     case RES_PROPERTY:
 1956         check_proptag (restriction->res.resProperty.ulPropTag);
 1957         check_proptag (restriction->res.resProperty.lpProp.ulPropTag);
 1958         break;
 1959     case RES_COMPAREPROPS:
 1960         check_proptag (restriction->res.resCompareProps.ulPropTag1);
 1961         check_proptag (restriction->res.resCompareProps.ulPropTag2);
 1962         break;
 1963     case RES_BITMASK:
 1964         check_proptag (restriction->res.resBitmask.ulPropTag);
 1965         break;
 1966     case RES_SIZE:
 1967         check_proptag (restriction->res.resSize.ulPropTag);
 1968         break;
 1969     case RES_EXIST:
 1970         check_proptag (restriction->res.resExist.ulPropTag);
 1971         break;
 1972     }
 1973 
 1974     #undef check_proptag
 1975 }
 1976 
 1977 static void
 1978 remove_unknown_proptags_mapi_SRestriction_rec (struct mapi_SRestriction *restriction,
 1979                            TALLOC_CTX *mem_ctx,
 1980                            GSList **new_rests)
 1981 {
 1982     gint ii;
 1983     GSList *sub_rests = NULL, *iter;
 1984 
 1985     if (!restriction)
 1986         return;
 1987 
 1988     g_return_if_fail (mem_ctx != NULL);
 1989 
 1990     #define proptag_is_ok(x) (((uint32_t) (x)) != 0 && ((uint32_t) (x)) != MAPI_E_RESERVED)
 1991 
 1992     switch (restriction->rt) {
 1993     case RES_AND:
 1994         for (ii = 0; ii < restriction->res.resAnd.cRes; ii++) {
 1995             remove_unknown_proptags_mapi_SRestriction_rec ((struct mapi_SRestriction *) &(restriction->res.resAnd.res[ii]), mem_ctx, &sub_rests);
 1996         }
 1997 
 1998         if (sub_rests) {
 1999             struct mapi_SRestriction *rest = talloc_zero (mem_ctx, struct mapi_SRestriction);
 2000             g_return_if_fail (rest != NULL);
 2001 
 2002             rest->rt = RES_AND;
 2003             rest->res.resAnd.cRes = g_slist_length (sub_rests);
 2004             rest->res.resAnd.res = talloc_zero_array (mem_ctx, struct mapi_SRestriction_and, rest->res.resAnd.cRes + 1);
 2005             g_return_if_fail (rest->res.resAnd.res != NULL);
 2006 
 2007             for (iter = sub_rests, ii = 0; iter; iter = iter->next, ii++) {
 2008                 struct mapi_SRestriction *subrest = iter->data;
 2009 
 2010                 g_return_if_fail (subrest != NULL);
 2011 
 2012                 rest->res.resAnd.res[ii].rt = subrest->rt;
 2013                 rest->res.resAnd.res[ii].res = subrest->res;
 2014             }
 2015 
 2016             *new_rests = g_slist_append (*new_rests, rest);
 2017         }
 2018         break;
 2019     case RES_OR:
 2020         for (ii = 0; ii < restriction->res.resOr.cRes; ii++) {
 2021             remove_unknown_proptags_mapi_SRestriction_rec ((struct mapi_SRestriction *) &(restriction->res.resOr.res[ii]), mem_ctx, &sub_rests);
 2022         }
 2023 
 2024         if (sub_rests) {
 2025             struct mapi_SRestriction *rest = talloc_zero (mem_ctx, struct mapi_SRestriction);
 2026             g_return_if_fail (rest != NULL);
 2027 
 2028             rest->rt = RES_OR;
 2029             rest->res.resOr.cRes = g_slist_length (sub_rests);
 2030             rest->res.resOr.res = talloc_zero_array (mem_ctx, struct mapi_SRestriction_or, rest->res.resOr.cRes + 1);
 2031             g_return_if_fail (rest->res.resOr.res != NULL);
 2032 
 2033             for (iter = sub_rests, ii = 0; iter; iter = iter->next, ii++) {
 2034                 struct mapi_SRestriction *subrest = iter->data;
 2035 
 2036                 g_return_if_fail (subrest != NULL);
 2037 
 2038                 rest->res.resOr.res[ii].rt = subrest->rt;
 2039                 rest->res.resOr.res[ii].res = subrest->res;
 2040             }
 2041 
 2042             *new_rests = g_slist_append (*new_rests, rest);
 2043         }
 2044         break;
 2045     #ifdef HAVE_RES_NOT_SUPPORTED
 2046     case RES_NOT:
 2047         remove_unknown_proptags_mapi_SRestriction_rec (restriction->res.resNot.res, mem_ctx, &sub_rests);
 2048         if (sub_rests) {
 2049             struct mapi_SRestriction *rest = talloc_zero (esp->mem_ctx, struct mapi_SRestriction);
 2050             g_return_if_fail (rest != NULL);
 2051 
 2052             rest->rt = RES_NOT;
 2053             res->res.resNot.res = sub_rests->data;
 2054         }
 2055         break;
 2056     #endif
 2057     case RES_CONTENT:
 2058         if (proptag_is_ok (restriction->res.resContent.ulPropTag) &&
 2059             proptag_is_ok (restriction->res.resContent.lpProp.ulPropTag)) {
 2060             *new_rests = g_slist_append (*new_rests, restriction);
 2061         }
 2062         break;
 2063     case RES_PROPERTY:
 2064         if (proptag_is_ok (restriction->res.resProperty.ulPropTag) &&
 2065             proptag_is_ok (restriction->res.resProperty.lpProp.ulPropTag)) {
 2066             *new_rests = g_slist_append (*new_rests, restriction);
 2067         }
 2068         break;
 2069     case RES_COMPAREPROPS:
 2070         if (proptag_is_ok (restriction->res.resCompareProps.ulPropTag1) &&
 2071             proptag_is_ok (restriction->res.resCompareProps.ulPropTag2)) {
 2072             *new_rests = g_slist_append (*new_rests, restriction);
 2073         }
 2074         break;
 2075     case RES_BITMASK:
 2076         if (proptag_is_ok (restriction->res.resBitmask.ulPropTag)) {
 2077             *new_rests = g_slist_append (*new_rests, restriction);
 2078         }
 2079         break;
 2080     case RES_SIZE:
 2081         if (proptag_is_ok (restriction->res.resSize.ulPropTag)) {
 2082             *new_rests = g_slist_append (*new_rests, restriction);
 2083         }
 2084         break;
 2085     case RES_EXIST:
 2086         if (proptag_is_ok (restriction->res.resExist.ulPropTag)) {
 2087             *new_rests = g_slist_append (*new_rests, restriction);
 2088         }
 2089         break;
 2090     default:
 2091         g_warn_if_reached ();
 2092         break;
 2093     }
 2094 
 2095     #undef proptag_is_ok
 2096 
 2097     g_slist_free (sub_rests);
 2098 }
 2099 
 2100 static void
 2101 remove_unknown_proptags_mapi_SRestriction (struct mapi_SRestriction **prestrictions,
 2102                        TALLOC_CTX *mem_ctx)
 2103 {
 2104     GSList *new_rests = NULL;
 2105 
 2106     g_return_if_fail (mem_ctx != NULL);
 2107 
 2108     remove_unknown_proptags_mapi_SRestriction_rec (*prestrictions, mem_ctx, &new_rests);
 2109 
 2110     if (new_rests) {
 2111         g_return_if_fail (g_slist_length (new_rests) == 1);
 2112 
 2113         *prestrictions = new_rests->data;
 2114 
 2115         g_slist_free (new_rests);
 2116     } else {
 2117         *prestrictions = NULL;
 2118     }
 2119 }
 2120 
 2121 static gboolean
 2122 change_mapi_SRestriction_named_ids (EMapiConnection *conn,
 2123                     mapi_object_t *obj_folder,
 2124                     TALLOC_CTX *mem_ctx,
 2125                     struct mapi_SRestriction **prestrictions,
 2126                     GCancellable *cancellable,
 2127                     GError **perror)
 2128 {
 2129     EResolveNamedIDsData *named_ids_list = NULL;
 2130     guint named_ids_len = 0;
 2131     gboolean res = FALSE;
 2132 
 2133     CHECK_CORRECT_CONN_AND_GET_PRIV (conn, FALSE);
 2134     e_return_val_mapi_error_if_fail (priv->session != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 2135     e_return_val_mapi_error_if_fail (obj_folder != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 2136     e_return_val_mapi_error_if_fail (prestrictions != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 2137     e_return_val_mapi_error_if_fail (*prestrictions != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 2138 
 2139     gather_mapi_SRestriction_named_ids (*prestrictions, &named_ids_list, &named_ids_len);
 2140 
 2141     if (!named_ids_list)
 2142         return TRUE;
 2143 
 2144     res = e_mapi_connection_resolve_named_props (conn, obj_folder, named_ids_list, named_ids_len, cancellable, perror);
 2145 
 2146     if (res) {
 2147         GHashTable *replace_hash = prepare_maybe_replace_hash (named_ids_list, named_ids_len, TRUE);
 2148 
 2149         if (replace_hash) {
 2150             replace_mapi_SRestriction_named_ids (*prestrictions, replace_hash);
 2151             g_hash_table_destroy (replace_hash);
 2152         }
 2153     }
 2154 
 2155     g_free (named_ids_list);
 2156 
 2157     remove_unknown_proptags_mapi_SRestriction (prestrictions, mem_ctx); 
 2158 
 2159     return res;
 2160 }
 2161 
 2162 /* deals with named IDs transparently, thus it's OK to pass Restrictions with PidLid and PidName constants */
 2163 gboolean
 2164 e_mapi_connection_list_objects (EMapiConnection *conn,
 2165                 mapi_object_t *obj_folder,
 2166                 BuildRestrictionsCB build_rs_cb,
 2167                 gpointer build_rs_cb_data,
 2168                 ListObjectsCB cb,
 2169                 gpointer user_data,
 2170                 GCancellable *cancellable,
 2171                 GError **perror)
 2172 {
 2173     enum MAPISTATUS ms;
 2174     TALLOC_CTX *mem_ctx;
 2175     mapi_object_t obj_table;
 2176     struct SPropTagArray *propTagArray;
 2177     struct ListObjectsInternalData loi_data;
 2178 
 2179     CHECK_CORRECT_CONN_AND_GET_PRIV (conn, FALSE);
 2180     e_return_val_mapi_error_if_fail (priv->session != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 2181     e_return_val_mapi_error_if_fail (cb != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 2182     e_return_val_mapi_error_if_fail (obj_folder != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 2183 
 2184     LOCK (cancellable, perror, FALSE);
 2185     mem_ctx = talloc_new (priv->session);
 2186     mapi_object_init (&obj_table);
 2187 
 2188     if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
 2189         ms = MAPI_E_USER_CANCEL;
 2190         goto cleanup;
 2191     }
 2192 
 2193     /* Get a handle on the container */
 2194     ms = GetContentsTable (obj_folder, &obj_table, TableFlags_UseUnicode, NULL);
 2195     if (ms != MAPI_E_SUCCESS) {
 2196         make_mapi_error (perror, "GetContentsTable", ms);
 2197         goto cleanup;
 2198     }
 2199 
 2200     propTagArray = set_SPropTagArray (mem_ctx, 4,
 2201         PidTagMid,
 2202         PidTagMessageClass,
 2203         PidTagMessageFlags,
 2204         PidTagLastModificationTime);
 2205         /* PidTagObjectType doesn't work with Exchange 2010 servers */
 2206 
 2207     /* Set primary columns to be fetched */
 2208     ms = SetColumns (&obj_table, propTagArray);
 2209     if (ms != MAPI_E_SUCCESS) {
 2210         make_mapi_error (perror, "SetColumns", ms);
 2211         goto cleanup;
 2212     }
 2213 
 2214     if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
 2215         ms = MAPI_E_USER_CANCEL;
 2216         goto cleanup;
 2217     }
 2218 
 2219     if (build_rs_cb) {
 2220         struct mapi_SRestriction *restrictions = NULL;
 2221 
 2222         if (!build_rs_cb (conn, mem_ctx, &restrictions, build_rs_cb_data, cancellable, perror)) {
 2223             ms = MAPI_E_CALL_FAILED;
 2224             make_mapi_error (perror, "build_restrictions", ms);
 2225             goto cleanup;
 2226         }
 2227 
 2228         if (restrictions) {
 2229             change_mapi_SRestriction_named_ids (conn, obj_folder, mem_ctx, &restrictions, cancellable, perror);
 2230 
 2231             if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
 2232                 ms = MAPI_E_USER_CANCEL;
 2233                 goto cleanup;
 2234             }
 2235 
 2236             if (restrictions) {
 2237                 /* Applying any restriction that are set. */
 2238                 ms = Restrict (&obj_table, restrictions, NULL);
 2239                 if (ms != MAPI_E_SUCCESS) {
 2240                     make_mapi_error (perror, "Restrict", ms);
 2241                     goto cleanup;
 2242                 }
 2243 
 2244                 if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
 2245                     ms = MAPI_E_USER_CANCEL;
 2246                     goto cleanup;
 2247                 }
 2248             }
 2249         }
 2250     }
 2251 
 2252     loi_data.cb = cb;
 2253     loi_data.user_data = user_data;
 2254 
 2255     ms = foreach_tablerow (conn, mem_ctx, &obj_table, list_objects_internal_cb, &loi_data, cancellable, perror);
 2256 
 2257  cleanup:
 2258     mapi_object_release (&obj_table);
 2259     talloc_free (mem_ctx);
 2260     UNLOCK ();
 2261 
 2262     return ms == MAPI_E_SUCCESS;
 2263 }
 2264 
 2265 static gboolean
 2266 has_embedded_message_without_body (EMapiObject *object)
 2267 {
 2268     EMapiAttachment *attach;
 2269 
 2270     if (!object)
 2271         return FALSE;
 2272 
 2273     for (attach = object->attachments; attach; attach = attach->next) {
 2274         if (!attach->embedded_object)
 2275             continue;
 2276 
 2277         if (!e_mapi_object_contains_prop (attach->embedded_object, PidTagBody))
 2278             return TRUE;
 2279 
 2280         if (has_embedded_message_without_body (attach->embedded_object))
 2281             return TRUE;
 2282     }
 2283 
 2284     return FALSE;
 2285 }
 2286 
 2287 static gboolean
 2288 get_additional_properties_cb (EMapiConnection *conn,
 2289                   TALLOC_CTX *mem_ctx,
 2290                   /* const */ EMapiObject *object,
 2291                   guint32 obj_index,
 2292                   guint32 obj_total,
 2293                   gpointer user_data,
 2294                   GCancellable *cancellable,
 2295                   GError **perror)
 2296 {
 2297     uint32_t ii;
 2298     EMapiObject *dest_object = user_data;
 2299 
 2300     g_return_val_if_fail (object != NULL, FALSE);
 2301     g_return_val_if_fail (dest_object != NULL, FALSE);
 2302 
 2303     for (ii = 0; ii < object->properties.cValues; ii++) {
 2304         uint32_t proptag = object->properties.lpProps[ii].ulPropTag;
 2305 
 2306         if ((proptag & 0xFFFF) == PT_ERROR
 2307             || e_mapi_util_find_array_propval (&dest_object->properties, proptag))
 2308             continue;
 2309 
 2310         dest_object->properties.cValues++;
 2311         dest_object->properties.lpProps = talloc_realloc (mem_ctx,
 2312                     dest_object->properties.lpProps,
 2313                     struct mapi_SPropValue,
 2314                     dest_object->properties.cValues + 1);
 2315         dest_object->properties.lpProps[dest_object->properties.cValues - 1] = object->properties.lpProps[ii];
 2316 
 2317         #define steal_ptr(x) (x) = talloc_steal (dest_object, (x))
 2318         switch (proptag & 0xFFFF) {
 2319         case PT_BOOLEAN:
 2320         case PT_I2:
 2321         case PT_LONG:
 2322         case PT_DOUBLE:
 2323         case PT_I8:
 2324         case PT_SYSTIME:
 2325             break;
 2326         case PT_STRING8:
 2327             steal_ptr (dest_object->properties.lpProps[dest_object->properties.cValues - 1].value.lpszA);
 2328             break;
 2329         case PT_UNICODE:
 2330             steal_ptr (dest_object->properties.lpProps[dest_object->properties.cValues - 1].value.lpszW);
 2331             break;
 2332         default:
 2333             g_debug ("%s: Do not know how to steal property type 0x%x, skipping it", G_STRFUNC, proptag & 0xFFFF);
 2334             dest_object->properties.cValues--;
 2335             break;
 2336         }
 2337         #undef steal_ptr
 2338 
 2339         dest_object->properties.lpProps[dest_object->properties.cValues].ulPropTag = 0;
 2340     }
 2341 
 2342     return TRUE;
 2343 }
 2344 
 2345 static void
 2346 traverse_attachments_for_body (EMapiConnection *conn,
 2347                    TALLOC_CTX *mem_ctx,
 2348                    EMapiObject *object,
 2349                    mapi_object_t *obj_message,
 2350                    GCancellable *cancellable,
 2351                    GError **perror)
 2352 {
 2353     EMapiAttachment *attach;
 2354 
 2355     g_return_if_fail (conn != NULL);
 2356     g_return_if_fail (mem_ctx != NULL);
 2357     g_return_if_fail (obj_message != NULL);
 2358 
 2359     for (attach = object->attachments; attach && !g_cancellable_is_cancelled (cancellable); attach = attach->next) {
 2360         if (attach->embedded_object) {
 2361             const uint32_t *pattach_num;
 2362             mapi_object_t obj_attach;
 2363             mapi_object_t obj_embedded;
 2364             gboolean have_embedded = FALSE;
 2365 
 2366             pattach_num = e_mapi_util_find_array_propval (&attach->properties, PidTagAttachNumber);
 2367             if (!pattach_num)
 2368                 continue;
 2369 
 2370             mapi_object_init (&obj_attach);
 2371             mapi_object_init (&obj_embedded);
 2372 
 2373             if (!e_mapi_object_contains_prop (attach->embedded_object, PidTagBody)) {
 2374                 struct SPropTagArray *tags;
 2375 
 2376                 if (OpenAttach (obj_message, *pattach_num, &obj_attach) != MAPI_E_SUCCESS)
 2377                     continue;
 2378 
 2379                 if (OpenEmbeddedMessage (&obj_attach, &obj_embedded, MAPI_READONLY) != MAPI_E_SUCCESS) {
 2380                     mapi_object_release (&obj_attach);
 2381                     continue;
 2382                 }
 2383 
 2384                 have_embedded = TRUE;
 2385 
 2386                 tags = set_SPropTagArray (mem_ctx, 1, PidTagBody);
 2387 
 2388                 e_mapi_fast_transfer_properties (conn, mem_ctx, &obj_embedded, tags, get_additional_properties_cb, attach->embedded_object, cancellable, perror);
 2389 
 2390                 talloc_free (tags);
 2391             }
 2392 
 2393             if (has_embedded_message_without_body (attach->embedded_object)) {
 2394                 if (!have_embedded) {
 2395                     if (OpenAttach (obj_message, *pattach_num, &obj_attach) != MAPI_E_SUCCESS)
 2396                         continue;
 2397 
 2398                     if (OpenEmbeddedMessage (&obj_attach, &obj_embedded, MAPI_READONLY) != MAPI_E_SUCCESS) {
 2399                         mapi_object_release (&obj_attach);
 2400                         continue;
 2401                     }
 2402 
 2403                     have_embedded = TRUE;
 2404                 }
 2405 
 2406                 traverse_attachments_for_body (conn, mem_ctx, attach->embedded_object, &obj_embedded, cancellable, perror);
 2407             }
 2408 
 2409             mapi_object_release (&obj_embedded);
 2410             mapi_object_release (&obj_attach);
 2411         }
 2412     }
 2413 }
 2414 
 2415 struct EnsureAdditionalPropertiesData
 2416 {
 2417     TransferObjectCB cb;
 2418     gpointer cb_user_data;
 2419     mapi_object_t *obj_folder;
 2420     guint32 downloaded;
 2421     guint32 download_offset;
 2422     guint32 download_total;
 2423 };
 2424 
 2425 static gboolean
 2426 ensure_additional_properties_cb (EMapiConnection *conn,
 2427                  TALLOC_CTX *mem_ctx,
 2428                  /* const */ EMapiObject *object,
 2429                  guint32 obj_index,
 2430                  guint32 obj_total,
 2431                  gpointer user_data,
 2432                  GCancellable *cancellable,
 2433                  GError **perror)
 2434 {
 2435     struct ap_data {
 2436         uint32_t orig_proptag, use_proptag;
 2437     } additional_properties[] = {
 2438         { PidTagBody, MAPI_E_RESERVED },
 2439         { PidTagMessageSize, MAPI_E_RESERVED },
 2440         { PidNameContentClass, MAPI_E_RESERVED }
 2441     };
 2442     struct EnsureAdditionalPropertiesData *eap = user_data;
 2443     gboolean need_any = FALSE;
 2444     uint32_t ii;
 2445 
 2446     g_return_val_if_fail (eap != NULL, FALSE);
 2447     g_return_val_if_fail (eap->cb != NULL, FALSE);
 2448     g_return_val_if_fail (object != NULL, FALSE);
 2449 
 2450     if (g_cancellable_is_cancelled (cancellable))
 2451         return FALSE;
 2452 
 2453     for (ii = 0; ii < G_N_ELEMENTS (additional_properties); ii++) {
 2454         uint32_t prop = additional_properties[ii].orig_proptag;
 2455 
 2456         if (!e_mapi_object_contains_prop (object, prop)) {
 2457             if (get_namedid_name (prop)) {
 2458                 prop = e_mapi_connection_resolve_named_prop (conn, eap->obj_folder, prop, cancellable, NULL);
 2459             }
 2460         } else {
 2461             prop = MAPI_E_RESERVED;
 2462         }
 2463 
 2464         additional_properties[ii].use_proptag = prop;
 2465         need_any = need_any || prop != MAPI_E_RESERVED;
 2466     }
 2467 
 2468     /* Fast-transfer transfers only Html or Body, never both */
 2469     if (!g_cancellable_is_cancelled (cancellable) && (need_any || has_embedded_message_without_body (object))) {
 2470         const mapi_id_t *mid;
 2471 
 2472         mid = e_mapi_util_find_array_propval (&object->properties, PidTagMid);
 2473         if (mid && *mid) {
 2474             mapi_object_t obj_message;
 2475             
 2476             mapi_object_init (&obj_message);
 2477 
 2478             if (OpenMessage (eap->obj_folder, mapi_object_get_id (eap->obj_folder), *mid, &obj_message, 0) == MAPI_E_SUCCESS) {
 2479                 struct SPropTagArray *tags = NULL;
 2480 
 2481                 for (ii = 0; ii < G_N_ELEMENTS (additional_properties); ii++) {
 2482                     uint32_t prop = additional_properties[ii].use_proptag;
 2483 
 2484                     if (prop == MAPI_E_RESERVED)
 2485                         continue;
 2486 
 2487                     if (!tags)
 2488                         tags = set_SPropTagArray (mem_ctx, 1, prop);
 2489                     else
 2490                         SPropTagArray_add (mem_ctx, tags, prop);
 2491                 }
 2492 
 2493                 if (tags) {
 2494                     uint32_t jj = object->properties.cValues;
 2495 
 2496                     e_mapi_fast_transfer_properties (conn, mem_ctx, &obj_message, tags, get_additional_properties_cb, object, cancellable, perror);
 2497 
 2498                     while (jj < object->properties.cValues) {
 2499                         for (ii = 0; ii < G_N_ELEMENTS (additional_properties); ii++) {
 2500                             uint32_t proptag = object->properties.lpProps[jj].ulPropTag;
 2501 
 2502                             if (additional_properties[ii].use_proptag == proptag ||
 2503                                 (((proptag & 0xFFFF) == PT_STRING8 || (proptag & 0xFFFF) == PT_UNICODE) &&
 2504                                     (proptag & ~0xFFFF) == (additional_properties[ii].use_proptag & ~0xFFFF))) {
 2505                                 /* string8 and unicode properties are interchangeable in the union, luckily */
 2506                                 object->properties.lpProps[jj].ulPropTag = additional_properties[ii].orig_proptag;
 2507                                 break;
 2508                             }
 2509                         }
 2510 
 2511                         jj++;
 2512                     }
 2513 
 2514                     talloc_free (tags);
 2515                 }
 2516 
 2517                 traverse_attachments_for_body (conn, mem_ctx, object, &obj_message, cancellable, perror);
 2518             }
 2519 
 2520             mapi_object_release (&obj_message);
 2521         }
 2522     }
 2523 
 2524     eap->downloaded++;
 2525 
 2526     return eap->cb (conn, mem_ctx, object, eap->downloaded + eap->download_offset, eap->download_total, eap->cb_user_data, cancellable, perror);
 2527 }
 2528 
 2529 static enum MAPISTATUS
 2530 fetch_object_property_as_stream (EMapiConnection *conn,
 2531                  TALLOC_CTX *mem_ctx,
 2532                  mapi_object_t *obj_message,
 2533                  uint32_t proptag,
 2534                  uint64_t *pcb,
 2535                  uint8_t **plpb,
 2536                  GCancellable *cancellable,
 2537                  GError **perror)
 2538 {
 2539     enum MAPISTATUS ms;
 2540     mapi_object_t obj_stream;
 2541     uint32_t buf_size, max_read;
 2542     uint16_t off_data, cn_read;
 2543     uint64_t cb = 0;
 2544     uint8_t *lpb = NULL;
 2545     gboolean done = FALSE;
 2546 
 2547     g_return_val_if_fail (conn != NULL, MAPI_E_INVALID_PARAMETER);
 2548     g_return_val_if_fail (mem_ctx != NULL, MAPI_E_INVALID_PARAMETER);
 2549     g_return_val_if_fail (obj_message != NULL, MAPI_E_INVALID_PARAMETER);
 2550     g_return_val_if_fail (pcb != NULL, MAPI_E_INVALID_PARAMETER);
 2551     g_return_val_if_fail (plpb != NULL, MAPI_E_INVALID_PARAMETER);
 2552 
 2553     mapi_object_init (&obj_stream);
 2554 
 2555     ms = OpenStream (obj_message, proptag, OpenStream_ReadOnly, &obj_stream);
 2556     if (ms != MAPI_E_SUCCESS) {
 2557         make_mapi_error (perror, "OpenStream", ms);
 2558         goto cleanup;
 2559     }
 2560 
 2561     cb = 0;
 2562 
 2563     ms = GetStreamSize (&obj_stream, &buf_size);
 2564     if (ms != MAPI_E_SUCCESS) {
 2565         make_mapi_error (perror, "GetStreamSize", ms);
 2566         goto cleanup;
 2567     }
 2568 
 2569     cb = buf_size;
 2570     lpb = talloc_size (mem_ctx, cb + 1);
 2571     if (!lpb || !cb)
 2572         goto cleanup;
 2573 
 2574     /* determine max_read first, to read by chunks as long as possible */
 2575     off_data = 0;
 2576     max_read = buf_size > STREAM_MAX_READ_SIZE ? STREAM_MAX_READ_SIZE : buf_size;
 2577     do {
 2578         ms = ReadStream (&obj_stream, lpb + off_data, max_read, &cn_read);
 2579         if (ms == MAPI_E_SUCCESS) {
 2580             if (cn_read == 0) {
 2581                 done = TRUE;
 2582             } else {
 2583                 off_data += cn_read;
 2584                 if (off_data >= buf_size)
 2585                     done = TRUE;
 2586             }
 2587             break;
 2588         }
 2589 
 2590         if (ms == 0x2c80)
 2591             max_read = max_read >> 1;
 2592         else
 2593             max_read = STREAM_MAX_READ_SIZE_DF;
 2594 
 2595         if (max_read < STREAM_MAX_READ_SIZE_DF)
 2596             max_read = STREAM_MAX_READ_SIZE_DF;
 2597     } while (ms == 0x2c80); /* an error when max_read is too large? */
 2598 
 2599     while (!done) {
 2600         ms = ReadStream (&obj_stream, lpb + off_data, max_read, &cn_read);
 2601         if (ms != MAPI_E_SUCCESS) {
 2602             make_mapi_error (perror, "ReadStream", ms);
 2603             done = TRUE;
 2604         } else if (cn_read == 0) {
 2605             done = TRUE;
 2606         } else {
 2607             off_data += cn_read;
 2608             if (off_data >= buf_size)
 2609                 done = TRUE;
 2610         }
 2611     }
 2612 
 2613  cleanup:
 2614     mapi_object_release (&obj_stream);
 2615 
 2616     *pcb = cb;
 2617     *plpb = lpb;
 2618 
 2619     return ms;
 2620 }
 2621 
 2622 static enum MAPISTATUS
 2623 e_mapi_connection_fetch_object_internal (EMapiConnection *conn,
 2624                      TALLOC_CTX *mem_ctx,
 2625                      mapi_object_t *obj_message,
 2626                      struct EnsureAdditionalPropertiesData *eap,
 2627                      EMapiObject **out_object,
 2628                      GCancellable *cancellable,
 2629                      GError **perror);
 2630 
 2631 struct FetchObjectAttachmentData
 2632 {
 2633     mapi_object_t *obj_message;
 2634     struct EnsureAdditionalPropertiesData *eap;
 2635     EMapiObject *object; /* to add attachments to */
 2636 };
 2637 
 2638 static gboolean
 2639 fetch_object_attachment_cb (EMapiConnection *conn,
 2640                 TALLOC_CTX *mem_ctx,
 2641                 struct SRow *srow,
 2642                 guint32 row_index,
 2643                 guint32 rows_total,
 2644                 gpointer user_data,
 2645                 GCancellable *cancellable,
 2646                 GError **perror)
 2647 {
 2648     enum MAPISTATUS ms;
 2649     struct FetchObjectAttachmentData *foa = user_data;
 2650     EMapiAttachment *attachment = NULL;
 2651     mapi_object_t obj_attach;
 2652     const uint32_t *attach_num, *attach_method;
 2653 
 2654     g_return_val_if_fail (conn != NULL, FALSE);
 2655     g_return_val_if_fail (mem_ctx != NULL, FALSE);
 2656     g_return_val_if_fail (srow != NULL, FALSE);
 2657     g_return_val_if_fail (user_data != NULL, FALSE);
 2658     g_return_val_if_fail (foa->obj_message != NULL, FALSE);
 2659     g_return_val_if_fail (foa->object != NULL, FALSE);
 2660 
 2661     mapi_object_init (&obj_attach);
 2662 
 2663     attach_num = e_mapi_util_find_row_propval (srow, PidTagAttachNumber);
 2664     if (!attach_num)
 2665         return FALSE;
 2666 
 2667     ms = OpenAttach (foa->obj_message, *attach_num, &obj_attach);
 2668     if (ms != MAPI_E_SUCCESS) {
 2669         make_mapi_error (perror, "OpenAttach", ms);
 2670         goto cleanup;
 2671     }
 2672 
 2673     attachment = e_mapi_attachment_new (foa->object);
 2674 
 2675     ms = GetPropsAll (&obj_attach, MAPI_UNICODE, &attachment->properties);
 2676     if (ms != MAPI_E_SUCCESS) {
 2677         make_mapi_error (perror, "Attachment::GetPropsAll", ms);
 2678         goto cleanup;
 2679     }
 2680 
 2681     if (attachment->properties.lpProps)
 2682         attachment->properties.lpProps = talloc_steal (attachment, attachment->properties.lpProps);
 2683 
 2684     attach_method = e_mapi_util_find_row_propval (srow, PidTagAttachMethod);
 2685     if (attach_method && *attach_method == ATTACH_BY_VALUE) {
 2686         if (!e_mapi_attachment_contains_prop (attachment, PidTagAttachDataBinary)) {
 2687             uint64_t cb = 0;
 2688             uint8_t *lpb = NULL;
 2689 
 2690             ms = fetch_object_property_as_stream (conn, mem_ctx, &obj_attach, PidTagAttachDataBinary, &cb, &lpb, cancellable, perror);
 2691             if (ms != MAPI_E_SUCCESS) {
 2692                 make_mapi_error (perror, "Attachment::fetch PidTagAttachDataBinary", ms);
 2693                 goto cleanup;
 2694             }
 2695 
 2696             e_mapi_attachment_add_streamed (attachment, PidTagAttachDataBinary, cb, lpb);
 2697         }
 2698     } else if (attach_method && *attach_method == ATTACH_EMBEDDED_MSG) {
 2699         mapi_object_t obj_emb_msg;
 2700 
 2701         mapi_object_init (&obj_emb_msg);
 2702 
 2703         if (OpenEmbeddedMessage (&obj_attach, &obj_emb_msg, MAPI_READONLY) == MAPI_E_SUCCESS) {
 2704             e_mapi_connection_fetch_object_internal (conn, mem_ctx, &obj_emb_msg, foa->eap, &attachment->embedded_object, cancellable, perror);
 2705         }
 2706 
 2707         mapi_object_release (&obj_emb_msg);
 2708     }
 2709 
 2710  cleanup:
 2711     mapi_object_release (&obj_attach);
 2712 
 2713     if (ms == MAPI_E_SUCCESS) {
 2714         if (!foa->object->attachments) {
 2715             foa->object->attachments = attachment;
 2716         } else {
 2717             EMapiAttachment *attach = foa->object->attachments;
 2718             while (attach->next)
 2719                 attach = attach->next;
 2720             attach->next = attachment;
 2721         }
 2722     } else {
 2723         e_mapi_attachment_free (attachment);
 2724     }
 2725 
 2726     return ms == MAPI_E_SUCCESS;
 2727 }
 2728 
 2729 static enum MAPISTATUS
 2730 e_mapi_connection_fetch_object_internal (EMapiConnection *conn,
 2731                      TALLOC_CTX *mem_ctx,
 2732                      mapi_object_t *obj_message,
 2733                      struct EnsureAdditionalPropertiesData *eap,
 2734                      EMapiObject **out_object,
 2735                      GCancellable *cancellable,
 2736                      GError **perror)
 2737 {
 2738     enum MAPISTATUS ms;
 2739     EMapiObject *object;
 2740     uint16_t ui16, uj16, np_count = 0, *np_propID = NULL;
 2741     uint32_t ui32;
 2742     struct MAPINAMEID *np_nameid = NULL;
 2743     const uint8_t *has_attachments;
 2744     struct SPropTagArray recipient_proptags;
 2745     struct SRowSet recipient_rows;
 2746     mapi_object_t attach_table;
 2747 
 2748     g_return_val_if_fail (conn != NULL, MAPI_E_INVALID_PARAMETER);
 2749     g_return_val_if_fail (mem_ctx != NULL, MAPI_E_INVALID_PARAMETER);
 2750     g_return_val_if_fail (obj_message != NULL, MAPI_E_INVALID_PARAMETER);
 2751     g_return_val_if_fail (eap != NULL, MAPI_E_INVALID_PARAMETER);
 2752     g_return_val_if_fail (out_object != NULL, MAPI_E_INVALID_PARAMETER);
 2753 
 2754     mapi_object_init (&attach_table);
 2755 
 2756     object = e_mapi_object_new (mem_ctx);
 2757 
 2758     ms = GetPropsAll (obj_message, MAPI_UNICODE, &object->properties);
 2759     if (ms != MAPI_E_SUCCESS) {
 2760         make_mapi_error (perror, "GetPropsAll", ms);
 2761         goto cleanup;
 2762     }
 2763 
 2764     if (!object->properties.lpProps) {
 2765         /* kinf of success, no properties received */
 2766         goto cleanup;
 2767     }
 2768 
 2769     object->properties.lpProps = talloc_steal (object, object->properties.lpProps);
 2770 
 2771     /* to transform named ids to their PidLid or PidName tags, like the fast-transfer does */
 2772     ms = QueryNamedProperties (obj_message, 0, NULL, &np_count, &np_propID, &np_nameid);
 2773     if (ms != MAPI_E_SUCCESS) {
 2774         make_mapi_error (perror, "QueryNamedProperties", ms);
 2775         goto cleanup;
 2776     }
 2777 
 2778     if (np_count && np_propID && np_nameid) {
 2779         for (ui16 = 0; ui16 < np_count; ui16++) {
 2780             uint32_t proptag = np_propID[ui16];
 2781 
 2782             for (uj16 = 0; uj16 < object->properties.cValues; uj16++) {
 2783                 if (object->properties.lpProps[uj16].ulPropTag == proptag) {
 2784                     uint32_t lid = MAPI_E_RESERVED;
 2785                     char *guid;
 2786 
 2787                     guid = GUID_string (mem_ctx, &(np_nameid[ui16].lpguid));
 2788 
 2789                     if (np_nameid[ui16].ulKind == MNID_ID) {
 2790                         if (mapi_nameid_lid_lookup_canonical (np_nameid[ui16].kind.lid, guid, &lid) != MAPI_E_SUCCESS)
 2791                             lid = MAPI_E_RESERVED;
 2792                     } else if (np_nameid[ui16].ulKind == MNID_STRING) {
 2793                         if (mapi_nameid_string_lookup_canonical (np_nameid[ui16].kind.lpwstr.Name, guid, &lid) != MAPI_E_SUCCESS)
 2794                             lid = MAPI_E_RESERVED;
 2795                     }
 2796 
 2797                     talloc_free (guid);
 2798 
 2799                     if (lid != MAPI_E_RESERVED && (lid & 0xFFFF) == (proptag & 0xFFFF)) {
 2800                         object->properties.lpProps[uj16].ulPropTag = lid;
 2801                     }
 2802 
 2803                     break;
 2804                 }
 2805             }
 2806         }
 2807     }
 2808 
 2809     talloc_free (np_propID);
 2810     talloc_free (np_nameid);
 2811 
 2812     /* ensure certain properties */
 2813     if (!e_mapi_object_contains_prop (object, PidTagHtml)) {
 2814         uint8_t best_body = 0;
 2815 
 2816         if (GetBestBody (obj_message, &best_body) == MAPI_E_SUCCESS && best_body == olEditorHTML) {
 2817             uint64_t cb = 0;
 2818             uint8_t *lpb = NULL;
 2819 
 2820             ms = fetch_object_property_as_stream (conn, mem_ctx, obj_message, PidTagHtml, &cb, &lpb, cancellable, perror);
 2821             if (ms != MAPI_E_SUCCESS) {
 2822                 make_mapi_error (perror, "Object::fetch PidTagHtml", ms);
 2823                 goto cleanup;
 2824             }
 2825 
 2826             e_mapi_object_add_streamed (object, PidTagHtml, cb, lpb);
 2827         }
 2828     }
 2829 
 2830     if (!e_mapi_object_contains_prop (object, PidTagBody)) {
 2831         uint64_t cb = 0;
 2832         uint8_t *lpb = NULL;
 2833 
 2834         if (fetch_object_property_as_stream (conn, mem_ctx, obj_message, PidTagBody, &cb, &lpb, cancellable, NULL) == MAPI_E_SUCCESS) {
 2835             object->properties.cValues++;
 2836             object->properties.lpProps = talloc_realloc (mem_ctx,
 2837                                      object->properties.lpProps,
 2838                                      struct mapi_SPropValue,
 2839                                      object->properties.cValues + 1);
 2840             object->properties.lpProps[object->properties.cValues - 1].ulPropTag = PidTagBody;
 2841             if (cb > 0 && lpb[cb - 1] == 0)
 2842                 object->properties.lpProps[object->properties.cValues - 1].value.lpszW = (const char *) talloc_steal (object, lpb);
 2843             else
 2844                 object->properties.lpProps[object->properties.cValues - 1].value.lpszW = talloc_strndup (object, (char *) lpb, cb);
 2845             object->properties.lpProps[object->properties.cValues].ulPropTag = 0;
 2846         }
 2847     }
 2848 
 2849     if (!e_mapi_util_find_array_propval (&object->properties, PidNameContentClass)) {
 2850         uint32_t prop = PidNameContentClass;
 2851 
 2852         prop = e_mapi_connection_resolve_named_prop (conn, eap->obj_folder, prop, cancellable, NULL);
 2853         if (prop != MAPI_E_RESERVED) {
 2854             struct SPropTagArray *tags;
 2855             struct SPropValue *lpProps = NULL;
 2856             uint32_t prop_count = 0;
 2857 
 2858             tags = set_SPropTagArray (mem_ctx, 1, prop);
 2859 
 2860             if (GetProps (obj_message, MAPI_PROPS_SKIP_NAMEDID_CHECK | MAPI_UNICODE, tags, &lpProps, &prop_count) == MAPI_E_SUCCESS && lpProps) {
 2861                 if (lpProps[0].ulPropTag == prop) {
 2862                     object->properties.cValues++;
 2863                     object->properties.lpProps = talloc_realloc (mem_ctx,
 2864                                              object->properties.lpProps,
 2865                                              struct mapi_SPropValue,
 2866                                              object->properties.cValues + 1);
 2867                     object->properties.lpProps[object->properties.cValues - 1].ulPropTag = PidNameContentClass;
 2868                     object->properties.lpProps[object->properties.cValues - 1].value.lpszW = talloc_strdup (object, lpProps[0].value.lpszW);
 2869                     object->properties.lpProps[object->properties.cValues].ulPropTag = 0;
 2870                 }
 2871             }
 2872 
 2873             talloc_free (tags);
 2874             talloc_free (lpProps);
 2875         }
 2876     }
 2877 
 2878     /* fetch attachments */
 2879     has_attachments = e_mapi_util_find_array_propval (&object->properties, PidTagHasAttachments);
 2880     if (has_attachments && *has_attachments) {
 2881         struct SPropTagArray *attach_columns;
 2882         struct FetchObjectAttachmentData foa;
 2883 
 2884         ms = GetAttachmentTable (obj_message, &attach_table);
 2885         if (ms != MAPI_E_SUCCESS) {
 2886             make_mapi_error (perror, "GetAttachmentTable", ms);
 2887             goto cleanup;
 2888         }
 2889 
 2890         attach_columns = set_SPropTagArray (mem_ctx, 1, PidTagAttachNumber);
 2891         ms = SetColumns (&attach_table, attach_columns);
 2892         if (ms != MAPI_E_SUCCESS) {
 2893             make_mapi_error (perror, "AttachTable::SetColumns", ms);
 2894             talloc_free (attach_columns);
 2895             goto cleanup;
 2896         }
 2897         talloc_free (attach_columns);
 2898 
 2899         foa.obj_message = obj_message;
 2900         foa.eap = eap;
 2901         foa.object = object;
 2902 
 2903         ms = foreach_tablerow (conn, mem_ctx, &attach_table, fetch_object_attachment_cb, &foa, cancellable, perror);
 2904         if (ms != MAPI_E_SUCCESS) {
 2905             make_mapi_error (perror, "AttachTable::foreach_tablerow", ms);
 2906             goto cleanup;
 2907         }
 2908     }
 2909 
 2910     /* get recipients */
 2911     ms = GetRecipientTable (obj_message, &recipient_rows, &recipient_proptags);
 2912     if (ms != MAPI_E_SUCCESS) {
 2913         make_mapi_error (perror, "GetRecipientTable", ms);
 2914         goto cleanup;
 2915     }
 2916 
 2917     if (recipient_rows.cRows > 0) {
 2918         uint32_t uj32, uk32;
 2919         EMapiRecipient *first_recipient = NULL;
 2920 
 2921         for (ui32 = 0; ui32 < recipient_rows.cRows; ui32++) {
 2922             struct SRow *row = &recipient_rows.aRow[ui32];
 2923             EMapiRecipient *recipient;
 2924 
 2925             recipient = e_mapi_recipient_new (object);
 2926             recipient->properties.cValues = row->cValues;
 2927             recipient->properties.lpProps = talloc_zero_array (recipient, struct mapi_SPropValue, recipient->properties.cValues + 1);
 2928 
 2929             for (uj32 = 0, uk32 = 0; uj32 < row->cValues; uj32++, uk32++) {
 2930                 if (may_skip_property (row->lpProps[uj32].ulPropTag) ||
 2931                     !e_mapi_utils_copy_to_mapi_SPropValue (recipient, &recipient->properties.lpProps[uk32], &row->lpProps[uj32])) {
 2932                     uk32--;
 2933                     recipient->properties.cValues--;
 2934                     recipient->properties.lpProps[recipient->properties.cValues].ulPropTag = 0;
 2935                 }
 2936             }
 2937 
 2938             recipient->properties.lpProps[recipient->properties.cValues].ulPropTag = 0;
 2939         }
 2940 
 2941         object->recipients = first_recipient;
 2942     }
 2943 
 2944  cleanup:
 2945     mapi_object_release (&attach_table);
 2946 
 2947     if (ms == MAPI_E_SUCCESS) {
 2948         *out_object = object;
 2949     } else {
 2950         *out_object = NULL;
 2951         e_mapi_object_free (object);
 2952     }
 2953 
 2954     return ms;
 2955 }
 2956 
 2957 static enum MAPISTATUS
 2958 e_mapi_connection_fetch_objects_internal (EMapiConnection *conn,
 2959                       TALLOC_CTX *mem_ctx,
 2960                       mapi_id_array_t *ids,
 2961                       struct EnsureAdditionalPropertiesData *eap,
 2962                       GCancellable *cancellable,
 2963                       GError **perror)
 2964 {
 2965     enum MAPISTATUS ms;
 2966     guint32 idx;
 2967     mapi_container_list_t *element;
 2968 
 2969     g_return_val_if_fail (conn != NULL, MAPI_E_INVALID_PARAMETER);
 2970     g_return_val_if_fail (mem_ctx != NULL, MAPI_E_INVALID_PARAMETER);
 2971     g_return_val_if_fail (ids != NULL, MAPI_E_INVALID_PARAMETER);
 2972     g_return_val_if_fail (eap != NULL, MAPI_E_INVALID_PARAMETER);
 2973     g_return_val_if_fail (eap->obj_folder != NULL, MAPI_E_INVALID_PARAMETER);
 2974     g_return_val_if_fail (eap->downloaded < ids->count, MAPI_E_INVALID_PARAMETER);
 2975 
 2976     for (idx = 0, element = ids->lpContainerList; idx < ids->count && idx < eap->downloaded && element; idx++) {
 2977         element = element->next;
 2978     }
 2979 
 2980     g_return_val_if_fail (idx < ids->count, MAPI_E_INVALID_PARAMETER);
 2981 
 2982     ms = MAPI_E_SUCCESS;
 2983     while (element && ms == MAPI_E_SUCCESS) {
 2984         mapi_object_t obj_message;
 2985         EMapiObject *object = NULL;
 2986         GError *local_error = NULL;
 2987 
 2988         if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
 2989             ms = MAPI_E_USER_CANCEL;
 2990             break;
 2991         }
 2992 
 2993         mapi_object_init (&obj_message);
 2994 
 2995         ms = OpenMessage (eap->obj_folder, mapi_object_get_id (eap->obj_folder), element->id, &obj_message, 0 /* read-only */);
 2996         if (ms != MAPI_E_SUCCESS) {
 2997             make_mapi_error (perror, "OpenMessage", ms);
 2998             mapi_object_release (&obj_message);
 2999             break;
 3000         }
 3001 
 3002         /* silently skip broken objects */
 3003         ms = e_mapi_connection_fetch_object_internal (conn, mem_ctx, &obj_message, eap, &object, cancellable, &local_error);
 3004         if (ms == MAPI_E_SUCCESS) {
 3005             if (!eap->cb (conn, mem_ctx, object, eap->downloaded + eap->download_offset, eap->download_total, eap->cb_user_data, cancellable, perror)) {
 3006                 ms = MAPI_E_USER_CANCEL;
 3007                 make_mapi_error (perror, "Object processing", ms);
 3008             }
 3009         } else {
 3010             e_mapi_debug_print ("%s: Failed to fetch object %016" G_GINT64_MODIFIER "X: %s",
 3011                 G_STRFUNC, element->id, local_error ? local_error->message : mapi_get_errstr (ms));
 3012         }
 3013 
 3014         e_mapi_object_free (object);
 3015         mapi_object_release (&obj_message);
 3016 
 3017         eap->downloaded++;
 3018 
 3019         element = element->next;
 3020     }
 3021 
 3022     return ms;
 3023 }
 3024 
 3025 /* deals with named IDs transparently, thus it's OK to check with PidLid and PidName constants only */
 3026 gboolean
 3027 e_mapi_connection_transfer_objects (EMapiConnection *conn,
 3028                     mapi_object_t *obj_folder,
 3029                     const GSList *mids,
 3030                     TransferObjectCB cb,
 3031                     gpointer cb_user_data,
 3032                     GCancellable *cancellable,
 3033                     GError **perror)
 3034 {
 3035     enum MAPISTATUS ms = MAPI_E_CALL_FAILED;
 3036     TALLOC_CTX *mem_ctx;
 3037     const GSList *iter;
 3038     struct EnsureAdditionalPropertiesData eap;
 3039 
 3040     CHECK_CORRECT_CONN_AND_GET_PRIV (conn, FALSE);
 3041     e_return_val_mapi_error_if_fail (priv->session != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 3042     e_return_val_mapi_error_if_fail (cb != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 3043     e_return_val_mapi_error_if_fail (obj_folder != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 3044 
 3045     LOCK (cancellable, perror, FALSE);
 3046     mem_ctx = talloc_new (priv->session);
 3047 
 3048     eap.download_offset = 0;
 3049     eap.download_total = g_slist_length ((GSList *) mids);
 3050 
 3051     iter = mids;
 3052     while (iter) {
 3053         mapi_id_array_t ids;
 3054 
 3055         ms = mapi_id_array_init (mem_ctx, &ids);
 3056         if (ms != MAPI_E_SUCCESS) {
 3057             make_mapi_error (perror, "mapi_id_array_init", ms);
 3058             goto cleanup;
 3059         }
 3060 
 3061         /* run this in chunks of 100 IDs */
 3062         for (; iter && ids.count < 100; iter = iter->next) {
 3063             mapi_id_t *pmid = iter->data;
 3064 
 3065             if (pmid)
 3066                 mapi_id_array_add_id (&ids, *pmid);
 3067         }
 3068 
 3069         if (g_cancellable_is_cancelled (cancellable)) {
 3070             if (perror && !*perror) {
 3071                 /* coverity[unchecked_value] */
 3072                 g_cancellable_set_error_if_cancelled (cancellable, perror);
 3073             }
 3074 
 3075             ms = MAPI_E_USER_CANCEL;
 3076             mapi_id_array_release (&ids);
 3077             goto cleanup;
 3078         }
 3079 
 3080         eap.cb = cb;
 3081         eap.cb_user_data = cb_user_data;
 3082         eap.obj_folder = obj_folder;
 3083         eap.downloaded = 0;
 3084 
 3085         ms = e_mapi_fast_transfer_objects (conn, mem_ctx, obj_folder, &ids, ensure_additional_properties_cb, &eap, cancellable, perror);
 3086         if (ms == MAPI_E_CALL_FAILED) {
 3087             /* err, fallback to slow transfer, probably FXGetBuffer failed;
 3088                see http://tracker.openchange.org/issues/378
 3089             */
 3090 
 3091             g_clear_error (perror);
 3092 
 3093             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);
 3094 
 3095             ms = e_mapi_connection_fetch_objects_internal (conn, mem_ctx, &ids, &eap, cancellable, perror);
 3096         }
 3097 
 3098         eap.download_offset += ids.count;
 3099 
 3100         mapi_id_array_release (&ids);
 3101     }
 3102 
 3103  cleanup:
 3104     talloc_free (mem_ctx);
 3105     UNLOCK ();
 3106 
 3107     return ms == MAPI_E_SUCCESS;
 3108 }
 3109 
 3110 gboolean
 3111 e_mapi_connection_transfer_object (EMapiConnection *conn,
 3112                    mapi_object_t *obj_folder,
 3113                    mapi_id_t message_id,
 3114                    TransferObjectCB cb,
 3115                    gpointer cb_user_data,
 3116                    GCancellable *cancellable,
 3117                    GError **perror)
 3118 {
 3119     GSList *mids;
 3120     gboolean res;
 3121 
 3122     mids = g_slist_append (NULL, &message_id);
 3123     res = e_mapi_connection_transfer_objects (conn, obj_folder, mids, cb, cb_user_data, cancellable, perror);
 3124     g_slist_free (mids);
 3125 
 3126     return res;
 3127 }
 3128 
 3129 struct GetSummaryData {
 3130     guint32 obj_index;
 3131     guint32 obj_total;
 3132     struct SPropValue *lpProps;
 3133     uint32_t prop_count;
 3134     TransferObjectCB cb;
 3135     gpointer cb_user_data;
 3136 };
 3137 
 3138 static gboolean
 3139 internal_get_summary_cb (EMapiConnection *conn,
 3140              TALLOC_CTX *mem_ctx,
 3141              /* const */ EMapiObject *object,
 3142              guint32 obj_index,
 3143              guint32 obj_total,
 3144              gpointer user_data,
 3145              GCancellable *cancellable,
 3146              GError **perror)
 3147 {
 3148     struct GetSummaryData *gsd = user_data;
 3149 
 3150     g_return_val_if_fail (gsd != NULL, FALSE);
 3151     g_return_val_if_fail (gsd->cb != NULL, FALSE);
 3152     g_return_val_if_fail (object != NULL, FALSE);
 3153 
 3154     if (g_cancellable_is_cancelled (cancellable))
 3155         return FALSE;
 3156 
 3157     /* also include properties received from GetProps,
 3158        as those like PR_MID are not included by default */
 3159     if (gsd->lpProps && gsd->prop_count > 0) {
 3160         uint32_t ii;
 3161 
 3162         for (ii = 0; ii < gsd->prop_count; ii++) {
 3163             /* skip errors and already included properties */
 3164             if ((gsd->lpProps[ii].ulPropTag & 0xFFFF) == PT_ERROR
 3165                 || e_mapi_object_contains_prop (object, gsd->lpProps[ii].ulPropTag))
 3166                 continue;
 3167 
 3168             object->properties.cValues++;
 3169             object->properties.lpProps = talloc_realloc (mem_ctx,
 3170                         object->properties.lpProps,
 3171                         struct mapi_SPropValue,
 3172                         object->properties.cValues + 1);
 3173             cast_mapi_SPropValue (mem_ctx, &object->properties.lpProps[object->properties.cValues - 1], &gsd->lpProps[ii]);
 3174             object->properties.lpProps[object->properties.cValues].ulPropTag = 0;
 3175         }
 3176     }
 3177 
 3178     return gsd->cb (conn, mem_ctx, object, gsd->obj_index, gsd->obj_total, gsd->cb_user_data, cancellable, perror);
 3179 }
 3180 
 3181 /* transfers items summary, which is either PidTagTransportMessageHeaders or
 3182    the object without attachment */
 3183 gboolean
 3184 e_mapi_connection_transfer_summary (EMapiConnection *conn,
 3185                     mapi_object_t *obj_folder,
 3186                     const GSList *mids,
 3187                     TransferObjectCB cb,
 3188                     gpointer cb_user_data,
 3189                     GCancellable *cancellable,
 3190                     GError **perror)
 3191 {
 3192     enum MAPISTATUS ms;
 3193     TALLOC_CTX *mem_ctx;
 3194     const GSList *iter;
 3195     guint32 index, total;
 3196 
 3197     CHECK_CORRECT_CONN_AND_GET_PRIV (conn, FALSE);
 3198     e_return_val_mapi_error_if_fail (priv->session != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 3199     e_return_val_mapi_error_if_fail (cb != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 3200     e_return_val_mapi_error_if_fail (obj_folder != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 3201 
 3202     LOCK (cancellable, perror, FALSE);
 3203     mem_ctx = talloc_new (priv->session);
 3204 
 3205     ms = MAPI_E_SUCCESS;
 3206     total = g_slist_length ((GSList *) mids);
 3207     for (iter = mids, index = 0; iter && ms == MAPI_E_SUCCESS; iter = iter->next, index++) {
 3208         mapi_id_t *pmid = iter->data;
 3209 
 3210         if (pmid) {
 3211             mapi_object_t obj_message;
 3212             struct SPropTagArray *tags;
 3213             struct SPropValue *lpProps = NULL;
 3214             uint32_t prop_count = 0, ii;
 3215 
 3216             mapi_object_init (&obj_message);
 3217 
 3218             ms = OpenMessage (obj_folder, mapi_object_get_id (obj_folder), *pmid, &obj_message, 0);
 3219             if (ms != MAPI_E_SUCCESS && ms != MAPI_E_NOT_FOUND) {
 3220                 make_mapi_error (perror, "OpenMessage", ms);
 3221                 goto cleanup;
 3222             }
 3223 
 3224             tags = set_SPropTagArray (mem_ctx, 9,
 3225                 PidTagFolderId,
 3226                 PidTagMid,
 3227                 PidTagMessageFlags,
 3228                 PidTagMessageSize,
 3229                 PidTagMessageClass,
 3230                 PidTagLastModificationTime,
 3231                 PidTagTransportMessageHeaders,
 3232                 PidTagIconIndex,
 3233                 PidTagReadReceiptRequested);
 3234 
 3235             ms = GetProps (&obj_message, MAPI_PROPS_SKIP_NAMEDID_CHECK | MAPI_UNICODE, tags, &lpProps, &prop_count);
 3236             if (ms == MAPI_E_SUCCESS) {
 3237                 ms = MAPI_E_NOT_FOUND;
 3238                 if (lpProps && prop_count > 0) {
 3239                     const gchar *headers = e_mapi_util_find_SPropVal_array_propval (lpProps, PidTagTransportMessageHeaders);
 3240 
 3241                     if (headers && *headers) {
 3242                         EMapiObject *object;
 3243 
 3244                         ms = MAPI_E_SUCCESS;
 3245 
 3246                         object = e_mapi_object_new (mem_ctx);
 3247                         for (ii = 0; ii < prop_count; ii++) {
 3248                             object->properties.cValues++;
 3249                             object->properties.lpProps = talloc_realloc (mem_ctx,
 3250                                         object->properties.lpProps,
 3251                                         struct mapi_SPropValue,
 3252                                         object->properties.cValues + 1);
 3253                             cast_mapi_SPropValue (mem_ctx, &object->properties.lpProps[object->properties.cValues - 1], &lpProps[ii]);
 3254                             object->properties.lpProps[object->properties.cValues].ulPropTag = 0;
 3255                         }
 3256 
 3257                         if (!cb (conn, mem_ctx, object, index, total, cb_user_data, cancellable, perror)) {
 3258                             ms = MAPI_E_USER_CANCEL;
 3259                             e_mapi_object_free (object);
 3260                             mapi_object_release (&obj_message);
 3261                             talloc_free (lpProps);
 3262                             talloc_free (tags);
 3263                             goto cleanup;
 3264                         }
 3265 
 3266                         e_mapi_object_free (object);
 3267                     }
 3268                 }
 3269             }
 3270 
 3271             if (ms == MAPI_E_NOT_FOUND) {
 3272                 struct GetSummaryData gsd;
 3273 
 3274                 gsd.obj_index = index;
 3275                 gsd.obj_total = total;
 3276                 gsd.lpProps = lpProps;
 3277                 gsd.prop_count = prop_count;
 3278                 gsd.cb = cb;
 3279                 gsd.cb_user_data = cb_user_data;
 3280 
 3281                 ms = e_mapi_fast_transfer_object (conn, mem_ctx, &obj_message, E_MAPI_FAST_TRANSFER_FLAG_RECIPIENTS, internal_get_summary_cb, &gsd, cancellable, perror);
 3282                 if (ms != MAPI_E_SUCCESS) {
 3283                     make_mapi_error (perror, "transfer_object", ms);
 3284                     mapi_object_release (&obj_message);
 3285                     talloc_free (lpProps);
 3286                     talloc_free (tags);
 3287                     goto cleanup;
 3288                 }
 3289             }
 3290 
 3291             mapi_object_release (&obj_message);
 3292             talloc_free (lpProps);
 3293             talloc_free (tags);
 3294         }
 3295 
 3296         if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
 3297             ms = MAPI_E_USER_CANCEL;
 3298             goto cleanup;
 3299         }
 3300     }
 3301 
 3302  cleanup:
 3303     talloc_free (mem_ctx);
 3304     UNLOCK ();
 3305 
 3306     return ms == MAPI_E_SUCCESS;
 3307 }
 3308 
 3309 static gboolean
 3310 convert_mapi_props_to_props (EMapiConnection *conn,
 3311                  mapi_object_t *obj_folder,
 3312                  const struct mapi_SPropValue_array *mapi_props,
 3313                  const EMapiStreamedProp *known_streams,
 3314                  guint known_streams_count,
 3315                  struct SPropValue **props,
 3316                  uint32_t *propslen,
 3317                  EMapiStreamedProp **streams, /* can be NULL for no streaming */
 3318                  guint *streamslen, /* can be NULL only if streams is NULL; is ignored if streams is NULL */
 3319                  TALLOC_CTX *mem_ctx,
 3320                  GCancellable *cancellable,
 3321                  GError **perror)
 3322 {
 3323     uint16_t ii;
 3324     EResolveNamedIDsData *named_ids_list = NULL;
 3325     guint named_ids_len = 0;
 3326     gboolean res = TRUE;
 3327 
 3328     e_return_val_mapi_error_if_fail (conn != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 3329     e_return_val_mapi_error_if_fail (mapi_props != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 3330     e_return_val_mapi_error_if_fail (props != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 3331     e_return_val_mapi_error_if_fail (propslen != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 3332     e_return_val_mapi_error_if_fail (mem_ctx != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 3333     if (streams) {
 3334         e_return_val_mapi_error_if_fail (streamslen != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 3335     } else {
 3336         e_return_val_mapi_error_if_fail (known_streams == NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 3337     }
 3338 
 3339     #define addstream() {                                       \
 3340             if (!*streams) {                                \
 3341                 *streams = g_new0 (EMapiStreamedProp, 1);               \
 3342                 *streamslen = 0;                            \
 3343             } else {                                    \
 3344                 *streams = g_renew (EMapiStreamedProp, *streams, *streamslen + 1);  \
 3345             }                                       \
 3346                                                     \
 3347             (*streams)[*streamslen].proptag = proptag;                  \
 3348             (*streams)[*streamslen].cb = 0;                         \
 3349             (*streams)[*streamslen].lpb = NULL;                     \
 3350             (*streams)[*streamslen].orig_value = NULL;                  \
 3351             (*streamslen) += 1;                             \
 3352         }
 3353 
 3354     for (ii = 0; ii < mapi_props->cValues; ii++) {
 3355         gboolean processed = FALSE;
 3356         uint32_t proptag = mapi_props->lpProps[ii].ulPropTag;
 3357         gconstpointer propdata = get_mapi_SPropValue_data (&mapi_props->lpProps[ii]);
 3358 
 3359         maybe_add_named_id_tag (proptag, &named_ids_list, &named_ids_len);
 3360 
 3361         if (streams && propdata) {
 3362             /* copy anything longer than 1KB as streams; this doesn't count total packet size needed,
 3363                but because this is usually useful only for PidTagBody, PidTagHtml, which are there
 3364                only once, then no big deal
 3365             */
 3366 
 3367             uint32_t sz;
 3368             const gchar *str;
 3369             const struct SBinary_short *bin;
 3370 
 3371             switch (proptag & 0xFFFF) {
 3372             case PT_BINARY:
 3373                 bin = propdata;
 3374                 if (bin->cb > MAX_PROPERTY_WRITE_SIZE) {
 3375                     addstream ();
 3376                     (*streams)[(*streamslen) - 1].cb = bin->cb;
 3377                     (*streams)[(*streamslen) - 1].lpb = bin->lpb;
 3378                     (*streams)[(*streamslen) - 1].orig_value = propdata;
 3379                     processed = TRUE;
 3380                 }
 3381                 break;
 3382             case PT_STRING8:
 3383                 str = propdata;
 3384                 sz = get_mapi_property_size (&mapi_props->lpProps[ii]);
 3385                 if (sz > MAX_PROPERTY_WRITE_SIZE) {
 3386                     addstream ();
 3387                     (*streams)[(*streamslen) - 1].cb = sz;
 3388                     (*streams)[(*streamslen) - 1].lpb = (uint8_t *) str;
 3389                     (*streams)[(*streamslen) - 1].orig_value = propdata;
 3390                     processed = TRUE;
 3391                 }
 3392                 break;
 3393             case PT_UNICODE:
 3394                 str = propdata;
 3395                 sz = get_mapi_property_size (&mapi_props->lpProps[ii]);
 3396                 if (sz > MAX_PROPERTY_WRITE_SIZE) {
 3397                     gchar *in_unicode;
 3398                     gsize written = 0;
 3399 
 3400                     addstream ();
 3401                     (*streams)[(*streamslen) - 1].orig_value = propdata;
 3402 
 3403                     in_unicode = g_convert (str, strlen (str), "UTF-16", "UTF-8", NULL, &written, NULL);
 3404                     if (in_unicode && written > 0) {
 3405                         uint8_t *bytes = talloc_zero_size (mem_ctx, written + 2);
 3406 
 3407                         /* skip Unicode marker, if there */
 3408                         if (written >= 2 && (const guchar) in_unicode[0] == 0xFF && (const guchar) in_unicode[1] == 0xFE) {
 3409                             memcpy (bytes, in_unicode + 2, written - 2);
 3410                             written -= 2;
 3411                         } else
 3412                             memcpy (bytes, in_unicode, written);
 3413 
 3414                         /* null-terminated unicode string */
 3415                         (*streams)[(*streamslen) - 1].lpb = bytes;
 3416                         (*streams)[(*streamslen) - 1].cb = written + 2;
 3417                     }
 3418                     g_free (in_unicode);
 3419                     processed = TRUE;
 3420                 }
 3421                 break;
 3422             }
 3423         }
 3424 
 3425         if (!processed)
 3426             e_mapi_utils_add_spropvalue (mem_ctx, props, propslen, proptag, propdata);
 3427     }
 3428 
 3429     if (known_streams && known_streams_count > 0 && streams) {
 3430         for (ii = 0; ii < known_streams_count; ii++) {
 3431             uint32_t proptag = known_streams[ii].proptag;
 3432 
 3433             maybe_add_named_id_tag (proptag, &named_ids_list, &named_ids_len);
 3434 
 3435             addstream ();
 3436             (*streams)[(*streamslen) - 1].cb = known_streams[ii].cb;
 3437             (*streams)[(*streamslen) - 1].lpb = known_streams[ii].lpb;
 3438             (*streams)[(*streamslen) - 1].orig_value = NULL;
 3439         }
 3440     }
 3441     #undef addstream
 3442 
 3443     if (named_ids_list) {
 3444         GHashTable *replace_hash = NULL;
 3445 
 3446         res = e_mapi_connection_resolve_named_props (conn, obj_folder, named_ids_list, named_ids_len, cancellable, perror);
 3447 
 3448         if (res)
 3449             replace_hash = prepare_maybe_replace_hash (named_ids_list, named_ids_len, TRUE);
 3450 
 3451         if (replace_hash && *props) {
 3452             for (ii = 0; ii < *propslen; ii++) {
 3453                 uint32_t proptag = (*props)[ii].ulPropTag;
 3454 
 3455                 maybe_replace_named_id_tag (&proptag, replace_hash);
 3456 
 3457                 (*props)[ii].ulPropTag = proptag;
 3458             }
 3459         }
 3460 
 3461         if (replace_hash && streams) {
 3462             for (ii = 0; ii < *streamslen; ii++) {
 3463                 maybe_replace_named_id_tag (&((*streams)[ii].proptag), replace_hash);
 3464             }
 3465         }
 3466 
 3467         if (replace_hash)
 3468             g_hash_table_destroy (replace_hash);
 3469     }
 3470 
 3471     g_free (named_ids_list);
 3472 
 3473     return res;
 3474 }
 3475 
 3476 static gboolean
 3477 write_streamed_prop (EMapiConnection *conn,
 3478              mapi_object_t *obj_object,
 3479              const EMapiStreamedProp *stream,
 3480              TALLOC_CTX *mem_ctx,
 3481              GCancellable *cancellable,
 3482              GError **perror)
 3483 {
 3484     enum MAPISTATUS ms;
 3485     uint64_t total_written;
 3486     gboolean done = FALSE;
 3487     mapi_object_t obj_stream;
 3488 
 3489     CHECK_CORRECT_CONN_AND_GET_PRIV (conn, FALSE);
 3490     e_return_val_mapi_error_if_fail (priv->session != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 3491     e_return_val_mapi_error_if_fail (obj_object != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 3492     e_return_val_mapi_error_if_fail (stream != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 3493 
 3494     LOCK (cancellable, perror, FALSE);
 3495 
 3496     mapi_object_init (&obj_stream);
 3497 
 3498     if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
 3499         ms = MAPI_E_USER_CANCEL;
 3500         goto cleanup;
 3501     }
 3502 
 3503     /* OpenStream on required proptag */
 3504     ms = OpenStream (obj_object, stream->proptag, OpenStream_Create, &obj_stream);
 3505     if (ms != MAPI_E_SUCCESS) {
 3506         if (ms == MAPI_E_NO_ACCESS && stream->orig_value) {
 3507             /* write property with SetProps, because this one cannot be written as stream */
 3508             struct SPropValue *props = NULL;
 3509             uint32_t propslen = 0;
 3510 
 3511             e_mapi_utils_add_spropvalue (mem_ctx, &props, &propslen, stream->proptag, stream->orig_value);
 3512 
 3513             ms = SetProps (obj_object, MAPI_PROPS_SKIP_NAMEDID_CHECK, props, propslen);
 3514 
 3515             talloc_free (props);
 3516 
 3517             if (ms != MAPI_E_SUCCESS)
 3518                 make_mapi_error (perror, "SetProps", ms);
 3519         } else {
 3520             make_mapi_error (perror, "OpenStream", ms);
 3521         }
 3522         goto cleanup;
 3523     }
 3524 
 3525     /* Set the stream size */
 3526     ms = SetStreamSize (&obj_stream, stream->cb);
 3527     if (ms != MAPI_E_SUCCESS) {
 3528         make_mapi_error (perror, "SetStreamSize", ms);
 3529         goto cleanup;
 3530     }
 3531 
 3532     total_written = 0;
 3533     /* Write stream */
 3534     while (!done) {
 3535         uint16_t cn_written = 0;
 3536         DATA_BLOB blob;
 3537 
 3538         if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
 3539             ms = MAPI_E_USER_CANCEL;
 3540             goto cleanup;
 3541         }
 3542 
 3543         blob.length = (stream->cb - total_written) < STREAM_MAX_WRITE_SIZE ?
 3544                   (stream->cb - total_written) : STREAM_MAX_WRITE_SIZE;
 3545         blob.data = (uint8_t *) (stream->lpb + total_written);
 3546 
 3547         ms = WriteStream (&obj_stream, &blob, &cn_written);
 3548         if (ms != MAPI_E_SUCCESS) {
 3549             make_mapi_error (perror, "WriteStream", ms);
 3550             done = TRUE;
 3551         } else if (cn_written == 0) {
 3552             done = TRUE;
 3553         } else {
 3554             total_written += cn_written;
 3555             if (total_written >= stream->cb)
 3556                 done = TRUE;
 3557         }
 3558     }
 3559 
 3560     if (ms == MAPI_E_SUCCESS) {
 3561         /* Commit the stream */
 3562         ms = CommitStream (&obj_stream);
 3563         if (ms != MAPI_E_SUCCESS) {
 3564             make_mapi_error (perror, "CommitStream", ms);
 3565             goto cleanup;
 3566         }
 3567     }
 3568 
 3569  cleanup:
 3570     mapi_object_release (&obj_stream);
 3571 
 3572     UNLOCK ();
 3573 
 3574     return ms == MAPI_E_SUCCESS;
 3575 }
 3576 
 3577 static gboolean
 3578 update_props_on_object (EMapiConnection *conn,
 3579             mapi_object_t *obj_folder,
 3580             mapi_object_t *obj_object,
 3581             const struct mapi_SPropValue_array *properties,
 3582             const EMapiStreamedProp *known_streams,
 3583             guint known_streams_count,
 3584             TALLOC_CTX *mem_ctx,
 3585             GCancellable *cancellable,
 3586             GError **perror)
 3587 {
 3588     enum MAPISTATUS ms = MAPI_E_RESERVED;
 3589     struct SPropValue *props = NULL;
 3590     uint32_t propslen = 0;
 3591     EMapiStreamedProp *streams = NULL;
 3592     guint streamslen = 0;
 3593 
 3594     CHECK_CORRECT_CONN_AND_GET_PRIV (conn, FALSE);
 3595     e_return_val_mapi_error_if_fail (priv->session != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 3596 
 3597     LOCK (cancellable, perror, FALSE);
 3598 
 3599     if (!convert_mapi_props_to_props (conn, obj_folder, properties, known_streams, known_streams_count, &props, &propslen, &streams, &streamslen, mem_ctx, cancellable, perror)) {
 3600         ms = MAPI_E_CALL_FAILED;
 3601         make_mapi_error (perror, "convert_mapi_props_to_props", ms);
 3602         goto cleanup;
 3603     }
 3604 
 3605     if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
 3606         ms = MAPI_E_USER_CANCEL;
 3607         goto cleanup;
 3608     }
 3609 
 3610     if (props) {
 3611         /* set properties for the item */
 3612         ms = SetProps (obj_object, MAPI_PROPS_SKIP_NAMEDID_CHECK, props, propslen);
 3613 
 3614         talloc_free (props);
 3615 
 3616         if (ms != MAPI_E_SUCCESS) {
 3617             make_mapi_error (perror, "SetProps", ms);
 3618             goto cleanup;
 3619         }
 3620     }
 3621 
 3622     if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
 3623         ms = MAPI_E_USER_CANCEL;
 3624         goto cleanup;
 3625     }
 3626 
 3627     if (streams) {
 3628         guint ii;
 3629 
 3630         for (ii = 0; ii < streamslen; ii++) {
 3631             if (!write_streamed_prop (conn, obj_object, &streams[ii], mem_ctx, cancellable, perror)) {
 3632                 ms = MAPI_E_CALL_FAILED;
 3633                 make_mapi_error (perror, "write_streamed_prop", ms);
 3634                 break;
 3635             }
 3636 
 3637             if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
 3638                 ms = MAPI_E_USER_CANCEL;
 3639                 break;
 3640             }
 3641         }
 3642 
 3643         g_free (streams);
 3644     }
 3645  cleanup:
 3646     UNLOCK ();
 3647 
 3648     return ms == MAPI_E_SUCCESS;
 3649 }
 3650 
 3651 static gboolean
 3652 update_recipient_properties (EMapiConnection *conn,
 3653                  mapi_object_t *obj_folder,
 3654                  struct SRow *aRow,
 3655                  EMapiRecipient *recipient,
 3656                  gboolean is_resolved,
 3657                  TALLOC_CTX *mem_ctx,
 3658                  GCancellable *cancellable,
 3659                  GError **perror)
 3660 {
 3661     struct SPropValue *props = NULL;
 3662     uint32_t propslen = 0, ii;
 3663 
 3664     g_return_val_if_fail (recipient != NULL, FALSE);
 3665 
 3666     if (!convert_mapi_props_to_props (conn, obj_folder, &recipient->properties, NULL, 0, &props, &propslen, NULL, NULL, mem_ctx, cancellable, perror))
 3667         return FALSE;
 3668 
 3669     for (ii = 0; ii < propslen; ii++) {
 3670         /* do not overwrite all properties, if recipient was resolved properly */
 3671         if (!is_resolved
 3672             || props[ii].ulPropTag == PidTagRecipientType
 3673             || props[ii].ulPropTag == PidTagSendInternetEncoding
 3674             || props[ii].ulPropTag == PidTagRecipientFlags
 3675             || props[ii].ulPropTag == PidTagRecipientTrackStatus)
 3676             SRow_addprop (aRow, props[ii]);
 3677     }
 3678 
 3679     return TRUE;
 3680 }
 3681 
 3682 static gboolean
 3683 delete_object_recipients (EMapiConnection *conn,
 3684               mapi_object_t *obj_folder,
 3685               mapi_object_t *obj_object,
 3686               TALLOC_CTX *mem_ctx,
 3687               GCancellable *cancellable,
 3688               GError **perror)
 3689 {
 3690     enum MAPISTATUS ms;
 3691 
 3692     CHECK_CORRECT_CONN_AND_GET_PRIV (conn, FALSE);
 3693     e_return_val_mapi_error_if_fail (priv->session != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 3694 
 3695     LOCK (cancellable, perror, FALSE);
 3696 
 3697     ms = RemoveAllRecipients (obj_object);
 3698     if (ms != MAPI_E_SUCCESS)
 3699         make_mapi_error (perror, "RemoveAllRecipients", ms);
 3700 
 3701     UNLOCK ();
 3702 
 3703     return ms == MAPI_E_SUCCESS;
 3704 }
 3705 
 3706 static gboolean
 3707 add_object_recipients (EMapiConnection *conn,
 3708                mapi_object_t *obj_folder,
 3709                mapi_object_t *obj_message,
 3710                EMapiRecipient *recipients,
 3711                TALLOC_CTX *mem_ctx,
 3712                GCancellable *cancellable,
 3713                GError **perror)
 3714 {
 3715     const uint32_t required_tags[] = {PidTagEntryId,
 3716                       PidTagDisplayName,
 3717                       PidTagObjectType,
 3718                       PidTagDisplayType,
 3719                       PidTagTransmittableDisplayName,
 3720                       PidTagEmailAddress,
 3721                       PidTagAddressType,
 3722                       PidTagSendRichInfo};
 3723     enum MAPISTATUS ms;
 3724     struct SPropTagArray *tags;
 3725     struct SRowSet *rows = NULL;
 3726     struct PropertyRowSet_r *prop_rows = NULL;
 3727     struct PropertyTagArray_r *flagList = NULL;
 3728     EResolveNamedIDsData *named_ids_list = NULL;
 3729     guint named_ids_len = 0;
 3730     const gchar **users = NULL;
 3731     EMapiRecipient *recipient;
 3732     EMapiRecipient **recips;
 3733     uint32_t ii, jj, count = 0;
 3734     GHashTable *all_proptags;
 3735     GHashTableIter iter;
 3736     gpointer key, value;
 3737 
 3738     CHECK_CORRECT_CONN_AND_GET_PRIV (conn, FALSE);
 3739     e_return_val_mapi_error_if_fail (priv->session != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 3740 
 3741     count = 0;
 3742     for (recipient = recipients, ii = 0; recipient; recipient = recipient->next, ii++) {
 3743         if (!e_mapi_util_find_array_propval (&recipient->properties, PidTagSmtpAddress)
 3744             && !e_mapi_util_find_array_propval (&recipient->properties, PidTagDisplayName))
 3745             g_debug ("%s: Cannot get email or display name for a recipient %d, skipping it", G_STRFUNC, ii);
 3746         else
 3747             count++;
 3748     }
 3749 
 3750     if (!count)
 3751         return TRUE;
 3752 
 3753     LOCK (cancellable, perror, FALSE);
 3754 
 3755     all_proptags = g_hash_table_new (g_direct_hash, g_direct_equal);
 3756     users = g_new0 (const gchar *, count + 1);
 3757     recips = g_new0 (EMapiRecipient *, count + 1);
 3758 
 3759     for (ii = 0; ii < G_N_ELEMENTS (required_tags); ii++) {
 3760         g_hash_table_insert (all_proptags, GUINT_TO_POINTER (required_tags[ii]), GUINT_TO_POINTER (1));
 3761     }
 3762 
 3763     for (ii = 0, jj = 0, recipient = recipients; ii < count && recipient != NULL; ii++, recipient = recipient->next) {
 3764         users[ii] = e_mapi_util_find_array_propval (&recipient->properties, PidTagSmtpAddress);
 3765         if (!users[ii])
 3766             users[ii] = e_mapi_util_find_array_propval (&recipient->properties, PidTagDisplayName);
 3767         if (!users[ii]) {
 3768             ii--;
 3769         } else {
 3770             uint32_t kk;
 3771 
 3772             recips[jj] = recipient;
 3773             jj++;
 3774 
 3775             for (kk = 0; kk < recipient->properties.cValues; kk++) {
 3776                 g_hash_table_insert (all_proptags, GUINT_TO_POINTER (recipient->properties.lpProps[kk].ulPropTag), GUINT_TO_POINTER (1));
 3777             }
 3778         }
 3779     }
 3780 
 3781     /* Attempt to resolve names from the server */
 3782     tags = NULL;
 3783     g_hash_table_iter_init (&iter, all_proptags);
 3784     while (g_hash_table_iter_next (&iter, &key, &value)) {
 3785         uint32_t proptag = GPOINTER_TO_UINT (key);
 3786 
 3787         maybe_add_named_id_tag (proptag, &named_ids_list, &named_ids_len);
 3788 
 3789         if (!tags)
 3790             tags = set_SPropTagArray (mem_ctx, 1, proptag);
 3791         else
 3792             SPropTagArray_add (mem_ctx, tags, proptag);
 3793     }
 3794 
 3795     if (named_ids_list) {
 3796         GHashTable *replace_hash;
 3797 
 3798         if (!e_mapi_connection_resolve_named_props (conn, obj_folder, named_ids_list, named_ids_len, cancellable, perror)) {
 3799             ms = MAPI_E_CALL_FAILED;
 3800             make_mapi_error (perror, "e_mapi_connection_resolve_named_props", ms);
 3801             goto cleanup;
 3802         }
 3803 
 3804         replace_hash = prepare_maybe_replace_hash (named_ids_list, named_ids_len, TRUE);
 3805 
 3806         for (ii = 0; ii < tags->cValues && replace_hash; ii++) {
 3807             uint32_t proptag = tags->aulPropTag[ii];
 3808 
 3809             maybe_replace_named_id_tag (&proptag, replace_hash);
 3810 
 3811             tags->aulPropTag[ii] = proptag;
 3812         }
 3813 
 3814         if (replace_hash)
 3815             g_hash_table_destroy (replace_hash);
 3816     }
 3817 
 3818     ms = ResolveNames (priv->session, users, tags, &prop_rows, &flagList, MAPI_UNICODE);
 3819     if (ms != MAPI_E_SUCCESS) {
 3820         make_mapi_error (perror, "ResolveNames", ms);
 3821         goto cleanup;
 3822     }
 3823 
 3824     if (count != flagList->cValues) {
 3825         g_warn_if_reached ();
 3826         goto cleanup;
 3827     }
 3828 
 3829     rows = talloc_zero (mem_ctx, struct SRowSet);
 3830 
 3831     /* 'prop_rows == NULL' happens when there are none resolved recipients */
 3832     if (prop_rows)
 3833         cast_PropertyRowSet_to_SRowSet (mem_ctx, prop_rows, rows);
 3834 
 3835     for (ii = 0, jj = 0; ii < count; ii++) {
 3836         recipient = recips[ii];
 3837 
 3838         if (flagList->aulPropTag[ii] == MAPI_AMBIGUOUS) {
 3839             /* We should never get an ambiguous resolution as we use the email-id for resolving.
 3840              * However, if we do still get an ambiguous entry, we can't handle it :-( */
 3841             ms = MAPI_E_AMBIGUOUS_RECIP;
 3842             /* Translators: %s is replaced with an email address which was found ambiguous on a remote server */
 3843             g_set_error (perror, E_MAPI_ERROR, ms, _("Recipient “%s” is ambiguous"), users[ii]);
 3844             goto cleanup;
 3845         } else if (flagList->aulPropTag[ii] == MAPI_UNRESOLVED) {
 3846             uint32_t last;
 3847 
 3848             /* If the recipient is unresolved, consider it is a SMTP one */
 3849             rows->aRow = talloc_realloc (mem_ctx, rows->aRow, struct SRow, rows->cRows + 1);
 3850             last = rows->cRows;
 3851             rows->aRow[last].cValues = 0;
 3852             rows->aRow[last].lpProps = talloc_zero (mem_ctx, struct SPropValue);
 3853             if (!update_recipient_properties (conn, obj_folder, &rows->aRow[last], recipient, FALSE, mem_ctx, cancellable, perror)) {
 3854                 ms = MAPI_E_CALL_FAILED;
 3855                 goto cleanup;
 3856             }
 3857             rows->cRows += 1;
 3858         } else if (flagList->aulPropTag[ii] == MAPI_RESOLVED) {
 3859             if (!update_recipient_properties (conn, obj_folder, &rows->aRow[jj], recipient, TRUE, mem_ctx, cancellable, perror)) {
 3860                 ms = MAPI_E_CALL_FAILED;
 3861                 goto cleanup;
 3862             }
 3863             jj += 1;
 3864         }
 3865     }
 3866 
 3867     /* Modify the recipient table */
 3868     ms = ModifyRecipients (obj_message, rows);
 3869     if (ms != MAPI_E_SUCCESS) {
 3870         make_mapi_error (perror, "ModifyRecipients", ms);
 3871         goto cleanup;
 3872     }
 3873 
 3874  cleanup:
 3875     talloc_free (rows);
 3876     talloc_free (prop_rows);
 3877     talloc_free (flagList);
 3878 
 3879     UNLOCK ();
 3880 
 3881     g_free (users);
 3882     g_free (recips);
 3883     g_free (named_ids_list);
 3884     g_hash_table_destroy (all_proptags);
 3885 
 3886     return ms == MAPI_E_SUCCESS;
 3887 }
 3888 
 3889 static gboolean
 3890 delete_attachment_cb (EMapiConnection *conn,
 3891               TALLOC_CTX *mem_ctx,
 3892               struct SRow *srow,
 3893               guint32 row_index,
 3894               guint32 rows_total,
 3895               gpointer user_data,
 3896               GCancellable *cancellable,
 3897               GError **perror)
 3898 {
 3899     const uint32_t *attach_num;
 3900     mapi_object_t *obj_object = user_data;
 3901     enum MAPISTATUS ms;
 3902 
 3903     g_return_val_if_fail (obj_object != NULL, FALSE);
 3904 
 3905     attach_num = e_mapi_util_find_row_propval (srow, PidTagAttachNumber);
 3906     g_return_val_if_fail (attach_num != NULL, FALSE);
 3907 
 3908     ms = DeleteAttach (obj_object, *attach_num);
 3909     if (ms != MAPI_E_SUCCESS) {
 3910         make_mapi_error (perror, "DeleteAttach", ms);
 3911     }
 3912 
 3913     return ms == MAPI_E_SUCCESS;
 3914 }
 3915 
 3916 static gboolean
 3917 delete_object_attachments (EMapiConnection *conn,
 3918                mapi_object_t *obj_folder,
 3919                mapi_object_t *obj_object,
 3920                TALLOC_CTX *mem_ctx,
 3921                GCancellable *cancellable,
 3922                GError **perror)
 3923 {
 3924     enum MAPISTATUS ms;
 3925     mapi_object_t obj_table;
 3926     struct SPropTagArray *proptags;
 3927 
 3928     CHECK_CORRECT_CONN_AND_GET_PRIV (conn, FALSE);
 3929 
 3930     LOCK (cancellable, perror, FALSE);
 3931 
 3932     mapi_object_init (&obj_table);
 3933 
 3934     /* open attachment table */
 3935     ms = GetAttachmentTable (obj_object, &obj_table);
 3936     if (ms != MAPI_E_SUCCESS) {
 3937         make_mapi_error (perror, "GetAttachmentTable", ms);
 3938         goto cleanup;
 3939     }
 3940 
 3941     proptags = set_SPropTagArray (mem_ctx, 1, PidTagAttachNumber);
 3942 
 3943     ms = SetColumns (&obj_table, proptags);
 3944     if (ms != MAPI_E_SUCCESS) {
 3945         make_mapi_error (perror, "SetColumns", ms);
 3946         goto cleanup;
 3947     }
 3948 
 3949     ms = foreach_tablerow (conn, mem_ctx, &obj_table, delete_attachment_cb, obj_object, cancellable, perror);
 3950     if (ms != MAPI_E_SUCCESS) {
 3951         make_mapi_error (perror, "foreach_tablerow", ms);
 3952     }
 3953 
 3954  cleanup:
 3955     mapi_object_release (&obj_table);
 3956 
 3957     UNLOCK ();
 3958 
 3959     return ms == MAPI_E_SUCCESS;
 3960 }
 3961 
 3962 static gboolean update_message_with_object (EMapiConnection *conn,
 3963                         mapi_object_t *obj_folder,
 3964                         mapi_object_t *obj_message,
 3965                         EMapiObject *object,
 3966                         TALLOC_CTX *mem_ctx,
 3967                         GCancellable *cancellable,
 3968                         GError **perror);
 3969 
 3970 static gboolean
 3971 add_object_attachments (EMapiConnection *conn,
 3972             mapi_object_t *obj_folder,
 3973             mapi_object_t *obj_message,
 3974             EMapiAttachment *attachments,
 3975             TALLOC_CTX *mem_ctx,
 3976             GCancellable *cancellable,
 3977             GError **perror)
 3978 {
 3979     enum MAPISTATUS ms = MAPI_E_SUCCESS;
 3980     EMapiAttachment *attachment;
 3981 
 3982     CHECK_CORRECT_CONN_AND_GET_PRIV (conn, FALSE);
 3983     e_return_val_mapi_error_if_fail (priv->session != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 3984     e_return_val_mapi_error_if_fail (obj_folder != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 3985     e_return_val_mapi_error_if_fail (obj_message != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 3986     e_return_val_mapi_error_if_fail (mem_ctx != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 3987 
 3988     LOCK (cancellable, perror, FALSE);
 3989 
 3990     for (attachment = attachments; attachment && ms == MAPI_E_SUCCESS; attachment = attachment->next) {
 3991         mapi_object_t obj_attach;
 3992 
 3993         if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
 3994             ms = MAPI_E_USER_CANCEL;
 3995             break;
 3996         }
 3997 
 3998         mapi_object_init (&obj_attach);
 3999 
 4000         ms = CreateAttach (obj_message, &obj_attach);
 4001         if (ms != MAPI_E_SUCCESS) {
 4002             make_mapi_error (perror, "CreateAttach", ms);
 4003             goto cleanup;
 4004         }
 4005 
 4006         if (!update_props_on_object (conn, obj_folder, &obj_attach,
 4007             &attachment->properties,
 4008             attachment->streamed_properties, attachment->streamed_properties_count,
 4009             mem_ctx, cancellable, perror)) {
 4010             ms = MAPI_E_CALL_FAILED;
 4011             make_mapi_error (perror, "update_props_on_object", ms);
 4012             goto cleanup;
 4013         }
 4014 
 4015         if (attachment->embedded_object) {
 4016             mapi_object_t obj_emb_msg;
 4017 
 4018             mapi_object_init (&obj_emb_msg);
 4019 
 4020             ms = OpenEmbeddedMessage (&obj_attach, &obj_emb_msg, MAPI_CREATE);
 4021             if (ms != MAPI_E_SUCCESS) {
 4022                 make_mapi_error (perror, "OpenEmbeddedMessage", ms);
 4023                 goto cleanup;
 4024             }
 4025 
 4026             if (!update_message_with_object (conn, obj_folder, &obj_emb_msg, attachment->embedded_object, mem_ctx, cancellable, perror)) {
 4027                 ms = MAPI_E_CALL_FAILED;
 4028                 make_mapi_error (perror, "SaveChangesMessage", ms);
 4029                 mapi_object_release (&obj_emb_msg);
 4030                 goto cleanup;
 4031             }
 4032 
 4033             ms = SaveChangesMessage (&obj_attach, &obj_emb_msg, KeepOpenReadOnly);
 4034             if (ms != MAPI_E_SUCCESS) {
 4035                 make_mapi_error (perror, "SaveChangesMessage", ms);
 4036                 mapi_object_release (&obj_emb_msg);
 4037                 goto cleanup;
 4038             }
 4039 
 4040             mapi_object_release (&obj_emb_msg);
 4041         }
 4042 
 4043         ms = SaveChangesAttachment (obj_message, &obj_attach, KeepOpenReadWrite);
 4044         if (ms != MAPI_E_SUCCESS) {
 4045             make_mapi_error (perror, "SaveChangesAttachment", ms);
 4046             goto cleanup;
 4047         }
 4048 
 4049      cleanup:
 4050         mapi_object_release (&obj_attach);
 4051     }
 4052 
 4053     UNLOCK ();
 4054 
 4055     return ms == MAPI_E_SUCCESS;
 4056 }
 4057 
 4058 static gboolean
 4059 update_message_with_object (EMapiConnection *conn,
 4060                 mapi_object_t *obj_folder,
 4061                 mapi_object_t *obj_message,
 4062                 EMapiObject *object,
 4063                 TALLOC_CTX *mem_ctx,
 4064                 GCancellable *cancellable,
 4065                 GError **perror)
 4066 {
 4067     CHECK_CORRECT_CONN_AND_GET_PRIV (conn, FALSE);
 4068     e_return_val_mapi_error_if_fail (priv->session != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 4069     e_return_val_mapi_error_if_fail (obj_folder != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 4070     e_return_val_mapi_error_if_fail (obj_message != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 4071     e_return_val_mapi_error_if_fail (object != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 4072 
 4073     if (!update_props_on_object (conn, obj_folder, obj_message,
 4074         &object->properties,
 4075         object->streamed_properties, object->streamed_properties_count,
 4076         mem_ctx, cancellable, perror))
 4077         return FALSE;
 4078 
 4079     if (g_cancellable_set_error_if_cancelled (cancellable, perror))
 4080         return FALSE;
 4081 
 4082     /* do not touch recipients if not set */
 4083     if (object->recipients) {
 4084         /* remove current recipients... */
 4085         if (!delete_object_recipients (conn, obj_folder, obj_message, mem_ctx, cancellable, perror))
 4086             return FALSE;
 4087 
 4088         if (g_cancellable_set_error_if_cancelled (cancellable, perror))
 4089             return FALSE;
 4090 
 4091         /* ... and add new */
 4092         if (!add_object_recipients (conn, obj_folder, obj_message, object->recipients, mem_ctx, cancellable, perror))
 4093             return FALSE;
 4094     }
 4095 
 4096     if (g_cancellable_set_error_if_cancelled (cancellable, perror))
 4097         return FALSE;
 4098 
 4099     /* remove current attachments... */
 4100     if (!delete_object_attachments (conn, obj_folder, obj_message, mem_ctx, cancellable, perror))
 4101         return FALSE;
 4102 
 4103     if (g_cancellable_set_error_if_cancelled (cancellable, perror))
 4104         return FALSE;
 4105 
 4106     /* ... and add new */
 4107     if (object->attachments && !add_object_attachments (conn, obj_folder, obj_message, object->attachments, mem_ctx, cancellable, perror))
 4108         return FALSE;
 4109 
 4110     if (g_cancellable_set_error_if_cancelled (cancellable, perror))
 4111         return FALSE;
 4112 
 4113     return TRUE;
 4114 }
 4115 
 4116 gboolean
 4117 e_mapi_connection_create_object (EMapiConnection *conn,
 4118                  mapi_object_t *obj_folder,
 4119                  uint32_t flags, /* bit-or of EMapiCreateFlags */
 4120                  WriteObjectCB write_object_cb,
 4121                  gpointer woc_data,
 4122                  mapi_id_t *out_mid,
 4123                  GCancellable *cancellable,
 4124                  GError **perror)
 4125 {
 4126     enum MAPISTATUS ms;
 4127     TALLOC_CTX *mem_ctx;
 4128     EMapiObject *object = NULL;
 4129     mapi_object_t obj_message;
 4130 
 4131     CHECK_CORRECT_CONN_AND_GET_PRIV (conn, FALSE);
 4132     e_return_val_mapi_error_if_fail (priv->session != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 4133     e_return_val_mapi_error_if_fail (obj_folder != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 4134     e_return_val_mapi_error_if_fail (write_object_cb != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 4135     e_return_val_mapi_error_if_fail (out_mid != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 4136 
 4137     LOCK (cancellable, perror, FALSE);
 4138 
 4139     *out_mid = 0;
 4140 
 4141     mem_ctx = talloc_new (priv->session);
 4142     mapi_object_init (&obj_message);
 4143 
 4144     if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
 4145         ms = MAPI_E_USER_CANCEL;
 4146         goto cleanup;
 4147     }
 4148 
 4149     if (!write_object_cb (conn, mem_ctx, &object, woc_data, cancellable, perror) || !object) {
 4150         ms = MAPI_E_CALL_FAILED;
 4151         make_mapi_error (perror, "write_object_cb", ms);
 4152         goto cleanup;
 4153     }
 4154 
 4155     if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
 4156         ms = MAPI_E_USER_CANCEL;
 4157         goto cleanup;
 4158     }
 4159 
 4160     ms = CreateMessage (obj_folder, &obj_message);
 4161     if (ms != MAPI_E_SUCCESS) {
 4162         make_mapi_error (perror, "CreateMessage", ms);
 4163         goto cleanup;
 4164     }
 4165 
 4166     if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
 4167         ms = MAPI_E_USER_CANCEL;
 4168         goto cleanup;
 4169     }
 4170 
 4171     if (!update_message_with_object (conn, obj_folder, &obj_message, object, mem_ctx, cancellable, perror)) {
 4172         ms = MAPI_E_CALL_FAILED;
 4173         make_mapi_error (perror, "update_message_with_object", ms);
 4174         goto cleanup;
 4175     }
 4176 
 4177     if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
 4178         ms = MAPI_E_USER_CANCEL;
 4179         goto cleanup;
 4180     }
 4181 
 4182     ms = SaveChangesMessage (obj_folder, &obj_message, KeepOpenReadWrite);
 4183     if (ms != MAPI_E_SUCCESS) {
 4184         make_mapi_error (perror, "SaveChangesMessage", ms);
 4185         goto cleanup;
 4186     }
 4187 
 4188     if ((flags & E_MAPI_CREATE_FLAG_SUBMIT) != 0) {
 4189         /* Mark message as ready to be sent */
 4190         ms = SubmitMessage (&obj_message);
 4191         if (ms != MAPI_E_SUCCESS) {
 4192             mapi_id_t mid;
 4193             make_mapi_error (perror, "SubmitMessage", ms);
 4194 
 4195             /*
 4196             The code is storing message right to Sent items instead of Outbox,
 4197             because fetching PR_ENTRYID or PR_IPM_SENTMAIL_ENTRYID didn't seem
 4198             to work in time of doing this change.
 4199 
 4200             For more information and other possible (correct) approaches see:
 4201             https://bugzilla.gnome.org/show_bug.cgi?id=561794
 4202             */
 4203             mid = mapi_object_get_id (&obj_message);
 4204 
 4205             mapi_object_release (&obj_message);
 4206             /* to not release a message object twice */
 4207             mapi_object_init (&obj_message);
 4208 
 4209             ms = DeleteMessage (obj_folder, &mid, 1);
 4210             if (ms != MAPI_E_SUCCESS) {
 4211                 make_mapi_error (perror, "DeleteMessage", ms);
 4212             }
 4213 
 4214             goto cleanup;
 4215         }
 4216     }
 4217 
 4218     *out_mid = mapi_object_get_id (&obj_message);
 4219 
 4220  cleanup:
 4221     e_mapi_object_free (object);
 4222     mapi_object_release (&obj_message);
 4223     talloc_free (mem_ctx);
 4224 
 4225     UNLOCK ();
 4226 
 4227     return ms == MAPI_E_SUCCESS;
 4228 }
 4229 
 4230 gboolean
 4231 e_mapi_connection_modify_object (EMapiConnection *conn,
 4232                  mapi_object_t *obj_folder,
 4233                  mapi_id_t mid,
 4234                  WriteObjectCB write_object_cb,
 4235                  gpointer woc_data,
 4236                  GCancellable *cancellable,
 4237                  GError **perror)
 4238 {
 4239     enum MAPISTATUS ms;
 4240     TALLOC_CTX *mem_ctx;
 4241     EMapiObject *object = NULL;
 4242     mapi_object_t obj_message;
 4243 
 4244     CHECK_CORRECT_CONN_AND_GET_PRIV (conn, FALSE);
 4245     e_return_val_mapi_error_if_fail (priv->session != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 4246     e_return_val_mapi_error_if_fail (obj_folder != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 4247     e_return_val_mapi_error_if_fail (write_object_cb != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 4248     e_return_val_mapi_error_if_fail (mid != 0, MAPI_E_INVALID_PARAMETER, FALSE);
 4249 
 4250     LOCK (cancellable, perror, FALSE);
 4251 
 4252     mem_ctx = talloc_new (priv->session);
 4253     mapi_object_init (&obj_message);
 4254 
 4255     if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
 4256         ms = MAPI_E_USER_CANCEL;
 4257         goto cleanup;
 4258     }
 4259 
 4260     if (!write_object_cb (conn, mem_ctx, &object, woc_data, cancellable, perror)) {
 4261         ms = MAPI_E_CALL_FAILED;
 4262         make_mapi_error (perror, "write_object_cb", ms);
 4263         goto cleanup;
 4264     }
 4265 
 4266     if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
 4267         ms = MAPI_E_USER_CANCEL;
 4268         goto cleanup;
 4269     }
 4270 
 4271     ms = OpenMessage (obj_folder, mapi_object_get_id (obj_folder), mid, &obj_message, MAPI_MODIFY);
 4272     if (ms != MAPI_E_SUCCESS) {
 4273         make_mapi_error (perror, "OpenMessage", ms);
 4274         goto cleanup;
 4275     }
 4276 
 4277     if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
 4278         ms = MAPI_E_USER_CANCEL;
 4279         goto cleanup;
 4280     }
 4281 
 4282     if (!update_message_with_object (conn, obj_folder, &obj_message, object, mem_ctx, cancellable, perror)) {
 4283         ms = MAPI_E_CALL_FAILED;
 4284         make_mapi_error (perror, "update_message_with_object", ms);
 4285         goto cleanup;
 4286     }
 4287 
 4288     ms = SaveChangesMessage (obj_folder, &obj_message, KeepOpenReadOnly);
 4289     if (ms != MAPI_E_SUCCESS) {
 4290         make_mapi_error (perror, "SaveChangesMessage", ms);
 4291         goto cleanup;
 4292     }
 4293 
 4294  cleanup:
 4295     e_mapi_object_free (object);
 4296     mapi_object_release (&obj_message);
 4297     talloc_free (mem_ctx);
 4298 
 4299     UNLOCK ();
 4300 
 4301     return ms == MAPI_E_SUCCESS;
 4302 }
 4303 
 4304 static void
 4305 e_mapi_cast_SPropValue_to_PropertyValue (struct SPropValue *spropvalue,
 4306                      struct PropertyValue_r *propvalue)
 4307 {
 4308     propvalue->ulPropTag = spropvalue->ulPropTag;
 4309 
 4310     switch (spropvalue->ulPropTag & 0xFFFF) {
 4311     case PT_BOOLEAN:
 4312         propvalue->value.b = spropvalue->value.b;
 4313         break;
 4314     case PT_I2:
 4315         propvalue->value.i = spropvalue->value.i;
 4316         break;
 4317     case PT_LONG:
 4318         propvalue->value.l = spropvalue->value.l;
 4319         break;
 4320     case PT_STRING8:
 4321         propvalue->value.lpszA = spropvalue->value.lpszA;
 4322         break;
 4323     case PT_UNICODE:
 4324         propvalue->value.lpszW = spropvalue->value.lpszW;
 4325         break;
 4326     case PT_SYSTIME:
 4327         propvalue->value.ft = spropvalue->value.ft;
 4328         break;
 4329     case PT_CLSID:
 4330         propvalue->value.lpguid = spropvalue->value.lpguid;
 4331         break;
 4332     case PT_SVREID:
 4333     case PT_BINARY:
 4334         propvalue->value.bin = spropvalue->value.bin;
 4335         break;
 4336         case PT_ERROR:
 4337                 propvalue->value.err = spropvalue->value.err;
 4338         break;
 4339     case PT_MV_LONG:
 4340         propvalue->value.MVl = spropvalue->value.MVl;
 4341         break;
 4342     case PT_MV_STRING8:
 4343         propvalue->value.MVszA = spropvalue->value.MVszA;
 4344         break;
 4345         case PT_MV_UNICODE:
 4346         propvalue->value.MVszW = spropvalue->value.MVszW;
 4347         break;
 4348     case PT_MV_CLSID:
 4349         propvalue->value.MVguid = spropvalue->value.MVguid;
 4350         break;
 4351     case PT_MV_BINARY:
 4352         propvalue->value.MVbin = spropvalue->value.MVbin;
 4353         break;
 4354         default:
 4355                 g_warning ("%s: unhandled conversion case: 0x%x", G_STRFUNC, (spropvalue->ulPropTag & 0xFFFF));
 4356         break;
 4357     }
 4358 }
 4359 
 4360 static void
 4361 convert_mapi_SRestriction_to_Restriction_r (struct mapi_SRestriction *restriction,
 4362                         struct Restriction_r *rr,
 4363                         TALLOC_CTX *mem_ctx,
 4364                         GHashTable *replace_hash)
 4365 {
 4366     guint i;
 4367     uint32_t proptag;
 4368 
 4369     g_return_if_fail (restriction != NULL);
 4370     g_return_if_fail (rr != NULL);
 4371     g_return_if_fail (mem_ctx != NULL);
 4372 
 4373     #define copy(x, y) rr->res.x = restriction->res.y
 4374     #define copy_prop(pprop, mprop) {                           \
 4375             struct SPropValue *helper = talloc_zero (mem_ctx, struct SPropValue);   \
 4376             rr->res.pprop = talloc_zero (mem_ctx, struct PropertyValue_r);      \
 4377             g_return_if_fail (rr->res.pprop != NULL);               \
 4378             rr->res.pprop->ulPropTag = restriction->res.mprop.ulPropTag;        \
 4379             rr->res.pprop->dwAlignPad = 0;                      \
 4380             cast_SPropValue (mem_ctx, &(restriction->res.mprop), helper);       \
 4381             e_mapi_cast_SPropValue_to_PropertyValue (helper, rr->res.pprop);    \
 4382         }
 4383     #define check_proptag(x) {                              \
 4384             proptag = x;                                \
 4385             maybe_replace_named_id_tag (&proptag, replace_hash);            \
 4386             /* workaround for unresolved properties */              \
 4387             if (proptag == MAPI_E_RESERVED)                     \
 4388                 proptag = PidTagDisplayName;                    \
 4389             x = proptag;                                \
 4390         }
 4391 
 4392     rr->rt = restriction->rt;
 4393 
 4394     switch (restriction->rt) {
 4395     case RES_AND:
 4396         rr->res.resAnd.lpRes = talloc_zero_array (mem_ctx, struct Restriction_r, restriction->res.resAnd.cRes);
 4397         g_return_if_fail (rr->res.resAnd.lpRes != NULL);
 4398 
 4399         copy (resAnd.cRes, resAnd.cRes);
 4400         for (i = 0; i < restriction->res.resAnd.cRes; i++) {
 4401             convert_mapi_SRestriction_to_Restriction_r (
 4402                 (struct mapi_SRestriction *) &(restriction->res.resAnd.res[i]),
 4403                 &(rr->res.resAnd.lpRes[i]),
 4404                 mem_ctx, replace_hash);
 4405         }
 4406         break;
 4407     case RES_OR:
 4408         rr->res.resOr.lpRes = talloc_zero_array (mem_ctx, struct Restriction_r, restriction->res.resOr.cRes);
 4409         g_return_if_fail (rr->res.resOr.lpRes != NULL);
 4410 
 4411         copy (resOr.cRes, resOr.cRes);
 4412         for (i = 0; i < restriction->res.resOr.cRes; i++) {
 4413             convert_mapi_SRestriction_to_Restriction_r (
 4414                 (struct mapi_SRestriction *) &(restriction->res.resOr.res[i]),
 4415                 &(rr->res.resOr.lpRes[i]),
 4416                 mem_ctx, replace_hash);
 4417         }
 4418         break;
 4419     #ifdef HAVE_RES_NOT_SUPPORTED
 4420     case RES_NOT:
 4421         rr->res.resNot.lpRes = talloc_zero (mem_ctx, struct Restriction_r);
 4422         g_return_if_fail (rr->res.resNot.lpRes != NULL);
 4423 
 4424         convert_mapi_SRestriction_to_Restriction_r (
 4425             restriction->res.resNot.res,
 4426             rr->res.resNot.lpRes,
 4427             mem_ctx, replace_hash);
 4428         break;
 4429     #endif
 4430     case RES_CONTENT:
 4431         copy (resContent.ulFuzzyLevel, resContent.fuzzy);
 4432         copy (resContent.ulPropTag, resContent.ulPropTag);
 4433         copy_prop (resContent.lpProp, resContent.lpProp);
 4434 
 4435         check_proptag (rr->res.resContent.ulPropTag);
 4436         check_proptag (rr->res.resContent.lpProp->ulPropTag);
 4437         break;
 4438     case RES_PROPERTY:
 4439         copy (resProperty.relop, resProperty.relop);
 4440         copy (resProperty.ulPropTag, resProperty.ulPropTag);
 4441         copy_prop (resProperty.lpProp, resProperty.lpProp);
 4442 
 4443 
 4444         check_proptag (rr->res.resProperty.ulPropTag);
 4445         check_proptag (rr->res.resProperty.lpProp->ulPropTag);
 4446         break;
 4447     case RES_COMPAREPROPS:
 4448         copy (resCompareProps.relop, resCompareProps.relop);
 4449         copy (resCompareProps.ulPropTag1, resCompareProps.ulPropTag1);
 4450         copy (resCompareProps.ulPropTag2, resCompareProps.ulPropTag2);
 4451 
 4452         check_proptag (rr->res.resCompareProps.ulPropTag1);
 4453         check_proptag (rr->res.resCompareProps.ulPropTag2);
 4454         break;
 4455     case RES_BITMASK:
 4456         copy (resBitMask.relMBR, resBitmask.relMBR);
 4457         copy (resBitMask.ulPropTag, resBitmask.ulPropTag);
 4458         copy (resBitMask.ulMask, resBitmask.ulMask);
 4459 
 4460         check_proptag (rr->res.resBitMask.ulPropTag);
 4461         break;
 4462     case RES_SIZE:
 4463         copy (resSize.relop, resSize.relop);
 4464         copy (resSize.ulPropTag, resSize.ulPropTag);
 4465         copy (resSize.cb, resSize.size);
 4466 
 4467         check_proptag (rr->res.resSize.ulPropTag);
 4468         break;
 4469     case RES_EXIST:
 4470         rr->res.resExist.ulReserved1 = 0;
 4471         rr->res.resExist.ulReserved2 = 0;
 4472         copy (resExist.ulPropTag, resExist.ulPropTag);
 4473 
 4474         check_proptag (rr->res.resExist.ulPropTag);
 4475         break;
 4476     }
 4477 
 4478     #undef check_proptag
 4479     #undef copy_prop
 4480     #undef copy
 4481 }
 4482 
 4483 static void
 4484 remove_unknown_proptags_Restriction_r_rec (struct Restriction_r *restriction,
 4485                        TALLOC_CTX *mem_ctx,
 4486                        GSList **new_rests)
 4487 {
 4488     gint ii;
 4489     GSList *sub_rests = NULL, *iter;
 4490 
 4491     if (!restriction)
 4492         return;
 4493 
 4494     g_return_if_fail (mem_ctx != NULL);
 4495 
 4496     #define proptag_is_ok(x) (((uint32_t) (x)) != 0 && ((uint32_t) (x)) != MAPI_E_RESERVED)
 4497 
 4498     switch (restriction->rt) {
 4499     case RES_AND:
 4500         for (ii = 0; ii < restriction->res.resAnd.cRes; ii++) {
 4501             remove_unknown_proptags_Restriction_r_rec (&(restriction->res.resAnd.lpRes[ii]), mem_ctx, &sub_rests);
 4502         }
 4503 
 4504         if (sub_rests) {
 4505             struct Restriction_r *rest = talloc_zero (mem_ctx, struct Restriction_r);
 4506             g_return_if_fail (rest != NULL);
 4507 
 4508             rest->rt = RES_AND;
 4509             rest->res.resAnd.cRes = g_slist_length (sub_rests);
 4510             rest->res.resAnd.lpRes = talloc_zero_array (mem_ctx, struct Restriction_r, rest->res.resAnd.cRes + 1);
 4511             g_return_if_fail (rest->res.resAnd.lpRes != NULL);
 4512 
 4513             for (iter = sub_rests, ii = 0; iter; iter = iter->next, ii++) {
 4514                 struct Restriction_r *subrest = iter->data;
 4515 
 4516                 g_return_if_fail (subrest != NULL);
 4517 
 4518                 rest->res.resAnd.lpRes[ii].rt = subrest->rt;
 4519                 rest->res.resAnd.lpRes[ii].res = subrest->res;
 4520             }
 4521 
 4522             *new_rests = g_slist_append (*new_rests, rest);
 4523         }
 4524         break;
 4525     case RES_OR:
 4526         for (ii = 0; ii < restriction->res.resOr.cRes; ii++) {
 4527             remove_unknown_proptags_Restriction_r_rec (&(restriction->res.resOr.lpRes[ii]), mem_ctx, &sub_rests);
 4528         }
 4529 
 4530         if (sub_rests) {
 4531             struct Restriction_r *rest = talloc_zero (mem_ctx, struct Restriction_r);
 4532             g_return_if_fail (rest != NULL);
 4533 
 4534             rest->rt = RES_OR;
 4535             rest->res.resOr.cRes = g_slist_length (sub_rests);
 4536             rest->res.resOr.lpRes = talloc_zero_array (mem_ctx, struct Restriction_r, rest->res.resOr.cRes + 1);
 4537             g_return_if_fail (rest->res.resOr.lpRes != NULL);
 4538 
 4539             for (iter = sub_rests, ii = 0; iter; iter = iter->next, ii++) {
 4540                 struct Restriction_r *subrest = iter->data;
 4541 
 4542                 g_return_if_fail (subrest != NULL);
 4543 
 4544                 rest->res.resOr.lpRes[ii].rt = subrest->rt;
 4545                 rest->res.resOr.lpRes[ii].res = subrest->res;
 4546             }
 4547 
 4548             *new_rests = g_slist_append (*new_rests, rest);
 4549         }
 4550         break;
 4551     #ifdef HAVE_RES_NOT_SUPPORTED
 4552     case RES_NOT:
 4553         remove_unknown_proptags_Restriction_r_rec (restriction->res.resNot.lpRes, mem_ctx, &sub_rests);
 4554         if (sub_rests) {
 4555             struct Restriction_r *rest = talloc_zero (esp->mem_ctx, struct Restriction_r);
 4556             g_return_if_fail (rest != NULL);
 4557 
 4558             rest->rt = RES_NOT;
 4559             res->res.resNot.lpRes = sub_rests->data;
 4560         }
 4561         break;
 4562     #endif
 4563     case RES_CONTENT:
 4564         if (proptag_is_ok (restriction->res.resContent.ulPropTag) &&
 4565             proptag_is_ok (restriction->res.resContent.lpProp->ulPropTag)) {
 4566             *new_rests = g_slist_append (*new_rests, restriction);
 4567         }
 4568         break;
 4569     case RES_PROPERTY:
 4570         if (proptag_is_ok (restriction->res.resProperty.ulPropTag) &&
 4571             proptag_is_ok (restriction->res.resProperty.lpProp->ulPropTag)) {
 4572             *new_rests = g_slist_append (*new_rests, restriction);
 4573         }
 4574         break;
 4575     case RES_COMPAREPROPS:
 4576         if (proptag_is_ok (restriction->res.resCompareProps.ulPropTag1) &&
 4577             proptag_is_ok (restriction->res.resCompareProps.ulPropTag2)) {
 4578             *new_rests = g_slist_append (*new_rests, restriction);
 4579         }
 4580         break;
 4581     case RES_BITMASK:
 4582         if (proptag_is_ok (restriction->res.resBitMask.ulPropTag)) {
 4583             *new_rests = g_slist_append (*new_rests, restriction);
 4584         }
 4585         break;
 4586     case RES_SIZE:
 4587         if (proptag_is_ok (restriction->res.resSize.ulPropTag)) {
 4588             *new_rests = g_slist_append (*new_rests, restriction);
 4589         }
 4590         break;
 4591     case RES_EXIST:
 4592         if (proptag_is_ok (restriction->res.resExist.ulPropTag)) {
 4593             *new_rests = g_slist_append (*new_rests, restriction);
 4594         }
 4595         break;
 4596     default:
 4597         g_warn_if_reached ();
 4598         break;
 4599     }
 4600 
 4601     #undef proptag_is_ok
 4602 
 4603     g_slist_free (sub_rests);
 4604 }
 4605 
 4606 static void
 4607 remove_unknown_proptags_Restriction_r (struct Restriction_r **prestrictions,
 4608                        TALLOC_CTX *mem_ctx)
 4609 {
 4610     GSList *new_rests = NULL;
 4611 
 4612     g_return_if_fail (mem_ctx != NULL);
 4613 
 4614     remove_unknown_proptags_Restriction_r_rec (*prestrictions, mem_ctx, &new_rests);
 4615 
 4616     if (new_rests) {
 4617         g_return_if_fail (g_slist_length (new_rests) == 1);
 4618 
 4619         *prestrictions = new_rests->data;
 4620 
 4621         g_slist_free (new_rests);
 4622     } else {
 4623         *prestrictions = NULL;
 4624     }
 4625 }
 4626 
 4627 static enum MAPISTATUS
 4628 process_gal_rows_chunk (EMapiConnection *conn,
 4629             TALLOC_CTX *mem_ctx,
 4630             uint32_t rows_offset,
 4631             uint32_t rows_total,
 4632             struct PropertyRowSet_r *rows,
 4633             struct PropertyTagArray_r *mids,
 4634             ForeachTableRowCB cb,
 4635             gpointer user_data,
 4636             GCancellable *cancellable,
 4637             GError **perror)
 4638 {
 4639     enum MAPISTATUS ms = MAPI_E_SUCCESS;
 4640     uint32_t ii;
 4641 
 4642     e_return_val_mapi_error_if_fail (mem_ctx != NULL, MAPI_E_INVALID_PARAMETER, MAPI_E_INVALID_PARAMETER);
 4643     e_return_val_mapi_error_if_fail (rows != NULL, MAPI_E_INVALID_PARAMETER, MAPI_E_INVALID_PARAMETER);
 4644     e_return_val_mapi_error_if_fail (mids != NULL, MAPI_E_INVALID_PARAMETER, MAPI_E_INVALID_PARAMETER);
 4645     e_return_val_mapi_error_if_fail (cb != NULL, MAPI_E_INVALID_PARAMETER, MAPI_E_INVALID_PARAMETER);
 4646     e_return_val_mapi_error_if_fail (rows->cRows <= mids->cValues, MAPI_E_INVALID_PARAMETER, MAPI_E_INVALID_PARAMETER);
 4647 
 4648     for (ii = 0; ii < rows->cRows; ii++) {
 4649         struct SRow *row;
 4650         int64_t mid = mids->aulPropTag[ii];
 4651 
 4652         row = talloc_zero (mem_ctx, struct SRow);
 4653         cast_PropertyRow_to_SRow (mem_ctx, &rows->aRow[ii], row);
 4654 
 4655         /* add the temporary mid as a PidTagMid */
 4656         if (!e_mapi_utils_add_spropvalue (mem_ctx, &row->lpProps, &row->cValues, PidTagMid, &mid)) {
 4657             ms = MAPI_E_CALL_FAILED;
 4658             make_mapi_error (perror, "e_mapi_utils_add_spropvalue", ms);
 4659             talloc_free (row);
 4660             break;
 4661         }
 4662 
 4663         if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
 4664             ms = MAPI_E_USER_CANCEL;
 4665             talloc_free (row);
 4666             break;
 4667         }
 4668 
 4669         if (!cb (conn, mem_ctx, row, rows_offset + ii + 1, rows_total, user_data, cancellable, perror)) {
 4670             ms = MAPI_E_USER_CANCEL;
 4671             talloc_free (row);
 4672             break;
 4673         }
 4674 
 4675         talloc_free (row);
 4676     }
 4677 
 4678     return ms;
 4679 }
 4680 
 4681 static enum MAPISTATUS
 4682 foreach_gal_tablerow (EMapiConnection *conn,
 4683               TALLOC_CTX *mem_ctx,
 4684               struct PropertyRowSet_r *first_rows,
 4685               struct PropertyTagArray_r *all_mids,
 4686               struct SPropTagArray *propTagArray,
 4687               ForeachTableRowCB cb,
 4688               gpointer user_data,
 4689               GCancellable *cancellable,
 4690               GError **perror)
 4691 {
 4692     enum MAPISTATUS ms;
 4693     struct PropertyRowSet_r *rows = NULL;
 4694     struct PropertyTagArray_r *to_query = NULL;
 4695     uint32_t  midspos;
 4696 
 4697     CHECK_CORRECT_CONN_AND_GET_PRIV (conn, MAPI_E_INVALID_PARAMETER);
 4698     e_return_val_mapi_error_if_fail (mem_ctx != NULL, MAPI_E_INVALID_PARAMETER, MAPI_E_INVALID_PARAMETER);
 4699     e_return_val_mapi_error_if_fail (first_rows != NULL, MAPI_E_INVALID_PARAMETER, MAPI_E_INVALID_PARAMETER);
 4700     e_return_val_mapi_error_if_fail (all_mids != NULL, MAPI_E_INVALID_PARAMETER, MAPI_E_INVALID_PARAMETER);
 4701     e_return_val_mapi_error_if_fail (cb != NULL, MAPI_E_INVALID_PARAMETER, MAPI_E_INVALID_PARAMETER);
 4702     e_return_val_mapi_error_if_fail (first_rows->cRows <= all_mids->cValues, MAPI_E_INVALID_PARAMETER, MAPI_E_INVALID_PARAMETER);
 4703 
 4704     midspos = 0;
 4705     ms = process_gal_rows_chunk (conn, mem_ctx, midspos, all_mids->cValues, first_rows, all_mids, cb, user_data, cancellable, perror);
 4706     if (ms != MAPI_E_SUCCESS) {
 4707         make_mapi_error (perror, "process_gal_rows_chunk", ms);
 4708         goto cleanup;
 4709     }
 4710 
 4711     midspos = first_rows->cRows;
 4712     to_query = talloc_zero (mem_ctx, struct PropertyTagArray_r);
 4713     to_query->aulPropTag = talloc_zero_array (mem_ctx, uint32_t, MAX_GAL_CHUNK);
 4714 
 4715     while (midspos < all_mids->cValues) {
 4716         uint32_t ii;
 4717 
 4718         to_query->cValues = 0;
 4719         for (ii = midspos; to_query->cValues < MAX_GAL_CHUNK && ii < all_mids->cValues; to_query->cValues++, ii++) {
 4720             to_query->aulPropTag[to_query->cValues] = all_mids->aulPropTag[ii];
 4721         }
 4722 
 4723         if (!to_query->cValues)
 4724             break;
 4725 
 4726         if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
 4727             ms = MAPI_E_USER_CANCEL;
 4728             break;
 4729         }
 4730 
 4731         ms = nspi_QueryRows (priv->session->nspi->ctx, mem_ctx, propTagArray, to_query, to_query->cValues, &rows);
 4732         if (ms != MAPI_E_SUCCESS) {
 4733             make_mapi_error (perror, "nspi_QueryRows", ms);
 4734             goto cleanup;
 4735         }
 4736 
 4737         if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
 4738             ms = MAPI_E_USER_CANCEL;
 4739             break;
 4740         }
 4741 
 4742         if (!rows || rows->cRows <= 0) {
 4743             /* success or finished, probably */
 4744             break;
 4745         }
 4746 
 4747         ms = process_gal_rows_chunk (conn, mem_ctx, midspos, all_mids->cValues, rows, to_query, cb, user_data, cancellable, perror);
 4748         if (ms != MAPI_E_SUCCESS) {
 4749             make_mapi_error (perror, "process_gal_rows_chunk", ms);
 4750             goto cleanup;
 4751         }
 4752 
 4753         midspos += rows->cRows;
 4754         talloc_free (rows);
 4755         rows = NULL;
 4756     }
 4757 
 4758  cleanup:
 4759     talloc_free (to_query);
 4760     talloc_free (rows);
 4761 
 4762     return ms;
 4763 }
 4764 
 4765 gboolean
 4766 e_mapi_connection_count_gal_objects (EMapiConnection *conn,
 4767                      guint32 *obj_total,
 4768                      GCancellable *cancellable,
 4769                      GError **perror)
 4770 {
 4771     enum MAPISTATUS ms;
 4772     uint32_t count = 0;
 4773 
 4774     CHECK_CORRECT_CONN_AND_GET_PRIV (conn, FALSE);
 4775     e_return_val_mapi_error_if_fail (priv->session != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 4776     e_return_val_mapi_error_if_fail (priv->session->nspi != NULL, MAPI_E_UNCONFIGURED, FALSE);
 4777     e_return_val_mapi_error_if_fail (priv->session->nspi->ctx != NULL, MAPI_E_UNCONFIGURED, FALSE);
 4778     e_return_val_mapi_error_if_fail (obj_total != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 4779 
 4780     *obj_total = 0;
 4781 
 4782     LOCK (cancellable, perror, FALSE);
 4783 
 4784     if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
 4785         ms = MAPI_E_USER_CANCEL;
 4786     } else {
 4787         ms = GetGALTableCount (priv->session, &count);
 4788         if (ms != MAPI_E_SUCCESS) {
 4789             make_mapi_error (perror, "GetGALTableCount", ms);
 4790         } else {
 4791             *obj_total = count;
 4792         }
 4793     }
 4794 
 4795     UNLOCK ();
 4796 
 4797     return ms == MAPI_E_SUCCESS;
 4798 }
 4799 
 4800 gboolean
 4801 e_mapi_connection_list_gal_objects (EMapiConnection *conn,
 4802                     BuildRestrictionsCB build_rs_cb,
 4803                     gpointer build_rs_cb_data,
 4804                     ListObjectsCB cb,
 4805                     gpointer user_data,
 4806                     GCancellable *cancellable,
 4807                     GError **perror)
 4808 {
 4809     enum MAPISTATUS ms;
 4810     TALLOC_CTX *mem_ctx;
 4811     struct SPropTagArray *propTagArray = NULL;
 4812     struct Restriction_r *use_restriction = NULL;
 4813     struct PropertyRowSet_r *rows = NULL;
 4814     struct PropertyTagArray_r *pMIds = NULL;
 4815     struct ListObjectsInternalData loi_data;
 4816 
 4817     CHECK_CORRECT_CONN_AND_GET_PRIV (conn, FALSE);
 4818     e_return_val_mapi_error_if_fail (priv->session != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 4819     e_return_val_mapi_error_if_fail (priv->session->nspi != NULL, MAPI_E_UNCONFIGURED, FALSE);
 4820     e_return_val_mapi_error_if_fail (priv->session->nspi->ctx != NULL, MAPI_E_UNCONFIGURED, FALSE);
 4821     e_return_val_mapi_error_if_fail (cb != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 4822 
 4823     LOCK (cancellable, perror, FALSE);
 4824     mem_ctx = talloc_new (priv->session);
 4825 
 4826     if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
 4827         ms = MAPI_E_USER_CANCEL;
 4828         goto cleanup;
 4829     }
 4830 
 4831     /* other listing tags not found/used by GAL */
 4832     propTagArray = set_SPropTagArray (mem_ctx, 1, PidTagLastModificationTime);
 4833 
 4834     if (build_rs_cb) {
 4835         struct mapi_SRestriction *restrictions = NULL;
 4836 
 4837         if (!build_rs_cb (conn, mem_ctx, &restrictions, build_rs_cb_data, cancellable, perror)) {
 4838             ms = MAPI_E_CALL_FAILED;
 4839             make_mapi_error (perror, "build_restrictions", ms);
 4840             goto cleanup;
 4841         }
 4842 
 4843         if (restrictions) {
 4844             EResolveNamedIDsData *named_ids_list = NULL;
 4845             guint named_ids_len = 0;
 4846             gboolean res = FALSE;
 4847 
 4848             gather_mapi_SRestriction_named_ids (restrictions, &named_ids_list, &named_ids_len);
 4849 
 4850             if (named_ids_list) {
 4851                 /* use NULL for GAL as a folder ID parameter */
 4852                 res = e_mapi_connection_resolve_named_props (conn, NULL, named_ids_list, named_ids_len, cancellable, perror);
 4853 
 4854                 if (res) {
 4855                     GHashTable *replace_hash = prepare_maybe_replace_hash (named_ids_list, named_ids_len, TRUE);
 4856 
 4857                     use_restriction = talloc_zero (mem_ctx, struct Restriction_r);
 4858                     convert_mapi_SRestriction_to_Restriction_r (restrictions, use_restriction, mem_ctx, replace_hash);
 4859 
 4860                     if (replace_hash)
 4861                         g_hash_table_destroy (replace_hash);
 4862                 } else {
 4863                     ms = MAPI_E_CALL_FAILED;
 4864                     goto cleanup;
 4865                 }
 4866 
 4867                 g_free (named_ids_list);
 4868             } else {
 4869                 use_restriction = talloc_zero (mem_ctx, struct Restriction_r);
 4870                 convert_mapi_SRestriction_to_Restriction_r (restrictions, use_restriction, mem_ctx, NULL);
 4871             }
 4872 
 4873             remove_unknown_proptags_Restriction_r (&use_restriction, mem_ctx);
 4874 
 4875             if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
 4876                 ms = MAPI_E_USER_CANCEL;
 4877                 goto cleanup;
 4878             }
 4879         }
 4880     }
 4881 
 4882     loi_data.cb = cb;
 4883     loi_data.user_data = user_data;
 4884 
 4885     ms = nspi_GetMatches (priv->session->nspi->ctx, mem_ctx, propTagArray, use_restriction, (uint32_t) -1, &rows, &pMIds);
 4886     if (ms == MAPI_E_TOO_COMPLEX && use_restriction && use_restriction->rt == RES_OR) {
 4887         /* case lazy MS servers which do not want to search sertain properties in OR-s */
 4888         gint ii;
 4889         gboolean any_good = FALSE;
 4890 
 4891         for (ii = 0; ii < use_restriction->res.resOr.cRes; ii++) {
 4892             talloc_free (pMIds);
 4893             talloc_free (rows);
 4894             pMIds = NULL;
 4895             rows = NULL;
 4896 
 4897             ms = nspi_GetMatches (priv->session->nspi->ctx, mem_ctx, propTagArray,
 4898                 &use_restriction->res.resOr.lpRes[ii],
 4899                 (uint32_t) -1, &rows, &pMIds);
 4900 
 4901             if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
 4902                 ms = MAPI_E_USER_CANCEL;
 4903                 goto cleanup;
 4904             }
 4905 
 4906             if (ms == MAPI_E_SUCCESS) {
 4907                 if (!rows)
 4908                     continue;
 4909 
 4910                 ms = foreach_gal_tablerow (conn, mem_ctx, rows, pMIds, propTagArray, list_objects_internal_cb, &loi_data, cancellable, perror);
 4911                 if (ms != MAPI_E_SUCCESS) {
 4912                     make_mapi_error (perror, "foreach_gal_tablerow", ms);
 4913                     goto cleanup;
 4914                 }
 4915 
 4916                 any_good = TRUE;
 4917             } else if (ms != MAPI_E_NOT_FOUND && ms != MAPI_E_TOO_COMPLEX && ms != MAPI_E_TABLE_TOO_BIG) {
 4918                 break;
 4919             }
 4920         }
 4921 
 4922         /* in case the last check fails, update based on the overall result */
 4923         if (any_good) {
 4924             ms = MAPI_E_SUCCESS;
 4925             goto cleanup;
 4926         } else {
 4927             ms = MAPI_E_TOO_COMPLEX;
 4928         }
 4929     }
 4930 
 4931     if (ms != MAPI_E_SUCCESS || !rows) {
 4932         if (ms == MAPI_E_NOT_FOUND || (!rows && ms == MAPI_E_SUCCESS))
 4933             ms = MAPI_E_SUCCESS;
 4934         else if (ms == MAPI_E_TABLE_TOO_BIG)
 4935             g_set_error (perror, E_MAPI_ERROR, MAPI_E_TABLE_TOO_BIG, _("Search result exceeded allowed size limit. Use more specific search term, please"));
 4936         else if (ms != MAPI_E_SUCCESS)
 4937             make_mapi_error (perror, "nspi_GetMatches", ms);
 4938         goto cleanup;
 4939     }
 4940 
 4941     if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
 4942         ms = MAPI_E_USER_CANCEL;
 4943         goto cleanup;
 4944     }
 4945 
 4946     ms = foreach_gal_tablerow (conn, mem_ctx, rows, pMIds, propTagArray, list_objects_internal_cb, &loi_data, cancellable, perror);
 4947     if (ms != MAPI_E_SUCCESS) {
 4948         make_mapi_error (perror, "foreach_gal_tablerow", ms);
 4949         goto cleanup;
 4950     }
 4951 
 4952  cleanup:
 4953     talloc_free (pMIds);
 4954     talloc_free (rows);
 4955     talloc_free (propTagArray);
 4956     talloc_free (mem_ctx);
 4957     UNLOCK ();
 4958 
 4959     return ms == MAPI_E_SUCCESS;
 4960 }
 4961 
 4962 struct TransferGALObjectData
 4963 {
 4964     GHashTable *reverse_replace_hash;
 4965     TransferObjectCB cb;
 4966     gpointer cb_user_data;
 4967 };
 4968 
 4969 static gboolean
 4970 e_mapi_transfer_gal_objects_cb (EMapiConnection *conn,
 4971                 TALLOC_CTX *mem_ctx,
 4972                 struct SRow *srow,
 4973                 guint32 row_index,
 4974                 guint32 rows_total,
 4975                 gpointer user_data,
 4976                 GCancellable *cancellable,
 4977                 GError **perror)
 4978 {
 4979     struct TransferGALObjectData *tgo = user_data;
 4980     EMapiObject *object;
 4981     uint32_t ii;
 4982     gboolean res;
 4983 
 4984     g_return_val_if_fail (conn != NULL, FALSE);
 4985     g_return_val_if_fail (mem_ctx != NULL, FALSE);
 4986     g_return_val_if_fail (srow != NULL, FALSE);
 4987     g_return_val_if_fail (tgo != NULL, FALSE);
 4988     g_return_val_if_fail (tgo->cb != NULL, FALSE);
 4989 
 4990     object = e_mapi_object_new (mem_ctx);
 4991 
 4992     res = TRUE;
 4993 
 4994     for (ii = 0; ii < srow->cValues; ii++) {
 4995         uint32_t proptag = srow->lpProps[ii].ulPropTag;
 4996         gconstpointer propdata = get_SPropValue_data (&srow->lpProps[ii]);
 4997 
 4998         if (!propdata || may_skip_property (srow->lpProps[ii].ulPropTag))
 4999             continue;
 5000 
 5001         /* reverse_replace_hash has them stored in opposite,
 5002            the key is the name-id-proptag as stored on the server,
 5003            the value is a pidlid/pidname proptag */
 5004         maybe_replace_named_id_tag (&proptag, tgo->reverse_replace_hash);
 5005 
 5006         if (!e_mapi_utils_add_property (&object->properties, proptag, propdata, object)) {
 5007             res = FALSE;
 5008             make_mapi_error (perror, "e_mapi_utils_add_property", MAPI_E_CALL_FAILED);
 5009             break;
 5010         }
 5011     }
 5012 
 5013     if (res)
 5014         res = tgo->cb (conn, mem_ctx, object, row_index, rows_total, tgo->cb_user_data, cancellable, perror);
 5015 
 5016     e_mapi_object_free (object);
 5017 
 5018     return res;
 5019 }
 5020 
 5021 static void
 5022 fill_reverse_replace_hash (gpointer key,
 5023                gpointer value,
 5024                gpointer user_data)
 5025 {
 5026     GHashTable *reverse_replace_hash = user_data;
 5027 
 5028     g_return_if_fail (reverse_replace_hash != NULL);
 5029 
 5030     g_hash_table_insert (reverse_replace_hash, value, key);
 5031 }
 5032 
 5033 gboolean
 5034 e_mapi_connection_transfer_gal_objects (EMapiConnection *conn,
 5035                     const GSList *mids,
 5036                     BuildReadPropsCB brp_cb,
 5037                     gpointer brp_cb_user_data,
 5038                     TransferObjectCB cb,
 5039                     gpointer cb_user_data,
 5040                     GCancellable *cancellable,
 5041                     GError **perror)
 5042 {
 5043     enum MAPISTATUS ms;
 5044     TALLOC_CTX *mem_ctx;
 5045     struct PropertyTagArray_r *ids = NULL;
 5046     struct SPropTagArray *propTagArray = NULL;
 5047     struct PropertyRowSet_r rows;
 5048     struct TransferGALObjectData tgo;
 5049     GHashTable *reverse_replace_hash = NULL;
 5050     EResolveNamedIDsData *named_ids_list = NULL;
 5051     guint named_ids_len = 0, ii;
 5052     const GSList *iter;
 5053 
 5054     CHECK_CORRECT_CONN_AND_GET_PRIV (conn, FALSE);
 5055     e_return_val_mapi_error_if_fail (priv->session != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 5056     e_return_val_mapi_error_if_fail (priv->session->nspi != NULL, MAPI_E_UNCONFIGURED, FALSE);
 5057     e_return_val_mapi_error_if_fail (priv->session->nspi->ctx != NULL, MAPI_E_UNCONFIGURED, FALSE);
 5058     e_return_val_mapi_error_if_fail (cb != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 5059 
 5060     LOCK (cancellable, perror, FALSE);
 5061     mem_ctx = talloc_new (priv->session);
 5062 
 5063     for (iter = mids; iter; iter = iter->next) {
 5064         mapi_id_t *pmid = iter->data;
 5065 
 5066         if (pmid) {
 5067             if (!ids) {
 5068                 ids = talloc_zero (mem_ctx, struct PropertyTagArray_r);
 5069             }
 5070             ids->cValues++;
 5071             ids->aulPropTag = talloc_realloc (mem_ctx,
 5072                 ids->aulPropTag,
 5073                 uint32_t,
 5074                 ids->cValues + 1);
 5075             ids->aulPropTag[ids->cValues - 1] = (uint32_t) (*pmid);
 5076             ids->aulPropTag[ids->cValues] = 0;
 5077         }
 5078     }
 5079 
 5080     if (!ids) {
 5081         ms = MAPI_E_INVALID_PARAMETER;
 5082         make_mapi_error (perror, "gather valid mids", ms);
 5083         goto cleanup;
 5084     }
 5085 
 5086     if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
 5087         ms = MAPI_E_USER_CANCEL;
 5088         goto cleanup;
 5089     }
 5090 
 5091     if (brp_cb) {
 5092         propTagArray = set_SPropTagArray (mem_ctx, 1, PidTagObjectType);
 5093         if (!brp_cb (conn, mem_ctx, propTagArray, brp_cb_user_data, cancellable, perror)) {
 5094             ms = MAPI_E_CALL_FAILED;
 5095             make_mapi_error (perror, "brp_cb", ms);
 5096             goto cleanup;
 5097         }
 5098     } else {
 5099         if (!e_mapi_book_utils_get_supported_mapi_proptags (mem_ctx, &propTagArray) || !propTagArray) {
 5100             ms = MAPI_E_CALL_FAILED;
 5101             make_mapi_error (perror, "e_mapi_book_utils_get_supported_mapi_proptags", ms);
 5102             goto cleanup;
 5103         }
 5104     }
 5105 
 5106     for (ii = 0; ii < propTagArray->cValues; ii++) {
 5107         maybe_add_named_id_tag (propTagArray->aulPropTag[ii], &named_ids_list, &named_ids_len);
 5108     }
 5109 
 5110     if (named_ids_list) {
 5111         GHashTable *replace_hash;
 5112 
 5113         /* use NULL for GAL as a folder ID parameter */
 5114         if (!e_mapi_connection_resolve_named_props (conn, NULL, named_ids_list, named_ids_len, cancellable, perror)) {
 5115             ms = MAPI_E_CALL_FAILED;
 5116             make_mapi_error (perror, "e_mapi_connection_resolve_named_props", ms);
 5117             goto cleanup;
 5118         }
 5119 
 5120         replace_hash = prepare_maybe_replace_hash (named_ids_list, named_ids_len, TRUE);
 5121 
 5122         if (replace_hash) {
 5123             guint prop_count = propTagArray->cValues, jj;
 5124 
 5125             for (ii = 0, jj = 0; ii < prop_count; ii++) {
 5126                 uint32_t proptag = propTagArray->aulPropTag[ii];
 5127 
 5128                 maybe_replace_named_id_tag (&proptag, replace_hash);
 5129 
 5130                 propTagArray->aulPropTag[jj] = proptag;
 5131 
 5132                 if (proptag == MAPI_E_RESERVED || proptag == 0)
 5133                     propTagArray->cValues--;
 5134                 else
 5135                     jj++;
 5136             }
 5137 
 5138             if (jj < ii)
 5139                 propTagArray->aulPropTag[jj] = 0;
 5140 
 5141             reverse_replace_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
 5142 
 5143             g_hash_table_foreach (replace_hash, fill_reverse_replace_hash, reverse_replace_hash);
 5144             g_hash_table_destroy (replace_hash);
 5145         }
 5146     }
 5147 
 5148     /* fake rows, to start reading from the first mid */
 5149     rows.cRows = 0;
 5150     rows.aRow = NULL;
 5151 
 5152     tgo.cb = cb;
 5153     tgo.cb_user_data = cb_user_data;
 5154     tgo.reverse_replace_hash = reverse_replace_hash;
 5155 
 5156     ms = foreach_gal_tablerow (conn, mem_ctx, &rows, ids, propTagArray, e_mapi_transfer_gal_objects_cb, &tgo, cancellable, perror);
 5157     if (ms != MAPI_E_SUCCESS) {
 5158         make_mapi_error (perror, "foreach_gal_tablerow", ms);
 5159         goto cleanup;
 5160     }
 5161 
 5162  cleanup:
 5163     if (reverse_replace_hash)
 5164         g_hash_table_destroy (reverse_replace_hash);
 5165     talloc_free (propTagArray);
 5166     talloc_free (ids);
 5167     talloc_free (mem_ctx);
 5168     UNLOCK ();
 5169 
 5170     return ms == MAPI_E_SUCCESS;
 5171 }
 5172 
 5173 gboolean
 5174 e_mapi_connection_transfer_gal_object (EMapiConnection *conn,
 5175                        mapi_id_t message_id,
 5176                        TransferObjectCB cb,
 5177                        gpointer cb_user_data,
 5178                        GCancellable *cancellable,
 5179                        GError **perror)
 5180 {
 5181     GSList *mids;
 5182     gboolean res;
 5183 
 5184     mids = g_slist_append (NULL, &message_id);
 5185     res = e_mapi_connection_transfer_gal_objects (conn, mids, NULL, NULL, cb, cb_user_data, cancellable, perror);
 5186     g_slist_free (mids);
 5187 
 5188     return res;
 5189 }
 5190 
 5191 gboolean
 5192 e_mapi_connection_create_folder (EMapiConnection *conn,
 5193                  mapi_object_t *obj_parent_folder, /* in */
 5194                  const gchar *name,
 5195                  const gchar *new_folder_type, /* usually IPF_NOTE and similar */
 5196                  mapi_id_t *new_fid, /* out */
 5197                  GCancellable *cancellable,
 5198                  GError **perror)
 5199 {
 5200     enum MAPISTATUS ms;
 5201     mapi_object_t obj_folder;
 5202     struct SPropValue vals[1];
 5203     mapi_id_t fid = 0;
 5204 
 5205     CHECK_CORRECT_CONN_AND_GET_PRIV (conn, FALSE);
 5206     e_return_val_mapi_error_if_fail (priv->session != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 5207     e_return_val_mapi_error_if_fail (obj_parent_folder != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 5208     e_return_val_mapi_error_if_fail (name != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 5209     e_return_val_mapi_error_if_fail (new_folder_type != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 5210     e_return_val_mapi_error_if_fail (new_fid != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 5211 
 5212     LOCK (cancellable, perror, FALSE);
 5213     mapi_object_init (&obj_folder);
 5214 
 5215     if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
 5216         ms = MAPI_E_USER_CANCEL;
 5217         goto cleanup;
 5218     }
 5219 
 5220     /* Attempt to create the folder */
 5221     ms = CreateFolder (obj_parent_folder, FOLDER_GENERIC, name, "Created using Evolution/LibMAPI", OPEN_IF_EXISTS | MAPI_UNICODE, &obj_folder);
 5222     if (ms != MAPI_E_SUCCESS) {
 5223         make_mapi_error (perror, "CreateFolder", ms);
 5224         goto cleanup;
 5225     }
 5226 
 5227     vals[0].value.lpszW = new_folder_type;
 5228     vals[0].ulPropTag = PidTagContainerClass;
 5229 
 5230     ms = SetProps (&obj_folder, MAPI_PROPS_SKIP_NAMEDID_CHECK, vals, 1);
 5231     if (ms != MAPI_E_SUCCESS) {
 5232         make_mapi_error (perror, "SetProps", ms);
 5233         goto cleanup;
 5234     }
 5235 
 5236     fid = mapi_object_get_id (&obj_folder);
 5237     if (fid == 0) {
 5238         ms = MAPI_E_CALL_FAILED;
 5239         make_mapi_error (perror, "mapi_object_get_id", ms);
 5240     } else {
 5241         *new_fid = fid;
 5242     }
 5243 
 5244  cleanup:
 5245     mapi_object_release (&obj_folder);
 5246 
 5247     UNLOCK ();
 5248 
 5249     return ms == MAPI_E_SUCCESS;
 5250 }
 5251 
 5252 gboolean
 5253 e_mapi_connection_empty_folder (EMapiConnection *conn,
 5254                 mapi_object_t *obj_folder,
 5255                 GCancellable *cancellable,
 5256                 GError **perror)
 5257 {
 5258     enum MAPISTATUS ms;
 5259 
 5260     CHECK_CORRECT_CONN_AND_GET_PRIV (conn, FALSE);
 5261     e_return_val_mapi_error_if_fail (priv->session != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
 5262     e_return_val_mapi_error_if_fail (obj_folder, MAPI_E_INVALID_PARAMETER, FALSE);
 5263 
 5264     LOCK (cancellable, perror, FALSE);
 5265 
 5266     if (g_cancellable_set_error_if_cancelled (cancellable, perror)) {
 5267         ms = MAPI_E_USER_CANCEL;
 5268         goto cleanup;
 5269     }
 5270 
 5271     /* Empty the contents of the folder */
 5272     ms = EmptyFolder (obj_folder);
 5273     if (ms != MAPI_E_SUCCESS) {
 5274         make_mapi_error (perror, "EmptyFolder", ms);
 5275         goto cleanup;
 5276     }
 5277 
 5278  cleanup:
 5279     UNLOCK ();
 5280 
 5281     return ms == MAPI_E_SUCCESS;
 5282 }
 5283 
 5284 static gboolean
 5285 add_parent_fid_prop_cb (EMapiConnection *conn,
 5286             TALLOC_CTX *mem_ctx,
 5287             struct SPropTagArray *props,
 5288             gpointer data,
 5289             GCancellable *cancellable,
 5290             GError **perror)
 5291 {
 5292     g_return_val_if_fail (mem_ctx != NULL, FALSE);
 5293     g_return_val_if_fail (props != NULL, FALSE);
 5294 
 5295     SPropTagArray_add (mem_ctx, props, PidTagParentFolderId);
 5296 
 5297     return TRUE;
 5298 }
 5299 
 5300 static gboolean
 5301 read_parent_fid_prop_cb (EMapiConnection *conn,
 5302              TALLOC_CTX *mem_ctx,
 5303              /* const */ struct mapi_SPropValue_array *properties,
 5304              gpointer user_data,
 5305              GCancellable *cancellable,
 5306              GError **perror)
 5307 {
 5308     mapi_id_t *pmid = user_data;
 5309     const mapi_id_t *cmid;
 5310 
 5311     g_return_val_if_fail (properties != NULL, FALSE);
 5312     g_return_val_if_fail (pmid != NULL, FALSE);
 5313 
 5314     cmid = e_mapi_util_find_array_propval (properties, PidTagParentFolderId);
 5315     g_return_val_if_fail (cmid != NULL, FALSE);
 5316 
 5317     *pmid = *cmid;
 5318 
 5319     return TRUE;
 5320 }
 5321 
 5322 static gboolean