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_zero_array(parser->mem_ctx, smb_ucs2_t, (numbytes/2) + 1);
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_zero_array(parser->mem_ctx, smb_ucs2_t, (length/2) + 1);
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 /* the object itself is sent too, thus download it as a binary,
373 not as a meaningless number, which is length of the object here */
374 if (!pull_binary(parser, &prop->value.bin))
381 if (!pull_uint32_t(parser, &num))
383 prop->value.err = num;
389 if (!pull_uint32_t(parser, &(prop->value.MVbin.cValues)) ||
390 parser->idx + prop->value.MVbin.cValues * 4 > parser->data.length)
392 prop->value.MVbin.lpbin = talloc_array(parser->mem_ctx, struct Binary_r, prop->value.MVbin.cValues);
393 for (i = 0; i < prop->value.MVbin.cValues; i++) {
394 if (!pull_binary(parser, &(prop->value.MVbin.lpbin[i])))
402 if (!pull_uint32_t(parser, &(prop->value.MVi.cValues)) ||
403 parser->idx + prop->value.MVi.cValues * 2 > parser->data.length)
405 prop->value.MVi.lpi = talloc_array(parser->mem_ctx, uint16_t, prop->value.MVi.cValues);
406 for (i = 0; i < prop->value.MVi.cValues; i++) {
407 if (!pull_uint16_t(parser, &(prop->value.MVi.lpi[i])))
415 if (!pull_uint32_t(parser, &(prop->value.MVl.cValues)) ||
416 parser->idx + prop->value.MVl.cValues * 4 > parser->data.length)
418 prop->value.MVl.lpl = talloc_array(parser->mem_ctx, uint32_t, prop->value.MVl.cValues);
419 for (i = 0; i < prop->value.MVl.cValues; i++) {
420 if (!pull_uint32_t(parser, &(prop->value.MVl.lpl[i])))
429 if (!pull_uint32_t(parser, &(prop->value.MVszA.cValues)) ||
430 parser->idx + prop->value.MVszA.cValues * 4 > parser->data.length)
432 prop->value.MVszA.lppszA = (const char **) talloc_array(parser->mem_ctx, char *, prop->value.MVszA.cValues);
433 for (i = 0; i < prop->value.MVszA.cValues; i++) {
435 if (!pull_string8(parser, &str))
437 prop->value.MVszA.lppszA[i] = str;
444 if (!pull_uint32_t(parser, &(prop->value.MVguid.cValues)) ||
445 parser->idx + prop->value.MVguid.cValues * 16 > parser->data.length)
447 prop->value.MVguid.lpguid = talloc_array(parser->mem_ctx, struct FlatUID_r *, prop->value.MVguid.cValues);
448 for (i = 0; i < prop->value.MVguid.cValues; i++) {
449 if (!pull_clsid(parser, &(prop->value.MVguid.lpguid[i])))
459 if (!pull_uint32_t(parser, &(prop->value.MVszW.cValues)) ||
460 parser->idx + prop->value.MVszW.cValues * 4 > parser->data.length)
462 prop->value.MVszW.lppszW = (const char **) talloc_array(parser->mem_ctx, char *, prop->value.MVszW.cValues);
463 for (i = 0; i < prop->value.MVszW.cValues; i++) {
465 if (!pull_unicode(parser, &str))
467 prop->value.MVszW.lppszW[i] = str;
474 if (!pull_uint32_t(parser, &(prop->value.MVft.cValues)) ||
475 parser->idx + prop->value.MVft.cValues * 8 > parser->data.length)
477 prop->value.MVft.lpft = talloc_array(parser->mem_ctx, struct FILETIME, prop->value.MVft.cValues);
478 for (i = 0; i < prop->value.MVft.cValues; i++) {
479 if (!pull_systime(parser, &(prop->value.MVft.lpft[i])))
485 printf("unhandled conversion case in fetch_property_value(): 0x%x\n", prop->ulPropTag);
491 static bool pull_named_property(struct fx_parser_context *parser, enum MAPISTATUS *ms)
494 if (!pull_guid(parser, &(parser->namedprop.lpguid)))
496 /* printf("guid : %s\n", GUID_string(parser->mem_ctx, &(parser->namedprop.lpguid))); */
497 if (!pull_uint8_t(parser, &type))
500 parser->namedprop.ulKind = MNID_ID;
501 if (!pull_uint32_t(parser, &(parser->namedprop.kind.lid)))
503 /* printf("LID dispid: 0x%08x\n", parser->namedprop.kind.lid); */
504 } else if (type == 1) {
505 smb_ucs2_t *ucs2_data = NULL;
507 parser->namedprop.ulKind = MNID_STRING;
508 if (!fetch_ucs2_nullterminated(parser, &ucs2_data))
510 pull_ucs2_talloc(parser->mem_ctx, (char**)&(parser->namedprop.kind.lpwstr.Name), ucs2_data, &(utf8_len));
511 parser->namedprop.kind.lpwstr.NameSize = utf8_len;
512 /* printf("named: %s\n", parser->namedprop.kind.lpwstr.Name); */
514 printf("unknown named property kind: 0x%02x\n", parser->namedprop.ulKind);
517 if (parser->op_namedprop) {
518 *ms = parser->op_namedprop(parser->lpProp.ulPropTag, parser->namedprop, parser->priv);
525 \details set a callback function for marker output
527 _PUBLIC_ void fxparser_set_marker_callback(struct fx_parser_context *parser, fxparser_marker_callback_t marker_callback)
529 parser->op_marker = marker_callback;
533 \details set a callback function for delete properties output
535 _PUBLIC_ void fxparser_set_delprop_callback(struct fx_parser_context *parser, fxparser_delprop_callback_t delprop_callback)
537 parser->op_delprop = delprop_callback;
541 \details set a callback function for named properties output
543 _PUBLIC_ void fxparser_set_namedprop_callback(struct fx_parser_context *parser, fxparser_namedprop_callback_t namedprop_callback)
545 parser->op_namedprop = namedprop_callback;
549 \details set a callback function for property output
551 _PUBLIC_ void fxparser_set_property_callback(struct fx_parser_context *parser, fxparser_property_callback_t property_callback)
553 parser->op_property = property_callback;
557 \details initialise a fast transfer parser
559 _PUBLIC_ struct fx_parser_context* fxparser_init(TALLOC_CTX *mem_ctx, void *priv)
561 struct fx_parser_context *parser = talloc_zero(mem_ctx, struct fx_parser_context);
563 parser->mem_ctx = mem_ctx;
564 parser->data = data_blob_talloc_named(parser->mem_ctx, NULL, 0, "fast transfer parser");
565 parser->state = ParserState_Entry;
567 parser->lpProp.ulPropTag = (enum MAPITAGS) 0;
568 parser->lpProp.dwAlignPad = 0;
569 parser->lpProp.value.l = 0;
576 \details parse a fast transfer buffer
578 _PUBLIC_ enum MAPISTATUS fxparser_parse(struct fx_parser_context *parser, DATA_BLOB *fxbuf)
580 enum MAPISTATUS ms = MAPI_E_SUCCESS;
582 data_blob_append(parser->mem_ctx, &(parser->data), fxbuf->data, fxbuf->length);
583 parser->enough_data = true;
584 while(ms == MAPI_E_SUCCESS && (parser->idx < parser->data.length) && parser->enough_data) {
585 uint32_t idx = parser->idx;
587 switch(parser->state) {
588 case ParserState_Entry:
590 if (pull_tag(parser)) {
591 /* printf("tag: 0x%08x\n", parser->tag); */
592 parser->state = ParserState_HaveTag;
594 parser->enough_data = false;
599 case ParserState_HaveTag:
601 switch (parser->tag) {
602 case PidTagStartTopFld:
603 case PidTagStartSubFld:
604 case PidTagEndFolder:
605 case PidTagStartMessage:
606 case PidTagStartFAIMsg:
607 case PidTagEndMessage:
608 case PidTagStartRecip:
609 case PidTagEndToRecip:
610 case PidTagNewAttach:
611 case PidTagEndAttach:
612 case PidTagStartEmbed:
614 if (parser->op_marker) {
615 ms = parser->op_marker(parser->tag, parser->priv);
617 parser->state = ParserState_Entry;
619 case PidTagFXDelProp:
622 if (pull_uint32_t(parser, &tag)) {
623 if (parser->op_delprop) {
624 ms = parser->op_delprop(tag, parser->priv);
626 parser->state = ParserState_Entry;
628 parser->enough_data = false;
635 /* standard property thing */
636 parser->lpProp.ulPropTag = (enum MAPITAGS) parser->tag;
637 parser->lpProp.dwAlignPad = 0;
638 if ((parser->lpProp.ulPropTag >> 16) & 0x8000) {
639 /* this is a named property */
640 // printf("tag: 0x%08x\n", parser->tag);
641 // TODO: this should probably be a separate parser state
642 // TODO: this needs to return the named property
643 if (pull_named_property(parser, &ms)) {
644 parser->state = ParserState_HavePropTag;
646 parser->enough_data = false;
650 parser->state = ParserState_HavePropTag;
656 case ParserState_HavePropTag:
658 if (fetch_property_value(parser, &(parser->data), &(parser->lpProp))) {
659 // printf("position %i of %zi\n", parser->idx, parser->data.length);
660 if (parser->op_property) {
661 ms = parser->op_property(parser->lpProp, parser->priv);
663 parser->state = ParserState_Entry;
665 parser->enough_data = false;
673 // Remove the part of the buffer that we've used
674 uint32_t remainder_len = parser->data.length - parser->idx;
675 DATA_BLOB remainder = data_blob_talloc_named(parser->mem_ctx, &(parser->data.data[parser->idx]), remainder_len, "fast transfer parser");
676 data_blob_free(&(parser->data));
677 parser->data = remainder;