diff options
| author | 2016-12-14 03:15:42 +0100 | |
|---|---|---|
| committer | 2016-12-14 03:15:42 +0100 | |
| commit | b2b56801b1342837d6d321a56e1f7d9b97015352 (patch) | |
| tree | 259d10803d077c2f4fb65606309a199bcdfb0897 /src | |
| parent | d53d0412e014872b71dd9c91727234de4f08fb2f (diff) | |
| download | libplist-b2b56801b1342837d6d321a56e1f7d9b97015352.tar.gz libplist-b2b56801b1342837d6d321a56e1f7d9b97015352.tar.bz2 | |
xplist: Improve text content parsing, reducing memory usage and unneeded copying
Diffstat (limited to 'src')
| -rw-r--r-- | src/xplist.c | 431 |
1 files changed, 318 insertions, 113 deletions
diff --git a/src/xplist.c b/src/xplist.c index 43b0422..9dd43fe 100644 --- a/src/xplist.c +++ b/src/xplist.c | |||
| @@ -460,60 +460,153 @@ static void find_next(parse_ctx ctx, const char *nextchars, int skip_quotes) | |||
| 460 | } | 460 | } |
| 461 | } | 461 | } |
| 462 | 462 | ||
| 463 | static char* get_text_content(parse_ctx ctx, const char* tag, int skip_ws, int unescape_entities) | 463 | typedef struct { |
| 464 | const char *begin; | ||
| 465 | size_t length; | ||
| 466 | int is_cdata; | ||
| 467 | void *next; | ||
| 468 | } text_part_t; | ||
| 469 | |||
| 470 | static text_part_t* text_part_init(text_part_t* part, const char *begin, size_t length, int is_cdata) | ||
| 471 | { | ||
| 472 | part->begin = begin; | ||
| 473 | part->length = length; | ||
| 474 | part->is_cdata = is_cdata; | ||
| 475 | part->next = NULL; | ||
| 476 | return part; | ||
| 477 | } | ||
| 478 | |||
| 479 | static void text_parts_free(text_part_t *tp) | ||
| 480 | { | ||
| 481 | while (tp) { | ||
| 482 | text_part_t *tmp = tp; | ||
| 483 | tp = tp->next; | ||
| 484 | free(tmp); | ||
| 485 | } | ||
| 486 | } | ||
| 487 | |||
| 488 | static text_part_t* text_part_append(text_part_t* parts, const char *begin, size_t length, int is_cdata) | ||
| 489 | { | ||
| 490 | text_part_t* newpart = malloc(sizeof(text_part_t)); | ||
| 491 | assert(newpart); | ||
| 492 | parts->next = text_part_init(newpart, begin, length, is_cdata); | ||
| 493 | return newpart; | ||
| 494 | } | ||
| 495 | |||
| 496 | static text_part_t* get_text_parts(parse_ctx ctx, const char* tag, int skip_ws, text_part_t *parts) | ||
| 464 | { | 497 | { |
| 465 | const char *p = NULL; | 498 | const char *p = NULL; |
| 466 | const char *q = NULL; | 499 | const char *q = NULL; |
| 467 | int taglen = 0; | 500 | int taglen = 0; |
| 468 | char *str = NULL; | 501 | text_part_t *last = NULL; |
| 469 | int i = 0; | ||
| 470 | 502 | ||
| 471 | if (skip_ws) { | 503 | if (skip_ws) { |
| 472 | parse_skip_ws(ctx); | 504 | parse_skip_ws(ctx); |
| 473 | } | 505 | } |
| 474 | p = ctx->pos; | 506 | do { |
| 475 | if (strncmp(ctx->pos, "<![CDATA[", 9) == 0) { | ||
| 476 | ctx->pos+=9; | ||
| 477 | p = ctx->pos; | 507 | p = ctx->pos; |
| 478 | find_str(ctx, "]]>", 0); | 508 | find_char(ctx, '<', 0); |
| 479 | if (ctx->pos >= ctx->end || strncmp(ctx->pos, "]]>", 3) != 0) { | 509 | if (*ctx->pos != '<') { |
| 480 | PLIST_XML_ERR("EOF while looking for end of CDATA block\n"); | 510 | PLIST_XML_ERR("EOF while looking for closing tag\n"); |
| 511 | ctx->err++; | ||
| 481 | return NULL; | 512 | return NULL; |
| 482 | } | 513 | } |
| 483 | q = ctx->pos; | 514 | q = ctx->pos; |
| 484 | ctx->pos+=3; | 515 | ctx->pos++; |
| 485 | unescape_entities = 0; | 516 | if (ctx->pos >= ctx->end) { |
| 486 | } | 517 | PLIST_XML_ERR("EOF while parsing '%s'\n", p); |
| 487 | find_char(ctx, '<', 0); | 518 | ctx->err++; |
| 488 | if (*ctx->pos != '<') { | 519 | return NULL; |
| 489 | PLIST_XML_ERR("EOF while looking for closing tag\n"); | 520 | } |
| 490 | return NULL; | 521 | if (*ctx->pos == '!') { |
| 491 | } | 522 | ctx->pos++; |
| 492 | if (!q) { | 523 | if (*ctx->pos == '-' && *(ctx->pos+1) == '-') { |
| 493 | q = ctx->pos; | 524 | if (last) { |
| 494 | } | 525 | last = text_part_append(last, p, q-p, 0); |
| 495 | ctx->pos++; | 526 | } else if (parts) { |
| 496 | if (ctx->pos >= ctx->end || *ctx->pos != '/') { PLIST_XML_ERR("EOF or empty tag while parsing '%s'\n",p); return NULL; } | 527 | last = text_part_init(parts, p, q-p, 0); |
| 528 | } | ||
| 529 | ctx->pos += 2; | ||
| 530 | find_str(ctx, "-->", 0); | ||
| 531 | if (ctx->pos >= ctx->end || strncmp(ctx->pos, "-->", 3) != 0) { | ||
| 532 | PLIST_XML_ERR("EOF while looking for end of comment\n"); | ||
| 533 | ctx->err++; | ||
| 534 | return NULL; | ||
| 535 | } | ||
| 536 | ctx->pos += 3; | ||
| 537 | } else if (*ctx->pos == '[') { | ||
| 538 | ctx->pos++; | ||
| 539 | if (ctx->pos >= ctx->end - 8) { | ||
| 540 | PLIST_XML_ERR("EOF while parsing <[ tag\n"); | ||
| 541 | ctx->err++; | ||
| 542 | return NULL; | ||
| 543 | } | ||
| 544 | if (strncmp(ctx->pos, "CDATA[", 6) == 0) { | ||
| 545 | if (q-p > 0) { | ||
| 546 | if (last) { | ||
| 547 | last = text_part_append(last, p, q-p, 0); | ||
| 548 | } else if (parts) { | ||
| 549 | last = text_part_init(parts, p, q-p, 0); | ||
| 550 | } | ||
| 551 | } | ||
| 552 | ctx->pos+=6; | ||
| 553 | p = ctx->pos; | ||
| 554 | find_str(ctx, "]]>", 0); | ||
| 555 | if (ctx->pos >= ctx->end || strncmp(ctx->pos, "]]>", 3) != 0) { | ||
| 556 | PLIST_XML_ERR("EOF while looking for end of CDATA block\n"); | ||
| 557 | ctx->err++; | ||
| 558 | return NULL; | ||
| 559 | } | ||
| 560 | q = ctx->pos; | ||
| 561 | if (last) { | ||
| 562 | last = text_part_append(last, p, q-p, 1); | ||
| 563 | } else if (parts) { | ||
| 564 | last = text_part_init(parts, p, q-p, 1); | ||
| 565 | } | ||
| 566 | ctx->pos += 3; | ||
| 567 | } else { | ||
| 568 | PLIST_XML_ERR("Invalid special tag <[%.6s encountered\n", ctx->pos); | ||
| 569 | ctx->err++; | ||
| 570 | return NULL; | ||
| 571 | } | ||
| 572 | } | ||
| 573 | } else if (*ctx->pos == '/') { | ||
| 574 | break; | ||
| 575 | } else { | ||
| 576 | PLIST_XML_ERR("Invalid tag %.10s inside %s tag\n", ctx->pos, tag); | ||
| 577 | ctx->err++; | ||
| 578 | return NULL; | ||
| 579 | } | ||
| 580 | } while (1); | ||
| 497 | ctx->pos++; | 581 | ctx->pos++; |
| 498 | taglen = strlen(tag); | 582 | taglen = strlen(tag); |
| 499 | if (ctx->pos >= ctx->end-taglen || strncmp(ctx->pos, tag, taglen)) { PLIST_XML_ERR("EOF or end tag mismatch\n"); return NULL;} | 583 | if (ctx->pos >= ctx->end-taglen || strncmp(ctx->pos, tag, taglen)) { |
| 584 | PLIST_XML_ERR("EOF or end tag mismatch\n"); | ||
| 585 | ctx->err++; | ||
| 586 | return NULL; | ||
| 587 | } | ||
| 500 | ctx->pos+=taglen; | 588 | ctx->pos+=taglen; |
| 501 | if (ctx->pos >= ctx->end || *ctx->pos != '>') { PLIST_XML_ERR("EOF or no '>' after tag name\n"); return NULL;} | 589 | if (ctx->pos >= ctx->end || *ctx->pos != '>') { |
| 502 | ctx->pos++; | 590 | PLIST_XML_ERR("EOF or no '>' after tag name\n"); |
| 503 | int len = q - p; | 591 | ctx->err++; |
| 504 | if (len < 0) { | ||
| 505 | PLIST_XML_ERR("Couldn't find matching '%s' end tag\n", tag); | ||
| 506 | return NULL; | 592 | return NULL; |
| 507 | } | 593 | } |
| 508 | str = malloc(len+1); | 594 | ctx->pos++; |
| 509 | strncpy(str, p, len); | ||
| 510 | str[len] = 0; | ||
| 511 | 595 | ||
| 512 | if (!unescape_entities) { | 596 | if (q-p > 0) { |
| 513 | return str; | 597 | if (last) { |
| 598 | last = text_part_append(last, p, q-p, 0); | ||
| 599 | } else if (parts) { | ||
| 600 | last = text_part_init(parts, p, q-p, 0); | ||
| 601 | } | ||
| 514 | } | 602 | } |
| 603 | return parts; | ||
| 604 | } | ||
| 515 | 605 | ||
| 516 | /* unescape entities */ | 606 | static void unescape_entities(char *str, size_t *length) |
| 607 | { | ||
| 608 | size_t i = 0; | ||
| 609 | size_t len = *length; | ||
| 517 | while (i < len-1) { | 610 | while (i < len-1) { |
| 518 | if (str[i] == '&') { | 611 | if (str[i] == '&') { |
| 519 | char *entp = str + i + 1; | 612 | char *entp = str + i + 1; |
| @@ -541,24 +634,24 @@ static char* get_text_content(parse_ctx ctx, const char* tag, int skip_ws, int u | |||
| 541 | char* ep = NULL; | 634 | char* ep = NULL; |
| 542 | if (entlen > 8) { | 635 | if (entlen > 8) { |
| 543 | PLIST_XML_ERR("Invalid numerical character reference encountered, sequence too long: &%.*s;\n", entlen, entp); | 636 | PLIST_XML_ERR("Invalid numerical character reference encountered, sequence too long: &%.*s;\n", entlen, entp); |
| 544 | return NULL; | 637 | return; |
| 545 | } | 638 | } |
| 546 | if (*(entp+1) == 'x' || *(entp+1) == 'X') { | 639 | if (*(entp+1) == 'x' || *(entp+1) == 'X') { |
| 547 | if (entlen < 3) { | 640 | if (entlen < 3) { |
| 548 | PLIST_XML_ERR("Invalid numerical character reference encountered, sequence too short: &%.*s;\n", entlen, entp); | 641 | PLIST_XML_ERR("Invalid numerical character reference encountered, sequence too short: &%.*s;\n", entlen, entp); |
| 549 | return NULL; | 642 | return; |
| 550 | } | 643 | } |
| 551 | val = strtoull(entp+2, &ep, 16); | 644 | val = strtoull(entp+2, &ep, 16); |
| 552 | } else { | 645 | } else { |
| 553 | if (entlen < 2) { | 646 | if (entlen < 2) { |
| 554 | PLIST_XML_ERR("Invalid numerical character reference encountered, sequence too short: &%.*s;\n", entlen, entp); | 647 | PLIST_XML_ERR("Invalid numerical character reference encountered, sequence too short: &%.*s;\n", entlen, entp); |
| 555 | return NULL; | 648 | return; |
| 556 | } | 649 | } |
| 557 | val = strtoull(entp+1, &ep, 10); | 650 | val = strtoull(entp+1, &ep, 10); |
| 558 | } | 651 | } |
| 559 | if (val == 0 || val > 0x10FFFF || ep-entp != entlen) { | 652 | if (val == 0 || val > 0x10FFFF || ep-entp != entlen) { |
| 560 | PLIST_XML_ERR("Invalid numerical character reference found: &%.*s;\n", entlen, entp); | 653 | PLIST_XML_ERR("Invalid numerical character reference found: &%.*s;\n", entlen, entp); |
| 561 | return NULL; | 654 | return; |
| 562 | } | 655 | } |
| 563 | /* convert to UTF8 */ | 656 | /* convert to UTF8 */ |
| 564 | if (val >= 0x10000) { | 657 | if (val >= 0x10000) { |
| @@ -585,30 +678,63 @@ static char* get_text_content(parse_ctx ctx, const char* tag, int skip_ws, int u | |||
| 585 | } | 678 | } |
| 586 | } else { | 679 | } else { |
| 587 | PLIST_XML_ERR("Invalid entity encountered: &%.*s;\n", entlen, entp); | 680 | PLIST_XML_ERR("Invalid entity encountered: &%.*s;\n", entlen, entp); |
| 588 | return NULL; | 681 | return; |
| 589 | } | 682 | } |
| 590 | memmove(entp, str+i+1, len - i); | 683 | memmove(entp, str+i+1, len - i); |
| 591 | i -= entlen; | 684 | i -= entlen; |
| 685 | len -= (entlen+1); | ||
| 592 | continue; | 686 | continue; |
| 593 | } | 687 | } |
| 594 | } | 688 | } |
| 595 | i++; | 689 | i++; |
| 596 | } | 690 | } |
| 597 | 691 | *length = len; | |
| 598 | return str; | ||
| 599 | } | 692 | } |
| 600 | 693 | ||
| 601 | static void skip_text_content(parse_ctx ctx, const char* tag) | 694 | static char* text_parts_get_content(text_part_t *tp, int unesc_entities, size_t *length, int *requires_free) |
| 602 | { | 695 | { |
| 603 | int taglen; | 696 | char *str = NULL; |
| 604 | find_char(ctx, '<', 1); | 697 | size_t total_length = 0; |
| 605 | if (*ctx->pos != '<') return; | 698 | |
| 606 | ctx->pos++; | 699 | if (!tp) { |
| 607 | taglen = strlen(tag); | 700 | return NULL; |
| 608 | if (ctx->pos >= ctx->end-taglen || strncmp(ctx->pos, tag, taglen)) return; | 701 | } |
| 609 | ctx->pos+=taglen; | 702 | char *p; |
| 610 | if (ctx->pos >= ctx->end || *ctx->pos != '>') return; | 703 | if (requires_free && !tp->next) { |
| 611 | ctx->pos++; | 704 | if (tp->is_cdata || !unesc_entities) { |
| 705 | *requires_free = 0; | ||
| 706 | if (length) { | ||
| 707 | *length = tp->length; | ||
| 708 | } | ||
| 709 | return (char*)tp->begin; | ||
| 710 | } | ||
| 711 | } | ||
| 712 | text_part_t *tmp = tp; | ||
| 713 | while (tp && tp->begin) { | ||
| 714 | total_length += tp->length; | ||
| 715 | tp = tp->next; | ||
| 716 | } | ||
| 717 | str = malloc(total_length + 1); | ||
| 718 | assert(str); | ||
| 719 | p = str; | ||
| 720 | tp = tmp; | ||
| 721 | while (tp && tp->begin) { | ||
| 722 | size_t len = tp->length; | ||
| 723 | strncpy(p, tp->begin, len); | ||
| 724 | if (!tp->is_cdata && unesc_entities) { | ||
| 725 | unescape_entities(p, &len); | ||
| 726 | } | ||
| 727 | p += len; | ||
| 728 | tp = tp->next; | ||
| 729 | } | ||
| 730 | *p = '\0'; | ||
| 731 | if (length) { | ||
| 732 | *length = p - str; | ||
| 733 | } | ||
| 734 | if (requires_free) { | ||
| 735 | *requires_free = 1; | ||
| 736 | } | ||
| 737 | return str; | ||
| 612 | } | 738 | } |
| 613 | 739 | ||
| 614 | static void node_from_xml(parse_ctx ctx, plist_t *plist) | 740 | static void node_from_xml(parse_ctx ctx, plist_t *plist) |
| @@ -749,80 +875,120 @@ static void node_from_xml(parse_ctx ctx, plist_t *plist) | |||
| 749 | data->type = PLIST_ARRAY; | 875 | data->type = PLIST_ARRAY; |
| 750 | } else if (!strcmp(tag, XPLIST_INT)) { | 876 | } else if (!strcmp(tag, XPLIST_INT)) { |
| 751 | if (!is_empty) { | 877 | if (!is_empty) { |
| 752 | char *str_content = get_text_content(ctx, tag, 1, 0); | 878 | text_part_t first_part = { NULL, 0, 0, NULL }; |
| 753 | if (!str_content) { | 879 | text_part_t *tp = get_text_parts(ctx, tag, 1, &first_part); |
| 754 | PLIST_XML_ERR("Couldn't find end tag for '%s'\n", tag); | 880 | if (!tp) { |
| 755 | ctx->pos = ctx->end; | 881 | PLIST_XML_ERR("Could not parse text content for '%s' node\n", tag); |
| 756 | ctx->err++; | 882 | text_parts_free(first_part.next); |
| 757 | free(tag); | 883 | free(tag); |
| 758 | free(keyname); | 884 | free(keyname); |
| 759 | return; | 885 | break; |
| 760 | } | 886 | } |
| 761 | char *str = str_content; | 887 | if (tp->begin) { |
| 762 | int is_negative = 0; | 888 | int requires_free = 0; |
| 763 | if ((str[0] == '-') || (str[0] == '+')) { | 889 | char *str_content = text_parts_get_content(tp, 0, NULL, &requires_free); |
| 764 | if (str[0] == '-') { | 890 | if (!str_content) { |
| 765 | is_negative = 1; | 891 | PLIST_XML_ERR("Could not get text content for '%s' node\n", tag); |
| 892 | text_parts_free(first_part.next); | ||
| 893 | ctx->err++; | ||
| 894 | free(tag); | ||
| 895 | free(keyname); | ||
| 896 | break; | ||
| 766 | } | 897 | } |
| 767 | str++; | 898 | char *str = str_content; |
| 768 | } | 899 | int is_negative = 0; |
| 769 | char* endp = NULL; | 900 | if ((str[0] == '-') || (str[0] == '+')) { |
| 770 | data->intval = strtoull((char*)str, &endp, 0); | 901 | if (str[0] == '-') { |
| 771 | if ((endp != NULL) && (strlen(endp) > 0)) { | 902 | is_negative = 1; |
| 772 | PLIST_XML_ERR("integer parse error: string contains invalid characters: '%s'\n", endp); | 903 | } |
| 773 | } | 904 | str++; |
| 774 | if (is_negative || (data->intval <= INT64_MAX)) { | 905 | } |
| 775 | int64_t v = data->intval; | 906 | data->intval = strtoull((char*)str, NULL, 0); |
| 776 | if (is_negative) { | 907 | if (is_negative || (data->intval <= INT64_MAX)) { |
| 777 | v = -v; | 908 | int64_t v = data->intval; |
| 909 | if (is_negative) { | ||
| 910 | v = -v; | ||
| 911 | } | ||
| 912 | data->intval = (uint64_t)v; | ||
| 913 | data->length = 8; | ||
| 914 | } else { | ||
| 915 | data->length = 16; | ||
| 916 | } | ||
| 917 | if (requires_free) { | ||
| 918 | free(str_content); | ||
| 778 | } | 919 | } |
| 779 | data->intval = (uint64_t)v; | ||
| 780 | data->length = 8; | ||
| 781 | } else { | 920 | } else { |
| 782 | data->length = 16; | 921 | is_empty = 1; |
| 783 | } | 922 | } |
| 784 | free(str_content); | 923 | text_parts_free(tp->next); |
| 785 | } else { | 924 | } |
| 925 | if (is_empty) { | ||
| 786 | data->intval = 0; | 926 | data->intval = 0; |
| 787 | data->length = 8; | 927 | data->length = 8; |
| 788 | } | 928 | } |
| 789 | data->type = PLIST_UINT; | 929 | data->type = PLIST_UINT; |
| 790 | } else if (!strcmp(tag, XPLIST_REAL)) { | 930 | } else if (!strcmp(tag, XPLIST_REAL)) { |
| 791 | if (!is_empty) { | 931 | if (!is_empty) { |
| 792 | char *strval = get_text_content(ctx, tag, 1, 0); | 932 | text_part_t first_part = { NULL, 0, 0, NULL }; |
| 793 | if (!strval) { | 933 | text_part_t *tp = get_text_parts(ctx, tag, 1, &first_part); |
| 794 | PLIST_XML_ERR("Couldn't get text content for '%s' node\n", tag); | 934 | if (!tp) { |
| 795 | ctx->pos = ctx->end; | 935 | PLIST_XML_ERR("Could not parse text content for '%s' node\n", tag); |
| 796 | ctx->err++; | 936 | text_parts_free(first_part.next); |
| 797 | free(tag); | 937 | free(tag); |
| 798 | free(keyname); | 938 | free(keyname); |
| 799 | return; | 939 | break; |
| 800 | } | 940 | } |
| 801 | data->realval = atof((char *) strval); | 941 | if (tp->begin) { |
| 802 | free(strval); | 942 | int requires_free = 0; |
| 943 | char *str_content = text_parts_get_content(tp, 0, NULL, &requires_free); | ||
| 944 | if (!str_content) { | ||
| 945 | PLIST_XML_ERR("Could not get text content for '%s' node\n", tag); | ||
| 946 | text_parts_free(first_part.next); | ||
| 947 | ctx->err++; | ||
| 948 | free(tag); | ||
| 949 | free(keyname); | ||
| 950 | break; | ||
| 951 | } | ||
| 952 | data->realval = atof(str_content); | ||
| 953 | if (requires_free) { | ||
| 954 | free(str_content); | ||
| 955 | } | ||
| 956 | } | ||
| 957 | text_parts_free(tp->next); | ||
| 803 | } | 958 | } |
| 804 | data->type = PLIST_REAL; | 959 | data->type = PLIST_REAL; |
| 805 | data->length = 8; | 960 | data->length = 8; |
| 806 | } else if (!strcmp(tag, XPLIST_TRUE)) { | 961 | } else if (!strcmp(tag, XPLIST_TRUE)) { |
| 807 | if (!is_empty) { | 962 | if (!is_empty) { |
| 808 | skip_text_content(ctx, tag); | 963 | get_text_parts(ctx, tag, 1, NULL); |
| 809 | } | 964 | } |
| 810 | data->type = PLIST_BOOLEAN; | 965 | data->type = PLIST_BOOLEAN; |
| 811 | data->boolval = 1; | 966 | data->boolval = 1; |
| 812 | data->length = 1; | 967 | data->length = 1; |
| 813 | } else if (!strcmp(tag, XPLIST_FALSE)) { | 968 | } else if (!strcmp(tag, XPLIST_FALSE)) { |
| 814 | if (!is_empty) { | 969 | if (!is_empty) { |
| 815 | skip_text_content(ctx, tag); | 970 | get_text_parts(ctx, tag, 1, NULL); |
| 816 | } | 971 | } |
| 817 | data->type = PLIST_BOOLEAN; | 972 | data->type = PLIST_BOOLEAN; |
| 818 | data->boolval = 0; | 973 | data->boolval = 0; |
| 819 | data->length = 1; | 974 | data->length = 1; |
| 820 | } else if (!strcmp(tag, XPLIST_STRING) || !strcmp(tag, XPLIST_KEY)) { | 975 | } else if (!strcmp(tag, XPLIST_STRING) || !strcmp(tag, XPLIST_KEY)) { |
| 821 | if (!is_empty) { | 976 | if (!is_empty) { |
| 822 | char *str = get_text_content(ctx, tag, 0, 1); | 977 | text_part_t first_part = { NULL, 0, 0, NULL }; |
| 978 | text_part_t *tp = get_text_parts(ctx, tag, 0, &first_part); | ||
| 979 | char *str = NULL; | ||
| 980 | size_t length = 0; | ||
| 981 | if (!tp) { | ||
| 982 | PLIST_XML_ERR("Could not parse text content for '%s' node\n", tag); | ||
| 983 | text_parts_free(first_part.next); | ||
| 984 | free(tag); | ||
| 985 | free(keyname); | ||
| 986 | break; | ||
| 987 | } | ||
| 988 | str = text_parts_get_content(tp, 1, &length, NULL); | ||
| 989 | text_parts_free(first_part.next); | ||
| 823 | if (!str) { | 990 | if (!str) { |
| 824 | PLIST_XML_ERR("Couldn't get text content for '%s' node\n", tag); | 991 | PLIST_XML_ERR("Could not get text content for '%s' node\n", tag); |
| 825 | ctx->pos = ctx->end; | ||
| 826 | ctx->err++; | 992 | ctx->err++; |
| 827 | free(tag); | 993 | free(tag); |
| 828 | free(keyname); | 994 | free(keyname); |
| @@ -836,7 +1002,7 @@ static void node_from_xml(parse_ctx ctx, plist_t *plist) | |||
| 836 | continue; | 1002 | continue; |
| 837 | } else { | 1003 | } else { |
| 838 | data->strval = str; | 1004 | data->strval = str; |
| 839 | data->length = strlen(str); | 1005 | data->length = length; |
| 840 | } | 1006 | } |
| 841 | } else { | 1007 | } else { |
| 842 | data->strval = strdup(""); | 1008 | data->strval = strdup(""); |
| @@ -845,40 +1011,79 @@ static void node_from_xml(parse_ctx ctx, plist_t *plist) | |||
| 845 | data->type = PLIST_STRING; | 1011 | data->type = PLIST_STRING; |
| 846 | } else if (!strcmp(tag, XPLIST_DATA)) { | 1012 | } else if (!strcmp(tag, XPLIST_DATA)) { |
| 847 | if (!is_empty) { | 1013 | if (!is_empty) { |
| 848 | char *strval = get_text_content(ctx, tag, 1, 0); | 1014 | text_part_t first_part = { NULL, 0, 0, NULL }; |
| 849 | if (!strval) { | 1015 | text_part_t *tp = get_text_parts(ctx, tag, 1, &first_part); |
| 850 | PLIST_XML_ERR("Couldn't get text content for '%s' node\n", tag); | 1016 | if (!tp) { |
| 851 | ctx->pos = ctx->end; | 1017 | PLIST_XML_ERR("Could not parse text content for '%s' node\n", tag); |
| 852 | ctx->err++; | 1018 | text_parts_free(first_part.next); |
| 853 | free(tag); | 1019 | free(tag); |
| 854 | free(keyname); | 1020 | free(keyname); |
| 855 | return; | 1021 | break; |
| 856 | } | 1022 | } |
| 857 | size_t size = 0; | 1023 | if (tp->begin) { |
| 858 | data->buff = base64decode((char*)strval, &size); | 1024 | int requires_free = 0; |
| 859 | free(strval); | 1025 | char *str_content = text_parts_get_content(tp, 0, NULL, &requires_free); |
| 860 | data->length = size; | 1026 | if (!str_content) { |
| 1027 | PLIST_XML_ERR("Could not get text content for '%s' node\n", tag); | ||
| 1028 | text_parts_free(first_part.next); | ||
| 1029 | ctx->err++; | ||
| 1030 | free(tag); | ||
| 1031 | free(keyname); | ||
| 1032 | break; | ||
| 1033 | } | ||
| 1034 | size_t size = tp->length; | ||
| 1035 | data->buff = base64decode(str_content, &size); | ||
| 1036 | data->length = size; | ||
| 1037 | |||
| 1038 | if (requires_free) { | ||
| 1039 | free(str_content); | ||
| 1040 | } | ||
| 1041 | } | ||
| 1042 | text_parts_free(tp->next); | ||
| 861 | } | 1043 | } |
| 862 | data->type = PLIST_DATA; | 1044 | data->type = PLIST_DATA; |
| 863 | } else if (!strcmp(tag, XPLIST_DATE)) { | 1045 | } else if (!strcmp(tag, XPLIST_DATE)) { |
| 864 | if (!is_empty) { | 1046 | if (!is_empty) { |
| 865 | char *strval = get_text_content(ctx, tag, 1, 0); | 1047 | text_part_t first_part = { NULL, 0, 0, NULL }; |
| 866 | if (!strval) { | 1048 | text_part_t *tp = get_text_parts(ctx, tag, 1, &first_part); |
| 867 | PLIST_XML_ERR("Couldn't get text content for '%s' node\n", tag); | 1049 | if (!tp) { |
| 868 | ctx->pos = ctx->end; | 1050 | PLIST_XML_ERR("Could not parse text content for '%s' node\n", tag); |
| 869 | ctx->err++; | 1051 | text_parts_free(first_part.next); |
| 870 | free(tag); | 1052 | free(tag); |
| 871 | free(keyname); | 1053 | free(keyname); |
| 872 | return; | 1054 | break; |
| 873 | } | 1055 | } |
| 874 | Time64_T timev = 0; | 1056 | Time64_T timev = 0; |
| 875 | if (strlen((const char*)strval) >= 11) { | 1057 | if (tp->begin) { |
| 876 | struct TM btime; | 1058 | int requires_free = 0; |
| 877 | parse_date((const char*)strval, &btime); | 1059 | size_t length = 0; |
| 878 | timev = timegm64(&btime); | 1060 | char *str_content = text_parts_get_content(tp, 0, &length, &requires_free); |
| 1061 | if (!str_content) { | ||
| 1062 | PLIST_XML_ERR("Could not get text content for '%s' node\n", tag); | ||
| 1063 | text_parts_free(first_part.next); | ||
| 1064 | ctx->err++; | ||
| 1065 | free(tag); | ||
| 1066 | free(keyname); | ||
| 1067 | break; | ||
| 1068 | } | ||
| 1069 | |||
| 1070 | if ((length >= 11) && (length < 32)) { | ||
| 1071 | /* we need to copy here and 0-terminate because sscanf will read the entire string (whole rest of XML data) which can be huge */ | ||
| 1072 | char strval[32]; | ||
| 1073 | struct TM btime; | ||
| 1074 | strncpy(strval, str_content, length); | ||
| 1075 | strval[tp->length] = '\0'; | ||
| 1076 | parse_date(strval, &btime); | ||
| 1077 | timev = timegm64(&btime); | ||
| 1078 | } else { | ||
| 1079 | PLIST_XML_ERR("Invalid text content in date node\n"); | ||
| 1080 | } | ||
| 1081 | if (requires_free) { | ||
| 1082 | free(str_content); | ||
| 1083 | } | ||
| 879 | } | 1084 | } |
| 1085 | text_parts_free(tp->next); | ||
| 880 | data->realval = (double)(timev - MAC_EPOCH); | 1086 | data->realval = (double)(timev - MAC_EPOCH); |
| 881 | free(strval); | ||
| 882 | } | 1087 | } |
| 883 | data->length = sizeof(double); | 1088 | data->length = sizeof(double); |
| 884 | data->type = PLIST_DATE; | 1089 | data->type = PLIST_DATE; |
