"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, ¤t_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