2 OpenChange MAPI implementation.
4 Copyright (C) Brad Hards <bradh@frogmouth.net> 2010.
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/>.
20 #include "libmapi/libmapi.h"
21 #include "libmapi/libmapi_private.h"
22 #include "libmapi/fxparser.h"
26 #define OC_ASSERT(x) assert(x)
34 \brief Fast Transfer stream parser
37 static bool pull_uint8_t(struct fx_parser_context *parser, uint8_t *val)
39 if ((parser->idx) + 1 > parser->data.length) {
43 *val = parser->data.data[parser->idx];
48 static bool pull_uint16_t(struct fx_parser_context *parser, uint16_t *val)
50 if ((parser->idx) + 2 > parser->data.length) {
54 *val = parser->data.data[parser->idx];
56 *val += parser->data.data[parser->idx] << 8;
61 static bool pull_uint32_t(struct fx_parser_context *parser, uint32_t *val)
63 if ((parser->idx) + 4 > parser->data.length) {
67 *val = parser->data.data[parser->idx];
69 *val += parser->data.data[parser->idx] << 8;
71 *val += parser->data.data[parser->idx] << 16;
73 *val += parser->data.data[parser->idx] << 24;
78 static bool pull_tag(struct fx_parser_context *parser)
80 return pull_uint32_t(parser, &(parser->tag));
83 static bool pull_uint8_data(struct fx_parser_context *parser, uint32_t read_len, uint8_t **data_read)
86 for (i = 0; i < read_len; i++) {
87 if (!pull_uint8_t(parser, (uint8_t*)&((*data_read)[i]))) {
94 static bool pull_int64_t(struct fx_parser_context *parser, int64_t *val)
97 if ((parser->idx) + 8 > parser->data.length) {
101 *val = parser->data.data[parser->idx];
104 tmp = parser->data.data[parser->idx];
108 tmp = parser->data.data[parser->idx];
112 tmp = parser->data.data[parser->idx];
116 tmp = parser->data.data[parser->idx];
120 tmp = parser->data.data[parser->idx];
124 tmp = parser->data.data[parser->idx];
128 tmp = parser->data.data[parser->idx];
135 static bool pull_double(struct fx_parser_context *parser, double *val)
137 return pull_int64_t(parser, (int64_t *)val);
140 static bool pull_guid(struct fx_parser_context *parser, struct GUID *guid)
144 if ((parser->idx) + 16 > parser->data.length) {
148 if (!pull_uint32_t(parser, &(guid->time_low)))
150 if (!pull_uint16_t(parser, &(guid->time_mid)))
152 if (!pull_uint16_t(parser, &(guid->time_hi_and_version)))
154 if (!pull_uint8_t(parser, &(guid->clock_seq[0])))
156 if (!pull_uint8_t(parser, &(guid->clock_seq[1])))
158 for (i = 0; i < 6; ++i) {
159 if (!pull_uint8_t(parser, &(guid->node[i])))
165 static bool pull_systime(struct fx_parser_context *parser, struct FILETIME *ft)
167 struct FILETIME filetime = {0,0};
169 if (parser->idx + 8 > parser->data.length ||
170 !pull_uint32_t(parser, &(filetime.dwLowDateTime)) ||
171 !pull_uint32_t(parser, &(filetime.dwHighDateTime)))
179 static bool pull_clsid(struct fx_parser_context *parser, struct FlatUID_r **pclsid)
181 struct FlatUID_r *clsid;
184 if (parser->idx + 16 > parser->data.length)
187 clsid = talloc_zero(parser->mem_ctx, struct FlatUID_r);
188 for (i = 0; i < 16; ++i) {
189 if (!pull_uint8_t(parser, &(clsid->ab[i])))
198 static bool pull_string8(struct fx_parser_context *parser, char **pstr)
203 if (!pull_uint32_t(parser, &length) ||
204 parser->idx + length > parser->data.length)
207 str = talloc_array(parser->mem_ctx, char, length + 1);
208 for (i = 0; i < length; i++) {
209 if (!pull_uint8_t(parser, (uint8_t*)&(str[i]))) {
220 static bool fetch_ucs2_data(struct fx_parser_context *parser, uint32_t numbytes, smb_ucs2_t **data_read)
222 if ((parser->idx) + numbytes > parser->data.length) {
223 // printf("insufficient data in fetch_ucs2_data (%i requested, %zi available)\n", numbytes, (parser->data.length - parser->idx));
227 *data_read = talloc_array(parser->mem_ctx, smb_ucs2_t, numbytes/2);
228 memcpy(*data_read, &(parser->data.data[parser->idx]), numbytes);
229 parser->idx += numbytes;
233 static bool fetch_ucs2_nullterminated(struct fx_parser_context *parser, smb_ucs2_t **data_read)
235 uint32_t idx_local = parser->idx;
237 while (idx_local < parser->data.length -1) {
238 smb_ucs2_t val = 0x0000;
239 val += parser->data.data[idx_local];
241 val += parser->data.data[idx_local] << 8;
250 return fetch_ucs2_data(parser, idx_local-(parser->idx), data_read);
253 static bool pull_unicode(struct fx_parser_context *parser, char **pstr)
255 smb_ucs2_t *ucs2_data = NULL;
256 char *utf8_data = NULL;
260 if (!pull_uint32_t(parser, &length) ||
261 parser->idx + length > parser->data.length)
264 ucs2_data = talloc_array(parser->mem_ctx, smb_ucs2_t, length/2);
266 if (!fetch_ucs2_data(parser, length, &ucs2_data)) {
269 pull_ucs2_talloc(parser->mem_ctx, &utf8_data, ucs2_data, &utf8_len);
276 static bool pull_binary(struct fx_parser_context *parser, struct Binary_r *bin)
278 if (!pull_uint32_t(parser, &(bin->cb)) ||
279 parser->idx + bin->cb > parser->data.length)
282 bin->lpb = talloc_array(parser->mem_ctx, uint8_t, bin->cb + 1);
284 return pull_uint8_data(parser, bin->cb, &(bin->lpb));
288 pull a property value from the blob, starting at position idx
290 static bool fetch_property_value(struct fx_parser_context *parser, DATA_BLOB *buf, struct SPropValue *prop)
292 switch(prop->ulPropTag & 0xFFFF) {
295 if (!pull_uint32_t(parser, &(prop->value.null)))
301 if (!pull_uint16_t(parser, &(prop->value.i)))
307 if (!pull_uint32_t(parser, &(prop->value.l)))
313 if (!pull_double(parser, (double *)&(prop->value.dbl)))
319 if (parser->idx + 2 > parser->data.length ||
320 !pull_uint8_t(parser, &(prop->value.b)))
323 /* special case for fast transfer, 2 bytes instead of one */
330 if (!pull_int64_t(parser, &(val)))
338 if (!pull_string8(parser, &str))
340 prop->value.lpszA = str;
346 if (!pull_unicode (parser, &str))
348 prop->value.lpszW = str;
353 if (!pull_systime(parser, &prop->value.ft))
359 if (!pull_clsid(parser, &prop->value.lpguid))
366 if (!pull_binary(parser, &prop->value.bin))
372 if (!pull_uint32_t(parser, &(prop->value.object)))
379 if (!pull_uint32_t(parser, &num))
381 prop->value.err = num;
387 if (!pull_uint32_t(parser, &(prop->value.MVbin.cValues)) ||
388 parser->idx + prop->value.MVbin.cValues * 4 > parser->data.length)
390 prop->value.MVbin.lpbin = talloc_array(parser->mem_ctx, struct Binary_r, prop->value.MVbin.cValues);
391 for (i = 0; i < prop->value.MVbin.cValues; i++) {
392 if (!pull_binary(parser, &(prop->value.MVbin.lpbin[i])))
400 if (!pull_uint32_t(parser, &(prop->value.MVi.cValues)) ||
401 parser->idx + prop->value.MVi.cValues * 2 > parser->data.length)
403 prop->value.MVi.lpi = talloc_array(parser->mem_ctx, uint16_t, prop->value.MVi.cValues);
404 for (i = 0; i < prop->value.MVi.cValues; i++) {
405 if (!pull_uint16_t(parser, &(prop->value.MVi.lpi[i])))
413 if (!pull_uint32_t(parser, &(prop->value.MVl.cValues)) ||
414 parser->idx + prop->value.MVl.cValues * 4 > parser->data.length)
416 prop->value.MVl.lpl = talloc_array(parser->mem_ctx, uint32_t, prop->value.MVl.cValues);
417 for (i = 0; i < prop->value.MVl.cValues; i++) {
418 if (!pull_uint32_t(parser, &(prop->value.MVl.lpl[i])))
427 if (!pull_uint32_t(parser, &(prop->value.MVszA.cValues)) ||
428 parser->idx + prop->value.MVszA.cValues * 4 > parser->data.length)
430 prop->value.MVszA.lppszA = (const char **) talloc_array(parser->mem_ctx, char *, prop->value.MVszA.cValues);
431 for (i = 0; i < prop->value.MVszA.cValues; i++) {
433 if (!pull_string8(parser, &str))
435 prop->value.MVszA.lppszA[i] = str;
442 if (!pull_uint32_t(parser, &(prop->value.MVguid.cValues)) ||
443 parser->idx + prop->value.MVguid.cValues * 16 > parser->data.length)
445 prop->value.MVguid.lpguid = talloc_array(parser->mem_ctx, struct FlatUID_r *, prop->value.MVguid.cValues);
446 for (i = 0; i < prop->value.MVguid.cValues; i++) {
447 if (!pull_clsid(parser, &(prop->value.MVguid.lpguid[i])))
457 if (!pull_uint32_t(parser, &(prop->value.MVszW.cValues)) ||
458 parser->idx + prop->value.MVszW.cValues * 4 > parser->data.length)
460 prop->value.MVszW.lppszW = (const char **) talloc_array(parser->mem_ctx, char *, prop->value.MVszW.cValues);
461 for (i = 0; i < prop->value.MVszW.cValues; i++) {
463 if (!pull_unicode(parser, &str))
465 prop->value.MVszW.lppszW[i] = str;
472 if (!pull_uint32_t(parser, &(prop->value.MVft.cValues)) ||
473 parser->idx + prop->value.MVft.cValues * 8 > parser->data.length)
475 prop->value.MVft.lpft = talloc_array(parser->mem_ctx, struct FILETIME, prop->value.MVft.cValues);
476 for (i = 0; i < prop->value.MVft.cValues; i++) {
477 if (!pull_systime(parser, &(prop->value.MVft.lpft[i])))
483 printf("unhandled conversion case in fetch_property_value(): 0x%x\n", (prop->ulPropTag & 0xFFFF));
489 static bool pull_named_property(struct fx_parser_context *parser, enum MAPISTATUS *ms)
492 if (!pull_guid(parser, &(parser->namedprop.lpguid)))
494 /* printf("guid : %s\n", GUID_string(parser->mem_ctx, &(parser->namedprop.lpguid))); */
495 if (!pull_uint8_t(parser, &type))
498 parser->namedprop.ulKind = MNID_ID;
499 if (!pull_uint32_t(parser, &(parser->namedprop.kind.lid)))
501 /* printf("LID dispid: 0x%08x\n", parser->namedprop.kind.lid); */
502 } else if (type == 1) {
503 smb_ucs2_t *ucs2_data = NULL;
505 parser->namedprop.ulKind = MNID_STRING;
506 if (!fetch_ucs2_nullterminated(parser, &ucs2_data))
508 pull_ucs2_talloc(parser->mem_ctx, (char**)&(parser->namedprop.kind.lpwstr.Name), ucs2_data, &(utf8_len));
509 parser->namedprop.kind.lpwstr.NameSize = utf8_len;
510 /* printf("named: %s\n", parser->namedprop.kind.lpwstr.Name); */
512 printf("unknown named property kind: 0x%02x\n", parser->namedprop.ulKind);
515 if (parser->op_namedprop) {
516 *ms = parser->op_namedprop(parser->lpProp.ulPropTag, parser->namedprop, parser->priv);
523 \details set a callback function for marker output
525 _PUBLIC_ void fxparser_set_marker_callback(struct fx_parser_context *parser, fxparser_marker_callback_t marker_callback)
527 parser->op_marker = marker_callback;
531 \details set a callback function for delete properties output
533 _PUBLIC_ void fxparser_set_delprop_callback(struct fx_parser_context *parser, fxparser_delprop_callback_t delprop_callback)
535 parser->op_delprop = delprop_callback;
539 \details set a callback function for named properties output
541 _PUBLIC_ void fxparser_set_namedprop_callback(struct fx_parser_context *parser, fxparser_namedprop_callback_t namedprop_callback)
543 parser->op_namedprop = namedprop_callback;
547 \details set a callback function for property output
549 _PUBLIC_ void fxparser_set_property_callback(struct fx_parser_context *parser, fxparser_property_callback_t property_callback)
551 parser->op_property = property_callback;
555 \details initialise a fast transfer parser
557 _PUBLIC_ struct fx_parser_context* fxparser_init(TALLOC_CTX *mem_ctx, void *priv)
559 struct fx_parser_context *parser = talloc_zero(mem_ctx, struct fx_parser_context);
561 parser->mem_ctx = mem_ctx;
562 parser->data = data_blob_talloc_named(parser->mem_ctx, NULL, 0, "fast transfer parser");
563 parser->state = ParserState_Entry;
565 parser->lpProp.ulPropTag = (enum MAPITAGS) 0;
566 parser->lpProp.dwAlignPad = 0;
567 parser->lpProp.value.l = 0;
574 \details parse a fast transfer buffer
576 _PUBLIC_ enum MAPISTATUS fxparser_parse(struct fx_parser_context *parser, DATA_BLOB *fxbuf)
578 enum MAPISTATUS ms = MAPI_E_SUCCESS;
580 data_blob_append(parser->mem_ctx, &(parser->data), fxbuf->data, fxbuf->length);
581 parser->enough_data = true;
582 while(ms == MAPI_E_SUCCESS && (parser->idx < parser->data.length) && parser->enough_data) {
583 uint32_t idx = parser->idx;
585 switch(parser->state) {
586 case ParserState_Entry:
588 if (pull_tag(parser)) {
589 /* printf("tag: 0x%08x\n", parser->tag); */
590 parser->state = ParserState_HaveTag;
592 parser->enough_data = false;
597 case ParserState_HaveTag:
599 switch (parser->tag) {
600 case PidTagStartTopFld:
601 case PidTagStartSubFld:
602 case PidTagEndFolder:
603 case PidTagStartMessage:
604 case PidTagStartFAIMsg:
605 case PidTagEndMessage:
606 case PidTagStartRecip:
607 case PidTagEndToRecip:
608 case PidTagNewAttach:
609 case PidTagEndAttach:
610 case PidTagStartEmbed:
612 if (parser->op_marker) {
613 ms = parser->op_marker(parser->tag, parser->priv);
615 parser->state = ParserState_Entry;
617 case PidTagFXDelProp:
620 if (pull_uint32_t(parser, &tag)) {
621 if (parser->op_delprop) {
622 ms = parser->op_delprop(tag, parser->priv);
624 parser->state = ParserState_Entry;
626 parser->enough_data = false;
633 /* standard property thing */
634 parser->lpProp.ulPropTag = (enum MAPITAGS) parser->tag;
635 parser->lpProp.dwAlignPad = 0;
636 if ((parser->lpProp.ulPropTag >> 16) & 0x8000) {
637 /* this is a named property */
638 // printf("tag: 0x%08x\n", parser->tag);
639 // TODO: this should probably be a separate parser state
640 // TODO: this needs to return the named property
641 if (pull_named_property(parser, &ms)) {
642 parser->state = ParserState_HavePropTag;
644 parser->enough_data = false;
648 parser->state = ParserState_HavePropTag;
654 case ParserState_HavePropTag:
656 if (fetch_property_value(parser, &(parser->data), &(parser->lpProp))) {
657 // printf("position %i of %zi\n", parser->idx, parser->data.length);
658 if (parser->op_property) {
659 ms = parser->op_property(parser->lpProp, parser->priv);
661 parser->state = ParserState_Entry;
663 parser->enough_data = false;
671 // Remove the part of the buffer that we've used
672 uint32_t remainder_len = parser->data.length - parser->idx;
673 DATA_BLOB remainder = data_blob_talloc_named(parser->mem_ctx, &(parser->data.data[parser->idx]), remainder_len, "fast transfer parser");
674 data_blob_free(&(parser->data));
675 parser->data = remainder;