Only add TYPE and PidTagMessageClass if ctx->type exists
[jelmer/openchange.git] / libocpf / ocpf_public.c
1 /*
2    OpenChange OCPF (OpenChange Property File) implementation.
3
4    Copyright (C) Julien Kerihuel 2008-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 /**
21    \file ocpf_public.c
22
23    \brief public OCPF API
24  */
25
26 #include <sys/stat.h>
27
28 #include "libocpf/ocpf.h"
29 #include "libocpf/ocpf_api.h"
30
31 int ocpf_yylex_init(void *);
32 int ocpf_yylex_init_extra(struct ocpf_context *, void *);
33 void ocpf_yyset_in(FILE *, void *);
34 int ocpf_yylex_destroy(void *);
35 int ocpf_yyparse(struct ocpf_context *, void *);
36
37 struct ocpf     *ocpf;
38 int             error_flag;
39
40
41 /**
42    \details Initialize OCPF context
43
44    Initialize ocpf context and allocate memory for internal structures
45
46    \return OCPF_SUCCESS on success, otherwise OCPF_ERROR
47
48    \sa ocpf_release, ocpf_parse
49  */
50 _PUBLIC_ int ocpf_init(void)
51 {
52         TALLOC_CTX      *mem_ctx;
53         
54         OCPF_RETVAL_IF(ocpf, NULL, OCPF_INITIALIZED, NULL);
55
56         mem_ctx = talloc_named(NULL, 0, "ocpf");
57         ocpf = talloc_zero(mem_ctx, struct ocpf);
58         ocpf->mem_ctx = mem_ctx;
59
60         ocpf->context = talloc_zero(mem_ctx, struct ocpf_context);
61         ocpf->free_id = talloc_zero(mem_ctx, struct ocpf_freeid);
62         ocpf->last_id = 1;
63
64         return OCPF_SUCCESS;
65 }
66
67
68 /**
69    \details Uninitialize OCPF context
70
71    Uninitialize the global OCPF context and release memory.
72
73    \return OCPF_SUCCESS on success, otherwise OCPF_ERROR
74
75    \sa ocpf_init
76  */
77 _PUBLIC_ int ocpf_release(void)
78 {
79         OCPF_RETVAL_IF(!ocpf || !ocpf->mem_ctx, NULL, OCPF_NOT_INITIALIZED, NULL);      
80
81         talloc_free(ocpf->mem_ctx);
82         ocpf = NULL;
83
84         return OCPF_SUCCESS;
85 }
86
87
88 /**
89    \details Create a new OCPF context
90
91    \param filename the filename to process
92    \param context_id pointer to the context identifier the function
93    \param flags Flags controlling how the OCPF should be opened
94
95    \return OCPF_SUCCESS on success, otherwise OCPF_ERROR
96  */
97 _PUBLIC_ int ocpf_new_context(const char *filename, uint32_t *context_id, uint8_t flags)
98 {
99         struct ocpf_context     *ctx;
100         bool                    existing = false;
101
102         OCPF_RETVAL_IF(!ocpf || !ocpf->mem_ctx, NULL, OCPF_NOT_INITIALIZED, NULL);
103
104         ctx = ocpf_context_add(ocpf, filename, context_id, flags, &existing);
105         if (!ctx) {
106                 return OCPF_ERROR;
107         }
108
109         if (existing == false) {
110                 DLIST_ADD_END(ocpf->context, ctx, struct ocpf_context *);
111                 return OCPF_SUCCESS;
112         } 
113
114         return OCPF_E_EXIST;
115 }
116
117
118 /**
119    \details Delete an OCPF context
120
121    \param context_id context identifier referencing the context to
122    delete
123
124    \return OCPF_SUCCESS on success, otherwise OCPF_ERROR
125  */
126 _PUBLIC_ int ocpf_del_context(uint32_t context_id)
127 {
128         int                     ret;
129         struct ocpf_context     *ctx;
130
131         /* Sanity checks */
132         OCPF_RETVAL_IF(!ocpf || !ocpf->mem_ctx, NULL, OCPF_NOT_INITIALIZED, NULL);
133
134         /* Search the context */
135         ctx = ocpf_context_search_by_context_id(ocpf->context, context_id);
136         OCPF_RETVAL_IF(!ctx, NULL, OCPF_INVALID_CONTEXT, NULL);
137
138         ret = ocpf_context_delete(ocpf, ctx);
139         if (ret == -1) return OCPF_ERROR;
140
141         return OCPF_SUCCESS;
142 }
143
144
145 /**
146    \details Parse OCPF file
147
148    Parse and process the given ocpf file.
149
150    \param context_id the identifier of the context holding the file to
151    be parsed
152
153    \return OCPF_SUCCESS on success, otherwise OCPF_ERROR
154
155    \sa ocpf_init
156  */
157 _PUBLIC_ int ocpf_parse(uint32_t context_id)
158 {
159         int                     ret;
160         struct ocpf_context     *ctx;
161         void                    *scanner;
162
163         /* Sanity checks */
164         OCPF_RETVAL_IF(!ocpf || !ocpf->mem_ctx, NULL, OCPF_NOT_INITIALIZED, NULL);
165
166         /* Step 1. Search the context */
167         ctx = ocpf_context_search_by_context_id(ocpf->context, context_id);
168         OCPF_RETVAL_IF(!ctx, NULL, OCPF_INVALID_CONTEXT, NULL);
169
170         ret = ocpf_yylex_init(&scanner);
171         ret = ocpf_yylex_init_extra(ctx, &scanner);
172         ocpf_yyset_in(ctx->fp, scanner);
173         ret = ocpf_yyparse(ctx, scanner);
174         ocpf_yylex_destroy(scanner);
175
176         return ret;
177 }
178
179
180 #define MAX_READ_SIZE   0x1000
181
182 static enum MAPISTATUS ocpf_stream(TALLOC_CTX *mem_ctx,
183                                    mapi_object_t *obj_parent,
184                                    uint32_t aulPropTag,
185                                    struct Binary_r *bin)
186 {
187         enum MAPISTATUS         retval;
188         mapi_object_t           obj_stream;
189         DATA_BLOB               stream;
190         uint32_t                access_flags = 2;       /* MAPI_MODIFY by default */
191         uint32_t                size;
192         uint32_t                offset;
193         uint16_t                read_size;
194
195         mapi_object_init(&obj_stream);
196
197         /* Step1. Open the Stream */
198         retval = OpenStream(obj_parent, aulPropTag, access_flags, &obj_stream);
199         MAPI_RETVAL_IF(retval, retval, NULL);
200
201         /* Step2. Write the Stream */
202         size = MAX_READ_SIZE;
203         offset = 0;
204         while (offset <= bin->cb) {
205                 stream.length = size;
206                 stream.data = talloc_size(mem_ctx, size);
207                 memcpy(stream.data, bin->lpb + offset, size);
208                 
209                 retval = WriteStream(&obj_stream, &stream, &read_size);
210                 talloc_free(stream.data);
211                 MAPI_RETVAL_IF(retval, retval, NULL);
212
213                 /* Exit when there is nothing left to write */
214                 if (!read_size) return MAPI_E_SUCCESS;
215                 
216                 offset += read_size;
217
218                 if ((offset + size) > bin->cb) {
219                         size = bin->cb - offset;
220                 }
221         }
222
223         mapi_object_release(&obj_stream);
224
225         return MAPI_E_SUCCESS;
226 }
227
228
229 /**
230    \details Build a SPropValue array from ocpf context
231
232    This function builds a SPropValue array from the ocpf context and
233    information stored.
234
235    \param mem_ctx the memory context to use for memory allocation
236    \param context_id identifier of the context to build a SPropValue
237    array for
238    \param obj_folder pointer the folder object we use for internal
239    MAPI operations
240    \param obj_message pointer to the message object we use for
241    internal MAPI operations
242
243    \return MAPI_E_SUCCESS on success, otherwise -1.
244
245    \note Developers should call GetLastError() to retrieve the last
246    MAPI error code. Possible MAPI error codes are:
247    - MAPI_E_NOT_INITIALIZED: MAPI subsystem has not been initialized
248
249    \sa ocpf_get_SPropValue
250  */
251
252 _PUBLIC_ enum MAPISTATUS ocpf_set_SPropValue(TALLOC_CTX *mem_ctx,
253                                              uint32_t context_id,
254                                              mapi_object_t *obj_folder,
255                                              mapi_object_t *obj_message)
256 {
257         enum MAPISTATUS         retval;
258         struct mapi_nameid      *nameid;
259         struct SPropTagArray    *SPropTagArray;
260         struct ocpf_property    *pel;
261         struct ocpf_nproperty   *nel;
262         struct ocpf_context     *ctx;
263         uint32_t                i;
264
265         /* sanity checks */
266         MAPI_RETVAL_IF(!ocpf, MAPI_E_NOT_INITIALIZED, NULL);
267         MAPI_RETVAL_IF(!obj_folder, MAPI_E_INVALID_PARAMETER, NULL);
268         
269         /* Step 0. Search for the context */
270         ctx = ocpf_context_search_by_context_id(ocpf->context, context_id);
271         OCPF_RETVAL_IF(!ctx, NULL, OCPF_INVALID_CONTEXT, NULL);
272
273         if (!mem_ctx) {
274                 mem_ctx = (TALLOC_CTX *) ctx;
275         }
276
277         /* Step 1. Allocate SPropValue */
278         ctx->cValues = 0;
279         ctx->lpProps = talloc_array(mem_ctx, struct SPropValue, 2);
280
281         /* Step2. build the list of named properties we want to set */
282         if (ctx->nprops && ctx->nprops->next) {
283                 nameid = mapi_nameid_new(mem_ctx);
284                 for (nel = ctx->nprops; nel->next; nel = nel->next) {
285                         if (nel->OOM) {
286                                 mapi_nameid_OOM_add(nameid, nel->OOM, nel->oleguid);
287                         } else if (nel->mnid_id) {
288                                 mapi_nameid_custom_lid_add(nameid, nel->mnid_id, nel->propType, nel->oleguid);
289                         } else if (nel->mnid_string) {
290                                 mapi_nameid_custom_string_add(nameid, nel->mnid_string, nel->propType, nel->oleguid);
291                         }
292                 }
293                 
294                 /* Step3. GetIDsFromNames and map property types */
295                 SPropTagArray = talloc_zero(mem_ctx, struct SPropTagArray);
296                 retval = GetIDsFromNames(obj_folder, nameid->count, 
297                                          nameid->nameid, 0, &SPropTagArray);
298                 if (retval != MAPI_E_SUCCESS) {
299                         MAPIFreeBuffer(SPropTagArray);
300                         MAPIFreeBuffer(nameid);
301                         return retval;
302                 }
303                 mapi_nameid_SPropTagArray(nameid, SPropTagArray);
304                 MAPIFreeBuffer(nameid);
305
306
307                 /* Step4. Add named properties */
308                 for (nel = ctx->nprops, i = 0; SPropTagArray->aulPropTag[i] && nel->next; nel = nel->next, i++) {
309                         if (SPropTagArray->aulPropTag[i]) {
310                                 if (((SPropTagArray->aulPropTag[i] & 0xFFFF) == PT_BINARY) && 
311                                     (((struct Binary_r *)nel->value)->cb > MAX_READ_SIZE)) {
312                                         retval = ocpf_stream(mem_ctx, obj_message, SPropTagArray->aulPropTag[i], 
313                                                              (struct Binary_r *)nel->value);
314                                         MAPI_RETVAL_IF(retval, retval, NULL);
315                                 } else {
316                                         ctx->lpProps = add_SPropValue(mem_ctx, ctx->lpProps, &ctx->cValues,
317                                                                        SPropTagArray->aulPropTag[i], nel->value);
318                                 }
319                         }
320                 }
321                 MAPIFreeBuffer(SPropTagArray);
322         }
323
324         /* Step5. Add Known properties */
325         if (ctx->props && ctx->props->next) {
326                 for (pel = ctx->props; pel->next; pel = pel->next) {
327                         if (((pel->aulPropTag & 0xFFFF) == PT_BINARY) && 
328                             (((struct Binary_r *)pel->value)->cb > MAX_READ_SIZE)) {
329                                 retval = ocpf_stream(mem_ctx, obj_message, pel->aulPropTag, 
330                                                      (struct Binary_r *)pel->value);
331                                 MAPI_RETVAL_IF(retval, retval, NULL);
332                         } else {
333                                 if ((pel->aulPropTag & 0xFFFF) == PT_STRING8) {
334                                         pel->aulPropTag = (pel->aulPropTag & 0xFFFF) + PT_UNICODE;
335                                 }
336                                 ctx->lpProps = add_SPropValue(mem_ctx, ctx->lpProps, &ctx->cValues, 
337                                                                pel->aulPropTag, pel->value);
338                         }
339                 }
340         }
341         /* Step 6. Add message class */
342         if (ctx->type) {
343                 ctx->lpProps = add_SPropValue(mem_ctx, ctx->lpProps, &ctx->cValues,
344                                                PidTagMessageClass, (const void *)ctx->type);
345         }
346
347         return MAPI_E_SUCCESS;
348 }
349
350 /**
351   \details Clear the known properties from the OCPF entity
352   
353   \param context_id identifier of the context to clear properties from
354   
355   \return MAPI_E_SUCCESS on success, otherwise a non-zero error code
356 */
357 enum MAPISTATUS ocpf_clear_props (uint32_t context_id)
358 {
359         struct ocpf_context     *ctx;
360
361         MAPI_RETVAL_IF(!ocpf, MAPI_E_NOT_INITIALIZED, NULL);
362         MAPI_RETVAL_IF(!ocpf->mem_ctx, MAPI_E_NOT_INITIALIZED, NULL);
363
364         /* Search the context */
365         ctx = ocpf_context_search_by_context_id(ocpf->context, context_id);
366         MAPI_RETVAL_IF(!ctx, MAPI_E_NOT_FOUND, NULL);
367
368         if (ctx->props) {
369                 talloc_free(ctx->props);
370         }
371         ctx->props = talloc_zero(ctx, struct ocpf_property);
372
373         return MAPI_E_SUCCESS;
374 }
375
376 /**
377    \details Get the OCPF SPropValue array
378
379    This function is an accessor designed to return the SPropValue
380    structure created with ocpf_set_SPropValue.
381
382    \param context_id identifier of the context to retrieve SPropValue
383    from
384    \param cValues pointer on the number of SPropValue entries
385
386    \return NULL on error, otherwise returns an allocated lpProps pointer
387
388    \sa ocpf_set_SPropValue
389  */
390 /* TODO: this should return a success/error code, with lpProps as a return parameter */
391 _PUBLIC_ struct SPropValue *ocpf_get_SPropValue(uint32_t context_id, uint32_t *cValues)
392 {
393         struct ocpf_context     *ctx;
394
395         OCPF_RETVAL_TYPE(!ocpf || !ocpf->mem_ctx, NULL, OCPF_NOT_INITIALIZED, NULL, NULL);
396
397         /* Search the context */
398         ctx = ocpf_context_search_by_context_id(ocpf->context, context_id);
399         OCPF_RETVAL_TYPE(!ctx, NULL, OCPF_INVALID_CONTEXT, NULL, NULL);
400
401         OCPF_RETVAL_TYPE(!ctx->lpProps || !ctx->cValues, ctx, OCPF_INVALID_PROPARRAY, NULL, NULL);
402
403         *cValues = ctx->cValues;
404
405         return ctx->lpProps;
406 }
407
408
409 static enum MAPISTATUS ocpf_folder_lookup(TALLOC_CTX *mem_ctx,
410                                           uint64_t sfid,
411                                           mapi_object_t *obj_parent,
412                                           mapi_id_t folder_id,
413                                           mapi_object_t *obj_ret)
414 {
415         enum MAPISTATUS         retval;
416         mapi_object_t           obj_folder;
417         mapi_object_t           obj_htable;
418         struct SPropTagArray    *SPropTagArray;
419         struct SRowSet          SRowSet;
420         uint32_t                i;
421         const uint64_t          *fid;
422
423         mapi_object_init(&obj_folder);
424         retval = OpenFolder(obj_parent, folder_id, &obj_folder);
425         if (retval != MAPI_E_SUCCESS) return false;
426
427         mapi_object_init(&obj_htable);
428         retval = GetHierarchyTable(&obj_folder, &obj_htable, 0, NULL);
429         if (retval != MAPI_E_SUCCESS) return false;
430
431         SPropTagArray = set_SPropTagArray(mem_ctx, 0x1, PR_FID);
432         retval = SetColumns(&obj_htable, SPropTagArray);
433         MAPIFreeBuffer(SPropTagArray);
434         if (retval != MAPI_E_SUCCESS) return false;
435
436         while (((retval = QueryRows(&obj_htable, 0x32, TBL_ADVANCE, &SRowSet)) != MAPI_E_NOT_FOUND && SRowSet.cRows)) {
437                 for (i = 0; i < SRowSet.cRows; i++) {
438                         fid = (const uint64_t *)find_SPropValue_data(&SRowSet.aRow[i], PR_FID);
439                         if (fid && *fid == sfid) {
440                                 retval = OpenFolder(&obj_folder, *fid, obj_ret);
441                                 mapi_object_release(&obj_htable);
442                                 mapi_object_release(&obj_folder);
443                                 return MAPI_E_SUCCESS;
444                         } else if (fid) {
445                                 retval = ocpf_folder_lookup(mem_ctx, sfid, &obj_folder, *fid, obj_ret);
446                                 if (retval == MAPI_E_SUCCESS) {
447                                         mapi_object_release(&obj_htable);
448                                         mapi_object_release(&obj_folder);
449                                         return MAPI_E_SUCCESS;
450                                 }
451                         }
452                 }
453         }
454
455         mapi_object_release(&obj_htable);
456         mapi_object_release(&obj_folder);
457
458         errno = MAPI_E_NOT_FOUND;
459         return MAPI_E_NOT_FOUND;
460 }
461
462
463 /**
464    \details Open OCPF folder
465
466    This function opens the folder associated with the ocpf folder
467    global context value.
468    
469    \param context_id identifier of the context to open the folder for
470    \param obj_store the store object
471    \param obj_folder the folder to open
472
473    \return MAPI_E_SUCCESS on success, otherwise MAPI_E_NOT_FOUND.
474
475    \note Developers should call GetLastError() to retrieve the last
476    MAPI error code. Possible MAPI error codes are:
477    - MAPI_E_NOT_INITIALIZED: MAPI subsystem has not been initialized.
478    - MAPI_E_INVALID_PARAMETER: obj_store is undefined
479    - MAPI_E_NOT_FOUND: The specified folder could not be found or is
480      not yet supported.
481
482      \sa ocpf_init, ocpf_parse
483  */
484 _PUBLIC_ enum MAPISTATUS ocpf_OpenFolder(uint32_t context_id,
485                                          mapi_object_t *obj_store,
486                                          mapi_object_t *obj_folder)
487 {
488         enum MAPISTATUS         retval;
489         struct ocpf_context     *ctx;
490         mapi_id_t               id_folder;
491         mapi_id_t               id_tis;
492
493         /* Sanity checks */
494         MAPI_RETVAL_IF(!ocpf, MAPI_E_NOT_INITIALIZED, NULL);
495         MAPI_RETVAL_IF(!obj_store, MAPI_E_INVALID_PARAMETER, NULL);
496
497         /* Step 1. Search for the context */
498         ctx = ocpf_context_search_by_context_id(ocpf->context, context_id);
499         MAPI_RETVAL_IF(!ctx, MAPI_E_INVALID_PARAMETER, NULL);
500         MAPI_RETVAL_IF(!ctx->folder, MAPI_E_NOT_FOUND, NULL);
501
502         mapi_object_init(obj_folder);
503         if (ctx->folder >= 1 && ctx->folder <= 26) {
504                 retval = GetDefaultFolder(obj_store, &id_folder, ctx->folder);
505                 MAPI_RETVAL_IF(retval, retval, NULL);
506
507                 retval = OpenFolder(obj_store, id_folder, obj_folder);
508                 MAPI_RETVAL_IF(retval, retval, NULL);
509
510         } else {
511                 retval = GetDefaultFolder(obj_store, &id_tis, olFolderTopInformationStore);
512                 MAPI_RETVAL_IF(retval, retval, NULL);
513
514                 retval = ocpf_folder_lookup((TALLOC_CTX *)ctx, ctx->folder, 
515                                             obj_store, id_tis, obj_folder);
516                 MAPI_RETVAL_IF(retval, retval, NULL);
517         }
518
519         return MAPI_E_SUCCESS;
520 }
521
522
523 /**
524  * We set external recipients at the end of aRow
525  */
526 static bool set_external_recipients(TALLOC_CTX *mem_ctx, struct SRowSet *SRowSet, const char *username, enum ulRecipClass RecipClass)
527 {
528         uint32_t                last;
529         struct SPropValue       SPropValue;
530
531         SRowSet->aRow = talloc_realloc(mem_ctx, SRowSet->aRow, struct SRow, SRowSet->cRows + 2);
532         last = SRowSet->cRows;
533         SRowSet->aRow[last].cValues = 0;
534         SRowSet->aRow[last].lpProps = talloc_zero(mem_ctx, struct SPropValue);
535         
536         /* PR_OBJECT_TYPE */
537         SPropValue.ulPropTag = PidTagObjectType;
538         SPropValue.value.l = MAPI_MAILUSER;
539         SRow_addprop(&(SRowSet->aRow[last]), SPropValue);
540
541         /* PR_DISPLAY_TYPE */
542         SPropValue.ulPropTag = PidTagDisplayType;
543         SPropValue.value.l = 0;
544         SRow_addprop(&(SRowSet->aRow[last]), SPropValue);
545
546         /* PR_GIVEN_NAME */
547         SPropValue.ulPropTag = PidTagGivenName;
548         SPropValue.value.lpszW = username;
549         SRow_addprop(&(SRowSet->aRow[last]), SPropValue);
550
551         /* PR_DISPLAY_NAME */
552         SPropValue.ulPropTag = PidTagDisplayName;
553         SPropValue.value.lpszW = username;
554         SRow_addprop(&(SRowSet->aRow[last]), SPropValue);
555
556         /* PR_7BIT_DISPLAY_NAME */
557         SPropValue.ulPropTag = PidTag7BitDisplayName;
558         SPropValue.value.lpszW = username;
559         SRow_addprop(&(SRowSet->aRow[last]), SPropValue);
560
561         /* PR_SMTP_ADDRESS */
562         SPropValue.ulPropTag = PidTagPrimarySmtpAddress;
563         SPropValue.value.lpszW = username;
564         SRow_addprop(&(SRowSet->aRow[last]), SPropValue);
565
566         /* PR_ADDRTYPE */
567         SPropValue.ulPropTag = PidTagAddressType;
568         SPropValue.value.lpszW = "SMTP";
569         SRow_addprop(&(SRowSet->aRow[last]), SPropValue);
570
571         SetRecipientType(&(SRowSet->aRow[last]), RecipClass);
572
573         SRowSet->cRows += 1;
574         return true;
575 }
576
577
578 /**
579    \details Set the message recipients from ocpf context
580
581    This function sets the recipient (To, Cc, Bcc) from the ocpf
582    context and information stored.
583
584    \param mem_ctx the memory context to use for memory allocation
585    \param context_id identifier to the context to set recipients for
586    \param obj_message pointer to the message object we use for
587    internal MAPI operations
588
589    \return OCPF_SUCCESS on success, otherwise OCPF_ERROR.
590
591    \sa ocpf
592  */
593 _PUBLIC_ enum MAPISTATUS ocpf_set_Recipients(TALLOC_CTX *mem_ctx,
594                                              uint32_t context_id,
595                                              mapi_object_t *obj_message)
596 {
597         enum MAPISTATUS                 retval;
598         struct ocpf_context             *ctx;
599         struct SPropTagArray            *SPropTagArray;
600         struct SPropValue               SPropValue;
601         struct SPropValue               *lpProps;
602         struct SRowSet                  *SRowSet;
603         struct PropertyTagArray_r       *flaglist = NULL;
604         char                            **usernames = NULL;
605         int                             *recipClass = NULL;
606         uint32_t                        counter;
607         uint32_t                        i;
608         const void                      *propdata;
609
610         MAPI_RETVAL_IF(!ocpf, MAPI_E_NOT_INITIALIZED, NULL);
611         MAPI_RETVAL_IF(!obj_message, MAPI_E_INVALID_PARAMETER, NULL);
612
613         /* Step 1. Search for the context */
614         ctx = ocpf_context_search_by_context_id(ocpf->context, context_id);
615         MAPI_RETVAL_IF(!ctx, MAPI_E_INVALID_PARAMETER, NULL);
616
617         MAPI_RETVAL_IF(!ctx->recipients->cRows, MAPI_E_NOT_FOUND, NULL);
618
619         SPropTagArray = set_SPropTagArray(mem_ctx, 0x8,
620                                           PidTagObjectType,
621                                           PidTagDisplayName,
622                                           PidTag7BitDisplayName,
623                                           PidTagDisplayName,
624                                           PidTagPrimarySmtpAddress,
625                                           PidTagGivenName,
626                                           PidTagEmailAddress,
627                                           PidTagAddressType);
628
629         /* Step 1. Group recipients and run ResolveNames */
630         usernames = talloc_array(mem_ctx, char *, ctx->recipients->cRows + 1);
631         recipClass = talloc_array(mem_ctx, int, ctx->recipients->cRows + 1);
632         for (i = 0; i < ctx->recipients->cRows; i++) {
633                 lpProps = get_SPropValue_SRow(&(ctx->recipients->aRow[i]), PidTag7BitDisplayName);
634                 propdata = get_SPropValue(lpProps, PidTag7BitDisplayName);
635                 usernames[i] = talloc_strdup((TALLOC_CTX *)usernames, (const char *) propdata);
636
637                 lpProps = get_SPropValue_SRow(&(ctx->recipients->aRow[i]), PidTagRecipientType);
638                 propdata = get_SPropValue(lpProps, PidTagRecipientType);
639                 recipClass[i] = *((uint32_t *)propdata);
640         }
641         usernames[i] = NULL;
642
643         retval = ResolveNames(mapi_object_get_session(obj_message), (const char **)usernames,
644                               SPropTagArray, &SRowSet, &flaglist, 0);
645         MAPIFreeBuffer(SPropTagArray);
646         MAPI_RETVAL_IF(retval, retval, usernames);
647
648         /* Step2. Associate resolved recipients to their respective recipClass */
649         if (!SRowSet) {
650                 SRowSet = talloc_zero(mem_ctx, struct SRowSet);
651         }
652
653         counter = 0;
654         for (i = 0; usernames[i]; i++) {
655                 if (flaglist->aulPropTag[i] == MAPI_UNRESOLVED) {
656                         set_external_recipients(mem_ctx, SRowSet, usernames[i], recipClass[i]);                 
657                 }
658                 if (flaglist->aulPropTag[i] == MAPI_RESOLVED) {
659                         SetRecipientType(&(SRowSet->aRow[counter]), recipClass[i]);
660                         counter++;
661                 }
662         }
663
664         /* Step3. Finish to build the ModifyRecipients SRowSet */
665         SPropValue.ulPropTag = PR_SEND_INTERNET_ENCODING;
666         SPropValue.value.l = 0;
667         SRowSet_propcpy(mem_ctx, SRowSet, SPropValue);
668
669         /* Step4. Call ModifyRecipients */
670         retval = ModifyRecipients(obj_message, SRowSet);
671         MAPI_RETVAL_IF(retval, retval, NULL);
672
673         return MAPI_E_SUCCESS;
674 }
675
676
677 /**
678    \details Get the message recipients from ocpf context
679
680    This function gets the recipient (To, Cc, Bcc) from the ocpf
681    context and information stored.
682
683    \param mem_ctx the memory context to use for memory allocation
684    \param context_id identifier to the context to set recipients for
685    \param _SRowSet pointer on pointer to the set of recipients to return
686
687    \return MAPI_E_SUCCESS on success, otherwise NULL
688
689    \sa ocpf
690  */
691 _PUBLIC_ enum MAPISTATUS ocpf_get_recipients(TALLOC_CTX *mem_ctx,
692                                              uint32_t context_id,
693                                              struct SRowSet **SRowSet)
694 {
695         struct ocpf_context     *ctx;
696
697         /* Sanity checks */
698         MAPI_RETVAL_IF(!ocpf, MAPI_E_NOT_INITIALIZED, NULL);
699         MAPI_RETVAL_IF(!SRowSet, MAPI_E_INVALID_PARAMETER, NULL);
700
701         /* Step 1. Search for the context */
702         ctx = ocpf_context_search_by_context_id(ocpf->context, context_id);
703         MAPI_RETVAL_IF(!ctx, MAPI_E_INVALID_PARAMETER, NULL);
704         MAPI_RETVAL_IF(!ctx->recipients->cRows, MAPI_E_NOT_FOUND, NULL);
705
706         *SRowSet = ctx->recipients;
707
708         return MAPI_E_SUCCESS;
709 }