33bae82cb866bbd0ce484d9172f34c0e11cb6af6
[jelmer/openchange.git] / libmapi / simple_mapi.c
1 /*
2    OpenChange MAPI implementation.
3
4    Copyright (C) Julien Kerihuel 2007-2011.
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include "libmapi/libmapi.h"
21 #include "libmapi/libmapi_private.h"
22
23
24 /**
25    \file simple_mapi.c
26
27    \brief Convenience functions.
28 */
29
30
31 /**
32    \details Retrieve the folder id for the specified default folder in
33    a public folder store
34
35    \param obj_store the store to search
36    \param id the type of folder to search for
37    \param folder the resulting folder reference
38
39    The following types of folders are supported:
40    - olFolderPublicRoot - the parent (directly or indirectly) for the folders below
41    - olFolderPublicIPMSubtree - Interpersonal Messages (IPM) folders
42    - olFolderPublicNonIPMSubtree - Non-interpersonal message folders
43    - olFolderPublicEFormsRoot - EForms Registry Root Folder
44    - olFolderPublicFreeBusyRoot - Free/busy root folder
45    - olFolderPublicOfflineAB - Offline address book root folder
46    - olFolderPublicEFormsRegistry - EForms Registry for the users locale
47    - olFolderPublicLocalFreeBusy - Site local free/busy folders
48    - olFolderPublicLocalOfflineAB - Site local Offline address book
49    - olFolderPublicNNTPArticle - NNTP article index folder
50
51    \return MAPI_E_SUCCESS on success, otherwise a failure code (MAPISTATUS)
52    indicating the error.
53
54    \note Developers may also call GetLastError() to retrieve the last
55    MAPI error code. Possible MAPI error codes are:
56    - MAPI_E_NOT_INITIALIZED: MAPI subsystem has not been initialized.
57    - MAPI_E_INVALID_PARAMETER: obj_store is undefined
58    - MAPI_E_NOT_FOUND: The specified folder could not be found or is
59      not yet supported.
60
61    \sa MAPIInitialize, OpenPublicFolder, GetLastError
62  */
63 _PUBLIC_ enum MAPISTATUS GetDefaultPublicFolder(mapi_object_t *obj_store,
64                                                 uint64_t *folder,
65                                                 const uint32_t id)
66 {
67         OPENCHANGE_RETVAL_IF(!obj_store, MAPI_E_INVALID_PARAMETER, NULL);
68
69         switch (id) {
70         case olFolderPublicRoot:
71                 *folder = ((mapi_object_store_t *)obj_store->private_data)->fid_pf_public_root;
72                 break;
73         case olFolderPublicIPMSubtree:
74                 *folder = ((mapi_object_store_t *)obj_store->private_data)->fid_pf_ipm_subtree;
75                 break;
76         case olFolderPublicNonIPMSubtree:
77                 *folder = ((mapi_object_store_t *)obj_store->private_data)->fid_pf_non_ipm_subtree;
78                 break;
79         case olFolderPublicEFormsRoot:
80                 *folder = ((mapi_object_store_t *)obj_store->private_data)->fid_pf_EFormsRegistryRoot;
81                 break;
82         case olFolderPublicFreeBusyRoot:
83                 *folder = ((mapi_object_store_t *)obj_store->private_data)->fid_pf_FreeBusyRoot;
84                 break;
85         case olFolderPublicOfflineAB:
86                 *folder = ((mapi_object_store_t *)obj_store->private_data)->fid_pf_OfflineAB;
87                 break;
88         case olFolderPublicEFormsRegistry:
89                 *folder = ((mapi_object_store_t *)obj_store->private_data)->fid_pf_EFormsRegistry;
90                 break;
91         case olFolderPublicLocalFreeBusy:
92                 *folder = ((mapi_object_store_t *)obj_store->private_data)->fid_pf_LocalSiteFreeBusy;
93                 break;
94         case olFolderPublicLocalOfflineAB:
95                 *folder = ((mapi_object_store_t *)obj_store->private_data)->fid_pf_LocalSiteOfflineAB;
96                 break;
97         case olFolderPublicNNTPArticle:
98                 *folder = ((mapi_object_store_t *)obj_store->private_data)->fid_pf_NNTPArticle;
99                 break;
100         default:
101                 OPENCHANGE_RETVAL_ERR(MAPI_E_NOT_FOUND, NULL);
102         }
103
104         return MAPI_E_SUCCESS;
105 }
106
107
108 static enum MAPISTATUS CacheDefaultFolders(mapi_object_t *obj_store)
109 {
110         enum MAPISTATUS         retval;
111         TALLOC_CTX              *mem_ctx;
112         mapi_object_store_t     *store;
113         mapi_object_t           obj_inbox;
114         mapi_id_t               id_inbox;
115         struct SPropTagArray    *SPropTagArray = NULL;
116         struct SRow             aRow;
117         struct SPropValue       *lpProps;
118         uint32_t                count;
119         const struct Binary_r   *entryid;
120
121         /* Sanity checks */
122         OPENCHANGE_RETVAL_IF(!obj_store, MAPI_E_INVALID_PARAMETER, NULL);
123
124         store = (mapi_object_store_t *)obj_store->private_data;
125         OPENCHANGE_RETVAL_IF(!store, MAPI_E_NOT_INITIALIZED, NULL);
126
127         mem_ctx = talloc_named(mapi_object_get_session(obj_store), 0, "CacheDefaultFolders");
128
129         mapi_object_init(&obj_inbox);
130         retval = GetReceiveFolder(obj_store, &id_inbox, NULL);
131         OPENCHANGE_RETVAL_IF(retval, retval, mem_ctx);
132         
133         retval = OpenFolder(obj_store, id_inbox, &obj_inbox);
134         OPENCHANGE_RETVAL_IF(retval, retval, mem_ctx);
135         
136         SPropTagArray = set_SPropTagArray(mem_ctx, 0x6,
137                                           PR_IPM_APPOINTMENT_ENTRYID,
138                                           PR_IPM_CONTACT_ENTRYID,
139                                           PR_IPM_JOURNAL_ENTRYID,
140                                           PR_IPM_NOTE_ENTRYID,
141                                           PR_IPM_TASK_ENTRYID,
142                                           PR_IPM_DRAFTS_ENTRYID);
143         
144         retval = GetProps(&obj_inbox, 0, SPropTagArray, &lpProps, &count);
145         MAPIFreeBuffer(SPropTagArray);
146         OPENCHANGE_RETVAL_IF(retval, retval, mem_ctx);
147         
148         aRow.cValues = count;
149         aRow.lpProps = lpProps;
150         
151         /* set cached calendar FID */
152         entryid = (const struct Binary_r *)find_SPropValue_data(&aRow, PR_IPM_APPOINTMENT_ENTRYID);
153         OPENCHANGE_RETVAL_IF(!entryid, MAPI_E_NOT_FOUND, mem_ctx);
154         retval = GetFIDFromEntryID(entryid->cb, entryid->lpb, id_inbox, &store->fid_calendar);
155         OPENCHANGE_RETVAL_IF(retval, retval, mem_ctx);
156         
157         /* set cached contact FID */
158         entryid = (const struct Binary_r *)find_SPropValue_data(&aRow, PR_IPM_CONTACT_ENTRYID);
159         OPENCHANGE_RETVAL_IF(!entryid, MAPI_E_NOT_FOUND, mem_ctx);
160         retval = GetFIDFromEntryID(entryid->cb, entryid->lpb, id_inbox, &store->fid_contact);
161         OPENCHANGE_RETVAL_IF(retval, retval, mem_ctx);
162         
163         /* set cached journal FID */
164         entryid = (const struct Binary_r *)find_SPropValue_data(&aRow, PR_IPM_JOURNAL_ENTRYID);
165         OPENCHANGE_RETVAL_IF(!entryid, MAPI_E_NOT_FOUND, mem_ctx);
166         retval = GetFIDFromEntryID(entryid->cb, entryid->lpb, id_inbox, &store->fid_journal);
167         OPENCHANGE_RETVAL_IF(retval, retval, mem_ctx);
168         
169         /* set cached note FID */
170         entryid = (const struct Binary_r *)find_SPropValue_data(&aRow, PR_IPM_NOTE_ENTRYID);
171         OPENCHANGE_RETVAL_IF(!entryid, MAPI_E_NOT_FOUND, mem_ctx);
172         retval = GetFIDFromEntryID(entryid->cb, entryid->lpb, id_inbox, &store->fid_note);
173         OPENCHANGE_RETVAL_IF(retval, retval, mem_ctx);
174         
175         /* set cached task FID */
176         entryid = (const struct Binary_r *)find_SPropValue_data(&aRow, PR_IPM_TASK_ENTRYID);
177         OPENCHANGE_RETVAL_IF(!entryid, MAPI_E_NOT_FOUND, mem_ctx);
178         retval = GetFIDFromEntryID(entryid->cb, entryid->lpb, id_inbox, &store->fid_task);
179         OPENCHANGE_RETVAL_IF(retval, retval, mem_ctx);
180         
181         /* set cached drafts FID */
182         entryid = (const struct Binary_r *)find_SPropValue_data(&aRow, PR_IPM_DRAFTS_ENTRYID);
183         OPENCHANGE_RETVAL_IF(!entryid, MAPI_E_NOT_FOUND, mem_ctx);
184         retval = GetFIDFromEntryID(entryid->cb, entryid->lpb, id_inbox, &store->fid_drafts);
185         OPENCHANGE_RETVAL_IF(retval, retval, mem_ctx);
186
187         store->store_type = PrivateFolderWithCachedFids;
188         
189         mapi_object_release(&obj_inbox);
190         talloc_free(mem_ctx);
191
192         return MAPI_E_SUCCESS;
193 }
194
195
196 /**
197    \details Retrieves the folder id for the specified default folder
198    in a mailbox store
199
200    \param obj_store the store to search
201    \param id the type of folder to search for
202    \param folder the resulting folder reference
203
204    The following types of folders are supported:
205    - olFolderTopInformationStore
206    - olFolderDeletedItems
207    - olFolderOutbox
208    - olFolderSentMail
209    - olFolderInbox
210    - olFolderCommonView
211    - olFolderCalendar
212    - olFolderContacts
213    - olFolderJournal
214    - olFolderNotes
215    - olFolderTasks
216    - olFolderDrafts
217    - olFolderReminders
218    - olFolderFinder
219
220    Note that this function will cache FID values for common accessed
221    folders such as calendar, contact, journal, note, task and drafts
222    until the store object got released.
223
224    \return MAPI_E_SUCCESS on success, otherwise a failure code (MAPISTATUS)
225    indicating the error.
226
227    \note Developers may also call GetLastError() to retrieve the last
228    MAPI error code. Possible MAPI error codes are:
229    - MAPI_E_NOT_INITIALIZED: MAPI subsystem has not been initialized.
230    - MAPI_E_INVALID_PARAMETER: obj_store is undefined
231    - MAPI_E_NOT_FOUND: The specified folder could not be found or is
232      not yet supported.
233
234    \sa MAPIInitialize, OpenMsgStore, GetLastError
235 */
236 _PUBLIC_ enum MAPISTATUS GetDefaultFolder(mapi_object_t *obj_store, 
237                                           uint64_t *folder,
238                                           const uint32_t id)
239 {
240         enum MAPISTATUS                 retval;
241         mapi_object_store_t             *store;
242
243         /* Sanity checks */
244         OPENCHANGE_RETVAL_IF(!obj_store, MAPI_E_INVALID_PARAMETER, NULL);
245         OPENCHANGE_RETVAL_IF(!folder, MAPI_E_INVALID_PARAMETER, NULL);
246
247         store = (mapi_object_store_t *)obj_store->private_data;
248         OPENCHANGE_RETVAL_IF(!store, MAPI_E_NOT_INITIALIZED, NULL);
249
250         if ((id > 6) && (store->store_type == PrivateFolderWithoutCachedFids)) {
251                 retval = CacheDefaultFolders(obj_store);
252                 OPENCHANGE_RETVAL_IF(retval, retval, NULL);
253         } 
254
255         switch (id) {
256         case olFolderMailboxRoot:
257                 *folder = store->fid_mailbox_root;
258                 return MAPI_E_SUCCESS;
259         case olFolderTopInformationStore:
260                 *folder = store->fid_top_information_store;
261                 return MAPI_E_SUCCESS;
262         case olFolderDeletedItems:
263                 *folder = store->fid_deleted_items;
264                 return MAPI_E_SUCCESS;
265         case olFolderOutbox:
266                 *folder = store->fid_outbox;
267                 return MAPI_E_SUCCESS;
268         case olFolderSentMail:
269                 *folder = store->fid_sent_items;
270                 return MAPI_E_SUCCESS;
271         case olFolderInbox:
272                 *folder = store->fid_inbox;
273                 return MAPI_E_SUCCESS;
274         case olFolderCommonView:
275                 *folder = store->fid_common_views;
276                 return MAPI_E_SUCCESS;
277         case olFolderCalendar:
278                 *folder = store->fid_calendar;
279                 return MAPI_E_SUCCESS;
280         case olFolderContacts:
281                 *folder = store->fid_contact;
282                 return MAPI_E_SUCCESS;
283         case olFolderJournal:
284                 *folder = store->fid_journal;
285                 return MAPI_E_SUCCESS;
286         case olFolderNotes:
287                 *folder = store->fid_note;
288                 return MAPI_E_SUCCESS;
289         case olFolderTasks:
290                 *folder = store->fid_task;
291                 return MAPI_E_SUCCESS;
292         case olFolderDrafts:
293                 *folder = store->fid_drafts;
294                 return MAPI_E_SUCCESS;
295         case olFolderFinder:
296                 *folder = store->fid_search;
297                 return MAPI_E_SUCCESS;
298         default:
299                 *folder = 0;
300                 OPENCHANGE_RETVAL_ERR(MAPI_E_NOT_FOUND, 0);
301         }
302 }
303
304
305 /**
306    \details Check if a given folder identifier matches with a
307    system/default one and optionally returns the olFolder type
308
309    \param obj_store pointer to the store object
310    \param fid reference to the folder identifier to check
311    \param olFolder pointer to the returned olFolder
312
313    \return true on success, otherwise false
314  */
315 _PUBLIC_ bool IsMailboxFolder(mapi_object_t *obj_store, 
316                               uint64_t fid, 
317                               uint32_t *olFolder)
318 {
319         enum MAPISTATUS         retval;
320         mapi_object_store_t     *store;
321         uint32_t                olFolderNum = 0;
322         bool                    ret = true;
323
324         if (!obj_store) {
325                 return false;
326         }
327         store = (mapi_object_store_t *) obj_store->private_data;
328         if (!store) {
329                 return false;
330         }
331
332         if (fid == 0x0) {
333                 return false;
334         }
335
336         if (store->store_type == PrivateFolderWithoutCachedFids) {
337                 retval = CacheDefaultFolders(obj_store);
338                 if (retval) {
339                         return false;
340                 }
341         }
342
343         if(fid == store->fid_top_information_store) {
344                 olFolderNum = olFolderTopInformationStore;
345         } else if (fid == store->fid_deleted_items) {
346                 olFolderNum = olFolderDeletedItems;
347         } else if (fid == store->fid_outbox) {
348                 olFolderNum = olFolderOutbox;
349         } else if (fid == store->fid_sent_items) {
350                 olFolderNum = olFolderSentMail;
351         } else if (fid == store->fid_inbox) {
352                 olFolderNum = olFolderInbox;
353         } else if (fid == store->fid_common_views) {
354                 olFolderNum = olFolderCommonView;
355         } else if (fid == store->fid_calendar) {
356                 olFolderNum = olFolderCalendar;
357         } else if (fid == store->fid_contact) {
358                 olFolderNum = olFolderContacts;
359         } else if (fid == store->fid_journal) {
360                 olFolderNum = olFolderJournal;
361         } else if (fid == store->fid_note) {
362                 olFolderNum = olFolderNotes;
363         } else if (fid == store->fid_task) {
364                 olFolderNum = olFolderTasks;
365         } else if (fid == store->fid_drafts) {
366                 olFolderNum = olFolderDrafts;
367         } else if (fid == store->fid_search) {
368                 olFolderNum = olFolderFinder;
369         } else if (fid == store->fid_pf_OfflineAB) {
370                 olFolderNum = olFolderPublicOfflineAB;
371         } else if (fid == store->fid_pf_FreeBusyRoot) {
372                 olFolderNum = olFolderPublicFreeBusyRoot;
373         } else if (fid == store->fid_pf_EFormsRegistryRoot) {
374                 olFolderNum = olFolderPublicEFormsRoot;
375         } else if (fid == store->fid_pf_EFormsRegistry) {
376                 olFolderNum = olFolderPublicEFormsRegistry;
377         } else if (fid == store->fid_pf_public_root) {
378                 olFolderNum = olFolderPublicRoot;
379         } else if (fid == store->fid_pf_ipm_subtree) {
380                 olFolderNum = olFolderPublicIPMSubtree;
381         } else if (fid == store->fid_pf_non_ipm_subtree) {
382                 olFolderNum = olFolderPublicNonIPMSubtree;
383         } else if (fid == store->fid_pf_LocalSiteFreeBusy) {
384                 olFolderNum = olFolderPublicLocalFreeBusy;
385         } else if (fid == store->fid_pf_LocalSiteOfflineAB) {
386                 olFolderNum = olFolderPublicLocalOfflineAB;
387         } else if (fid == store->fid_pf_NNTPArticle) {
388                 olFolderNum = olFolderPublicNNTPArticle;
389         } else {
390                 olFolderNum = 0xFFFFFFFF;
391                 ret = false;
392         }
393
394         if (olFolder) *olFolder = olFolderNum;
395         return ret;
396 }
397
398 /**
399    \details Retrieves the total and unread number of items for a
400     specified folder.
401
402     \param obj_folder the folder to get item counts for
403     \param unread the number of items in the folder (result)
404     \param total the number of items in the folder, including unread
405     items (result)
406
407    \return MAPI_E_SUCCESS on success, otherwise a failure code (MAPISTATUS)
408    indicating the error.
409
410    \note Developers may also call GetLastError() to retrieve the last
411    MAPI error code. Possible MAPI error codes are:
412    - MAPI_E_NOT_INITIALIZED: MAPI subsystem has not been initialized.
413    - MAPI_E_INVALID_PARAMETER: obj_folder is undefined
414    - MAPI_E_NOT_FOUND: The specified folder could not be found or is
415      not yet supported.
416
417    \sa MAPIInitialize, OpenFolder, GetLastError
418 */
419 _PUBLIC_ enum MAPISTATUS GetFolderItemsCount(mapi_object_t *obj_folder,
420                                              uint32_t *unread,
421                                              uint32_t *total)
422 {
423         enum MAPISTATUS         retval;
424         TALLOC_CTX              *mem_ctx;
425         struct SPropTagArray    *SPropTagArray;
426         struct SPropValue       *lpProps;
427         uint32_t                count;
428
429         OPENCHANGE_RETVAL_IF(!obj_folder, MAPI_E_INVALID_PARAMETER, NULL);
430         OPENCHANGE_RETVAL_IF(!unread, MAPI_E_INVALID_PARAMETER, NULL);
431         OPENCHANGE_RETVAL_IF(!total, MAPI_E_INVALID_PARAMETER, NULL);
432
433         mem_ctx = talloc_named(mapi_object_get_session(obj_folder), 0, "GetFolderItemsCount");
434
435         SPropTagArray = set_SPropTagArray(mem_ctx, 0x2, 
436                                           PR_CONTENT_UNREAD,
437                                           PR_CONTENT_COUNT);
438
439         retval = GetProps(obj_folder, 0, SPropTagArray, &lpProps, &count);
440         MAPIFreeBuffer(SPropTagArray);
441         OPENCHANGE_RETVAL_IF(retval, retval, mem_ctx);
442
443         *unread = lpProps[0].value.l;
444         *total = lpProps[1].value.l;
445
446         talloc_free(mem_ctx);
447         
448         return MAPI_E_SUCCESS;
449 }
450
451
452 /**
453    \details Adds permissions for a user on a given folder
454
455    \param obj_folder the folder we add permission for
456    \param username the Exchange username we add permissions for
457    \param role the permission mask value
458
459    The following permissions and rights are supported:
460    - RightsNone
461    - RightsReadItems
462    - RightsCreateItems
463    - RightsEditOwn
464    - RightsDeleteOwn
465    - RightsEditAll
466    - RightsDeleteAll
467    - RightsCreateSubfolders
468    - RightsFolderOwner
469    - RightsFolderContact
470    - RoleNone
471    - RoleReviewer
472    - RoleContributor
473    - RoleNoneditingAuthor
474    - RoleAuthor
475    - RoleEditor
476    - RolePublishAuthor
477    - RolePublishEditor
478    - RightsAll
479    - RoleOwner
480
481    \return MAPI_E_SUCCESS on success, otherwise a failure code (MAPISTATUS)
482    indicating the error.
483
484    \note Developers may also call GetLastError() to retrieve the last
485    MAPI error code. Possible MAPI error codes are:
486    - MAPI_E_NOT_INITIALIZED: MAPI subsystem has not been initialized.
487    - MAPI_E_INVALID_PARAMETER: username is NULL
488
489    \sa ResolveNames, ModifyPermissions
490  */
491 _PUBLIC_ enum MAPISTATUS AddUserPermission(mapi_object_t *obj_folder, const char *username, enum ACLRIGHTS role)
492 {
493         enum MAPISTATUS                 retval;
494         TALLOC_CTX                      *mem_ctx;
495         struct SPropTagArray            *SPropTagArray;
496         const char                      *names[2];
497         struct SRowSet                  *rows = NULL;
498         struct PropertyTagArray_r       *flaglist = NULL;
499         struct mapi_PermissionsData     rowList;
500
501         /* Sanity checks */
502         OPENCHANGE_RETVAL_IF(!obj_folder, MAPI_E_INVALID_PARAMETER, NULL);
503         OPENCHANGE_RETVAL_IF(!username, MAPI_E_INVALID_PARAMETER, NULL);
504
505         rowList.ModifyFlags = 0;
506
507         mem_ctx = talloc_named(mapi_object_get_session(obj_folder), 0, "AddUserPermission");
508
509         /* query Address book */
510
511         SPropTagArray = set_SPropTagArray(mem_ctx, 2, PR_ENTRYID, PR_DISPLAY_NAME);
512         names[0] = username;
513         names[1] = NULL;
514         retval = ResolveNames(mapi_object_get_session(obj_folder), (const char **)names,
515                               SPropTagArray, &rows, &flaglist, 0);
516         MAPIFreeBuffer(SPropTagArray);
517         OPENCHANGE_RETVAL_IF(retval, retval, mem_ctx);
518
519         /* Sanity checks */
520         OPENCHANGE_RETVAL_IF(!flaglist, MAPI_E_NOT_FOUND, mem_ctx);
521         OPENCHANGE_RETVAL_IF(!rows, MAPI_E_NOT_FOUND, mem_ctx);
522         /* Check if the username was found */
523         OPENCHANGE_RETVAL_IF((flaglist->aulPropTag[0] != MAPI_RESOLVED), MAPI_E_NOT_FOUND, mem_ctx);
524
525         rowList.ModifyCount = 1;
526         rowList.PermissionsData = talloc_array(mem_ctx, struct PermissionData, 1);
527         rowList.PermissionsData[0].PermissionDataFlags = ROW_ADD;
528         rowList.PermissionsData[0].lpProps.cValues = 2;
529         rowList.PermissionsData[0].lpProps.lpProps = talloc_array(mem_ctx, struct mapi_SPropValue, 2);
530         cast_mapi_SPropValue((TALLOC_CTX *)rowList.PermissionsData[0].lpProps.lpProps,
531                              &rowList.PermissionsData[0].lpProps.lpProps[0], &rows->aRow[0].lpProps[0]);
532         rowList.PermissionsData[0].lpProps.lpProps[1].ulPropTag = PR_MEMBER_RIGHTS;
533         rowList.PermissionsData[0].lpProps.lpProps[1].value.l = role;
534
535         retval = ModifyPermissions(obj_folder, 0, &rowList);
536         OPENCHANGE_RETVAL_IF(retval, retval, mem_ctx);
537
538         talloc_free(mem_ctx);
539
540         return MAPI_E_SUCCESS;
541 }
542
543
544 /**
545    \details Modify permissions for a user on a given folder
546    
547    \param obj_folder the folder to modify permissions for
548    \param username the Exchange username to modify permissions for
549    \param role the permission mask value (see AddUserPermission)
550
551    \return MAPI_E_SUCCESS on success, otherwise a failure code (MAPISTATUS)
552    indicating the error.
553
554    \note Developers may also call GetLastError() to retrieve the last
555    MAPI error code. Possible MAPI error codes are:
556    - MAPI_E_NOT_INITIALIZED: MAPI subsystem has not been initialized.
557    - MAPI_E_INVALID_PARAMETER: username is NULL
558    - MAPI_E_NOT_FOUND: couldn't find or change permissions for the
559      given user
560
561    \sa AddUserPermission, ResolveNames, GetPermissionsTable, ModifyPermissions
562  */
563 _PUBLIC_ enum MAPISTATUS ModifyUserPermission(mapi_object_t *obj_folder, 
564                                               const char *username, 
565                                               enum ACLRIGHTS role)
566 {
567         enum MAPISTATUS                 retval;
568         TALLOC_CTX                      *mem_ctx;
569         struct SPropTagArray            *SPropTagArray;
570         const char                      *names[2];
571         const char                      *user = NULL;
572         struct SRowSet                  *rows = NULL;
573         struct SRowSet                  rowset;
574         struct PropertyTagArray_r       *flaglist = NULL;
575         struct mapi_PermissionsData     rowList;
576         struct SPropValue               *lpProp;
577         mapi_object_t                   obj_table;
578         uint32_t                        Numerator;
579         uint32_t                        Denominator;
580         bool                            found = false;
581         uint32_t                        i = 0;
582
583         OPENCHANGE_RETVAL_IF(!obj_folder, MAPI_E_INVALID_PARAMETER, NULL);
584         OPENCHANGE_RETVAL_IF(!username, MAPI_E_INVALID_PARAMETER, NULL);
585
586         rowList.ModifyFlags = 0;
587
588         mem_ctx = talloc_named(mapi_object_get_session(obj_folder), 0, "ModifyUserPermission");
589
590         SPropTagArray = set_SPropTagArray(mem_ctx, 2, PR_ENTRYID, PR_DISPLAY_NAME);
591         names[0] = username;
592         names[1] = NULL;
593         retval = ResolveNames(mapi_object_get_session(obj_folder), (const char **)names, 
594                               SPropTagArray, &rows, &flaglist, 0);
595         MAPIFreeBuffer(SPropTagArray);
596         OPENCHANGE_RETVAL_IF(retval, retval, mem_ctx);
597
598         if (flaglist->aulPropTag[0] == MAPI_RESOLVED) {
599           user = (const char *) find_SPropValue_data(&(rows->aRow[0]), PR_DISPLAY_NAME);
600         } else {
601                 /* Special case: Not a AD user account but Default or
602                  * Anonymous. Since names are language specific, we
603                  * can't use strcmp 
604                  */
605                 user = username;
606         }
607
608         mapi_object_init(&obj_table);
609         retval = GetPermissionsTable(obj_folder, 0x00, &obj_table);
610
611         OPENCHANGE_RETVAL_IF(retval, retval, mem_ctx);
612
613         SPropTagArray = set_SPropTagArray(mem_ctx, 4,
614                                           PR_ENTRYID,
615                                           PR_MEMBER_RIGHTS,
616                                           PR_MEMBER_ID,
617                                           PR_MEMBER_NAME);
618         retval = SetColumns(&obj_table, SPropTagArray);
619         MAPIFreeBuffer(SPropTagArray);
620         OPENCHANGE_RETVAL_IF(retval, retval, mem_ctx);
621
622         retval = QueryPosition(&obj_table, &Numerator, &Denominator);
623         OPENCHANGE_RETVAL_IF(retval, retval, mem_ctx);
624
625         retval = QueryRows(&obj_table, Denominator, TBL_ADVANCE, &rowset);
626         OPENCHANGE_RETVAL_IF(retval, retval, mem_ctx);
627
628         for (i = 0; i < rowset.cRows; i++) {
629                 lpProp = get_SPropValue_SRow(&rowset.aRow[i], PR_MEMBER_NAME);
630                 if (lpProp && lpProp->value.lpszA) {
631                         if (!strcmp(lpProp->value.lpszA, user)) {
632                                 rowList.ModifyCount = 1;
633                                 rowList.PermissionsData = talloc_array(mem_ctx, struct PermissionData, 1);
634                                 rowList.PermissionsData[0].PermissionDataFlags = ROW_MODIFY;
635                                 rowList.PermissionsData[0].lpProps.cValues = 2;
636                                 rowList.PermissionsData[0].lpProps.lpProps = talloc_array(mem_ctx, struct mapi_SPropValue, 2);
637                                 lpProp = get_SPropValue_SRow(&(rowset.aRow[i]), PR_MEMBER_ID);
638                                 if (!lpProp) {
639                                         continue;
640                                 }
641                                 rowList.PermissionsData[0].lpProps.lpProps[0].ulPropTag = PR_MEMBER_ID;
642                                 rowList.PermissionsData[0].lpProps.lpProps[0].value.d = lpProp->value.d;
643                                 rowList.PermissionsData[0].lpProps.lpProps[1].ulPropTag = PR_MEMBER_RIGHTS;
644                                 rowList.PermissionsData[0].lpProps.lpProps[1].value.l = role;
645                                 
646                                 retval = ModifyPermissions(obj_folder, 0, &rowList);
647                                 OPENCHANGE_RETVAL_IF(retval, retval, mem_ctx);
648
649                                 found = true;
650                                 break;
651                         }
652                 }
653         }
654
655         mapi_object_release(&obj_table);
656         talloc_free(mem_ctx);
657
658         OPENCHANGE_RETVAL_IF((!found), MAPI_E_NOT_FOUND, 0);
659
660         return MAPI_E_SUCCESS;
661 }
662
663
664 /**
665    \details Remove permissions for a user on a given folder
666
667    \param obj_folder the folder to remove permission from
668    \param username the Exchange username to remove permissions for
669
670    \return MAPI_E_SUCCESS on success, otherwise a failure code (MAPISTATUS)
671    indicating the error.
672
673    \note Developers may also call GetLastError() to retrieve the last
674    MAPI error code. Possible MAPI error codes are:
675    - MAPI_E_NOT_INITIALIZED: MAPI subsystem has not been initialized.
676    - MAPI_E_INVALID_PARAMETER: username or obj_folder are NULL
677    - MAPI_E_NOT_FOUND: couldn't find or remove permissions for the
678      given user
679
680    \sa ResolveNames, GetPermissionsTable, ModifyPermissions
681  */
682 _PUBLIC_ enum MAPISTATUS RemoveUserPermission(mapi_object_t *obj_folder, 
683                                               const char *username)
684 {
685         enum MAPISTATUS                 retval;
686         TALLOC_CTX                      *mem_ctx;
687         struct SPropTagArray            *SPropTagArray;
688         const char                      *names[2];
689         const char                      *user = NULL;
690         struct SRowSet                  *rows = NULL;
691         struct SRowSet                  rowset;
692         struct PropertyTagArray_r       *flaglist = NULL;
693         struct mapi_PermissionsData     rowList;
694         struct SPropValue               *lpProp;
695         mapi_object_t                   obj_table;
696         uint32_t                        Numerator;
697         uint32_t                        Denominator;
698         bool                            found = false;
699         uint32_t                        i = 0;
700
701         OPENCHANGE_RETVAL_IF(!obj_folder, MAPI_E_INVALID_PARAMETER, NULL);
702         OPENCHANGE_RETVAL_IF(!username, MAPI_E_INVALID_PARAMETER, NULL);
703
704         mem_ctx = talloc_named(mapi_object_get_session(obj_folder), 0, "RemoveUserPermission");
705
706         SPropTagArray = set_SPropTagArray(mem_ctx, 2, PR_ENTRYID, PR_DISPLAY_NAME);
707         names[0] = username;
708         names[1] = NULL;
709         retval = ResolveNames(mapi_object_get_session(obj_folder), (const char **)names, 
710                               SPropTagArray, &rows, &flaglist, 0);
711         MAPIFreeBuffer(SPropTagArray);
712         OPENCHANGE_RETVAL_IF(retval, retval, mem_ctx);
713
714         /* Check if the username was found */
715         OPENCHANGE_RETVAL_IF((flaglist->aulPropTag[0] != MAPI_RESOLVED), MAPI_E_NOT_FOUND, mem_ctx);
716
717         user = (const char *)find_SPropValue_data(&(rows->aRow[0]), PR_DISPLAY_NAME);
718
719         mapi_object_init(&obj_table);
720         retval = GetPermissionsTable(obj_folder, 0x00, &obj_table);
721         OPENCHANGE_RETVAL_IF(retval, retval, mem_ctx);
722
723         SPropTagArray = set_SPropTagArray(mem_ctx, 4,
724                                           PR_ENTRYID,
725                                           PR_MEMBER_RIGHTS,
726                                           PR_MEMBER_ID,
727                                           PR_MEMBER_NAME);
728         retval = SetColumns(&obj_table, SPropTagArray);
729         MAPIFreeBuffer(SPropTagArray);
730         OPENCHANGE_RETVAL_IF(retval, retval, mem_ctx);
731
732         retval = QueryPosition(&obj_table, &Numerator, &Denominator);
733         OPENCHANGE_RETVAL_IF(retval, retval, mem_ctx);
734
735         retval = QueryRows(&obj_table, Denominator, TBL_ADVANCE, &rowset);
736         OPENCHANGE_RETVAL_IF(retval, retval, mem_ctx);
737
738         for (i = 0; i < rowset.cRows; i++) {
739                 lpProp = get_SPropValue_SRow(&rowset.aRow[i], PR_MEMBER_NAME);
740                 if (lpProp && lpProp->value.lpszA) {
741                         if (!strcmp(lpProp->value.lpszA, user)) {
742                                 rowList.ModifyCount = 1;
743                                 rowList.PermissionsData = talloc_array(mem_ctx, struct PermissionData, 1);
744                                 rowList.PermissionsData[0].PermissionDataFlags = ROW_REMOVE;
745                                 rowList.PermissionsData[0].lpProps.cValues = 1;
746                                 rowList.PermissionsData[0].lpProps.lpProps = talloc_array(mem_ctx, struct mapi_SPropValue, 1);
747                                 lpProp = get_SPropValue_SRow(&(rowset.aRow[i]), PR_MEMBER_ID);
748                                 if (!lpProp) {
749                                         continue;
750                                 }
751                                 rowList.PermissionsData[0].lpProps.lpProps[0].ulPropTag = PR_MEMBER_ID;
752                                 rowList.PermissionsData[0].lpProps.lpProps[0].value.d = lpProp->value.d;
753                                 
754                                 retval = ModifyPermissions(obj_folder, 0, &rowList);
755                                 OPENCHANGE_RETVAL_IF(retval, retval, mem_ctx);
756                                 found = true;
757                                 break;
758                         }
759                 }
760         }
761
762         mapi_object_release(&obj_table);
763         talloc_free(mem_ctx);
764
765         OPENCHANGE_RETVAL_IF((found != true), MAPI_E_NOT_FOUND, 0);
766
767         return MAPI_E_SUCCESS;
768 }
769
770
771 /**
772    \details Implement the BestBody algorithm and return the best body
773    content type for a given message.
774
775    \param obj_message the message we find the best body for
776    \param format the format - see below.
777
778    \return MAPI_E_SUCCESS on success, otherwise MAPI_E_NOT_FOUND. If
779    MAPI_E_NOT_FOUND is returned then format is set to 0x0
780    (undefined). If MAPI_E_SUCCESS is returned, then format can have
781    one of the following values:
782    - olEditorText: format is plain text
783    - olEditorHTML: format is HTML
784    - olEditorRTF: format is RTF
785  */
786 _PUBLIC_ enum MAPISTATUS GetBestBody(mapi_object_t *obj_message, 
787                                      uint8_t *format)
788 {
789         struct mapi_context     *mapi_ctx;
790         struct mapi_session     *session;
791         enum MAPISTATUS         retval;
792         struct SPropTagArray    *SPropTagArray = NULL;
793         struct SPropValue       *lpProps;
794         struct SRow             aRow;
795         uint32_t                count;
796         uint8_t                 RtfInSync;
797         uint32_t                PlainStatus;
798         uint32_t                RtfStatus;
799         uint32_t                HtmlStatus;
800         const uint32_t          *err_code;
801
802         /* Sanity checks */
803         OPENCHANGE_RETVAL_IF(!obj_message, MAPI_E_INVALID_PARAMETER, NULL);
804         OPENCHANGE_RETVAL_IF(!format, MAPI_E_INVALID_PARAMETER, NULL);
805
806         session = mapi_object_get_session(obj_message);
807         mapi_ctx = session->mapi_ctx;
808
809         /* Step 1. Retrieve properties needed by the BestBody algorithm */
810         SPropTagArray = set_SPropTagArray(mapi_ctx->mem_ctx, 0x4,
811                                           PR_BODY_UNICODE,
812                                           PR_RTF_COMPRESSED,
813                                           PR_HTML,
814                                           PR_RTF_IN_SYNC);
815         retval = GetProps(obj_message, 0, SPropTagArray, &lpProps, &count);
816         MAPIFreeBuffer(SPropTagArray);
817         if (retval != MAPI_E_SUCCESS) {
818                 *format = 0;
819                 OPENCHANGE_RETVAL_ERR(MAPI_E_NOT_FOUND, 0);
820         }
821
822         aRow.ulAdrEntryPad = 0;
823         aRow.cValues = count;
824         aRow.lpProps = lpProps;
825
826         /* Step 2. Retrieve properties values and map errors */
827         err_code = (const uint32_t *)find_SPropValue_data(&aRow, PR_RTF_IN_SYNC);
828         RtfInSync = (err_code) ? *err_code : 0;
829
830         err_code = (const uint32_t *)find_SPropValue_data(&aRow, PR_BODY_ERROR);
831         PlainStatus = (err_code) ? *err_code : 0;
832
833         err_code = (const uint32_t *)find_SPropValue_data(&aRow, PR_RTF_COMPRESSED_ERROR);
834         RtfStatus = (err_code) ? *err_code : 0;
835
836         err_code = (const uint32_t *)find_SPropValue_data(&aRow, PR_BODY_HTML_ERROR);
837         HtmlStatus = (err_code) ? *err_code : 0;
838
839         /* Step 3. Determine the body format (9 possible cases) */
840
841         /* case 1 */
842         if ((PlainStatus == MAPI_E_NOT_FOUND) && (RtfStatus == MAPI_E_NOT_FOUND) && 
843             (HtmlStatus == MAPI_E_NOT_FOUND)) {
844                 *format = 0;
845                 OPENCHANGE_RETVAL_ERR(MAPI_E_NOT_FOUND, 0);
846         }
847         
848         /* case 2 */
849         if (((PlainStatus == MAPI_E_NOT_ENOUGH_MEMORY) || (PlainStatus == 0)) && 
850             (RtfStatus == MAPI_E_NOT_FOUND) && (HtmlStatus == MAPI_E_NOT_FOUND)) {
851                 *format = olEditorText;
852                 return MAPI_E_SUCCESS;
853         }
854
855         /* case 3 */
856         if ((PlainStatus == MAPI_E_NOT_ENOUGH_MEMORY) &&
857             (RtfStatus == MAPI_E_NOT_ENOUGH_MEMORY) && 
858             (HtmlStatus == MAPI_E_NOT_FOUND)) {
859                 *format = olEditorRTF;
860                 return MAPI_E_SUCCESS;
861         }
862
863         /* case 4 */
864         if ((PlainStatus == MAPI_E_NOT_ENOUGH_MEMORY) &&
865             (RtfStatus == MAPI_E_NOT_ENOUGH_MEMORY) &&
866             (HtmlStatus == MAPI_E_NOT_ENOUGH_MEMORY) &&
867             (RtfInSync == 1)) {
868                 *format = olEditorRTF;
869                 return MAPI_E_SUCCESS;
870         }
871
872         /* case 5 */
873         if ((PlainStatus == MAPI_E_NOT_ENOUGH_MEMORY) &&
874             (RtfStatus == MAPI_E_NOT_ENOUGH_MEMORY) &&
875             (HtmlStatus == MAPI_E_NOT_ENOUGH_MEMORY) &&
876             (RtfInSync == 0)) {
877                 *format = olEditorHTML;
878                 return MAPI_E_SUCCESS;
879         }
880
881         /* case 6 */
882         if (((RtfStatus == 0) || (RtfStatus == MAPI_E_NOT_ENOUGH_MEMORY)) &&
883             ((HtmlStatus == 0) || (HtmlStatus == MAPI_E_NOT_ENOUGH_MEMORY)) &&
884             (RtfInSync == 1)) {
885                 *format = olEditorRTF;
886                 return MAPI_E_SUCCESS;
887         }
888
889         /* case 7 */
890         if (((RtfStatus == 0) || (RtfStatus == MAPI_E_NOT_ENOUGH_MEMORY)) &&
891             ((HtmlStatus == 0) || (HtmlStatus == MAPI_E_NOT_ENOUGH_MEMORY)) &&
892             (RtfInSync == 0)) {
893                 *format = olEditorHTML;
894                 return MAPI_E_SUCCESS;
895         }
896         
897         /* case 8 */
898         if (((PlainStatus == 0) || (PlainStatus == MAPI_E_NOT_ENOUGH_MEMORY)) &&
899             ((RtfStatus == 0) || (RtfStatus == MAPI_E_NOT_ENOUGH_MEMORY)) &&
900             (RtfInSync == 1)) {
901                 *format = olEditorRTF;
902                 return MAPI_E_SUCCESS;
903         }
904
905         /* case 9 */
906         if (((PlainStatus == 0) || (PlainStatus == MAPI_E_NOT_ENOUGH_MEMORY)) &&
907             ((RtfStatus == 0) || (RtfStatus == MAPI_E_NOT_ENOUGH_MEMORY)) &&
908             (RtfInSync == 0)) {
909                 *format = olEditorText;
910                 return MAPI_E_SUCCESS;
911         }
912
913         OPENCHANGE_RETVAL_ERR(MAPI_E_NOT_FOUND, 0);
914 }