2 OpenChange OCPF (OpenChange Property File) implementation.
4 Copyright (C) Julien Kerihuel 2008-2011.
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.
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.
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/>.
23 \brief public OCPF API
28 #include "libocpf/ocpf.h"
29 #include "libocpf/ocpf_api.h"
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 *);
42 \details Initialize OCPF context
44 Initialize ocpf context and allocate memory for internal structures
46 \return OCPF_SUCCESS on success, otherwise OCPF_ERROR
48 \sa ocpf_release, ocpf_parse
50 _PUBLIC_ int ocpf_init(void)
54 OCPF_RETVAL_IF(ocpf, NULL, OCPF_INITIALIZED, NULL);
56 mem_ctx = talloc_named(NULL, 0, "ocpf");
57 ocpf = talloc_zero(mem_ctx, struct ocpf);
58 ocpf->mem_ctx = mem_ctx;
60 ocpf->context = talloc_zero(mem_ctx, struct ocpf_context);
61 ocpf->free_id = talloc_zero(mem_ctx, struct ocpf_freeid);
69 \details Uninitialize OCPF context
71 Uninitialize the global OCPF context and release memory.
73 \return OCPF_SUCCESS on success, otherwise OCPF_ERROR
77 _PUBLIC_ int ocpf_release(void)
79 OCPF_RETVAL_IF(!ocpf || !ocpf->mem_ctx, NULL, OCPF_NOT_INITIALIZED, NULL);
81 talloc_free(ocpf->mem_ctx);
89 \details Create a new OCPF context
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
95 \return OCPF_SUCCESS on success, otherwise OCPF_ERROR
97 _PUBLIC_ int ocpf_new_context(const char *filename, uint32_t *context_id, uint8_t flags)
99 struct ocpf_context *ctx;
100 bool existing = false;
102 OCPF_RETVAL_IF(!ocpf || !ocpf->mem_ctx, NULL, OCPF_NOT_INITIALIZED, NULL);
104 ctx = ocpf_context_add(ocpf, filename, context_id, flags, &existing);
109 if (existing == false) {
110 DLIST_ADD_END(ocpf->context, ctx, struct ocpf_context *);
119 \details Delete an OCPF context
121 \param context_id context identifier referencing the context to
124 \return OCPF_SUCCESS on success, otherwise OCPF_ERROR
126 _PUBLIC_ int ocpf_del_context(uint32_t context_id)
129 struct ocpf_context *ctx;
132 OCPF_RETVAL_IF(!ocpf || !ocpf->mem_ctx, NULL, OCPF_NOT_INITIALIZED, NULL);
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);
138 ret = ocpf_context_delete(ocpf, ctx);
139 if (ret == -1) return OCPF_ERROR;
146 \details Parse OCPF file
148 Parse and process the given ocpf file.
150 \param context_id the identifier of the context holding the file to
153 \return OCPF_SUCCESS on success, otherwise OCPF_ERROR
157 _PUBLIC_ int ocpf_parse(uint32_t context_id)
160 struct ocpf_context *ctx;
164 OCPF_RETVAL_IF(!ocpf || !ocpf->mem_ctx, NULL, OCPF_NOT_INITIALIZED, NULL);
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);
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);
180 #define MAX_READ_SIZE 0x1000
182 static enum MAPISTATUS ocpf_stream(TALLOC_CTX *mem_ctx,
183 mapi_object_t *obj_parent,
185 struct Binary_r *bin)
187 enum MAPISTATUS retval;
188 mapi_object_t obj_stream;
190 uint32_t access_flags = 2; /* MAPI_MODIFY by default */
195 mapi_object_init(&obj_stream);
197 /* Step1. Open the Stream */
198 retval = OpenStream(obj_parent, aulPropTag, access_flags, &obj_stream);
199 MAPI_RETVAL_IF(retval, retval, NULL);
201 /* Step2. Write the Stream */
202 size = MAX_READ_SIZE;
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);
209 retval = WriteStream(&obj_stream, &stream, &read_size);
210 talloc_free(stream.data);
211 MAPI_RETVAL_IF(retval, retval, NULL);
213 /* Exit when there is nothing left to write */
214 if (!read_size) return MAPI_E_SUCCESS;
218 if ((offset + size) > bin->cb) {
219 size = bin->cb - offset;
223 mapi_object_release(&obj_stream);
225 return MAPI_E_SUCCESS;
230 \details Build a SPropValue array from ocpf context
232 This function builds a SPropValue array from the ocpf context and
235 \param mem_ctx the memory context to use for memory allocation
236 \param context_id identifier of the context to build a SPropValue
238 \param obj_folder pointer the folder object we use for internal
240 \param obj_message pointer to the message object we use for
241 internal MAPI operations
243 \return MAPI_E_SUCCESS on success, otherwise -1.
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
249 \sa ocpf_get_SPropValue
252 _PUBLIC_ enum MAPISTATUS ocpf_set_SPropValue(TALLOC_CTX *mem_ctx,
254 mapi_object_t *obj_folder,
255 mapi_object_t *obj_message)
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;
266 MAPI_RETVAL_IF(!ocpf, MAPI_E_NOT_INITIALIZED, NULL);
267 MAPI_RETVAL_IF(!obj_folder, MAPI_E_INVALID_PARAMETER, NULL);
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);
274 mem_ctx = (TALLOC_CTX *) ctx;
277 /* Step 1. Allocate SPropValue */
279 ctx->lpProps = talloc_array(mem_ctx, struct SPropValue, 2);
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) {
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);
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);
303 mapi_nameid_SPropTagArray(nameid, SPropTagArray);
304 MAPIFreeBuffer(nameid);
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);
316 ctx->lpProps = add_SPropValue(mem_ctx, ctx->lpProps, &ctx->cValues,
317 SPropTagArray->aulPropTag[i], nel->value);
321 MAPIFreeBuffer(SPropTagArray);
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);
333 if ((pel->aulPropTag & 0xFFFF) == PT_STRING8) {
334 pel->aulPropTag = (pel->aulPropTag & 0xFFFF) + PT_UNICODE;
336 ctx->lpProps = add_SPropValue(mem_ctx, ctx->lpProps, &ctx->cValues,
337 pel->aulPropTag, pel->value);
341 /* Step 6. Add message class */
343 ctx->lpProps = add_SPropValue(mem_ctx, ctx->lpProps, &ctx->cValues,
344 PidTagMessageClass, (const void *)ctx->type);
347 return MAPI_E_SUCCESS;
351 \details Clear the known properties from the OCPF entity
353 \param context_id identifier of the context to clear properties from
355 \return MAPI_E_SUCCESS on success, otherwise a non-zero error code
357 enum MAPISTATUS ocpf_clear_props (uint32_t context_id)
359 struct ocpf_context *ctx;
361 MAPI_RETVAL_IF(!ocpf, MAPI_E_NOT_INITIALIZED, NULL);
362 MAPI_RETVAL_IF(!ocpf->mem_ctx, MAPI_E_NOT_INITIALIZED, NULL);
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);
369 talloc_free(ctx->props);
371 ctx->props = talloc_zero(ctx, struct ocpf_property);
373 return MAPI_E_SUCCESS;
377 \details Get the OCPF SPropValue array
379 This function is an accessor designed to return the SPropValue
380 structure created with ocpf_set_SPropValue.
382 \param context_id identifier of the context to retrieve SPropValue
384 \param cValues pointer on the number of SPropValue entries
386 \return NULL on error, otherwise returns an allocated lpProps pointer
388 \sa ocpf_set_SPropValue
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)
393 struct ocpf_context *ctx;
395 OCPF_RETVAL_TYPE(!ocpf || !ocpf->mem_ctx, NULL, OCPF_NOT_INITIALIZED, NULL, NULL);
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);
401 OCPF_RETVAL_TYPE(!ctx->lpProps || !ctx->cValues, ctx, OCPF_INVALID_PROPARRAY, NULL, NULL);
403 *cValues = ctx->cValues;
409 static enum MAPISTATUS ocpf_folder_lookup(TALLOC_CTX *mem_ctx,
411 mapi_object_t *obj_parent,
413 mapi_object_t *obj_ret)
415 enum MAPISTATUS retval;
416 mapi_object_t obj_folder;
417 mapi_object_t obj_htable;
418 struct SPropTagArray *SPropTagArray;
419 struct SRowSet SRowSet;
423 mapi_object_init(&obj_folder);
424 retval = OpenFolder(obj_parent, folder_id, &obj_folder);
425 if (retval != MAPI_E_SUCCESS) return false;
427 mapi_object_init(&obj_htable);
428 retval = GetHierarchyTable(&obj_folder, &obj_htable, 0, NULL);
429 if (retval != MAPI_E_SUCCESS) return false;
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;
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;
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;
455 mapi_object_release(&obj_htable);
456 mapi_object_release(&obj_folder);
458 errno = MAPI_E_NOT_FOUND;
459 return MAPI_E_NOT_FOUND;
464 \details Open OCPF folder
466 This function opens the folder associated with the ocpf folder
467 global context value.
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
473 \return MAPI_E_SUCCESS on success, otherwise MAPI_E_NOT_FOUND.
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
482 \sa ocpf_init, ocpf_parse
484 _PUBLIC_ enum MAPISTATUS ocpf_OpenFolder(uint32_t context_id,
485 mapi_object_t *obj_store,
486 mapi_object_t *obj_folder)
488 enum MAPISTATUS retval;
489 struct ocpf_context *ctx;
494 MAPI_RETVAL_IF(!ocpf, MAPI_E_NOT_INITIALIZED, NULL);
495 MAPI_RETVAL_IF(!obj_store, MAPI_E_INVALID_PARAMETER, NULL);
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);
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);
507 retval = OpenFolder(obj_store, id_folder, obj_folder);
508 MAPI_RETVAL_IF(retval, retval, NULL);
511 retval = GetDefaultFolder(obj_store, &id_tis, olFolderTopInformationStore);
512 MAPI_RETVAL_IF(retval, retval, NULL);
514 retval = ocpf_folder_lookup((TALLOC_CTX *)ctx, ctx->folder,
515 obj_store, id_tis, obj_folder);
516 MAPI_RETVAL_IF(retval, retval, NULL);
519 return MAPI_E_SUCCESS;
524 * We set external recipients at the end of aRow
526 static bool set_external_recipients(TALLOC_CTX *mem_ctx, struct SRowSet *SRowSet, const char *username, enum ulRecipClass RecipClass)
529 struct SPropValue SPropValue;
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);
537 SPropValue.ulPropTag = PidTagObjectType;
538 SPropValue.value.l = MAPI_MAILUSER;
539 SRow_addprop(&(SRowSet->aRow[last]), SPropValue);
541 /* PR_DISPLAY_TYPE */
542 SPropValue.ulPropTag = PidTagDisplayType;
543 SPropValue.value.l = 0;
544 SRow_addprop(&(SRowSet->aRow[last]), SPropValue);
547 SPropValue.ulPropTag = PidTagGivenName;
548 SPropValue.value.lpszW = username;
549 SRow_addprop(&(SRowSet->aRow[last]), SPropValue);
551 /* PR_DISPLAY_NAME */
552 SPropValue.ulPropTag = PidTagDisplayName;
553 SPropValue.value.lpszW = username;
554 SRow_addprop(&(SRowSet->aRow[last]), SPropValue);
556 /* PR_7BIT_DISPLAY_NAME */
557 SPropValue.ulPropTag = PidTag7BitDisplayName;
558 SPropValue.value.lpszW = username;
559 SRow_addprop(&(SRowSet->aRow[last]), SPropValue);
561 /* PR_SMTP_ADDRESS */
562 SPropValue.ulPropTag = PidTagPrimarySmtpAddress;
563 SPropValue.value.lpszW = username;
564 SRow_addprop(&(SRowSet->aRow[last]), SPropValue);
567 SPropValue.ulPropTag = PidTagAddressType;
568 SPropValue.value.lpszW = "SMTP";
569 SRow_addprop(&(SRowSet->aRow[last]), SPropValue);
571 SetRecipientType(&(SRowSet->aRow[last]), RecipClass);
579 \details Set the message recipients from ocpf context
581 This function sets the recipient (To, Cc, Bcc) from the ocpf
582 context and information stored.
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
589 \return OCPF_SUCCESS on success, otherwise OCPF_ERROR.
593 _PUBLIC_ enum MAPISTATUS ocpf_set_Recipients(TALLOC_CTX *mem_ctx,
595 mapi_object_t *obj_message)
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;
608 const void *propdata;
610 MAPI_RETVAL_IF(!ocpf, MAPI_E_NOT_INITIALIZED, NULL);
611 MAPI_RETVAL_IF(!obj_message, MAPI_E_INVALID_PARAMETER, NULL);
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);
617 MAPI_RETVAL_IF(!ctx->recipients->cRows, MAPI_E_NOT_FOUND, NULL);
619 SPropTagArray = set_SPropTagArray(mem_ctx, 0x8,
622 PidTag7BitDisplayName,
624 PidTagPrimarySmtpAddress,
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);
637 lpProps = get_SPropValue_SRow(&(ctx->recipients->aRow[i]), PidTagRecipientType);
638 propdata = get_SPropValue(lpProps, PidTagRecipientType);
639 recipClass[i] = *((uint32_t *)propdata);
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);
648 /* Step2. Associate resolved recipients to their respective recipClass */
650 SRowSet = talloc_zero(mem_ctx, struct SRowSet);
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]);
658 if (flaglist->aulPropTag[i] == MAPI_RESOLVED) {
659 SetRecipientType(&(SRowSet->aRow[counter]), recipClass[i]);
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);
669 /* Step4. Call ModifyRecipients */
670 retval = ModifyRecipients(obj_message, SRowSet);
671 MAPI_RETVAL_IF(retval, retval, NULL);
673 return MAPI_E_SUCCESS;
678 \details Get the message recipients from ocpf context
680 This function gets the recipient (To, Cc, Bcc) from the ocpf
681 context and information stored.
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
687 \return MAPI_E_SUCCESS on success, otherwise NULL
691 _PUBLIC_ enum MAPISTATUS ocpf_get_recipients(TALLOC_CTX *mem_ctx,
693 struct SRowSet **SRowSet)
695 struct ocpf_context *ctx;
698 MAPI_RETVAL_IF(!ocpf, MAPI_E_NOT_INITIALIZED, NULL);
699 MAPI_RETVAL_IF(!SRowSet, MAPI_E_INVALID_PARAMETER, NULL);
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);
706 *SRowSet = ctx->recipients;
708 return MAPI_E_SUCCESS;