Merge in sogo revs r3786-3829
[jelmer/openchange.git] / mapiproxy / servers / default / emsmdb / emsmdbp_object.c
1 /*
2    OpenChange Server implementation
3
4    EMSMDBP: EMSMDB Provider implementation
5
6    Copyright (C) Julien Kerihuel 2009
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  */
21
22 /**
23    \file emsmdbp_object.c
24
25    \brief Server-side specific objects init/release routines
26  */
27
28 #include <ctype.h>
29 #include <time.h>
30
31 #include "mapiproxy/dcesrv_mapiproxy.h"
32 #include "mapiproxy/libmapiproxy/libmapiproxy.h"
33 #include "mapiproxy/libmapiserver/libmapiserver.h"
34 #include "mapiproxy/libmapistore/mapistore_nameid.h"
35 #include "libmapi/property_tags.h"
36 #include "libmapi/property_altnames.h"
37
38 #include "dcesrv_exchange_emsmdb.h"
39
40 static const int        max_mins_per_month = 31 * 24 * 60;
41
42 const char *emsmdbp_getstr_type(struct emsmdbp_object *object)
43 {
44         switch (object->type) {
45         case EMSMDBP_OBJECT_UNDEF:
46                 return "undefined";
47         case EMSMDBP_OBJECT_MAILBOX:
48                 return "mailbox";
49         case EMSMDBP_OBJECT_FOLDER:
50                 return "folder";
51         case EMSMDBP_OBJECT_MESSAGE:
52                 return "message";
53         case EMSMDBP_OBJECT_TABLE:
54                 return "table";
55         case EMSMDBP_OBJECT_STREAM:
56                 return "stream";
57         case EMSMDBP_OBJECT_ATTACHMENT:
58                 return "attachment";
59         case EMSMDBP_OBJECT_SUBSCRIPTION:
60                 return "subscription";
61         case EMSMDBP_OBJECT_SYNCCONTEXT:
62                 return "synccontext";
63         case EMSMDBP_OBJECT_FTCONTEXT:
64                 return "ftcontext";
65         default:
66                 return "unknown";
67         }
68 }
69
70 /**
71    \details Convenient function to determine whether specified
72    object is using mapistore or not
73
74    \param object pointer to the emsmdp object
75
76    \return true if parent is using mapistore, otherwise false
77  */
78 bool emsmdbp_is_mapistore(struct emsmdbp_object *object)
79 {
80         /* Sanity checks - probably pointless */
81         if (!object) {
82                 return false;
83         }
84
85         switch (object->type) {
86         case EMSMDBP_OBJECT_MAILBOX:
87                 return false;
88         case EMSMDBP_OBJECT_FOLDER:
89                 if (object->object.folder->mapistore_root) {
90                         return true;
91                 }
92         default:
93                 if (object->parent_object) {
94                         return emsmdbp_is_mapistore(object->parent_object);
95                 }
96         }
97
98         return false;
99 }
100
101 static struct emsmdbp_object *emsmdbp_get_mailbox(struct emsmdbp_object *object)
102 {
103         if (object->type == EMSMDBP_OBJECT_MAILBOX) {
104                 return object;
105         }
106
107         return emsmdbp_get_mailbox(object->parent_object);
108 }
109
110 /**
111    \details Convenient function to determine whether specified
112    mapi_handles refers to object within mailbox or public folders
113    store.
114
115    \param object pointer to the emsmdp object
116
117    \return true if parent is within mailbox store, otherwise false
118  */
119 bool emsmdbp_is_mailboxstore(struct emsmdbp_object *object)
120 {
121         struct emsmdbp_object *mailbox = emsmdbp_get_mailbox(object);
122
123         return mailbox->object.mailbox->mailboxstore;
124 }
125
126 /**
127    \details Convenience function to determine the owner of an object
128
129    \param object pointer to the emsmdp object
130
131    \return true if parent is within mailbox store, otherwise false
132  */
133 char *emsmdbp_get_owner(struct emsmdbp_object *object)
134 {
135         struct emsmdbp_object *mailbox;
136
137         mailbox = emsmdbp_get_mailbox(object);
138
139         return mailbox->object.mailbox->owner_username;
140 }
141
142
143 /**
144    \details Return the contextID associated to a handle
145
146    \param object pointer to the emsmdp object
147
148    \return contextID value on success, otherwise -1
149  */
150 _PUBLIC_ uint32_t emsmdbp_get_contextID(struct emsmdbp_object *object)
151 {
152         switch (object->type) {
153         case EMSMDBP_OBJECT_MAILBOX:
154                 return -1;
155         case EMSMDBP_OBJECT_FOLDER:
156                 if (object->object.folder->mapistore_root) {
157                         return object->object.folder->contextID;
158                 }
159         default:
160                 if (object->parent_object) {
161                         return emsmdbp_get_contextID(object->parent_object);
162                 }
163         }
164
165         return -1;
166 }
167
168 _PUBLIC_ enum mapistore_error emsmdbp_object_get_fid_by_name(struct emsmdbp_context *emsmdbp_ctx, struct emsmdbp_object *parent_folder, const char *name, uint64_t *fidp)
169 {
170         uint64_t        folderID;
171
172         if (!emsmdbp_ctx) return MAPISTORE_ERROR;
173         if (!parent_folder) return MAPISTORE_ERROR;
174         if (!name) return MAPISTORE_ERROR;
175         if (!fidp) return MAPISTORE_ERROR;
176
177         if (parent_folder->type == EMSMDBP_OBJECT_FOLDER) {
178                 folderID = parent_folder->object.folder->folderID;
179         }
180         else if (parent_folder->type == EMSMDBP_OBJECT_MAILBOX) {
181                 folderID = parent_folder->object.mailbox->folderID;
182         }
183         else {
184                 return MAPISTORE_ERROR;
185         }
186
187         if (emsmdbp_is_mapistore(parent_folder)) {
188                 if (mapistore_folder_get_child_fid_by_name(emsmdbp_ctx->mstore_ctx, emsmdbp_get_contextID(parent_folder), parent_folder->backend_object, name, fidp)) {
189                         return MAPISTORE_ERR_NOT_FOUND;
190                 }
191
192                 return MAPISTORE_SUCCESS;
193         }
194         else {
195                 return openchangedb_get_fid_by_name(emsmdbp_ctx->oc_ctx, folderID, name, fidp);
196         }
197 }
198
199 static enum mapistore_context_role emsmdbp_container_class_to_role(const char *container_class)
200 {
201         enum mapistore_context_role     i, role = MAPISTORE_FALLBACK_ROLE;
202         static const char               **container_classes = NULL;
203         bool                            found = false;
204
205         if (!container_classes) {
206                 container_classes = talloc_array(NULL, const char *, MAPISTORE_MAX_ROLES);
207                 for (i = MAPISTORE_MAIL_ROLE; i < MAPISTORE_MAX_ROLES; i++) {
208                         container_classes[i] = "IPF.Note";
209                 }
210                 container_classes[MAPISTORE_CALENDAR_ROLE] = "IPF.Appointment";
211                 container_classes[MAPISTORE_CONTACTS_ROLE] = "IPF.Contact";
212                 container_classes[MAPISTORE_TASKS_ROLE] = "IPF.Task";
213                 container_classes[MAPISTORE_NOTES_ROLE] = "IPF.StickyNote";
214                 container_classes[MAPISTORE_JOURNAL_ROLE] = "IPF.Journal";
215                 container_classes[MAPISTORE_FALLBACK_ROLE] = "";
216         }
217
218         if (container_class) {
219                 for (i = 0; !found && i < MAPISTORE_MAX_ROLES; i++) {
220                         if (strcmp(container_class, container_classes[i]) == 0) {
221                                 role = i;
222                                 found = true;
223                         }
224                 }
225         }
226
227         return role;
228 }
229
230 static enum mapistore_error emsmdbp_object_folder_commit_creation(struct emsmdbp_context *emsmdbp_ctx, struct emsmdbp_object *new_folder, bool force_container_class)
231 {
232         enum mapistore_error            ret = MAPISTORE_SUCCESS;
233         enum MAPISTATUS                 retval;
234         struct SPropValue               *value;
235         char                            *mapistore_uri, *owner;
236         enum mapistore_context_role     role;
237         TALLOC_CTX                      *mem_ctx;
238         uint64_t                        parent_fid, fid;
239         uint32_t                        context_id;
240
241         if (!new_folder->object.folder->postponed_props) {
242                 return ret;
243         }
244
245         mem_ctx = talloc_zero(NULL, TALLOC_CTX);
246
247         value = get_SPropValue_SRow(new_folder->object.folder->postponed_props, PR_CONTAINER_CLASS_UNICODE);
248         if (!value) {
249                 /* Sometimes Outlook does pass non-unicode values. */
250                 value = get_SPropValue_SRow(new_folder->object.folder->postponed_props, PR_CONTAINER_CLASS);
251         }
252         if (value) {
253                 role = emsmdbp_container_class_to_role(value->value.lpszW);
254         }
255         else if (force_container_class) {
256                 DEBUG(5, (__location__": forcing folder backend role to 'fallback'\n"));
257                 role = MAPISTORE_FALLBACK_ROLE;
258         }
259         else {
260                 DEBUG(5, (__location__": container class not set yet\n"));
261                 goto end;
262         }
263
264         value = get_SPropValue_SRow(new_folder->object.folder->postponed_props, PR_DISPLAY_NAME_UNICODE);
265         if (!value) {
266                 DEBUG(5, (__location__": display name not set yet\n"));
267                 goto end;
268         }
269
270         fid = new_folder->object.folder->folderID;
271         owner = emsmdbp_get_owner(new_folder);
272
273         ret = mapistore_create_root_folder(owner, role, fid, value->value.lpszW, mem_ctx, &mapistore_uri);
274         if (ret != MAPISTORE_SUCCESS) {
275                 goto end;
276         }
277
278         ret = mapistore_add_context(emsmdbp_ctx->mstore_ctx, owner, mapistore_uri, fid, &context_id, &new_folder->backend_object);
279         if (ret != MAPISTORE_SUCCESS) {
280                 abort();
281         }
282
283         new_folder->object.folder->contextID = context_id;
284
285         if (new_folder->parent_object->type == EMSMDBP_OBJECT_MAILBOX) {
286                 parent_fid = new_folder->parent_object->object.mailbox->folderID;
287         }
288         else { /* EMSMDBP_OBJECT_FOLDER */
289                 parent_fid = new_folder->parent_object->object.folder->folderID;
290         }
291
292         value = get_SPropValue_SRow(new_folder->object.folder->postponed_props, PidTagChangeNumber);
293         retval = openchangedb_create_folder(emsmdbp_ctx->oc_ctx, parent_fid, fid, value->value.d, mapistore_uri, -1);
294         if (retval != MAPI_E_SUCCESS) {
295                 ret = MAPISTORE_ERR_NOT_FOUND;
296                 DEBUG(0, (__location__": openchangedb folder creation failed: 0x%.8x\n", retval));
297                 abort();
298         }
299
300         mapistore_indexing_record_add_fid(emsmdbp_ctx->mstore_ctx, context_id, owner, fid);
301         new_folder->object.folder->contextID = context_id;
302
303         openchangedb_set_folder_properties(emsmdbp_ctx->oc_ctx, fid, new_folder->object.folder->postponed_props);
304         mapistore_properties_set_properties(emsmdbp_ctx->mstore_ctx, context_id, new_folder->backend_object, new_folder->object.folder->postponed_props);
305
306         talloc_unlink(new_folder, new_folder->object.folder->postponed_props);
307         new_folder->object.folder->postponed_props = NULL;
308
309         DEBUG(5, ("new mapistore context created at uri: %s\n", mapistore_uri));
310
311 end:
312         talloc_free(mem_ctx);
313
314         return ret;
315 }
316
317 _PUBLIC_ enum MAPISTATUS emsmdbp_object_create_folder(struct emsmdbp_context *emsmdbp_ctx, struct emsmdbp_object *parent_folder, TALLOC_CTX *mem_ctx, uint64_t fid, struct SRow *rowp, struct emsmdbp_object **new_folderp)
318 {
319         uint64_t                        parentFolderID, testFolderID;
320         struct SPropValue               *value;
321         int                             retval;
322         struct emsmdbp_object           *new_folder;
323         struct SRow                     *postponed_props;
324
325         /* Sanity checks */
326         if (!emsmdbp_ctx) return MAPISTORE_ERROR;
327         if (!parent_folder) return MAPISTORE_ERROR;
328         if (!rowp) return MAPISTORE_ERROR;
329
330         new_folder = emsmdbp_object_folder_init(mem_ctx, emsmdbp_ctx, fid, parent_folder);
331         if (emsmdbp_is_mapistore(parent_folder)) {
332                 retval = mapistore_folder_create_folder(emsmdbp_ctx->mstore_ctx, emsmdbp_get_contextID(parent_folder), parent_folder->backend_object, new_folder, fid, rowp, &new_folder->backend_object);
333                 if (retval != MAPISTORE_SUCCESS) {
334                         talloc_free(new_folder);
335                         if (retval == MAPISTORE_ERR_EXIST) {
336                                 /* folder with this name already exists */
337                                 DEBUG(5, (__location__": folder already exists\n"));
338                                 return MAPI_E_COLLISION;
339                         }
340                         else if (retval == MAPISTORE_ERR_DENIED) {
341                                 DEBUG(5, (__location__": folder creation denied\n"));
342                                 return MAPI_E_NO_ACCESS;
343                         }
344                         else {
345                                 return MAPI_E_NOT_FOUND;
346                         }
347                 }
348         }
349         else {
350                 parentFolderID = parent_folder->object.folder->folderID;
351                 value = get_SPropValue_SRow(rowp, PR_DISPLAY_NAME_UNICODE);
352                 if (!value) {
353                         value = get_SPropValue_SRow(rowp, PR_DISPLAY_NAME);
354                 }
355                 if (!value) {
356                         talloc_free(new_folder);
357                         return MAPI_E_INVALID_PARAMETER;
358                 }
359                 if (openchangedb_get_fid_by_name(emsmdbp_ctx->oc_ctx, parentFolderID,
360                                                  value->value.lpszW, &testFolderID) == MAPI_E_SUCCESS) {
361                         /* this folder already exists */
362                         DEBUG(4, ("emsmdbp_object: CreateFolder Duplicate Folder error\n"));
363                         talloc_free(new_folder);
364                         return MAPI_E_COLLISION;
365                 }
366
367                 value = get_SPropValue_SRow(rowp, PidTagChangeNumber);
368                 if (value) {
369                         postponed_props = talloc_zero(new_folder, struct SRow);
370                         postponed_props->cValues = rowp->cValues;
371                         postponed_props->lpProps = talloc_array(postponed_props, struct SPropValue, rowp->cValues);
372                         mapi_copy_spropvalues(postponed_props->lpProps, rowp->lpProps, postponed_props->lpProps, rowp->cValues);
373                         new_folder->object.folder->postponed_props = postponed_props;
374                         new_folder->object.folder->mapistore_root = true;
375
376                         emsmdbp_object_folder_commit_creation(emsmdbp_ctx, new_folder, false);
377                 }
378                 else {
379                         DEBUG(0, (__location__": PidTagChangeNumber *must* be present\n"));
380                         abort();
381                 }
382         }
383         *new_folderp = new_folder;
384
385         return MAPI_E_SUCCESS;
386 }
387
388 _PUBLIC_ enum mapistore_error emsmdbp_object_open_folder(TALLOC_CTX *mem_ctx, struct emsmdbp_context *emsmdbp_ctx, struct emsmdbp_object *parent, uint64_t fid, struct emsmdbp_object **folder_object_p)
389 {
390         struct emsmdbp_object                   *folder_object, *mailbox_object;
391         enum mapistore_error                    retval;
392         enum MAPISTATUS                         ret;
393         char                                    *path;
394         char                                    *owner;
395         uint32_t                                contextID;
396         uint64_t                                parent_fid, oc_parent_fid;
397         void                                    *local_ctx;
398
399         folder_object = emsmdbp_object_folder_init(mem_ctx, emsmdbp_ctx, fid, parent);
400         if (emsmdbp_is_mapistore(parent)) {
401                 DEBUG(0, ("%s: opening child mapistore folder\n", __FUNCTION__));
402                 retval = mapistore_folder_open_folder(emsmdbp_ctx->mstore_ctx, emsmdbp_get_contextID(parent), parent->backend_object, folder_object, fid, &folder_object->backend_object);
403                 if (retval != MAPISTORE_SUCCESS) {
404                         talloc_free(folder_object);
405                         return retval;
406                 }
407         }
408         else {
409                 local_ctx = talloc_zero(NULL, void);
410         
411                 retval = openchangedb_get_mapistoreURI(local_ctx, emsmdbp_ctx->oc_ctx, fid, &path, true);
412                 if (retval == MAPISTORE_SUCCESS && path) {
413                         folder_object->object.folder->mapistore_root = true;
414                         /* system/special folder */
415                         DEBUG(0, ("%s: opening base mapistore folder\n", __FUNCTION__));
416
417                         retval = mapistore_search_context_by_uri(emsmdbp_ctx->mstore_ctx, path, &contextID, &folder_object->backend_object);
418                         if (retval == MAPISTORE_SUCCESS) {
419                                 retval = mapistore_add_context_ref_count(emsmdbp_ctx->mstore_ctx, contextID);
420                         } else {
421                                 owner = emsmdbp_get_owner(folder_object);
422                                 retval = mapistore_add_context(emsmdbp_ctx->mstore_ctx, owner, path, folder_object->object.folder->folderID, &contextID, &folder_object->backend_object);
423                                 if (retval != MAPISTORE_SUCCESS) {
424                                         talloc_free(local_ctx);
425                                         talloc_free(folder_object);
426                                         return retval;
427                                 }
428                                 mapistore_indexing_record_add_fid(emsmdbp_ctx->mstore_ctx, contextID, owner, fid);
429                         }
430                         folder_object->object.folder->contextID = contextID;
431                         /* (void) talloc_reference(folder_object, folder_object->backend_object); */
432                 }
433                 else {
434                         switch (parent->type) {
435                         case EMSMDBP_OBJECT_MAILBOX:
436                                 parent_fid = parent->object.mailbox->folderID;
437                                 break;
438                         case EMSMDBP_OBJECT_FOLDER:
439                                 parent_fid = parent->object.folder->folderID;
440                                 break;
441                         default:
442                                 DEBUG(5, ("you should never get here\n"));
443                                 abort();
444                         }
445                         mailbox_object = emsmdbp_get_mailbox(parent);
446                         ret = openchangedb_get_parent_fid(emsmdbp_ctx->oc_ctx, fid, &oc_parent_fid, mailbox_object->object.mailbox->mailboxstore);
447                         if (ret != MAPI_E_SUCCESS) {
448                                 DEBUG(0, ("folder %.16"PRIx64" or %.16"PRIx64" does not exist\n", parent_fid, fid));
449                                 talloc_free(local_ctx);
450                                 talloc_free(folder_object);
451                                 return MAPISTORE_ERR_NOT_FOUND;
452                         }
453                         if (oc_parent_fid != parent_fid) {
454                                 DEBUG(0, ("parent folder mismatch: expected %.16"PRIx64" but got %.16"PRIx64"\n", parent_fid, oc_parent_fid));
455                                 talloc_free(local_ctx);
456                                 talloc_free(folder_object);
457                                 return MAPISTORE_ERR_NOT_FOUND;
458                         }
459                         DEBUG(0, ("%s: opening openchangedb folder\n", __FUNCTION__));
460                 }
461                 talloc_free(local_ctx);
462         }
463
464         *folder_object_p = folder_object;
465
466         return MAPISTORE_SUCCESS;
467 }
468
469 _PUBLIC_ int emsmdbp_get_uri_from_fid(TALLOC_CTX *mem_ctx, struct emsmdbp_context *emsmdbp_ctx, uint64_t fid, char **urip)
470 {
471         enum MAPISTATUS retval;
472         bool            soft_deleted;
473
474         retval = openchangedb_get_mapistoreURI(mem_ctx, emsmdbp_ctx->oc_ctx, fid, urip, true); /* FIXME: always mailboxstore */
475         if (retval == MAPI_E_SUCCESS) {
476                 return MAPISTORE_SUCCESS;
477         }
478         return mapistore_indexing_record_get_uri(emsmdbp_ctx->mstore_ctx, emsmdbp_ctx->username, mem_ctx, fid, urip, &soft_deleted);
479 }
480
481 _PUBLIC_ int emsmdbp_get_fid_from_uri(struct emsmdbp_context *emsmdbp_ctx, const char *uri, uint64_t *fidp)
482 {
483         int     ret;
484         bool    soft_deleted;
485
486         ret = openchangedb_get_fid(emsmdbp_ctx->oc_ctx, uri, fidp);
487         if (ret != MAPI_E_SUCCESS) {
488                 ret = mapistore_indexing_record_get_fmid(emsmdbp_ctx->mstore_ctx, emsmdbp_ctx->username, uri, false, fidp, &soft_deleted);
489         }
490
491         return ret;
492 }
493
494 static char *emsmdbp_compute_parent_uri(TALLOC_CTX *mem_ctx, char *uri)
495 {
496         char *parent_uri, *slash, *lastchar;
497         int len;
498
499         if (!uri) return NULL;
500
501         parent_uri = talloc_strdup(mem_ctx, uri);
502         len = strlen(parent_uri);
503         lastchar = parent_uri + len - 1;
504         if (*lastchar == '/') {
505                 *lastchar = 0;
506         }
507         slash = strrchr(parent_uri, '/');
508         if (slash) {
509                 *(slash + 1) = 0;
510         }
511         else {
512                 talloc_free(parent_uri);
513                 parent_uri = NULL;
514         }
515
516         return parent_uri;
517 }
518
519 static int emsmdbp_get_parent_fid(struct emsmdbp_context *emsmdbp_ctx, uint64_t fid, uint64_t *parent_fidp)
520 {
521         TALLOC_CTX      *mem_ctx;
522         int             retval = MAPISTORE_SUCCESS;
523         bool            soft_deleted;
524         char            *uri, *parent_uri;
525
526         mem_ctx = talloc_zero(NULL, void);
527         retval = openchangedb_get_parent_fid(emsmdbp_ctx->oc_ctx, fid, parent_fidp, true);
528         if (retval == MAPISTORE_SUCCESS) {
529                 goto end;
530         }
531         retval = openchangedb_get_parent_fid(emsmdbp_ctx->oc_ctx, fid, parent_fidp, false);
532         if (retval == MAPISTORE_SUCCESS) {
533                 goto end;
534         }
535
536         retval = mapistore_indexing_record_get_uri(emsmdbp_ctx->mstore_ctx, emsmdbp_ctx->username, mem_ctx, fid, &uri, &soft_deleted);
537         if (retval == MAPISTORE_SUCCESS) {
538                 parent_uri = emsmdbp_compute_parent_uri(mem_ctx, uri);
539                 if (parent_uri) {
540                         retval = emsmdbp_get_fid_from_uri(emsmdbp_ctx, parent_uri, parent_fidp);
541                 }
542                 else {
543                         retval = MAPISTORE_ERR_NOT_FOUND;
544                 }
545         }
546
547 end:
548         talloc_free(mem_ctx);
549
550         return retval;
551 }
552
553 /**
554    \details Return the folder object associated to specified folder identified
555
556    \param mem_ctx pointer to the memory context
557    \param emsmdbp_ctx pointer to the emsmdbp context
558    \param context_object pointer to current context object
559    \param fid pointer to the Folder Identifier to lookup
560
561    \return Valid emsmdbp object structure on success, otherwise NULL
562  */
563 _PUBLIC_ enum mapistore_error emsmdbp_object_open_folder_by_fid(TALLOC_CTX *mem_ctx, struct emsmdbp_context *emsmdbp_ctx, struct emsmdbp_object *context_object, uint64_t fid, struct emsmdbp_object **folder_object_p)
564 {
565         uint64_t                parent_fid;
566         int                     retval;
567         struct emsmdbp_object   *parent_object;
568         
569         if ((context_object->type == EMSMDBP_OBJECT_MAILBOX
570              && fid == context_object->object.mailbox->folderID)
571             || (context_object->type == EMSMDBP_OBJECT_FOLDER
572                 && fid == context_object->object.folder->folderID)) {
573                 *folder_object_p = context_object;
574                 return MAPISTORE_SUCCESS;
575         }
576         else {
577                 parent_object = emsmdbp_get_mailbox(context_object);
578                 if (fid == parent_object->object.mailbox->folderID) {
579                         *folder_object_p = parent_object;
580                         return MAPISTORE_SUCCESS;
581                 }
582         }
583
584         retval = emsmdbp_get_parent_fid(emsmdbp_ctx, fid, &parent_fid);
585         if (retval == MAPISTORE_SUCCESS) {
586                 if (parent_fid) {
587                         retval = emsmdbp_object_open_folder_by_fid(mem_ctx, emsmdbp_ctx, context_object, parent_fid, &parent_object);
588                         if (retval != MAPISTORE_SUCCESS) {
589                                 return retval;
590                         }
591                         return emsmdbp_object_open_folder(mem_ctx, emsmdbp_ctx, parent_object, fid, folder_object_p);
592                 }
593                 else {
594                         *folder_object_p = emsmdbp_object_folder_init(mem_ctx, emsmdbp_ctx, fid, NULL);
595                         return MAPISTORE_SUCCESS;
596                 }
597         }
598
599         return MAPISTORE_ERROR;
600 }
601
602 _PUBLIC_ int emsmdbp_object_stream_commit(struct emsmdbp_object *stream_object)
603 {
604         int                             rc;
605         struct emsmdbp_object_stream    *stream;
606         void                            *stream_data;
607         uint8_t                         *utf8_buffer;
608         struct Binary_r                 *binary_data;
609         struct SRow                     aRow;
610         size_t                          converted_size;
611         uint16_t                        propType;
612
613         if (!stream_object || stream_object->type != EMSMDBP_OBJECT_STREAM) return MAPISTORE_ERROR;
614
615         stream = stream_object->object.stream;
616
617         rc = MAPISTORE_SUCCESS;
618         if (stream->needs_commit) {
619                 stream->needs_commit = false;
620                 aRow.cValues = 1;
621                 aRow.lpProps = talloc_zero(NULL, struct SPropValue);
622
623                 propType = stream->property & 0xffff;
624                 if (propType == PT_BINARY) {
625                         binary_data = talloc(aRow.lpProps, struct Binary_r);
626                         binary_data->cb = stream->stream.buffer.length;
627                         binary_data->lpb = stream->stream.buffer.data;
628                         stream_data = binary_data;
629                 }
630                 else if (propType == PT_STRING8) {
631                         stream_data = stream->stream.buffer.data;
632                 }
633                 else {
634                         /* PT_UNICODE */
635                         utf8_buffer = talloc_array(aRow.lpProps, uint8_t, stream->stream.buffer.length + 2);
636                         convert_string(CH_UTF16LE, CH_UTF8,
637                                        stream->stream.buffer.data, stream->stream.buffer.length,
638                                        utf8_buffer, stream->stream.buffer.length, &converted_size);
639                         utf8_buffer[converted_size] = 0;
640                         stream_data = utf8_buffer;
641                 }
642                 set_SPropValue_proptag(aRow.lpProps, stream->property, stream_data);
643
644                 emsmdbp_object_set_properties(stream_object->emsmdbp_ctx, stream_object->parent_object, &aRow);
645                 talloc_free(aRow.lpProps);
646         }
647
648         return rc;
649 }
650
651 /**
652    \details talloc destructor for emsmdbp_objects
653
654    \param data generic pointer on data
655
656    \return 0 on success, otherwise -1
657  */
658 static int emsmdbp_object_destructor(void *data)
659 {
660         struct emsmdbp_object   *object = (struct emsmdbp_object *) data;
661         int                     ret = MAPISTORE_SUCCESS;
662         uint32_t                contextID;
663
664         if (!data) return -1;
665         if (!emsmdbp_is_mapistore(object)) goto nomapistore;
666
667         DEBUG(4, ("[%s:%d]: emsmdbp %s object released\n", __FUNCTION__, __LINE__,
668                   emsmdbp_getstr_type(object)));
669
670         contextID = emsmdbp_get_contextID(object);
671         switch (object->type) {
672         case EMSMDBP_OBJECT_FOLDER:
673                 if (object->object.folder->mapistore_root) {
674                         ret = mapistore_del_context(object->emsmdbp_ctx->mstore_ctx, contextID);
675                 }
676                 DEBUG(4, ("[%s:%d] mapistore folder context retval = %d\n", __FUNCTION__, __LINE__, ret));
677                 break;
678         case EMSMDBP_OBJECT_TABLE:
679                 if (emsmdbp_is_mapistore(object) && object->backend_object && object->object.table->handle > 0) {
680                         mapistore_table_handle_destructor(object->emsmdbp_ctx->mstore_ctx, emsmdbp_get_contextID(object), object->backend_object, object->object.table->handle);
681                 }
682                 if (object->object.table->subscription_list) {
683                         DLIST_REMOVE(object->emsmdbp_ctx->mstore_ctx->subscriptions, object->object.table->subscription_list);
684                         talloc_free(object->object.table->subscription_list);
685                         /* talloc_unlink(object->emsmdbp_ctx, object->object.table->subscription_list); */
686                 }
687                 break;
688         case EMSMDBP_OBJECT_STREAM:
689                 emsmdbp_object_stream_commit(object);
690                 break;
691         case EMSMDBP_OBJECT_SUBSCRIPTION:
692                 if (object->object.subscription->subscription_list) {
693                         DLIST_REMOVE(object->emsmdbp_ctx->mstore_ctx->subscriptions, object->object.subscription->subscription_list);
694                         talloc_free(object->object.subscription->subscription_list);
695                 }
696                 break;
697         case EMSMDBP_OBJECT_UNDEF:
698         case EMSMDBP_OBJECT_MAILBOX:
699         case EMSMDBP_OBJECT_MESSAGE:
700         case EMSMDBP_OBJECT_ATTACHMENT:
701         case EMSMDBP_OBJECT_FTCONTEXT:
702         case EMSMDBP_OBJECT_SYNCCONTEXT:
703                 break;
704         }
705         
706 nomapistore:
707         talloc_unlink(object, object->parent_object);
708
709         return 0;
710 }
711
712 /**
713    \details Initialize an emsmdbp_object
714
715    \param mem_ctx pointer to the memory context
716    \param emsmdbp_ctx pointer to the emsmdb provider context
717
718    \return Allocated emsmdbp object on success, otherwise NULL
719  */
720 _PUBLIC_ struct emsmdbp_object *emsmdbp_object_init(TALLOC_CTX *mem_ctx, struct emsmdbp_context *emsmdbp_ctx, struct emsmdbp_object *parent_object)
721 {
722         struct emsmdbp_object   *object = NULL;
723
724         object = talloc_zero(mem_ctx, struct emsmdbp_object);
725         if (!object) return NULL;
726
727         talloc_set_destructor((void *)object, (int (*)(void *))emsmdbp_object_destructor);
728
729         object->type = EMSMDBP_OBJECT_UNDEF;
730         object->emsmdbp_ctx = emsmdbp_ctx;
731         object->object.mailbox = NULL;
732         object->object.folder = NULL;
733         object->object.message = NULL;
734         object->object.stream = NULL;
735         object->backend_object = NULL;
736         object->parent_object = parent_object;
737         (void) talloc_reference(object, parent_object);
738
739         object->stream_data = NULL;
740
741         return object;
742 }
743
744 static int emsmdbp_copy_properties(struct emsmdbp_context *emsmdbp_ctx, struct emsmdbp_object *source_object, struct emsmdbp_object *dest_object, struct SPropTagArray *excluded_tags)
745 {
746         TALLOC_CTX              *mem_ctx;
747         bool                    *properties_exclusion;
748         struct SPropTagArray    *properties, *needed_properties;
749         void                    **data_pointers;
750         enum MAPISTATUS         *retvals = NULL;
751         struct SRow             *aRow;
752         struct SPropValue       newValue;
753         uint32_t                i;
754
755         mem_ctx = talloc_zero(NULL, TALLOC_CTX);
756         if (emsmdbp_object_get_available_properties(mem_ctx, emsmdbp_ctx, source_object, &properties) == MAPISTORE_ERROR) {
757                 DEBUG(0, ("["__location__"] - mapistore support not implemented yet - shouldn't occur\n"));
758                 talloc_free(mem_ctx);
759                 return MAPI_E_NO_SUPPORT;
760         }
761
762         /* 1. Exclusions */
763         properties_exclusion = talloc_array(mem_ctx, bool, 65536);
764         memset(properties_exclusion, 0, 65536 * sizeof(bool));
765
766         /* 1a. Explicit exclusions */
767         properties_exclusion[(uint16_t) (PR_ROW_TYPE >> 16)] = true;
768         properties_exclusion[(uint16_t) (PR_INSTANCE_KEY >> 16)] = true;
769         properties_exclusion[(uint16_t) (PR_INSTANCE_NUM >> 16)] = true;
770         properties_exclusion[(uint16_t) (PR_INST_ID >> 16)] = true;
771         properties_exclusion[(uint16_t) (PR_FID >> 16)] = true;
772         properties_exclusion[(uint16_t) (PR_MID >> 16)] = true;
773         properties_exclusion[(uint16_t) (PR_SOURCE_KEY >> 16)] = true;
774         properties_exclusion[(uint16_t) (PR_PARENT_SOURCE_KEY >> 16)] = true;
775         properties_exclusion[(uint16_t) (PR_PARENT_FID >> 16)] = true;
776
777         /* 1b. Request exclusions */
778         if (excluded_tags != NULL) {
779                 for (i = 0; i < excluded_tags->cValues; i++) {
780                         properties_exclusion[(uint16_t) (excluded_tags->aulPropTag[i] >> 16)] = true;
781                 }
782         }
783
784         needed_properties = talloc_zero(mem_ctx, struct SPropTagArray);
785         needed_properties->aulPropTag = talloc_zero(needed_properties, void);
786         for (i = 0; i < properties->cValues; i++) {
787                 if (!properties_exclusion[(uint16_t) (properties->aulPropTag[i] >> 16)]) {
788                         SPropTagArray_add(mem_ctx, needed_properties, properties->aulPropTag[i]);
789                 }
790         }
791
792         data_pointers = emsmdbp_object_get_properties(mem_ctx, emsmdbp_ctx, source_object, needed_properties, &retvals);
793         if (data_pointers) {
794                 aRow = talloc_zero(mem_ctx, struct SRow);
795                 for (i = 0; i < needed_properties->cValues; i++) {
796                         if (retvals[i] == MAPI_E_SUCCESS) {
797                                 /* _PUBLIC_ enum MAPISTATUS SRow_addprop(struct SRow *aRow, struct SPropValue spropvalue) */
798                                 set_SPropValue_proptag(&newValue, needed_properties->aulPropTag[i], data_pointers[i]);
799                                 SRow_addprop(aRow, newValue);
800                         }
801                 }
802                 if (emsmdbp_object_set_properties(emsmdbp_ctx, dest_object, aRow) != MAPISTORE_SUCCESS) {
803                         talloc_free(mem_ctx);
804                         return MAPI_E_NO_SUPPORT;
805                 }
806         }
807         else {
808                 talloc_free(mem_ctx);
809                 return MAPI_E_NO_SUPPORT;
810         }
811
812         talloc_free(mem_ctx);
813
814         return MAPI_E_SUCCESS;
815 }
816
817 /* FIXME: this function is already present in oxcmsg... */
818 struct emsmdbp_prop_index {
819         uint32_t display_name; /* PR_DISPLAY_NAME_UNICODE or PR_7BIT_DISPLAY_NAME_UNICODE or PR_RECIPIENT_DISPLAY_NAME_UNICODE */
820         uint32_t email_address; /* PR_EMAIL_ADDRESS_UNICODE or PR_SMTP_ADDRESS_UNICODE */
821 };
822
823 static inline void emsmdbp_fill_prop_index(struct emsmdbp_prop_index *prop_index, struct SPropTagArray *properties)
824 {
825         if (SPropTagArray_find(*properties, PR_DISPLAY_NAME_UNICODE, &prop_index->display_name) == MAPI_E_NOT_FOUND
826             && SPropTagArray_find(*properties, PR_7BIT_DISPLAY_NAME_UNICODE, &prop_index->display_name) == MAPI_E_NOT_FOUND
827             && SPropTagArray_find(*properties, PR_RECIPIENT_DISPLAY_NAME_UNICODE, &prop_index->display_name) == MAPI_E_NOT_FOUND) {
828                 prop_index->display_name = (uint32_t) -1;
829         }
830         if (SPropTagArray_find(*properties, PR_EMAIL_ADDRESS_UNICODE, &prop_index->email_address) == MAPI_E_NOT_FOUND
831             && SPropTagArray_find(*properties, PR_SMTP_ADDRESS_UNICODE, &prop_index->email_address) == MAPI_E_NOT_FOUND) {
832                 prop_index->email_address = (uint32_t) -1;
833         }
834 }
835
836 static inline int emsmdbp_copy_message_recipients_mapistore(struct emsmdbp_context *emsmdbp_ctx, struct emsmdbp_object *source_object, struct emsmdbp_object *dest_object)
837 {
838         TALLOC_CTX                      *mem_ctx;
839         struct mapistore_message        *msg_data;
840         uint32_t                        contextID, i;
841         struct emsmdbp_prop_index       prop_index;
842         struct SPropTagArray            *new_columns;
843         void                            **new_data;
844
845         if (!emsmdbp_is_mapistore(source_object) || !emsmdbp_is_mapistore(dest_object)) {
846                 /* we silently fail for non-mapistore messages */
847                 return MAPI_E_SUCCESS;
848         }
849
850         /* Fetch data from source message */
851         mem_ctx = talloc_zero(NULL, TALLOC_CTX);
852         contextID = emsmdbp_get_contextID(source_object);
853         mapistore_message_get_message_data(emsmdbp_ctx->mstore_ctx, contextID, source_object->backend_object, mem_ctx, &msg_data);
854
855         /* By convention, we pass PR_DISPLAY_NAME_UNICODE and PR_EMAIL_ADDRESS_UNICODE to the backend, so we prepend them to each values array */
856         if (msg_data->recipients_count > 0
857             && (msg_data->columns->cValues < 2 || msg_data->columns->aulPropTag[0] != PR_DISPLAY_NAME_UNICODE || msg_data->columns->aulPropTag[1] != PR_EMAIL_ADDRESS_UNICODE)) {
858                 emsmdbp_fill_prop_index(&prop_index, msg_data->columns);
859
860                 new_columns = talloc_zero(mem_ctx, struct SPropTagArray);
861                 new_columns->cValues = msg_data->columns->cValues + 2;
862                 new_columns->aulPropTag = talloc_array(new_columns, enum MAPITAGS, new_columns->cValues);
863                 memcpy(new_columns->aulPropTag + 2, msg_data->columns->aulPropTag, sizeof(enum MAPITAGS) * msg_data->columns->cValues);
864                 new_columns->aulPropTag[0] = PR_DISPLAY_NAME_UNICODE;
865                 new_columns->aulPropTag[1] = PR_EMAIL_ADDRESS_UNICODE;
866
867                 for (i = 0; i < msg_data->recipients_count; i++) {
868                         new_data = talloc_array(mem_ctx, void *, new_columns->cValues);
869                         memcpy(new_data + 2, msg_data->recipients[i].data, sizeof(void *) * msg_data->columns->cValues);
870                         if (prop_index.display_name != (uint32_t) -1) {
871                                 new_data[0] = msg_data->recipients[i].data[prop_index.display_name];
872                         }
873                         else {
874                                 new_data[0] = NULL;
875                         }
876                         if (prop_index.email_address != (uint32_t) -1) {
877                                 new_data[1] = msg_data->recipients[i].data[prop_index.email_address];
878                         }
879                         else {
880                                 new_data[1] = NULL;
881                         }
882                         msg_data->recipients[i].data = new_data;
883                 }
884                 msg_data->columns = new_columns;
885
886                 /* Copy data into dest message */
887                 mapistore_message_modify_recipients(emsmdbp_ctx->mstore_ctx, contextID, dest_object->backend_object, msg_data->columns, msg_data->recipients_count, msg_data->recipients);
888         }
889
890         talloc_free(mem_ctx);
891
892         return MAPI_E_SUCCESS;
893 }
894
895 static inline int emsmdbp_copy_message_attachments_mapistore(struct emsmdbp_context *emsmdbp_ctx, struct emsmdbp_object *source_object, struct emsmdbp_object *dest_object)
896 {
897         TALLOC_CTX              *mem_ctx;
898         uint32_t                i, count, contextID, dest_num;
899         void                    **data_pointers;
900         enum MAPISTATUS         *retvals;
901         uint32_t                *attach_nums;
902         struct emsmdbp_object   *table_object, *source_attach, *dest_attach;
903         enum MAPITAGS           column;
904         int                     ret;
905
906         if (!emsmdbp_is_mapistore(source_object) || !emsmdbp_is_mapistore(dest_object)) {
907                 /* we silently fail for non-mapistore messages */
908                 return MAPI_E_SUCCESS;
909         }
910
911         mem_ctx = talloc_zero(NULL, TALLOC_CTX);
912
913         /* we fetch the attachment nums */
914         table_object = emsmdbp_object_message_open_attachment_table(mem_ctx, emsmdbp_ctx, source_object);
915         if (!table_object) {
916                 talloc_free(mem_ctx);
917                 return MAPI_E_NOT_FOUND;
918         }
919
920         column = PR_ATTACH_NUM;
921         table_object->object.table->prop_count = 1;
922         table_object->object.table->properties = &column;
923
924         contextID = emsmdbp_get_contextID(table_object);
925         mapistore_table_set_columns(emsmdbp_ctx->mstore_ctx, contextID, table_object->backend_object, 1, &column);
926
927         count = table_object->object.table->denominator;
928         attach_nums = talloc_array(mem_ctx, uint32_t, count);
929         for (i = 0; i < table_object->object.table->denominator; i++) {
930                 data_pointers = emsmdbp_object_table_get_row_props(mem_ctx, emsmdbp_ctx, table_object, i, MAPISTORE_PREFILTERED_QUERY, &retvals);
931                 if (!data_pointers) {
932                         talloc_free(mem_ctx);
933                         return MAPISTORE_ERROR;
934                 }
935                 if (retvals[0] != MAPI_E_SUCCESS) {
936                         talloc_free(mem_ctx);
937                         DEBUG(5, ("cannot copy attachments without PR_ATTACH_NUM\n"));
938                         return MAPISTORE_ERROR;
939                 }
940                 attach_nums[i] = *(uint32_t *) data_pointers[0];
941         }
942
943         /* we open each attachment manually and copy their props to created dest attachments */
944         for (i = 0; i < count; i++) {
945                 source_attach = emsmdbp_object_attachment_init(mem_ctx, emsmdbp_ctx, source_object->object.message->messageID, source_object);
946                 if (!source_attach
947                     || mapistore_message_open_attachment(emsmdbp_ctx->mstore_ctx, contextID, source_object->backend_object, source_attach, attach_nums[i], &source_attach->backend_object)) {
948                         talloc_free(mem_ctx);
949                         return MAPISTORE_ERROR;
950                 }
951
952                 dest_attach = emsmdbp_object_attachment_init(mem_ctx, emsmdbp_ctx, dest_object->object.message->messageID, dest_object);
953                 if (!dest_attach
954                     || mapistore_message_create_attachment(emsmdbp_ctx->mstore_ctx, contextID, dest_object->backend_object, dest_attach, &dest_attach->backend_object, &dest_num)) {
955                         talloc_free(mem_ctx);
956                         return MAPISTORE_ERROR;
957                 }
958
959                 ret = emsmdbp_copy_properties(emsmdbp_ctx, source_attach, dest_attach, NULL);
960                 if (ret != MAPI_E_SUCCESS) {
961                         talloc_free(mem_ctx);
962                         return ret;
963                 }
964         }
965
966         talloc_free(mem_ctx);
967
968         return MAPI_E_SUCCESS;
969 }
970
971 /**
972    \details Copy properties from an object to another object
973
974    \param emsmdbp_ctx pointer to the emsmdb provider context
975    \param source_object pointer to the source object
976    \param target_object pointer to the target object
977    \param excluded_properties pointer to a SPropTagArray listing properties that must not be copied
978    \param deep_copy indicates whether subobjects must be copied
979
980    \return Allocated emsmdbp object on success, otherwise NULL
981  */
982 _PUBLIC_ int emsmdbp_object_copy_properties(struct emsmdbp_context *emsmdbp_ctx, struct emsmdbp_object *source_object, struct emsmdbp_object *target_object, struct SPropTagArray *excluded_properties, bool deep_copy)
983 {
984         int ret;
985
986         if (!(source_object->type == EMSMDBP_OBJECT_FOLDER
987               || source_object->type == EMSMDBP_OBJECT_MAILBOX
988               || source_object->type == EMSMDBP_OBJECT_MESSAGE
989               || source_object->type == EMSMDBP_OBJECT_ATTACHMENT)) {
990                 DEBUG(0, (__location__": object must be EMSMDBP_OBJECT_FOLDER, EMSMDBP_OBJECT_MAILBOX, EMSMDBP_OBJECT_MESSAGE or EMSMDBP_OBJECT_ATTACHMENT (type =  %d)\n", source_object->type));
991                 ret = MAPI_E_NO_SUPPORT;
992                 goto end;
993         }
994         if (target_object->type != source_object->type) {
995                 DEBUG(0, ("source and destination objects type must match (type =  %d)\n", target_object->type));
996                 ret = MAPI_E_NO_SUPPORT;
997                 goto end;
998         }
999
1000         /* copy properties (common to all object types) */
1001         ret = emsmdbp_copy_properties(emsmdbp_ctx, source_object, target_object, excluded_properties);
1002         if (ret != MAPI_E_SUCCESS) {
1003                 goto end;
1004         }
1005
1006         /* type specific ops */
1007         switch (source_object->type) {
1008         case EMSMDBP_OBJECT_MESSAGE:
1009                 if (emsmdbp_is_mapistore(source_object) && emsmdbp_is_mapistore(target_object)) {
1010                         ret = emsmdbp_copy_message_recipients_mapistore(emsmdbp_ctx, source_object, target_object);
1011                         if (ret != MAPI_E_SUCCESS) {
1012                                 goto end;
1013                         }
1014                         if (deep_copy) {
1015                                 ret = emsmdbp_copy_message_attachments_mapistore(emsmdbp_ctx, source_object, target_object);
1016                                 if (ret != MAPI_E_SUCCESS) {
1017                                         goto end;
1018                                 }
1019                         }
1020                 }
1021                 else {
1022                         DEBUG(0, ("Cannot copy recipients or attachments to or from non-mapistore messages\n"));
1023                 }
1024                 break;
1025         default:
1026                 if (deep_copy) {
1027                         DEBUG(0, ("Cannot deep copy non-message objects\n"));
1028                 }
1029         }
1030
1031 end:
1032
1033         return ret;
1034 }
1035
1036 /**
1037    \details Initialize a mailbox object
1038
1039    \param mem_ctx pointer to the memory context
1040    \param emsmdbp_ctx pointer to the emsmdb provider context
1041    \param request pointer to the Logon MAPI request
1042    \param mailboxstore boolean which specifies whether the mailbox
1043    object is a PF store or a private mailbox store
1044
1045    \return Allocated emsmdbp object on success, otherwise NULL
1046  */
1047 _PUBLIC_ struct emsmdbp_object *emsmdbp_object_mailbox_init(TALLOC_CTX *mem_ctx,
1048                                                             struct emsmdbp_context *emsmdbp_ctx,
1049                                                             const char *essDN,
1050                                                             bool mailboxstore)
1051 {
1052         struct emsmdbp_object           *object;
1053         const char                      *displayName, *cn;
1054         const char * const              recipient_attrs[] = { "*", NULL };
1055         int                             ret;
1056         struct ldb_result               *res = NULL;
1057
1058         /* Sanity checks */
1059         if (!emsmdbp_ctx) return NULL;
1060         if (!essDN) return NULL;
1061
1062         object = emsmdbp_object_init(mem_ctx, emsmdbp_ctx, NULL);
1063         if (!object) return NULL;
1064
1065         /* Initialize the mailbox object */
1066         object->object.mailbox = talloc_zero(object, struct emsmdbp_object_mailbox);
1067         if (!object->object.mailbox) {
1068                 talloc_free(object);
1069                 return NULL;
1070         }
1071
1072         object->type = EMSMDBP_OBJECT_MAILBOX;
1073         object->object.mailbox->owner_Name = NULL;
1074         object->object.mailbox->owner_EssDN = NULL;
1075         object->object.mailbox->szUserDN = NULL;
1076         object->object.mailbox->folderID = 0x0;
1077         object->object.mailbox->mailboxstore = mailboxstore;
1078
1079         if (mailboxstore == true) {
1080                 object->object.mailbox->owner_EssDN = talloc_strdup(object->object.mailbox, essDN);
1081                 ret = ldb_search(emsmdbp_ctx->samdb_ctx, mem_ctx, &res,
1082                                  ldb_get_default_basedn(emsmdbp_ctx->samdb_ctx),
1083                                  LDB_SCOPE_SUBTREE, recipient_attrs, "legacyExchangeDN=%s", 
1084                                  object->object.mailbox->owner_EssDN);
1085                 if (!ret && res->count == 1) {
1086                         cn = ldb_msg_find_attr_as_string(res->msgs[0], "cn", NULL);
1087                         if (cn) {
1088                                 object->object.mailbox->owner_username = talloc_strdup(object->object.mailbox,  cn);
1089
1090                                 /* Retrieve Mailbox folder identifier */
1091                                 openchangedb_get_SystemFolderID(emsmdbp_ctx->oc_ctx, object->object.mailbox->owner_username,
1092                                                                 0x1, &object->object.mailbox->folderID);
1093                         }
1094                         displayName = ldb_msg_find_attr_as_string(res->msgs[0], "displayName", NULL);
1095                         if (displayName) {
1096                                 object->object.mailbox->owner_Name = talloc_strdup(object->object.mailbox, 
1097                                                                                    displayName);
1098                         }
1099                 }
1100         } else {
1101                 /* Retrieve Public folder identifier */
1102                 openchangedb_get_PublicFolderID(emsmdbp_ctx->oc_ctx, EMSMDBP_PF_ROOT, &object->object.mailbox->folderID);
1103         }
1104
1105         object->object.mailbox->szUserDN = talloc_strdup(object->object.mailbox, emsmdbp_ctx->szUserDN);
1106
1107         talloc_free(res);
1108
1109         return object;
1110 }
1111
1112
1113 /**
1114    \details Initialize a folder object
1115
1116    \param mem_ctx pointer to the memory context
1117    \param emsmdbp_ctx pointer to the emsmdb provider context
1118    \param folderID the folder identifier
1119    \param parent emsmdbp object of the parent folder for this folder
1120
1121    \return Allocated emsmdbp object on success, otherwise NULL
1122  */
1123 _PUBLIC_ struct emsmdbp_object *emsmdbp_object_folder_init(TALLOC_CTX *mem_ctx,
1124                                                            struct emsmdbp_context *emsmdbp_ctx,
1125                                                            uint64_t folderID,
1126                                                            struct emsmdbp_object *parent_object)
1127 {
1128         struct emsmdbp_object                   *object;
1129
1130         /* Sanity checks */
1131         if (!emsmdbp_ctx) return NULL;
1132
1133         object = emsmdbp_object_init(mem_ctx, emsmdbp_ctx, parent_object);
1134         if (!object) return NULL;
1135
1136         object->object.folder = talloc_zero(object, struct emsmdbp_object_folder);
1137         if (!object->object.folder) {
1138                 talloc_free(object);
1139                 return NULL;
1140         }
1141
1142         object->type = EMSMDBP_OBJECT_FOLDER;
1143         object->object.folder->folderID = folderID;
1144         object->object.folder->mapistore_root = false;
1145         object->object.folder->contextID = (uint32_t) -1;
1146  
1147         return object;
1148 }
1149
1150 int emsmdbp_folder_get_folder_count(struct emsmdbp_context *emsmdbp_ctx, struct emsmdbp_object *folder, uint32_t *row_countp)
1151 {
1152         int             retval;
1153         uint64_t        folderID;
1154
1155         if (emsmdbp_is_mapistore(folder)) {
1156                 retval = mapistore_folder_get_child_count(emsmdbp_ctx->mstore_ctx, emsmdbp_get_contextID(folder),
1157                                                           folder->backend_object, MAPISTORE_FOLDER_TABLE, row_countp);
1158         }
1159         else {
1160                 if (folder->type == EMSMDBP_OBJECT_FOLDER) {
1161                         folderID = folder->object.folder->folderID;
1162                 }
1163                 else if (folder->type == EMSMDBP_OBJECT_MAILBOX) {
1164                         folderID = folder->object.folder->folderID;
1165                 }
1166                 else {
1167                         DEBUG(5, ("unsupported object type\n"));
1168                         return MAPISTORE_ERROR;
1169                 }
1170                 printf("emsmdbp_folder_get_folder_count: folderID = %"PRIu64"\n", folderID);
1171                 retval = openchangedb_get_folder_count(emsmdbp_ctx->oc_ctx, folderID, row_countp);
1172         }
1173
1174         return retval;
1175 }
1176
1177 _PUBLIC_ enum mapistore_error emsmdbp_folder_delete(struct emsmdbp_context *emsmdbp_ctx, struct emsmdbp_object *parent_folder, uint64_t fid, uint8_t flags)
1178 {
1179         enum mapistore_error    ret;
1180         enum MAPISTATUS         mapiret;
1181         TALLOC_CTX              *mem_ctx;
1182         bool                    mailboxstore;
1183         uint32_t                context_id;
1184         void                    *subfolder;
1185         char                    *mapistoreURL;
1186
1187         mem_ctx = talloc_zero(NULL, TALLOC_CTX);
1188
1189         mailboxstore = emsmdbp_is_mailboxstore(parent_folder);
1190         if (emsmdbp_is_mapistore(parent_folder)) {      /* fid is not a mapistore root */
1191                 DEBUG(0, ("Deleting mapistore folder\n"));
1192                 /* handled by mapistore */
1193                 context_id = emsmdbp_get_contextID(parent_folder);
1194
1195                 ret = mapistore_folder_open_folder(emsmdbp_ctx->mstore_ctx, context_id, parent_folder->backend_object, mem_ctx, fid, &subfolder);
1196                 if (ret != MAPISTORE_SUCCESS) {
1197                         goto end;
1198                 }
1199
1200                 ret = mapistore_folder_delete(emsmdbp_ctx->mstore_ctx, context_id, subfolder, flags);
1201                 if (ret != MAPISTORE_SUCCESS) {
1202                         goto end;
1203                 }
1204         }
1205         else {
1206                 mapiret = openchangedb_get_mapistoreURI(mem_ctx, emsmdbp_ctx->oc_ctx, fid, &mapistoreURL, mailboxstore);
1207                 if (mapiret != MAPI_E_SUCCESS) {
1208                         ret = MAPISTORE_ERR_NOT_FOUND;
1209                         goto end;
1210                 }
1211
1212                 mapiret = openchangedb_delete_folder(emsmdbp_ctx->oc_ctx, fid);
1213                 if (mapiret != MAPI_E_SUCCESS) {
1214                         ret = MAPISTORE_ERR_NOT_FOUND;
1215                         goto end;
1216                 }
1217
1218                 if (mapistoreURL) {     /* fid is mapistore root */
1219                         ret = mapistore_search_context_by_uri(emsmdbp_ctx->mstore_ctx, mapistoreURL, &context_id, &subfolder);
1220                         if (ret == MAPISTORE_SUCCESS) {
1221                                 mapistore_add_context_ref_count(emsmdbp_ctx->mstore_ctx, context_id);
1222                         } else {
1223                                 ret = mapistore_add_context(emsmdbp_ctx->mstore_ctx, emsmdbp_ctx->username, mapistoreURL, fid, &context_id, &subfolder);
1224                                 if (ret != MAPISTORE_SUCCESS) {
1225                                         goto end;
1226                                 }
1227                         }
1228
1229                         ret = mapistore_folder_delete(emsmdbp_ctx->mstore_ctx, context_id, subfolder, flags);
1230                         if (ret != MAPISTORE_SUCCESS) {
1231                                 goto end;
1232                         }
1233
1234                          mapistore_del_context(emsmdbp_ctx->mstore_ctx, context_id);
1235                 }
1236         }
1237
1238         ret = MAPISTORE_SUCCESS;
1239
1240 end:
1241         talloc_free(mem_ctx);
1242
1243         return ret;
1244 }
1245
1246 _PUBLIC_ struct emsmdbp_object *emsmdbp_folder_open_table(TALLOC_CTX *mem_ctx, 
1247                                                           struct emsmdbp_object *parent_object, 
1248                                                           uint32_t table_type, uint32_t handle_id)
1249 {
1250         struct emsmdbp_object   *table_object;
1251         uint64_t                folderID;
1252         uint8_t                 mstore_type;
1253         int                     ret;
1254
1255         if (!(parent_object->type != EMSMDBP_OBJECT_FOLDER || parent_object->type != EMSMDBP_OBJECT_MAILBOX)) {
1256                 DEBUG(0, (__location__": parent_object must be EMSMDBP_OBJECT_FOLDER or EMSMDBP_OBJECT_MAILBOX (type =  %d)\n", parent_object->type));
1257                 return NULL;
1258         }
1259
1260         if (parent_object->type == EMSMDBP_OBJECT_FOLDER && parent_object->object.folder->postponed_props) {
1261                 emsmdbp_object_folder_commit_creation(parent_object->emsmdbp_ctx, parent_object, true);
1262         }
1263
1264         table_object = emsmdbp_object_table_init(mem_ctx, parent_object->emsmdbp_ctx, parent_object);
1265         if (table_object) {
1266                 table_object->object.table->handle = handle_id;
1267                 table_object->object.table->ulType = table_type;
1268                 if (emsmdbp_is_mapistore(parent_object)) {
1269                         switch (table_type) {
1270                         case MAPISTORE_MESSAGE_TABLE:
1271                                 mstore_type = MAPISTORE_MESSAGE_TABLE;
1272                                 break;
1273                         case MAPISTORE_FAI_TABLE:
1274                                 mstore_type = MAPISTORE_FAI_TABLE;
1275                                 break;
1276                         case MAPISTORE_FOLDER_TABLE:
1277                                 mstore_type = MAPISTORE_FOLDER_TABLE;
1278                                 break;
1279                         case MAPISTORE_PERMISSIONS_TABLE:
1280                                 mstore_type = MAPISTORE_PERMISSIONS_TABLE;
1281                                 break;
1282                         default:
1283                                 DEBUG(5, ("Unhandled table type for folders: %d\n", table_type));
1284                                 abort();
1285                         }
1286
1287                         ret = mapistore_folder_open_table(parent_object->emsmdbp_ctx->mstore_ctx, emsmdbp_get_contextID(parent_object), parent_object->backend_object, table_object, mstore_type, handle_id, &table_object->backend_object, &table_object->object.table->denominator);
1288                         if (ret != MAPISTORE_SUCCESS) {
1289                                 talloc_free(table_object);
1290                                 table_object = NULL;
1291                         }
1292                 }
1293                 else {
1294                         if (table_type == MAPISTORE_FOLDER_TABLE) {
1295                                 /* this gets data both for openchangedb and mapistore: needs improvement */
1296                                 emsmdbp_folder_get_folder_count(parent_object->emsmdbp_ctx, parent_object, &table_object->object.table->denominator);
1297                         }
1298                         else {
1299                                 /* Retrieve folder ID */
1300                                 switch (parent_object->type) {
1301                                 case EMSMDBP_OBJECT_FOLDER:
1302                                         folderID = parent_object->object.folder->folderID;
1303                                         break;
1304                                 case EMSMDBP_OBJECT_MAILBOX:
1305                                         folderID = parent_object->object.mailbox->folderID;
1306                                         break;
1307                                 default:
1308                                         DEBUG(5, ("Unsupported object type"));
1309                                         table_object->object.table->denominator = 0;
1310                                         return table_object;
1311                                 }
1312
1313                                 /* Non-mapistore message tables */
1314                                 switch (table_type) {
1315                                 case MAPISTORE_MESSAGE_TABLE:
1316                                         openchangedb_get_message_count(parent_object->emsmdbp_ctx->oc_ctx, 
1317                                                                        folderID, 
1318                                                                        &table_object->object.table->denominator,
1319                                                                        false);
1320                                         break;
1321                                 case MAPISTORE_FAI_TABLE:
1322                                         openchangedb_get_message_count(parent_object->emsmdbp_ctx->oc_ctx, 
1323                                                                        folderID, 
1324                                                                        &table_object->object.table->denominator,
1325                                                                        true);
1326                                         break;
1327                                 default:
1328                                         DEBUG(0, ("Unhandled openchangedb table type for folders: %d\n", table_type));
1329                                         table_object->object.table->denominator = 0;
1330                                         abort();
1331                                 }
1332                         }
1333                         if (!emsmdbp_is_mapistore(parent_object)) {
1334                                 /* Retrieve folder ID */
1335                                 switch (parent_object->type) {
1336                                 case EMSMDBP_OBJECT_FOLDER:
1337                                         folderID = parent_object->object.folder->folderID;
1338                                         break;
1339                                 case EMSMDBP_OBJECT_MAILBOX:
1340                                         folderID = parent_object->object.mailbox->folderID;
1341                                         break;
1342                                 default:
1343                                         DEBUG(5, ("Unsupported object type"));
1344                                         table_object->object.table->denominator = 0;
1345                                         return table_object;
1346                                 }
1347                                 DEBUG(0, ("Initializaing openchangedb table\n"));
1348                                 openchangedb_table_init((TALLOC_CTX *)table_object, table_type, folderID, &table_object->backend_object);
1349                         }
1350                 }
1351         }
1352
1353         return table_object;    
1354 }
1355
1356 /**
1357    \details Initialize a table object
1358
1359    \param mem_ctx pointer to the memory context
1360    \param emsmdbp_ctx pointer to the emsmdb provider context
1361    \param parent emsmdbp object of the parent
1362
1363    \return Allocated emsmdbp object on success, otherwise NULL
1364  */
1365 _PUBLIC_ struct emsmdbp_object *emsmdbp_object_table_init(TALLOC_CTX *mem_ctx,
1366                                                           struct emsmdbp_context *emsmdbp_ctx,
1367                                                           struct emsmdbp_object *parent)
1368 {
1369         struct emsmdbp_object   *object;
1370
1371         /* Sanity checks */
1372         if (!emsmdbp_ctx) return NULL;
1373         if (!parent) return NULL;
1374         if (parent->type != EMSMDBP_OBJECT_FOLDER && parent->type != EMSMDBP_OBJECT_MAILBOX && parent->type != EMSMDBP_OBJECT_MESSAGE) return NULL;
1375
1376         /* Initialize table object */
1377         object = emsmdbp_object_init(mem_ctx, emsmdbp_ctx, parent);
1378         if (!object) return NULL;
1379         
1380         object->object.table = talloc_zero(object, struct emsmdbp_object_table);
1381         if (!object->object.table) {
1382                 talloc_free(object);
1383                 return NULL;
1384         }
1385
1386         object->type = EMSMDBP_OBJECT_TABLE;
1387         object->object.table->prop_count = 0;
1388         object->object.table->properties = NULL;
1389         object->object.table->numerator = 0;
1390         object->object.table->denominator = 0;
1391         object->object.table->ulType = 0;
1392         object->object.table->restricted = false;
1393         object->object.table->subscription_list = NULL;
1394
1395         return object;
1396 }
1397
1398 _PUBLIC_ int emsmdbp_object_table_get_available_properties(TALLOC_CTX *mem_ctx, struct emsmdbp_context *emsmdbp_ctx, struct emsmdbp_object *table_object, struct SPropTagArray **propertiesp)
1399 {
1400         int                             retval;
1401         struct SPropTagArray            *properties;
1402         uint32_t                        contextID;
1403
1404         if (!table_object->type == EMSMDBP_OBJECT_TABLE) {
1405                 return MAPISTORE_ERROR;
1406         }
1407
1408         if (emsmdbp_is_mapistore(table_object)) {
1409                 contextID = emsmdbp_get_contextID(table_object);
1410                 retval = mapistore_table_get_available_properties(emsmdbp_ctx->mstore_ctx, contextID, table_object->backend_object, mem_ctx, propertiesp);
1411         }
1412         else {
1413                 properties = talloc_zero(mem_ctx, struct SPropTagArray);
1414                 properties->aulPropTag = talloc_zero(properties, enum MAPITAGS);
1415                 /* TODO: this list might not be complete */
1416                 SPropTagArray_add(properties, properties, PR_FID);
1417                 SPropTagArray_add(properties, properties, PR_PARENT_FID);
1418                 SPropTagArray_add(properties, properties, PR_DISPLAY_NAME_UNICODE);
1419                 SPropTagArray_add(properties, properties, PR_COMMENT_UNICODE);
1420                 SPropTagArray_add(properties, properties, PR_ACCESS);
1421                 SPropTagArray_add(properties, properties, PR_CREATION_TIME);
1422                 SPropTagArray_add(properties, properties, PR_NTSD_MODIFICATION_TIME);
1423                 SPropTagArray_add(properties, properties, PR_LAST_MODIFICATION_TIME);
1424                 SPropTagArray_add(properties, properties, PR_ADDITIONAL_REN_ENTRYIDS);
1425                 SPropTagArray_add(properties, properties, PR_ADDITIONAL_REN_ENTRYIDS_EX);
1426                 SPropTagArray_add(properties, properties, PR_CREATOR_SID);
1427                 SPropTagArray_add(properties, properties, PR_LAST_MODIFIER_SID);
1428                 SPropTagArray_add(properties, properties, PR_ATTR_HIDDEN);
1429                 SPropTagArray_add(properties, properties, PR_ATTR_SYSTEM);
1430                 SPropTagArray_add(properties, properties, PR_ATTR_READONLY);
1431                 SPropTagArray_add(properties, properties, PR_EXTENDED_ACL_DATA);
1432                 SPropTagArray_add(properties, properties, PR_CONTAINER_CLASS_UNICODE);
1433                 SPropTagArray_add(properties, properties, PR_MESSAGE_CLASS_UNICODE);
1434                 SPropTagArray_add(properties, properties, PR_RIGHTS);
1435                 SPropTagArray_add(properties, properties, PR_CONTENT_COUNT);
1436                 SPropTagArray_add(properties, properties, PidTagAssociatedContentCount);
1437                 SPropTagArray_add(properties, properties, PR_SUBFOLDERS);
1438                 SPropTagArray_add(properties, properties, PR_MAPPING_SIGNATURE);
1439                 SPropTagArray_add(properties, properties, PR_USER_ENTRYID);
1440                 SPropTagArray_add(properties, properties, PR_MAILBOX_OWNER_ENTRYID);
1441                 SPropTagArray_add(properties, properties, PR_MAILBOX_OWNER_NAME_UNICODE);
1442                 SPropTagArray_add(properties, properties, PR_IPM_APPOINTMENT_ENTRYID);
1443                 SPropTagArray_add(properties, properties, PR_IPM_CONTACT_ENTRYID);
1444                 SPropTagArray_add(properties, properties, PR_IPM_JOURNAL_ENTRYID);
1445                 SPropTagArray_add(properties, properties, PR_IPM_NOTE_ENTRYID);
1446                 SPropTagArray_add(properties, properties, PR_IPM_TASK_ENTRYID);
1447                 SPropTagArray_add(properties, properties, PR_IPM_DRAFTS_ENTRYID);
1448                 SPropTagArray_add(properties, properties, PR_REMINDERS_ONLINE_ENTRYID);
1449                 SPropTagArray_add(properties, properties, PR_IPM_PUBLIC_FOLDERS_ENTRYID);
1450                 SPropTagArray_add(properties, properties, PR_FOLDER_XVIEWINFO_E);
1451                 SPropTagArray_add(properties, properties, PR_FOLDER_VIEWLIST);
1452                 SPropTagArray_add(properties, properties, PR_FREEBUSY_ENTRYIDS);
1453                 *propertiesp = properties;
1454
1455                 retval = MAPISTORE_SUCCESS;
1456         }
1457
1458         return retval;
1459 }
1460
1461 _PUBLIC_ void **emsmdbp_object_table_get_row_props(TALLOC_CTX *mem_ctx, struct emsmdbp_context *emsmdbp_ctx, struct emsmdbp_object *table_object, uint32_t row_id, enum mapistore_query_type query_type, enum MAPISTATUS **retvalsp)
1462 {
1463         void                            **data_pointers;
1464         enum MAPISTATUS                 retval;
1465         enum mapistore_error            ret;
1466         enum MAPISTATUS                 *retvals;
1467         struct emsmdbp_object_table     *table;
1468         struct mapistore_property_data  *properties;
1469         uint32_t                        contextID, i, num_props, *obj_count;
1470         struct emsmdbp_object           *rowobject;
1471         uint64_t                        *rowFMId;
1472         uint64_t                        parentFolderId;
1473         bool                            mapistore_folder;
1474         uint8_t                         *has_subobj;
1475         void                            *odb_ctx;
1476         char                            *owner;
1477         struct Binary_r                 *binr;
1478
1479         table = table_object->object.table;
1480         num_props = table_object->object.table->prop_count;
1481
1482         data_pointers = talloc_array(mem_ctx, void *, num_props);
1483         memset(data_pointers, 0, sizeof(void *) * num_props);
1484         retvals = talloc_array(mem_ctx, enum MAPISTATUS, num_props);
1485         memset(retvals, 0, sizeof(uint32_t) * num_props);
1486
1487         contextID = emsmdbp_get_contextID(table_object);
1488         if (emsmdbp_is_mapistore(table_object)) {
1489                 retval = mapistore_table_get_row(emsmdbp_ctx->mstore_ctx, contextID,
1490                                                  table_object->backend_object, data_pointers,
1491                                                  query_type, row_id, &properties);
1492                 if (retval == MAPI_E_SUCCESS) {
1493                         for (i = 0; i < num_props; i++) {
1494                                 data_pointers[i] = properties[i].data;
1495                                         
1496                                 if (properties[i].error) {
1497                                         if (properties[i].error == MAPISTORE_ERR_NOT_FOUND)
1498                                                 retvals[i] = MAPI_E_NOT_FOUND;
1499                                         else if (properties[i].error == MAPISTORE_ERR_NO_MEMORY)
1500                                                 retvals[i] = MAPI_E_NOT_ENOUGH_MEMORY;
1501                                         else {
1502                                                 retvals[i] = MAPI_E_NO_SUPPORT;
1503                                                 DEBUG (4, ("%s: unknown mapistore error: %.8x\n", __PRETTY_FUNCTION__, properties[i].error));
1504                                         }
1505                                 }
1506                                 else {
1507                                         if (properties[i].data == NULL) {
1508                                                 retvals[i] = MAPI_E_NOT_FOUND;
1509                                         }
1510                                 }
1511                         }
1512                 }
1513                 else {
1514                         DEBUG(5, ("%s: invalid object (likely due to a restriction)\n", __location__));
1515                         talloc_free(retvals);
1516                         talloc_free(data_pointers);
1517                         return NULL;
1518                 }
1519         } else {
1520                 if (table_object->parent_object->type == EMSMDBP_OBJECT_FOLDER) {
1521                         parentFolderId = table_object->parent_object->object.folder->folderID;
1522                 }
1523                 else if (table_object->parent_object->type == EMSMDBP_OBJECT_MAILBOX) {
1524                         parentFolderId = table_object->parent_object->object.mailbox->folderID;
1525                 }
1526                 else {
1527                         DEBUG(5, ("%s: non-mapistore tables can only be client of folder objects\n", __location__));
1528                         talloc_free(retvals);
1529                         talloc_free(data_pointers);
1530                         return NULL;
1531                 }
1532
1533                 odb_ctx = talloc_zero(NULL, void);
1534
1535                 /* Setup table_filter for openchangedb */
1536                 /* switch (table_object->object.table->ulType) { */
1537                 /* case MAPISTORE_MESSAGE_TABLE: */
1538                 /*      table_filter = talloc_asprintf(odb_ctx, "(&(PidTagParentFolderId=%"PRIu64")(PidTagMessageId=*))", folderID); */
1539                 /*      break; */
1540                 /* case MAPISTORE_FOLDER_TABLE: */
1541                 /*      table_filter = talloc_asprintf(odb_ctx, "(&(PidTagParentFolderId=%"PRIu64")(PidTagFolderId=*))", folderID); */
1542                 /*      break; */
1543                 /* default: */
1544                 /*      DEBUG(5, ("[%s:%d]: Unsupported table type for openchangedb: %d\n", __FUNCTION__, __LINE__,  */
1545                 /*                    table_object->object.table->ulType)); */
1546                 /*      talloc_free(retvals); */
1547                 /*      talloc_free(data_pointers); */
1548                 /*      return NULL; */
1549                 /* } */
1550
1551                 /* 1. retrieve the object id from odb */
1552                 switch (table_object->object.table->ulType) {
1553                 case MAPISTORE_FOLDER_TABLE:
1554                         retval = openchangedb_table_get_property(odb_ctx, table_object->backend_object, emsmdbp_ctx->oc_ctx, PR_FID, row_id, (query_type == MAPISTORE_LIVEFILTERED_QUERY), (void **) &rowFMId);
1555                         break;
1556                 case MAPISTORE_MESSAGE_TABLE:
1557                         retval = openchangedb_table_get_property(odb_ctx, table_object->backend_object, emsmdbp_ctx->oc_ctx, PR_MID, row_id, (query_type == MAPISTORE_LIVEFILTERED_QUERY), (void **) &rowFMId);
1558                         break;
1559                         /* case MAPISTORE_FAI_TABLE: 
1560                            retval = openchangedb_table_get_property(odb_ctx, table_object->backend_object, emsmdbp_ctx->oc_ctx,
1561                            PR_MID, row_id, (query_type == MAPISTORE_LIVEFILTERED_QUERY), (void **) &rowFMId);
1562                            break; */
1563                 default:
1564                         DEBUG(5, ("table type %d not supported for non-mapistore table\n", table_object->object.table->ulType));
1565                         retval = MAPI_E_INVALID_OBJECT;
1566                 }
1567                 /* printf("openchangedb_table_get_property retval = 0x%.8x\n", retval); */
1568                 if (retval != MAPI_E_SUCCESS) {
1569                         talloc_free(retvals);
1570                         talloc_free(data_pointers);
1571                         talloc_free(odb_ctx);
1572                         return NULL;
1573                 }
1574
1575                 /* 2. open the corresponding object */
1576                 switch (table_object->object.table->ulType) {
1577                 case MAPISTORE_FOLDER_TABLE:
1578                         ret = emsmdbp_object_open_folder(odb_ctx, table_object->parent_object->emsmdbp_ctx, table_object->parent_object, *(uint64_t *)rowFMId, &rowobject);
1579                         mapistore_folder = emsmdbp_is_mapistore(rowobject);
1580                         break;
1581                 case MAPISTORE_MESSAGE_TABLE:
1582                         ret = emsmdbp_object_message_open(odb_ctx, table_object->parent_object->emsmdbp_ctx, table_object->parent_object, parentFolderId, *(uint64_t *)rowFMId, false, &rowobject, NULL);
1583                         mapistore_folder = false;
1584                         break;
1585                 default:
1586                         DEBUG(5, ("you should never get here\n"));
1587                         abort();
1588                 }
1589                 if (ret != MAPISTORE_SUCCESS) {
1590                         talloc_free(retvals);
1591                         talloc_free(data_pointers);
1592                         talloc_free(odb_ctx);
1593                         return NULL;
1594                 }
1595
1596                 /* read the row properties */
1597                 retval = MAPI_E_SUCCESS;
1598                 for (i = 0; retval != MAPI_E_INVALID_OBJECT && i < num_props; i++) {
1599                         if (mapistore_folder) {
1600                                 /* a hack to avoid fetching dynamic fields from openchange.ldb */
1601                                 switch (table->properties[i]) {
1602                                 case PR_CONTENT_COUNT:
1603                                         obj_count = talloc_zero(data_pointers, uint32_t);
1604                                         retval = mapistore_folder_get_child_count(emsmdbp_ctx->mstore_ctx, contextID, rowobject,
1605                                                                                   MAPISTORE_MESSAGE_TABLE, obj_count);
1606                                         data_pointers[i] = obj_count;
1607                                         break;
1608                                 case PidTagAssociatedContentCount:
1609                                         obj_count = talloc_zero(data_pointers, uint32_t);
1610                                         retval = mapistore_folder_get_child_count(emsmdbp_ctx->mstore_ctx, contextID, rowobject,
1611                                                                                   MAPISTORE_FAI_TABLE, obj_count);
1612                                         data_pointers[i] = obj_count;
1613                                         break;
1614                                 case PidTagFolderChildCount:
1615                                         obj_count = talloc_zero(data_pointers, uint32_t);
1616                                         retval = emsmdbp_folder_get_folder_count(emsmdbp_ctx, rowobject, obj_count);
1617                                         data_pointers[i] = obj_count;
1618                                         break;
1619                                 case PidTagSourceKey:
1620                                         owner = emsmdbp_get_owner(table_object);
1621                                         emsmdbp_source_key_from_fmid(data_pointers, emsmdbp_ctx, owner, rowobject->object.folder->folderID, &binr);
1622                                         data_pointers[i] = binr;
1623                                         break;
1624                                 case PR_SUBFOLDERS:
1625                                         obj_count = talloc_zero(NULL, uint32_t);
1626                                         retval = emsmdbp_folder_get_folder_count(emsmdbp_ctx, rowobject, obj_count);
1627                                         has_subobj = talloc_zero(data_pointers, uint8_t);
1628                                         *has_subobj = (*obj_count > 0) ? 1 : 0;
1629                                         data_pointers[i] = has_subobj;
1630                                         talloc_free(obj_count);
1631                                         break;
1632                                 case PR_CONTENT_UNREAD:
1633                                 case PidTagDeletedCountTotal:
1634                                         /* TODO: temporary */
1635                                         obj_count = talloc_zero(data_pointers, uint32_t);
1636                                         data_pointers[i] = obj_count;
1637                                         retval = MAPI_E_SUCCESS;
1638                                         break;
1639                                 default:
1640                                         retval = openchangedb_table_get_property(data_pointers, table_object->backend_object, 
1641                                                                                  emsmdbp_ctx->oc_ctx,
1642                                                                                  table->properties[i], 
1643                                                                                  row_id,
1644                                                                                  (query_type == MAPISTORE_LIVEFILTERED_QUERY),
1645                                                                                  data_pointers + i);
1646                                         /* retval = openchangedb_get_table_property(data_pointers, emsmdbp_ctx->oc_ctx,  */
1647                                         /*                                       emsmdbp_ctx->username, */
1648                                         /*                                       table_filter, table->properties[i],  */
1649                                         /*                                       row_id, data_pointers + i); */
1650                                 }
1651                         }
1652                         else {
1653                                 retval = openchangedb_table_get_property(data_pointers, table_object->backend_object, 
1654                                                                          emsmdbp_ctx->oc_ctx,
1655                                                                          table->properties[i], 
1656                                                                          row_id,
1657                                                                          (query_type == MAPISTORE_LIVEFILTERED_QUERY),
1658                                                                          data_pointers + i);
1659                         }
1660
1661                         /* DEBUG(5, ("  %.8x: %d", table->properties[j], retval)); */
1662                         if (retval == MAPI_E_INVALID_OBJECT) {
1663                                 DEBUG(5, ("%s: invalid object in non-mapistore folder, count set to 0\n", __location__));
1664                                 talloc_free(retvals);
1665                                 talloc_free(data_pointers);
1666                                 talloc_free(odb_ctx);
1667                                 return NULL;
1668                         }
1669                         else {
1670                                 retvals[i] = retval;
1671                         }
1672                 }
1673
1674                 talloc_free(odb_ctx);
1675         }
1676
1677         if (retvalsp) {
1678                 *retvalsp = retvals;
1679         }
1680
1681         return data_pointers;
1682 }
1683
1684 _PUBLIC_ void emsmdbp_fill_table_row_blob(TALLOC_CTX *mem_ctx, struct emsmdbp_context *emsmdbp_ctx,
1685                                           DATA_BLOB *table_row, uint16_t num_props,
1686                                           enum MAPITAGS *properties,
1687                                           void **data_pointers, enum MAPISTATUS *retvals)
1688 {
1689         uint16_t i;
1690         uint8_t flagged;
1691         enum MAPITAGS property;
1692         void *data;
1693         uint32_t retval;
1694
1695         flagged = 0;
1696
1697         for (i = 0; !flagged && i < num_props; i++) {
1698                 if (retvals[i] != MAPI_E_SUCCESS) {
1699                         flagged = 1;
1700                 }
1701         }
1702
1703         if (flagged) {
1704                 libmapiserver_push_property(mem_ctx,
1705                                             0x0000000b, (const void *)&flagged,
1706                                             table_row, 0, 0, 0);
1707         }
1708         else {
1709                 libmapiserver_push_property(mem_ctx,
1710                                             0x00000000, (const void *)&flagged,
1711                                             table_row, 0, 1, 0);
1712         }
1713
1714         for (i = 0; i < num_props; i++) {
1715                 property = properties[i];
1716                 retval = retvals[i];
1717                 if (retval != MAPI_E_SUCCESS) {
1718                         property = (property & 0xFFFF0000) + PT_ERROR;
1719                         data = &retval;
1720                 }
1721                 else {
1722                         data = data_pointers[i];
1723                 }
1724
1725                 libmapiserver_push_property(mem_ctx,
1726                                             property, data, table_row,
1727                                             flagged?PT_ERROR:0, flagged, 0);
1728         }
1729 }
1730
1731 /**
1732    \details Initialize a message object
1733
1734    \param mem_ctx pointer to the memory context
1735    \param emsmdbp_ctx pointer to the emsmdb provider context
1736    \param messageID the message identifier
1737    \param parent emsmdbp object of the parent
1738
1739    \return Allocated emsmdbp object on success, otherwise NULL
1740  */
1741 _PUBLIC_ struct emsmdbp_object *emsmdbp_object_message_init(TALLOC_CTX *mem_ctx,
1742                                                             struct emsmdbp_context *emsmdbp_ctx,
1743                                                             uint64_t messageID,
1744                                                             struct emsmdbp_object *parent)
1745 {
1746         struct emsmdbp_object   *object;
1747
1748         /* Sanity checks */
1749         if (!emsmdbp_ctx) return NULL;
1750         if (!parent) return NULL;
1751         if (parent->type != EMSMDBP_OBJECT_FOLDER && parent->type != EMSMDBP_OBJECT_MAILBOX) {
1752                 DEBUG(5, ("expecting EMSMDBP_OBJECT_FOLDER/_MAILBOX as type of parent object\n"));
1753                 return NULL;
1754         }
1755
1756         /* Initialize message object */
1757         object = emsmdbp_object_init(mem_ctx, emsmdbp_ctx, parent);
1758         if (!object) return NULL;
1759
1760         object->object.message = talloc_zero(object, struct emsmdbp_object_message);
1761         if (!object->object.message) {
1762                 talloc_free(object);
1763                 return NULL;
1764         }
1765
1766         object->type = EMSMDBP_OBJECT_MESSAGE;
1767         object->object.message->messageID = messageID;
1768         object->object.message->read_write = false;
1769
1770         return object;
1771 }
1772
1773 static int emsmdbp_days_in_month(int month, int year)
1774 {
1775         static int      max_mdays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
1776         int             dec_year, days;
1777
1778         if (month == 1) {
1779                 dec_year = year % 100;
1780                 if ((dec_year == 0
1781                      && ((((year + 1900) / 100) % 4) == 0))
1782                     || (dec_year % 4) == 0) {
1783                         days = 29;
1784                 }
1785                 else {
1786                         days = max_mdays[month];
1787                 }
1788         }
1789         else {
1790                 days = max_mdays[month];
1791         }
1792
1793         return days;
1794 }
1795
1796 static int emsmdbp_mins_in_ymon(uint32_t ymon)
1797 {
1798         return emsmdbp_days_in_month((ymon & 0xf) - 1, ymon >> 4) * 24 * 60;
1799 }
1800
1801 static inline void emsmdbp_freebusy_make_range(struct tm *start_time, struct tm *end_time)
1802 {
1803         time_t                                                  now;
1804         struct tm                                               time_data;
1805         int                                                     mw_delta, month;
1806
1807         /* (from OXOPFFB - 3.1.4.1.1)
1808            Start of range is 12:00 A.M. UTC on the first day of the month or the first day of the week, whichever occurs earlier at the time of publishing.
1809            End of range is calculated by adding the value of the PidTagFreeBusyCountMonths property ([MS-OXOCAL] section 2.2.12.1) to start of range.
1810
1811            Since PidTagFreeBusyCountMonths is not supported yet, we use a count of 3 months
1812         */
1813
1814         now = time(NULL);
1815         time_data = *gmtime(&now);
1816         time_data.tm_hour = 0;
1817         time_data.tm_min = 0;
1818         time_data.tm_sec = 0;
1819
1820         /* take the first day of the week OR the first day of the month */
1821         month = time_data.tm_mon;
1822         if (time_data.tm_mday < 7) {
1823                 mw_delta = (time_data.tm_wday + 1 - time_data.tm_mday);
1824                 if (mw_delta > 0) {
1825                         if (time_data.tm_mon > 0) {
1826                                 time_data.tm_mon--;
1827                         }
1828                         else {
1829                                 time_data.tm_mon = 11;
1830                                 time_data.tm_year--;
1831                         }
1832                         time_data.tm_mday = emsmdbp_days_in_month(time_data.tm_mon, time_data.tm_year) + 1 - mw_delta;
1833                 }
1834                 else {
1835                         time_data.tm_mday = 1;
1836                 }
1837         }
1838         else {
1839                 mw_delta = 0;
1840                 time_data.tm_mday = 1;
1841         }
1842
1843         *start_time = time_data;
1844
1845         time_data.tm_mon = month + 2;
1846         if (time_data.tm_mon > 11) {
1847                 time_data.tm_year++;
1848                 time_data.tm_mon -= 12;
1849         }
1850         time_data.tm_mday = emsmdbp_days_in_month(time_data.tm_mon, time_data.tm_year) + 1 - mw_delta;
1851         time_data.tm_hour = 23;
1852         time_data.tm_min = 59;
1853         time_data.tm_sec = 59;
1854
1855         *end_time = time_data;
1856 }
1857
1858 static void emsmdbp_freebusy_convert_filetime(struct FILETIME *ft_value, uint32_t *ymon, uint32_t *mins)
1859 {
1860         NTTIME          nt_time;
1861         time_t          u_time;
1862         struct tm       *gm_time;
1863
1864         nt_time = ((NTTIME) ft_value->dwHighDateTime << 32) | ft_value->dwLowDateTime;
1865         u_time = nt_time_to_unix(nt_time);
1866         gm_time = gmtime(&u_time);
1867
1868         *ymon = ((gm_time->tm_year + 1900) << 4) | (gm_time->tm_mon + 1);
1869         *mins = gm_time->tm_min + (gm_time->tm_hour + ((gm_time->tm_mday - 1) * 24)) * 60;
1870 }
1871
1872 static uint16_t emsmdbp_freebusy_find_month_range(uint32_t ymon, uint32_t *months_ranges, uint16_t nbr_months, bool *overflow)
1873 {
1874         uint16_t        range;
1875
1876         if (nbr_months > 0) {
1877                 if (months_ranges[0] > ymon) {
1878                         *overflow = true;
1879                         return 0;
1880                 }
1881                 else {
1882                         if (months_ranges[nbr_months - 1] < ymon) {
1883                                 *overflow = true;
1884                                 return (nbr_months - 1);
1885                         }
1886                         else {
1887                                 *overflow = false;
1888                                 for (range = 0; range < nbr_months; range++) {
1889                                         if (months_ranges[range] == ymon) {
1890                                                 return range;
1891                                         }
1892                                 }
1893                         }
1894                 }
1895         }
1896
1897         return (uint16_t) -1;
1898 }
1899
1900 /* TODO: both following methods could be merged. This would certainly enhance performance by avoiding to wander through long arrays multiple times */
1901 static void emsmdbp_freebusy_fill_fbarray(uint8_t **minutes_array, uint32_t *months_ranges, uint16_t nbr_months, struct FILETIME *start, struct FILETIME *end)
1902 {
1903         uint32_t        i, max, start_ymon, start_mins, end_ymon, end_mins;
1904         uint16_t        start_mr_idx, end_mr_idx;
1905         bool            start_range_overflow, end_range_overflow;
1906
1907         emsmdbp_freebusy_convert_filetime(start, &start_ymon, &start_mins);
1908         emsmdbp_freebusy_convert_filetime(end, &end_ymon, &end_mins);
1909
1910         start_mr_idx = emsmdbp_freebusy_find_month_range(start_ymon, months_ranges, nbr_months, &start_range_overflow);
1911         if (start_range_overflow) {
1912                 start_mins = 0;
1913         }
1914         end_mr_idx = emsmdbp_freebusy_find_month_range(end_ymon, months_ranges, nbr_months, &end_range_overflow);
1915         if (end_range_overflow) {
1916                 end_mins = emsmdbp_mins_in_ymon(end_ymon);
1917         }
1918
1919         /* head */
1920         if (end_mr_idx > start_mr_idx) {
1921                 /* end occurs after start range */
1922
1923                 /* middle */
1924                 for (i = start_mr_idx + 1; i < end_mr_idx; i++) {
1925                         memset(minutes_array[i], 1, emsmdbp_mins_in_ymon(months_ranges[i]));
1926                 }
1927
1928                 /* tail */
1929                 memset(minutes_array[end_mr_idx], 1, end_mins);
1930
1931                 max = emsmdbp_mins_in_ymon(start_ymon); /* = max chunk for first range */
1932         }
1933         else {
1934                 /* end occurs on same range as start */
1935
1936                 max = end_mins;
1937         }
1938         memset(minutes_array[start_mr_idx] + start_mins, 1, (max - start_mins));
1939 }
1940
1941 static void emsmdbp_freebusy_compile_fbarray(TALLOC_CTX *mem_ctx, uint8_t *minutes_array, struct Binary_r *fb_bin)
1942 {
1943         int                     i;
1944         bool                    filled;
1945         struct ndr_push         *ndr;
1946         TALLOC_CTX              *local_mem_ctx;
1947
1948         local_mem_ctx = talloc_zero(NULL, TALLOC_CTX);
1949
1950         ndr = ndr_push_init_ctx(local_mem_ctx);
1951
1952         filled = (minutes_array[0] != 0);
1953         if (filled) {
1954                 ndr_push_uint16(ndr, NDR_SCALARS, 0);
1955         }
1956
1957         for (i = 1; i < max_mins_per_month; i++) {
1958                 if (filled && !minutes_array[i]) {
1959                         ndr_push_uint16(ndr, NDR_SCALARS, (i - 1));
1960                         filled = false;
1961                 }
1962                 else if (!filled && minutes_array[i]) {
1963                         ndr_push_uint16(ndr, NDR_SCALARS, i);
1964                         filled = true;
1965                 }
1966         }
1967         if (filled) {
1968                 ndr_push_uint16(ndr, NDR_SCALARS, (max_mins_per_month - 1));
1969         }
1970
1971         fb_bin->cb = ndr->offset;
1972         fb_bin->lpb = ndr->data;
1973         (void) talloc_reference(mem_ctx, fb_bin->lpb);
1974
1975         talloc_free(local_mem_ctx);
1976 }
1977
1978 static void emsmdbp_freebusy_merge_subarray(uint8_t *minutes_array, uint8_t *included_array)
1979 {
1980         int i;
1981
1982         for (i = 0; i < max_mins_per_month; i++) {
1983                 if (included_array[i]) {
1984                         minutes_array[i] = 1;
1985                 }
1986         }
1987 }
1988
1989 static void emsmdbp_object_message_fill_freebusy_properties(struct emsmdbp_object *message_object)
1990 {
1991         /* freebusy mechanism:
1992            - lookup events in range now + 3 months, requesting end date, start date and PidLidBusyStatus
1993            - fill (olTentative) PidTagScheduleInfoMonthsTentative,
1994            PidTagScheduleInfoFreeBusyTentative
1995            - fill (olBusy) PidTagScheduleInfoMonthsBusy,
1996            PidTagScheduleInfoFreeBusyBusy
1997            - fill (olOutOfOffice) PidTagScheduleInfoMonthsAway,
1998            PidTagScheduleInfoFreeBusyAway
1999            - fill (olBusy + olOutOfOffice) PidTagScheduleInfoMonthsMerged,
2000            PidTagScheduleInfoFreeBusyMerged
2001            - fill PidTagFreeBusyPublishStart, PidTagFreeBusyPublishEnd and
2002            PidTagFreeBusyRangeTimestamp.
2003            - fill PidTagFreeBusyMessageEmailAddress */
2004
2005         TALLOC_CTX                                              *mem_ctx;
2006         struct emsmdbp_object_message_freebusy_properties       *fb_props;
2007         char                                                    *subject, *email, *username, *tmp;
2008         struct SPropTagArray                                    *props;
2009         void                                                    **data_pointers;
2010         enum MAPISTATUS                                         *retvals = NULL;
2011         struct emsmdbp_object                                   *mailbox, *inbox, *calendar, *table;
2012         uint64_t                                                inboxFID, calendarFID;
2013         uint32_t                                                contextID;
2014         struct mapi_SRestriction                                and_res;
2015         uint8_t                                                 state;
2016         struct tm                                               start_tm, end_tm;
2017         time_t                                                  start_time, end_time;
2018         NTTIME                                                  nt_time;
2019         struct mapi_SRestriction_and                            time_restrictions[2];
2020         int                                                     i, month, nbr_months;
2021         uint8_t                                                 **minutes_array, **tentative_array, **busy_array, **oof_array;
2022         char                                                    *tz;
2023
2024         mem_ctx = talloc_zero(NULL, TALLOC_CTX);
2025
2026         /* 1. retrieve subject and deduce username */
2027         props = talloc_zero(mem_ctx, struct SPropTagArray);
2028         props->cValues = 1;
2029         props->aulPropTag = talloc_zero(props, enum MAPITAGS);
2030         props->aulPropTag[0] = PR_NORMALIZED_SUBJECT_UNICODE;
2031         data_pointers = emsmdbp_object_get_properties(mem_ctx, message_object->emsmdbp_ctx, message_object, props, &retvals);
2032         if (!data_pointers || retvals[0] != MAPI_E_SUCCESS) {
2033                 goto end;
2034         }
2035         subject = data_pointers[0];
2036         // format is "..../CN="
2037         username = strrchr(subject, '/');
2038         if (!username) {
2039                 goto end;
2040         }
2041         username += 4;
2042         username = talloc_strdup(mem_ctx, username);
2043
2044         fb_props = talloc_zero(message_object, struct emsmdbp_object_message_freebusy_properties);
2045         message_object->object.message->fb_properties = fb_props;
2046
2047         // WARNING: the mechanism here will fail if username is not all lower-case, as LDB does not support case-insensitive queries
2048         tmp = username;
2049         while (*tmp) {
2050                 *tmp = tolower(*tmp);
2051                 tmp++;
2052         }
2053         email = talloc_asprintf(mem_ctx, "/o=First Organization/ou=First Administrative Group/cn=Recipients/cn=%s", username);
2054         fb_props->email_address = email;
2055
2056         /* open user mailbox */
2057         mailbox = emsmdbp_object_mailbox_init(mem_ctx, message_object->emsmdbp_ctx, email, true);
2058         if (!mailbox) {
2059                 goto end;
2060         }
2061
2062         /* open Inbox */
2063         openchangedb_get_SystemFolderID(message_object->emsmdbp_ctx->oc_ctx, username, EMSMDBP_INBOX, &inboxFID);
2064         if (emsmdbp_object_open_folder_by_fid(mem_ctx, message_object->emsmdbp_ctx, mailbox, inboxFID, &inbox) != MAPISTORE_SUCCESS) {
2065                 goto end;
2066         }
2067
2068         /* retrieve Calendar entry id */
2069         props->aulPropTag[0] = PR_IPM_APPOINTMENT_ENTRYID;
2070         data_pointers = emsmdbp_object_get_properties(mem_ctx, message_object->emsmdbp_ctx, inbox, props, &retvals);
2071         if (!data_pointers || retvals[0] != MAPI_E_SUCCESS) {
2072                 goto end;
2073         }
2074         calendarFID = 0;
2075         for (i = 0; i < 6; i++) {
2076                 calendarFID <<= 8;
2077                 calendarFID |= *(((struct Binary_r *) data_pointers[0])->lpb + (43 - i));
2078         }
2079         calendarFID <<= 16;
2080         calendarFID |= 1;
2081
2082         /* open user calendar */
2083         if (emsmdbp_object_open_folder_by_fid(mem_ctx, message_object->emsmdbp_ctx, mailbox, calendarFID, &calendar) != MAPISTORE_SUCCESS) {
2084                 goto end;
2085         }
2086         if (!emsmdbp_is_mapistore(calendar)) {
2087                 DEBUG(5, ("non-mapistore calendars are not supported for freebusy\n"));
2088                 goto end;
2089         }
2090
2091         /* fetch events from this month for 3 months: start + enddate + fbstatus */
2092         table = emsmdbp_folder_open_table(mem_ctx, calendar, MAPISTORE_MESSAGE_TABLE, 0);
2093         if (!table) {
2094                 goto end;
2095         }
2096         contextID = emsmdbp_get_contextID(calendar);
2097
2098         /* fetch freebusy range */
2099         emsmdbp_freebusy_make_range(&start_tm, &end_tm);
2100
2101         unix_to_nt_time(&nt_time, time(NULL));
2102         fb_props->timestamp.dwLowDateTime = (nt_time & 0xffffffff);
2103         fb_props->timestamp.dwHighDateTime = nt_time >> 32;
2104
2105         tz = getenv("TZ");
2106         setenv("TZ", "", 1);
2107         tzset();
2108         start_time = mktime(&start_tm);
2109         end_time = mktime(&end_tm);
2110         if (tz) {
2111                 setenv("TZ", tz, 1);
2112         }
2113         else {
2114                 unsetenv("TZ");
2115         }
2116         tzset();
2117
2118         /* setup restriction */
2119         and_res.rt = RES_AND;
2120         and_res.res.resAnd.cRes = 2;
2121         and_res.res.resAnd.res = time_restrictions;
2122
2123         time_restrictions[0].rt = RES_PROPERTY;
2124         time_restrictions[0].res.resProperty.relop = RELOP_GE;
2125         time_restrictions[0].res.resProperty.ulPropTag = PidLidAppointmentEndWhole; 
2126         time_restrictions[0].res.resProperty.lpProp.ulPropTag = PidLidAppointmentEndWhole;
2127         unix_to_nt_time(&nt_time, start_time);
2128         time_restrictions[0].res.resProperty.lpProp.value.ft.dwLowDateTime = (nt_time & 0xffffffff);
2129         time_restrictions[0].res.resProperty.lpProp.value.ft.dwHighDateTime = nt_time >> 32;
2130         fb_props->publish_start = (uint32_t) (nt_time / (60 * 10000000));
2131
2132         time_restrictions[1].rt = RES_PROPERTY;
2133         time_restrictions[1].res.resProperty.relop = RELOP_LE;
2134         time_restrictions[1].res.resProperty.ulPropTag = PidLidAppointmentStartWhole; 
2135         time_restrictions[1].res.resProperty.lpProp.ulPropTag = PidLidAppointmentStartWhole;
2136         unix_to_nt_time(&nt_time, end_time);
2137         time_restrictions[1].res.resProperty.lpProp.value.ft.dwLowDateTime = (nt_time & 0xffffffff);
2138         time_restrictions[1].res.resProperty.lpProp.value.ft.dwHighDateTime = nt_time >> 32;
2139         fb_props->publish_end = (uint32_t) (nt_time / (60 * 10000000));
2140
2141         mapistore_table_set_restrictions(message_object->emsmdbp_ctx->mstore_ctx, contextID, table->backend_object, &and_res, &state);
2142
2143         /* setup table columns */
2144         props->cValues = 3;
2145         props->aulPropTag = talloc_array(props, enum MAPITAGS, props->cValues);
2146         props->aulPropTag[0] = PidLidAppointmentStartWhole;
2147         props->aulPropTag[1] = PidLidAppointmentEndWhole;
2148         props->aulPropTag[2] = PidLidBusyStatus;
2149         mapistore_table_set_columns(message_object->emsmdbp_ctx->mstore_ctx, contextID, table->backend_object, props->cValues, props->aulPropTag);
2150         table->object.table->prop_count = props->cValues;
2151         table->object.table->properties = props->aulPropTag;
2152
2153         /* setup months arrays */
2154         if (start_tm.tm_year == end_tm.tm_year) {
2155                 nbr_months = (end_tm.tm_mon - start_tm.tm_mon + 1);
2156         }
2157         else {
2158                 nbr_months = (12 - start_tm.tm_mon) + end_tm.tm_mon + 1;
2159         }
2160         fb_props->months_ranges = talloc_array(fb_props, uint32_t, nbr_months);
2161         if (start_tm.tm_year == end_tm.tm_year) {
2162                 for (i = 0; i < nbr_months; i++) {
2163                         fb_props->months_ranges[i] = ((start_tm.tm_year + 1900) << 4) + (start_tm.tm_mon + 1 + i);
2164                 }
2165         }
2166         else {
2167                 month = start_tm.tm_mon;
2168                 i = 0;
2169                 while (month < 12) {
2170                         fb_props->months_ranges[i] = ((start_tm.tm_year + 1900) << 4) + month + 1;
2171                         i++;
2172                         month++;
2173                 }
2174                 month = 0;
2175                 while (month < end_tm.tm_mon) {
2176                         fb_props->months_ranges[i] = ((end_tm.tm_year + 1900) << 4) + month + 1;
2177                         i++;
2178                         month++;
2179                 }
2180                 fb_props->months_ranges[i] = ((end_tm.tm_year + 1900) << 4) + month + 1;
2181         }
2182
2183         /* fetch events and fill freebusy arrays */
2184         tentative_array = talloc_array(mem_ctx, uint8_t *, nbr_months);
2185         busy_array = talloc_array(mem_ctx, uint8_t *, nbr_months);
2186         oof_array = talloc_array(mem_ctx, uint8_t *, nbr_months);
2187         for (i = 0; i < nbr_months; i++) {
2188                 tentative_array[i] = talloc_array(tentative_array, uint8_t, max_mins_per_month);
2189                 memset(tentative_array[i], 0, max_mins_per_month);
2190                 busy_array[i] = talloc_array(tentative_array, uint8_t, max_mins_per_month);
2191                 memset(busy_array[i], 0, max_mins_per_month);
2192                 oof_array[i] = talloc_array(tentative_array, uint8_t, max_mins_per_month);
2193                 memset(oof_array[i], 0, max_mins_per_month);
2194         }
2195
2196         i = 0;
2197         while ((data_pointers = emsmdbp_object_table_get_row_props(mem_ctx, message_object->emsmdbp_ctx, table, i, MAPISTORE_PREFILTERED_QUERY, &retvals))) {
2198                 if (retvals[0] == MAPI_E_SUCCESS && retvals[1] == MAPI_E_SUCCESS && retvals[2] == MAPI_E_SUCCESS) {
2199                         switch (*((uint32_t *) data_pointers[2])) {
2200                         case olTentative:
2201                                 minutes_array = tentative_array;
2202                                 break;
2203                         case olBusy:
2204                                 minutes_array = busy_array;
2205                                 break;
2206                         case olOutOfOffice:
2207                                 minutes_array = oof_array;
2208                                 break;
2209                         default:
2210                                 minutes_array = NULL;
2211                         }
2212                         if (minutes_array) {
2213                                 emsmdbp_freebusy_fill_fbarray(minutes_array, fb_props->months_ranges, nbr_months, data_pointers[0], data_pointers[1]);
2214                         }
2215                 }
2216                 i++;
2217         }
2218
2219         /* compile minutes array into arrays of ranges */
2220         fb_props->nbr_months = nbr_months;
2221         fb_props->freebusy_tentative = talloc_array(fb_props, struct Binary_r, nbr_months);
2222         fb_props->freebusy_busy = talloc_array(fb_props, struct Binary_r, nbr_months);
2223         fb_props->freebusy_away = talloc_array(fb_props, struct Binary_r, nbr_months);
2224         fb_props->freebusy_merged = talloc_array(fb_props, struct Binary_r, nbr_months);
2225         for (i = 0; i < nbr_months; i++) {
2226                 emsmdbp_freebusy_compile_fbarray(fb_props, tentative_array[i], fb_props->freebusy_tentative + i);
2227                 emsmdbp_freebusy_compile_fbarray(fb_props, busy_array[i], fb_props->freebusy_busy + i);
2228                 emsmdbp_freebusy_compile_fbarray(fb_props, oof_array[i], fb_props->freebusy_away + i);
2229                 emsmdbp_freebusy_merge_subarray(busy_array[i], oof_array[i]);
2230                 emsmdbp_freebusy_compile_fbarray(fb_props, busy_array[i], fb_props->freebusy_merged + i);
2231         }
2232
2233 end:
2234         talloc_free(mem_ctx);
2235
2236         return;
2237 }
2238
2239 _PUBLIC_ enum mapistore_error emsmdbp_object_message_open(TALLOC_CTX *mem_ctx, struct emsmdbp_context *emsmdbp_ctx, struct emsmdbp_object *parent_object, uint64_t folderID, uint64_t messageID, bool read_write, struct emsmdbp_object **messageP, struct mapistore_message **msgp)
2240 {
2241         struct emsmdbp_object *folder_object, *message_object = NULL;
2242         uint32_t contextID;
2243         bool mapistore;
2244         TALLOC_CTX *local_mem_ctx;
2245         enum mapistore_error ret = MAPISTORE_SUCCESS;
2246
2247         if (!messageP) return MAPISTORE_ERROR;
2248         if (!parent_object) return MAPISTORE_ERROR;
2249
2250         local_mem_ctx = talloc_zero(NULL, TALLOC_CTX);
2251         ret = emsmdbp_object_open_folder_by_fid(local_mem_ctx, emsmdbp_ctx, parent_object, folderID, &folder_object);
2252         if (ret != MAPISTORE_SUCCESS)  {
2253                 goto end;
2254         }
2255
2256         mapistore = emsmdbp_is_mapistore(folder_object);
2257         switch (mapistore) {
2258         case false:
2259                 /* system/special folder */
2260                 message_object = emsmdbp_object_message_init(mem_ctx, emsmdbp_ctx, messageID, folder_object);
2261                 ret = openchangedb_message_open(mem_ctx, emsmdbp_ctx->oc_ctx, messageID, folderID, &message_object->backend_object, (void **)msgp);
2262                 if (ret != MAPISTORE_SUCCESS) {
2263                         printf("Invalid openchangedb message\n");
2264                         talloc_free(message_object);
2265                         goto end;
2266                 }
2267
2268                 emsmdbp_object_message_fill_freebusy_properties(message_object);
2269                 break;
2270         case true:
2271                 /* mapistore implementation goes here */
2272                 message_object = emsmdbp_object_message_init(mem_ctx, emsmdbp_ctx, messageID, folder_object);
2273                 contextID = emsmdbp_get_contextID(folder_object);
2274                 ret = mapistore_folder_open_message(emsmdbp_ctx->mstore_ctx, contextID, folder_object->backend_object, message_object, messageID, read_write, &message_object->backend_object);
2275                 if (ret == MAPISTORE_SUCCESS && msgp) {
2276                         if (mapistore_message_get_message_data(emsmdbp_ctx->mstore_ctx, contextID, message_object->backend_object, mem_ctx, msgp) != MAPISTORE_SUCCESS) {
2277                                 ret = MAPISTORE_ERROR;
2278                         }
2279                 }
2280                 if (ret != MAPISTORE_SUCCESS) {
2281                         talloc_free(message_object);
2282                 }
2283         }
2284
2285 end:
2286         talloc_free(local_mem_ctx);
2287
2288         if (ret == MAPISTORE_SUCCESS) {
2289                 message_object->object.message->read_write = read_write;
2290                 *messageP = message_object;
2291         }
2292
2293         return ret;
2294 }
2295
2296 _PUBLIC_ struct emsmdbp_object *emsmdbp_object_message_open_attachment_table(TALLOC_CTX *mem_ctx, struct emsmdbp_context *emsmdbp_ctx, struct emsmdbp_object *message_object)
2297 {
2298         struct emsmdbp_object   *table_object;
2299         uint32_t                contextID;
2300
2301         /* Sanity checks */
2302         if (!emsmdbp_ctx) return NULL;
2303         if (!message_object || message_object->type != EMSMDBP_OBJECT_MESSAGE) return NULL;
2304
2305         switch (emsmdbp_is_mapistore(message_object)) {
2306         case false:
2307                 /* system/special folder */
2308                 DEBUG(0, ("[%s] not implemented yet - shouldn't occur\n", __location__));
2309                 table_object = NULL;
2310                 break;
2311         case true:
2312                 contextID = emsmdbp_get_contextID(message_object);
2313
2314                 table_object = emsmdbp_object_table_init(mem_ctx, emsmdbp_ctx, message_object);
2315                 if (table_object) {
2316                         table_object->object.table->ulType = MAPISTORE_ATTACHMENT_TABLE;
2317                         mapistore_message_get_attachment_table(emsmdbp_ctx->mstore_ctx, contextID,
2318                                                                message_object->backend_object,
2319                                                                table_object, &table_object->backend_object,
2320                                                                &table_object->object.table->denominator);
2321                 }
2322         }
2323
2324         return table_object;
2325 }
2326
2327 /**
2328    \details Initialize a stream object
2329
2330    \param mem_ctx pointer to the memory context
2331    \param emsmdbp_ctx pointer to the emsmdb provider cotnext
2332    \param property the stream property identifier
2333    \param parent emsmdbp object of the parent
2334  */
2335 _PUBLIC_ struct emsmdbp_object *emsmdbp_object_stream_init(TALLOC_CTX *mem_ctx,
2336                                                            struct emsmdbp_context *emsmdbp_ctx,
2337                                                            struct emsmdbp_object *parent)
2338 {
2339         struct emsmdbp_object   *object;
2340
2341         /* Sanity checks */
2342         if (!emsmdbp_ctx) return NULL;
2343         if (!parent) return NULL;
2344
2345         object = emsmdbp_object_init(mem_ctx, emsmdbp_ctx, parent);
2346         if (!object) return NULL;
2347
2348         object->object.stream = talloc_zero(object, struct emsmdbp_object_stream);
2349         if (!object->object.stream) {
2350                 talloc_free(object);
2351                 return NULL;
2352         }
2353
2354         object->type = EMSMDBP_OBJECT_STREAM;
2355         object->object.stream->property = 0;
2356         object->object.stream->needs_commit = false;
2357         object->object.stream->stream.buffer.data = NULL;
2358         object->object.stream->stream.buffer.length = 0;
2359         object->object.stream->stream.position = 0;
2360
2361         return object;
2362 }
2363
2364
2365 /**
2366    \details Initialize a attachment object
2367
2368    \param mem_ctx pointer to the memory context
2369    \param emsmdbp_ctx pointer to the emsmdb provider cotnext
2370    \param folderID the folder identifier
2371    \param messageID the message identifier
2372    \param parent emsmdbp object of the parent 
2373  */
2374 _PUBLIC_ struct emsmdbp_object *emsmdbp_object_attachment_init(TALLOC_CTX *mem_ctx,
2375                                                                struct emsmdbp_context *emsmdbp_ctx,
2376                                                                uint64_t messageID,
2377                                                                struct emsmdbp_object *parent)
2378 {
2379         struct emsmdbp_object   *object;
2380
2381         /* Sanity checks */
2382         if (!emsmdbp_ctx) return NULL;
2383         if (!parent) return NULL;
2384
2385         object = emsmdbp_object_init(mem_ctx, emsmdbp_ctx, parent);
2386         if (!object) return NULL;
2387
2388         object->object.attachment = talloc_zero(object, struct emsmdbp_object_attachment);
2389         if (!object->object.attachment) {
2390                 talloc_free(object);
2391                 return NULL;
2392         }
2393
2394         object->type = EMSMDBP_OBJECT_ATTACHMENT;
2395         object->object.attachment->attachmentID = -1;
2396
2397         return object;
2398 }
2399
2400 /**
2401    \details Initialize a notification subscription object
2402
2403    \param mem_ctx pointer to the memory context
2404    \param emsmdbp_ctx pointer to the emsmdb provider cotnext
2405    \param whole_store whether the subscription applies to the specified change on the entire store or stricly on the specified folder/message
2406    \param folderID the folder identifier
2407    \param messageID the message identifier
2408    \param parent emsmdbp object of the parent
2409  */
2410 _PUBLIC_ struct emsmdbp_object *emsmdbp_object_subscription_init(TALLOC_CTX *mem_ctx,
2411                                                                  struct emsmdbp_context *emsmdbp_ctx,
2412                                                                  struct emsmdbp_object *parent)
2413 {
2414         struct emsmdbp_object   *object;
2415
2416         /* Sanity checks */
2417         if (!emsmdbp_ctx) return NULL;
2418         if (!parent) return NULL;
2419
2420         object = emsmdbp_object_init(mem_ctx, emsmdbp_ctx, parent);
2421         if (!object) return NULL;
2422
2423         object->object.subscription = talloc_zero(object, struct emsmdbp_object_subscription);
2424         if (!object->object.subscription) {
2425                 talloc_free(object);
2426                 return NULL;
2427         }
2428
2429         object->type = EMSMDBP_OBJECT_SUBSCRIPTION;
2430         object->object.subscription->subscription_list = NULL;
2431
2432         return object;
2433 }
2434
2435 _PUBLIC_ int emsmdbp_object_get_available_properties(TALLOC_CTX *mem_ctx, struct emsmdbp_context *emsmdbp_ctx, struct emsmdbp_object *object, struct SPropTagArray **propertiesp)
2436 {
2437         uint32_t contextID;
2438
2439         if (!(object->type == EMSMDBP_OBJECT_FOLDER
2440               || object->type == EMSMDBP_OBJECT_MAILBOX
2441               || object->type == EMSMDBP_OBJECT_MESSAGE
2442               || object->type == EMSMDBP_OBJECT_ATTACHMENT)) {
2443                 DEBUG(0, (__location__": object must be EMSMDBP_OBJECT_FOLDER, EMSMDBP_OBJECT_MAILBOX, EMSMDBP_OBJECT_MESSAGE or EMSMDBP_OBJECT_ATTACHMENT (type =  %d)\n", object->type));
2444                 return MAPISTORE_ERROR;
2445         }
2446         
2447         if (!emsmdbp_is_mapistore(object)) {
2448                 DEBUG(5, (__location__": only mapistore is supported at this time\n"));
2449                 return MAPISTORE_ERROR;
2450         }
2451
2452         contextID = emsmdbp_get_contextID(object);
2453
2454         return mapistore_properties_get_available_properties(emsmdbp_ctx->mstore_ctx, contextID, object->backend_object, mem_ctx, propertiesp);
2455 }
2456
2457 static int emsmdbp_object_get_properties_systemspecialfolder(TALLOC_CTX *mem_ctx, struct emsmdbp_context *emsmdbp_ctx, struct emsmdbp_object *object, struct SPropTagArray *properties, void **data_pointers, enum MAPISTATUS *retvals)
2458 {
2459         enum MAPISTATUS                 retval = MAPI_E_SUCCESS;
2460         struct emsmdbp_object_folder    *folder;
2461         char                            *owner;
2462         int                             i;
2463         uint32_t                        *obj_count;
2464         uint8_t                         *has_subobj;
2465         struct Binary_r                 *binr;
2466         time_t                          unix_time;
2467         NTTIME                          nt_time;
2468         struct FILETIME                 *ft;
2469
2470         folder = (struct emsmdbp_object_folder *) object->object.folder;
2471         for (i = 0; i < properties->cValues; i++) {
2472                 if (properties->aulPropTag[i] == PR_FOLDER_CHILD_COUNT) {
2473                         obj_count = talloc_zero(data_pointers, uint32_t);
2474                         retval = openchangedb_get_folder_count(emsmdbp_ctx->oc_ctx, object->object.folder->folderID, obj_count);
2475                         data_pointers[i] = obj_count;
2476                 }
2477                 else if (properties->aulPropTag[i] == PR_SUBFOLDERS) {
2478                         obj_count = talloc_zero(NULL, uint32_t);
2479                         retval = openchangedb_get_folder_count(emsmdbp_ctx->oc_ctx, object->object.folder->folderID, obj_count);
2480                         has_subobj = talloc_zero(data_pointers, uint8_t);
2481                         *has_subobj = (*obj_count > 0) ? 1 : 0;
2482                         data_pointers[i] = has_subobj;
2483                         talloc_free(obj_count);
2484                 }
2485                 else if (properties->aulPropTag[i] == PR_SOURCE_KEY) {
2486                         owner = emsmdbp_get_owner(object);
2487                         emsmdbp_source_key_from_fmid(data_pointers, emsmdbp_ctx, owner, object->object.folder->folderID, &binr);
2488                         data_pointers[i] = binr;
2489                         retval = MAPI_E_SUCCESS;
2490                 }
2491                 else if (properties->aulPropTag[i] == PR_CONTENT_COUNT
2492                          || properties->aulPropTag[i] == PidTagAssociatedContentCount
2493                          || properties->aulPropTag[i] == PR_CONTENT_UNREAD
2494                          || properties->aulPropTag[i] == PR_DELETED_COUNT_TOTAL) {
2495                         obj_count = talloc_zero(data_pointers, uint32_t);
2496                         *obj_count = 0;
2497                         data_pointers[i] = obj_count;
2498                         retval = MAPI_E_SUCCESS;
2499                 }
2500                 else if (properties->aulPropTag[i] == PidTagLocalCommitTimeMax) {
2501                         /* TODO: temporary hack */
2502                         unix_time = time(NULL) & 0xffffff00;
2503                         unix_to_nt_time(&nt_time, unix_time);
2504                         ft = talloc_zero(data_pointers, struct FILETIME);
2505                         ft->dwLowDateTime = (nt_time & 0xffffffff);
2506                         ft->dwHighDateTime = nt_time >> 32;
2507                         data_pointers[i] = ft;
2508                         retval = MAPI_E_SUCCESS;
2509                 }
2510                 else {
2511                         retval = openchangedb_get_folder_property(data_pointers, emsmdbp_ctx->oc_ctx, properties->aulPropTag[i], folder->folderID, data_pointers + i);
2512                 }
2513                 retvals[i] = retval;
2514         }
2515
2516         return MAPISTORE_SUCCESS;
2517 }
2518
2519 static int emsmdbp_object_get_properties_message(TALLOC_CTX *mem_ctx, struct emsmdbp_context *emsmdbp_ctx,
2520                                                  struct emsmdbp_object *object, struct SPropTagArray *properties,
2521                                                  void **data_pointers, enum MAPISTATUS *retvals)
2522 {
2523         enum MAPISTATUS                                         retval;
2524         int                                                     i;
2525         char                                                    *owner;
2526         struct Binary_r                                         *binr;
2527         struct emsmdbp_object_message_freebusy_properties       *fb_props;
2528         struct LongArray_r                                      *long_array;
2529         struct BinaryArray_r                                    *bin_array;
2530
2531         fb_props = object->object.message->fb_properties;
2532
2533         /* Look over properties */
2534         for (i = 0; i < properties->cValues; i++) {
2535                 if (properties->aulPropTag[i] == PR_SOURCE_KEY) {
2536                         owner = emsmdbp_get_owner(object);
2537                         emsmdbp_source_key_from_fmid(data_pointers, emsmdbp_ctx, owner, object->object.message->folderID,
2538                                                      &binr);
2539                         data_pointers[i] = binr;
2540                         retval = MAPI_E_SUCCESS;
2541                 } else {
2542                         if (fb_props) {
2543                                 switch (properties->aulPropTag[i]) {
2544                                 case PR_SCHDINFO_MONTHS_TENTATIVE:
2545                                 case PR_SCHDINFO_MONTHS_BUSY:
2546                                 case PR_SCHDINFO_MONTHS_OOF:
2547                                 case PR_SCHDINFO_MONTHS_MERGED:
2548                                         long_array = talloc_zero(data_pointers, struct LongArray_r);
2549                                         long_array->cValues = fb_props->nbr_months;
2550                                         long_array->lpl = fb_props->months_ranges;
2551                                         data_pointers[i] = long_array;
2552                                         retval = MAPI_E_SUCCESS;
2553                                         break;
2554                                 case PR_SCHDINFO_FREEBUSY_TENTATIVE:
2555                                         bin_array = talloc_zero(data_pointers, struct BinaryArray_r);
2556                                         bin_array->cValues = fb_props->nbr_months;
2557                                         bin_array->lpbin = fb_props->freebusy_tentative;
2558                                         data_pointers[i] = bin_array;
2559                                         retval = MAPI_E_SUCCESS;
2560                                         break;
2561                                 case PR_SCHDINFO_FREEBUSY_BUSY:
2562                                         bin_array = talloc_zero(data_pointers, struct BinaryArray_r);
2563                                         bin_array->cValues = fb_props->nbr_months;
2564                                         bin_array->lpbin = fb_props->freebusy_busy;
2565                                         data_pointers[i] = bin_array;
2566                                         retval = MAPI_E_SUCCESS;
2567                                         break;
2568                                 case PR_SCHDINFO_FREEBUSY_OOF:
2569                                         bin_array = talloc_zero(data_pointers, struct BinaryArray_r);
2570                                         bin_array->cValues = fb_props->nbr_months;
2571                                         bin_array->lpbin = fb_props->freebusy_away;
2572                                         data_pointers[i] = bin_array;
2573                                         retval = MAPI_E_SUCCESS;
2574                                         break;
2575                                 case PR_SCHDINFO_FREEBUSY_MERGED:
2576                                         bin_array = talloc_zero(data_pointers, struct BinaryArray_r);
2577                                         bin_array->cValues = fb_props->nbr_months;
2578                                         bin_array->lpbin = fb_props->freebusy_merged;
2579                                         data_pointers[i] = bin_array;
2580                                         retval = MAPI_E_SUCCESS;
2581                                         break;
2582                                 case PR_FREEBUSY_PUBLISH_START:
2583                                         data_pointers[i] = &fb_props->publish_start;
2584                                         retval = MAPI_E_SUCCESS;
2585                                         break;
2586                                 case PR_FREEBUSY_PUBLISH_END:
2587                                         data_pointers[i] = &fb_props->publish_end;
2588                                         retval = MAPI_E_SUCCESS;
2589                                         break;
2590                                 case PidTagFreeBusyMessageEmailAddress:
2591                                         data_pointers[i] = fb_props->email_address;
2592                                         retval = MAPI_E_SUCCESS;
2593                                         break;
2594                                 case PR_FREEBUSY_RANGE_TIMESTAMP:
2595                                         data_pointers[i] = &fb_props->timestamp;
2596                                         retval = MAPI_E_SUCCESS;
2597                                         break;
2598                                 default:
2599                                         retval = openchangedb_message_get_property(data_pointers, object->backend_object, properties->aulPropTag[i], data_pointers + i);
2600                                 }
2601                         }
2602                         else {
2603                                 retval = openchangedb_message_get_property(data_pointers, object->backend_object, properties->aulPropTag[i], data_pointers + i);
2604                         }
2605                 }
2606                 retvals[i] = retval;
2607         }
2608
2609         return MAPI_E_SUCCESS;
2610 }
2611
2612 static int emsmdbp_object_get_properties_mapistore_root(TALLOC_CTX *mem_ctx, struct emsmdbp_context *emsmdbp_ctx, struct emsmdbp_object *object, struct SPropTagArray *properties, void **data_pointers, enum MAPISTATUS *retvals)
2613 {
2614         enum MAPISTATUS                 retval = MAPI_E_SUCCESS;
2615         struct emsmdbp_object_folder    *folder;
2616         char                            *owner;
2617         struct Binary_r                 *binr;
2618         int                             i;
2619         uint32_t                        contextID;
2620         uint32_t                        *obj_count;
2621         uint8_t                         *has_subobj;
2622         /* time_t                               unix_time; */
2623         /* NTTIME                               nt_time; */
2624         /* struct FILETIME                      *ft; */
2625
2626         contextID = emsmdbp_get_contextID(object);
2627
2628         folder = (struct emsmdbp_object_folder *) object->object.folder;
2629         for (i = 0; i < properties->cValues; i++) {
2630                 if (properties->aulPropTag[i] == PR_CONTENT_COUNT) {
2631                         /* a hack to avoid fetching dynamic fields from openchange.ldb */
2632                         obj_count = talloc_zero(data_pointers, uint32_t);
2633                         retval = mapistore_folder_get_child_count(emsmdbp_ctx->mstore_ctx, contextID, object->backend_object, MAPISTORE_MESSAGE_TABLE, obj_count);
2634                         if (!retval) {
2635                                 data_pointers[i] = obj_count;
2636                         }
2637                 }
2638                 else if (properties->aulPropTag[i] == PR_SOURCE_KEY) {
2639                         owner = emsmdbp_get_owner(object);
2640                         emsmdbp_source_key_from_fmid(data_pointers, emsmdbp_ctx, owner, object->object.folder->folderID, &binr);
2641                         data_pointers[i] = binr;
2642                         retval = MAPI_E_SUCCESS;
2643                 }
2644                 else if (properties->aulPropTag[i] == PR_FOLDER_TYPE) {
2645                         obj_count = talloc_zero(data_pointers, uint32_t);
2646                         *obj_count = FOLDER_GENERIC;
2647                         data_pointers[i] = obj_count;
2648                         retval = MAPI_E_SUCCESS;
2649                 }
2650                 else if (properties->aulPropTag[i] == PidTagAssociatedContentCount) {
2651                         obj_count = talloc_zero(data_pointers, uint32_t);
2652                         retval = mapistore_folder_get_child_count(emsmdbp_ctx->mstore_ctx, emsmdbp_get_contextID(object), object->backend_object,
2653                                                                   MAPISTORE_FAI_TABLE, obj_count);
2654                         if (!retval) {
2655                                 data_pointers[i] = obj_count;
2656                         }
2657                 }
2658                 else if (properties->aulPropTag[i] == PR_FOLDER_CHILD_COUNT) {
2659                         obj_count = talloc_zero(data_pointers, uint32_t);
2660                         retval = emsmdbp_folder_get_folder_count(emsmdbp_ctx, object, obj_count);
2661                         if (!retval) {
2662                                 data_pointers[i] = obj_count;
2663                         }
2664                 }
2665                 else if (properties->aulPropTag[i] == PR_SUBFOLDERS) {
2666                         obj_count = talloc_zero(NULL, uint32_t);
2667                         retval = emsmdbp_folder_get_folder_count(emsmdbp_ctx, object, obj_count);
2668                         if (!retval) {
2669                                 has_subobj = talloc_zero(data_pointers, uint8_t);
2670                                 *has_subobj = (*obj_count > 0) ? 1 : 0;
2671                                 data_pointers[i] = has_subobj;
2672                         }
2673                         talloc_free(obj_count);
2674                 }
2675                 else if (properties->aulPropTag[i] == PR_CONTENT_UNREAD || properties->aulPropTag[i] == PR_DELETED_COUNT_TOTAL) {
2676                         /* TODO: temporary hack */
2677                         obj_count = talloc_zero(data_pointers, uint32_t);
2678                         *obj_count = 0;
2679                         data_pointers[i] = obj_count;
2680                         retval = MAPI_E_SUCCESS;
2681                 }
2682                 else if (properties->aulPropTag[i] == PidTagLocalCommitTimeMax || properties->aulPropTag[i] == PR_ACCESS || properties->aulPropTag[i] == PR_ACCESS_LEVEL) {
2683                         struct mapistore_property_data prop_data;
2684
2685                         mapistore_properties_get_properties(emsmdbp_ctx->mstore_ctx, contextID,
2686                                                             object->backend_object,
2687                                                             data_pointers,
2688                                                             1,
2689                                                             properties->aulPropTag + i,
2690                                                             &prop_data);
2691                         data_pointers[i] = prop_data.data;
2692                         retval = prop_data.error;
2693                 }
2694                 else {
2695                         retval = openchangedb_get_folder_property(data_pointers, emsmdbp_ctx->oc_ctx, properties->aulPropTag[i], folder->folderID, data_pointers + i);
2696                 }
2697                 retvals[i] = retval;
2698         }
2699
2700         return MAPISTORE_SUCCESS;
2701 }
2702
2703 static int emsmdbp_object_get_properties_mailbox(TALLOC_CTX *mem_ctx, struct emsmdbp_context *emsmdbp_ctx, struct emsmdbp_object *object, struct SPropTagArray *properties, void **data_pointers, enum MAPISTATUS *retvals)
2704 {
2705         uint32_t                        i;
2706         struct SBinary_short            *bin;
2707
2708         for (i = 0; i < properties->cValues; i++) {
2709                 switch (properties->aulPropTag[i]) {
2710                 case PR_MAPPING_SIGNATURE:
2711                 case PidTagIpmPublicFoldersEntryId:
2712                         retvals[i] = MAPI_E_NO_ACCESS;
2713                         break;
2714                 case PR_USER_ENTRYID:
2715                         bin = talloc_zero(data_pointers, struct SBinary_short);
2716                         retvals[i] = entryid_set_AB_EntryID(data_pointers, object->object.mailbox->szUserDN, bin);
2717                         data_pointers[i] = bin;
2718                         break;
2719                 case PR_MAILBOX_OWNER_ENTRYID:
2720                         if (object->object.mailbox->mailboxstore == false) {
2721                                 retvals[i] = MAPI_E_NO_ACCESS;
2722                         } else {
2723                                 bin = talloc_zero(data_pointers, struct SBinary_short);
2724                                 retvals[i] = entryid_set_AB_EntryID(data_pointers, object->object.mailbox->owner_EssDN,
2725                                                                     bin);
2726                                 data_pointers[i] = bin;
2727                         }
2728                         break;
2729                 case PidTagMailboxOwnerName:
2730                         if (object->object.mailbox->mailboxstore == false) {
2731                                 retvals[i] = MAPI_E_NO_ACCESS;
2732                         } else {
2733                                 retvals[i] = MAPI_E_SUCCESS;
2734                                 data_pointers[i] = talloc_strdup(data_pointers, object->object.mailbox->owner_Name);
2735                         }
2736                         break;
2737                 default:
2738                         retvals[i] = openchangedb_get_folder_property(data_pointers, emsmdbp_ctx->oc_ctx, properties->aulPropTag[i], object->object.mailbox->folderID, data_pointers + i);
2739                 }
2740         }
2741
2742         return MAPISTORE_SUCCESS;
2743 }
2744
2745 static int emsmdbp_object_get_properties_mapistore(TALLOC_CTX *mem_ctx, struct emsmdbp_context *emsmdbp_ctx, struct emsmdbp_object *object, struct SPropTagArray *properties, void **data_pointers, enum MAPISTATUS *retvals)
2746 {
2747         uint32_t                contextID = -1;
2748         struct mapistore_property_data  *prop_data;
2749         int                     i, ret;
2750
2751         contextID = emsmdbp_get_contextID(object);
2752         prop_data = talloc_array(NULL, struct mapistore_property_data, properties->cValues);
2753         memset(prop_data, 0, sizeof(struct mapistore_property_data) * properties->cValues);
2754
2755         ret = mapistore_properties_get_properties(emsmdbp_ctx->mstore_ctx, contextID,
2756                                                   object->backend_object,
2757                                                   prop_data,
2758                                                   properties->cValues,
2759                                                   properties->aulPropTag,
2760                                                   prop_data);
2761         if (ret == MAPISTORE_SUCCESS) {
2762                 for (i = 0; i < properties->cValues; i++) {
2763                         if (prop_data[i].error) {
2764                                 if (prop_data[i].error == MAPISTORE_ERR_NOT_FOUND) {
2765                                         retvals[i] = MAPI_E_NOT_FOUND;
2766                                 }
2767                                 else {
2768                                         retvals[i] = MAPI_E_NO_SUPPORT;
2769                                         DEBUG (4, ("%s: unknown mapistore error: %.8x\n", __PRETTY_FUNCTION__, prop_data[i].error));
2770                                 }
2771                         }
2772                         else {
2773                                 if (prop_data[i].data == NULL) {
2774                                         retvals[i] = MAPI_E_NOT_FOUND;
2775                                 }
2776                                 else {
2777                                         data_pointers[i] = prop_data[i].data;
2778                                         (void) talloc_reference(data_pointers, prop_data[i].data);
2779                                 }
2780                         }
2781                 }
2782         }
2783         talloc_free(prop_data);
2784
2785         return ret;
2786 }
2787
2788 _PUBLIC_ void **emsmdbp_object_get_properties(TALLOC_CTX *mem_ctx, struct emsmdbp_context *emsmdbp_ctx, struct emsmdbp_object *object, struct SPropTagArray *properties, enum MAPISTATUS **retvalsp)
2789 {
2790         void            **data_pointers;
2791         enum MAPISTATUS *retvals;
2792         bool            mapistore;
2793         int             retval;
2794
2795         data_pointers = talloc_array(mem_ctx, void *, properties->cValues);
2796         memset(data_pointers, 0, sizeof(void *) * properties->cValues);
2797
2798         retvals = talloc_array(mem_ctx, enum MAPISTATUS, properties->cValues);
2799         memset(retvals, 0, sizeof(enum MAPISTATUS) * properties->cValues);
2800
2801         /* Temporary hack: If this is a mapistore root container
2802          * (e.g. Inbox, Calendar etc.), directly stored under
2803          * IPM.Subtree, then fetch properties from openchange
2804          * dispatcher db, not mapistore */
2805         if (object && object->type == EMSMDBP_OBJECT_FOLDER &&
2806             object->object.folder->mapistore_root == true) {
2807                 if (object->object.folder->postponed_props) {
2808                         emsmdbp_object_folder_commit_creation(emsmdbp_ctx, object, true);
2809                 }
2810
2811                 retval = emsmdbp_object_get_properties_mapistore_root(mem_ctx, emsmdbp_ctx, object, properties, data_pointers, retvals);
2812         } else {
2813                 mapistore = emsmdbp_is_mapistore(object);
2814                 /* Nasty hack */
2815                 if (!object) {
2816                         DEBUG(5, ("[%s] what's that hack!??\n", __location__));
2817                         mapistore = true;
2818                 }
2819
2820                 switch (mapistore) {
2821                 case false:
2822                         switch (object->type) {
2823                         case EMSMDBP_OBJECT_MAILBOX:
2824                                 retval = emsmdbp_object_get_properties_mailbox(mem_ctx, emsmdbp_ctx, object, properties, data_pointers, retvals);
2825                                 break;
2826                         case EMSMDBP_OBJECT_FOLDER:
2827                                 retval = emsmdbp_object_get_properties_systemspecialfolder(mem_ctx, emsmdbp_ctx, object, properties, data_pointers, retvals);
2828                                 break;
2829                         case EMSMDBP_OBJECT_MESSAGE:
2830                                 retval = emsmdbp_object_get_properties_message(mem_ctx, emsmdbp_ctx, object, properties, data_pointers, retvals);
2831                                 break;
2832                         default:
2833                                 retval = MAPISTORE_ERROR;
2834                                 break;
2835                         }
2836                         break;
2837                 case true:
2838                         /* folder or messages handled by mapistore */
2839                         retval = emsmdbp_object_get_properties_mapistore(mem_ctx, emsmdbp_ctx, object, properties, data_pointers, retvals);
2840                         break;
2841                 }
2842         }
2843
2844         if (retvalsp) {
2845                 *retvalsp = retvals;
2846         }
2847
2848         if (retval) {
2849                 talloc_free(data_pointers);
2850                 data_pointers = NULL;
2851         }
2852
2853         return data_pointers;
2854 }
2855
2856 /* TODO: handling of "property problems" */
2857 _PUBLIC_ int emsmdbp_object_set_properties(struct emsmdbp_context *emsmdbp_ctx, struct emsmdbp_object *object, struct SRow *rowp)
2858 {
2859         uint32_t                contextID, new_cvalues;
2860         bool                    mapistore;
2861         enum mapistore_error    ret;
2862         struct SRow             *postponed_props;
2863
2864         /* Sanity checks */
2865         if (!emsmdbp_ctx) return MAPI_E_CALL_FAILED;
2866         if (!object) return MAPI_E_CALL_FAILED;
2867         if (!rowp) return MAPI_E_CALL_FAILED;
2868         if (!(object->type == EMSMDBP_OBJECT_FOLDER
2869               || object->type == EMSMDBP_OBJECT_MAILBOX
2870               || object->type == EMSMDBP_OBJECT_MESSAGE
2871               || object->type == EMSMDBP_OBJECT_ATTACHMENT)) {
2872                 DEBUG(0, (__location__": object must be EMSMDBP_OBJECT_FOLDER, EMSMDBP_OBJECT_MAILBOX, EMSMDBP_OBJECT_MESSAGE or EMSMDBP_OBJECT_ATTACHMENT (type = %d)\n", object->type));
2873                 return MAPI_E_NO_SUPPORT;
2874         }
2875
2876         if (object->type == EMSMDBP_OBJECT_FOLDER) {
2877                 postponed_props = object->object.folder->postponed_props;
2878                 if (postponed_props) {
2879                         new_cvalues = postponed_props->cValues + rowp->cValues;
2880                         postponed_props->lpProps = talloc_realloc(postponed_props, postponed_props->lpProps, struct SPropValue, new_cvalues);
2881                         mapi_copy_spropvalues(postponed_props, rowp->lpProps, postponed_props->lpProps + postponed_props->cValues, rowp->cValues);
2882                         postponed_props->cValues = new_cvalues;
2883
2884                         ret = emsmdbp_object_folder_commit_creation(emsmdbp_ctx, object, false);
2885                         if (ret == MAPISTORE_SUCCESS) {
2886                                 return MAPI_E_SUCCESS;
2887                         }
2888                         else {
2889                                 return MAPI_E_NOT_FOUND;
2890                         }
2891                 }
2892         }
2893
2894         /* Temporary hack: If this is a mapistore root container
2895          * (e.g. Inbox, Calendar etc.), directly stored under
2896          * IPM.Subtree, then set properties from openchange
2897          * dispatcher db, not mapistore */
2898         if (object->type == EMSMDBP_OBJECT_FOLDER
2899             && object->object.folder->mapistore_root == true) {
2900                 openchangedb_set_folder_properties(emsmdbp_ctx->oc_ctx, object->object.folder->folderID, rowp);
2901                 contextID = emsmdbp_get_contextID(object);
2902                 mapistore_properties_set_properties(emsmdbp_ctx->mstore_ctx, contextID, object->backend_object, rowp);
2903         }
2904         else {
2905                 contextID = emsmdbp_get_contextID(object);
2906                 mapistore = emsmdbp_is_mapistore(object);
2907                 switch (mapistore) {
2908                 case false:
2909                         if (object->type == EMSMDBP_OBJECT_FOLDER) {
2910                                 openchangedb_set_folder_properties(emsmdbp_ctx->oc_ctx, object->object.folder->folderID, rowp);
2911                         }
2912                         else if (object->type == EMSMDBP_OBJECT_MAILBOX) {
2913                                 openchangedb_set_folder_properties(emsmdbp_ctx->oc_ctx, object->object.mailbox->folderID, rowp);
2914                         }
2915                         else if (object->type == EMSMDBP_OBJECT_MESSAGE) {
2916                                 openchangedb_message_set_properties((TALLOC_CTX *)object->object.message, 
2917                                                                     object->backend_object, rowp);
2918                         }
2919                         else {
2920                                 DEBUG(0, ("Setting properties on openchangedb not implemented yet for non-folder object type\n"));
2921                                 return MAPI_E_NO_SUPPORT;
2922                         }
2923                         break;
2924                 case true:
2925                         mapistore_properties_set_properties(emsmdbp_ctx->mstore_ctx, contextID, object->backend_object, rowp);
2926                         break;
2927                 }
2928         }
2929
2930         return MAPI_E_SUCCESS;
2931 }
2932
2933 _PUBLIC_ void emsmdbp_fill_row_blob(TALLOC_CTX *mem_ctx,
2934                                     struct emsmdbp_context *emsmdbp_ctx,
2935                                     uint8_t *layout,
2936                                     DATA_BLOB *property_row,
2937                                     struct SPropTagArray *properties,
2938                                     void **data_pointers,
2939                                     enum MAPISTATUS *retvals,
2940                                     bool *untyped_status)
2941 {
2942         uint16_t i;
2943         uint8_t flagged;
2944         enum MAPITAGS property;
2945         void *data;
2946         uint32_t retval;
2947
2948         flagged = 0;
2949         for (i = 0; !flagged && i < properties->cValues; i++) {
2950                 if (retvals[i] != MAPI_E_SUCCESS || untyped_status[i] || !data_pointers[i]) {
2951                         flagged = 1;
2952                 }
2953         }
2954         *layout = flagged;
2955
2956         for (i = 0; i < properties->cValues; i++) {
2957                 retval = retvals[i];
2958                 if (retval != MAPI_E_SUCCESS) {
2959                         property = (properties->aulPropTag[i] & 0xFFFF0000) + PT_ERROR;
2960                         data = &retval;
2961                 }
2962                 else {
2963                         property = properties->aulPropTag[i];
2964                         data = data_pointers[i];
2965                 }
2966                 libmapiserver_push_property(mem_ctx,
2967                                             property, data, property_row,
2968                                             flagged ? PT_ERROR : 0, flagged, untyped_status[i]);
2969         }
2970 }
2971
2972 _PUBLIC_ struct emsmdbp_stream_data *emsmdbp_stream_data_from_value(TALLOC_CTX *mem_ctx, enum MAPITAGS prop_tag, void *value)
2973 {
2974         uint16_t                        prop_type;
2975         struct emsmdbp_stream_data      *stream_data;
2976         size_t                          converted_size;
2977
2978         stream_data = talloc_zero(mem_ctx, struct emsmdbp_stream_data);
2979         stream_data->prop_tag = prop_tag;
2980         prop_type = prop_tag & 0xffff;
2981         if (prop_type == PT_STRING8) {
2982                 stream_data->data.length = strlen(value) + 1;
2983                 stream_data->data.data = value;
2984                 (void) talloc_reference(stream_data, stream_data->data.data);
2985         }
2986         else if (prop_type == PT_UNICODE) {
2987                 stream_data->data.length = strlen_m_ext((char *) value, CH_UTF8, CH_UTF16LE) * 2;
2988                 stream_data->data.data = talloc_array(stream_data, uint8_t, stream_data->data.length + 2);
2989                 convert_string(CH_UTF8, CH_UTF16LE,
2990                                value, strlen(value),
2991                                stream_data->data.data, stream_data->data.length,
2992                                &converted_size);
2993                 memset(stream_data->data.data + stream_data->data.length, 0, 2 * sizeof(uint8_t));
2994         }
2995         else if (prop_type == PT_BINARY) {
2996                 stream_data->data.length = ((struct Binary_r *) value)->cb;
2997                 stream_data->data.data = ((struct Binary_r *) value)->lpb;
2998                 (void) talloc_reference(stream_data, stream_data->data.data);
2999         }
3000         else {
3001                 talloc_free(stream_data);
3002                 return NULL;
3003         }
3004
3005         return stream_data;
3006 }
3007
3008 _PUBLIC_ DATA_BLOB emsmdbp_stream_read_buffer(struct emsmdbp_stream *stream, uint32_t length)
3009 {
3010         DATA_BLOB buffer;
3011         uint32_t real_length;
3012
3013         real_length = length;
3014         if (real_length + stream->position > stream->buffer.length) {
3015                 real_length = stream->buffer.length - stream->position;
3016         }
3017         buffer.length = real_length;
3018         buffer.data = stream->buffer.data + stream->position;
3019         stream->position += real_length;
3020
3021         return buffer;
3022 }
3023
3024 _PUBLIC_ void emsmdbp_stream_write_buffer(TALLOC_CTX *mem_ctx, struct emsmdbp_stream *stream, DATA_BLOB new_buffer)
3025 {
3026         uint32_t new_position, old_length;
3027         uint8_t *old_data;
3028
3029         new_position = stream->position + new_buffer.length;
3030         if (new_position >= stream->buffer.length) {
3031                 old_length = stream->buffer.length;
3032                 stream->buffer.length = new_position;
3033                 if (stream->buffer.data) {
3034                         old_data = stream->buffer.data;
3035                         stream->buffer.data = talloc_realloc(mem_ctx, stream->buffer.data, uint8_t, stream->buffer.length);
3036                         if (!stream->buffer.data) {
3037                                 DEBUG(5, ("WARNING: [bug] lost buffer pointer (data = NULL)\n"));
3038                                 stream->buffer.data = talloc_array(mem_ctx, uint8_t, stream->buffer.length);
3039                                 memcpy(stream->buffer.data, old_data, old_length);
3040                         }
3041                 }
3042                 else {
3043                         stream->buffer.data = talloc_array(mem_ctx, uint8_t, stream->buffer.length);
3044                 }
3045         }
3046
3047         memcpy(stream->buffer.data + stream->position, new_buffer.data, new_buffer.length);
3048         stream->position = new_position;
3049 }
3050
3051 _PUBLIC_ struct emsmdbp_stream_data *emsmdbp_object_get_stream_data(struct emsmdbp_object *object, enum MAPITAGS prop_tag)
3052 {
3053         struct emsmdbp_stream_data *current_data;
3054
3055         for (current_data = object->stream_data; current_data; current_data = current_data->next) {
3056                 if (current_data->prop_tag == prop_tag) {
3057                         DEBUG(5, ("[%s]: found data for tag %.8x\n", __FUNCTION__, prop_tag));
3058                         return current_data;
3059                 }
3060         }
3061
3062         return NULL;
3063 }
3064
3065 /**
3066    \details Initialize a synccontext object
3067
3068    \param mem_ctx pointer to the memory context
3069    \param emsmdbp_ctx pointer to the emsmdb provider cotnext
3070    \param whole_store whether the subscription applies to the specified change on the entire store or stricly on the specified folder/message
3071    \param folderID the folder identifier
3072    \param messageID the message identifier
3073    \param parent emsmdbp object of the parent
3074  */
3075 _PUBLIC_ struct emsmdbp_object *emsmdbp_object_synccontext_init(TALLOC_CTX *mem_ctx,
3076                                                                 struct emsmdbp_context *emsmdbp_ctx,
3077                                                                 struct emsmdbp_object *parent_object)
3078 {
3079         struct emsmdbp_object   *synccontext_object;
3080
3081         /* Sanity checks */
3082         if (!emsmdbp_ctx) return NULL;
3083         if (!parent_object) return NULL;
3084         if (!(parent_object->type == EMSMDBP_OBJECT_FOLDER || parent_object->type == EMSMDBP_OBJECT_MAILBOX)) {
3085                 DEBUG(0, (__location__": parent_object must be EMSMDBP_OBJECT_FOLDER or EMSMDBP_OBJECT_MAILBOX (type = %d)\n", parent_object->type));
3086                 return NULL;
3087         }
3088
3089         synccontext_object = emsmdbp_object_init(mem_ctx, emsmdbp_ctx, parent_object);
3090         if (!synccontext_object) return NULL;
3091
3092         synccontext_object->object.synccontext = talloc_zero(synccontext_object, struct emsmdbp_object_synccontext);
3093         if (!synccontext_object->object.synccontext) {
3094                 talloc_free(synccontext_object);
3095                 return NULL;
3096         }
3097
3098         synccontext_object->type = EMSMDBP_OBJECT_SYNCCONTEXT;
3099
3100         (void) talloc_reference(synccontext_object->object.synccontext, parent_object);
3101         synccontext_object->object.synccontext->state_property = 0;
3102         synccontext_object->object.synccontext->state_stream.buffer.length = 0;
3103         synccontext_object->object.synccontext->state_stream.buffer.data = talloc_zero(synccontext_object->object.synccontext, uint8_t);
3104         synccontext_object->object.synccontext->stream.buffer.length = 0;
3105         synccontext_object->object.synccontext->stream.buffer.data = NULL;
3106
3107         synccontext_object->object.synccontext->cnset_seen = talloc_zero(emsmdbp_ctx, struct idset);
3108         openchangedb_get_MailboxReplica(emsmdbp_ctx->oc_ctx, emsmdbp_ctx->username, NULL, &synccontext_object->object.synccontext->cnset_seen->repl.guid);
3109         synccontext_object->object.synccontext->cnset_seen->ranges = talloc_zero(synccontext_object->object.synccontext->cnset_seen, struct globset_range);
3110         synccontext_object->object.synccontext->cnset_seen->range_count = 1;
3111         synccontext_object->object.synccontext->cnset_seen->ranges->next = NULL;
3112         synccontext_object->object.synccontext->cnset_seen->ranges->prev = synccontext_object->object.synccontext->cnset_seen->ranges;
3113         synccontext_object->object.synccontext->cnset_seen->ranges->low = 0xffffffffffffffffLL;
3114         synccontext_object->object.synccontext->cnset_seen->ranges->high = 0x0;
3115
3116         /* synccontext_object->object.synccontext->property_tags.cValues = 0; */
3117         /* synccontext_object->object.synccontext->property_tags.aulPropTag = NULL; */
3118
3119         return synccontext_object;
3120 }
3121
3122 /**
3123    \details Initialize a ftcontext object
3124
3125    \param mem_ctx pointer to the memory context
3126    \param emsmdbp_ctx pointer to the emsmdb provider cotnext
3127    \param whole_store whether the subscription applies to the specified change on the entire store or stricly on the specified folder/message
3128    \param folderID the folder identifier
3129    \param messageID the message identifier
3130    \param parent emsmdbp object of the parent
3131  */
3132 _PUBLIC_ struct emsmdbp_object *emsmdbp_object_ftcontext_init(TALLOC_CTX *mem_ctx,
3133                                                               struct emsmdbp_context *emsmdbp_ctx,
3134                                                               struct emsmdbp_object *parent)
3135 {
3136         struct emsmdbp_object   *object;
3137
3138         /* Sanity checks */
3139         if (!emsmdbp_ctx) return NULL;
3140         if (!parent) return NULL;
3141
3142         object = emsmdbp_object_init(mem_ctx, emsmdbp_ctx, parent);
3143         if (!object) return NULL;
3144
3145         object->object.ftcontext = talloc_zero(object, struct emsmdbp_object_ftcontext);
3146         if (!object->object.ftcontext) {
3147                 talloc_free(object);
3148                 return NULL;
3149         }
3150
3151         object->type = EMSMDBP_OBJECT_FTCONTEXT;
3152
3153         return object;
3154 }