diff options
Diffstat (limited to 'src/xplist.c')
| -rw-r--r-- | src/xplist.c | 182 |
1 files changed, 103 insertions, 79 deletions
diff --git a/src/xplist.c b/src/xplist.c index 7f20ef2..022f1cd 100644 --- a/src/xplist.c +++ b/src/xplist.c | |||
| @@ -2,7 +2,7 @@ | |||
| 2 | * xplist.c | 2 | * xplist.c |
| 3 | * XML plist implementation | 3 | * XML plist implementation |
| 4 | * | 4 | * |
| 5 | * Copyright (c) 2010-2016 Nikias Bassen All Rights Reserved. | 5 | * Copyright (c) 2010-2017 Nikias Bassen All Rights Reserved. |
| 6 | * Copyright (c) 2010-2015 Martin Szulecki All Rights Reserved. | 6 | * Copyright (c) 2010-2015 Martin Szulecki All Rights Reserved. |
| 7 | * Copyright (c) 2008 Jonathan Beck All Rights Reserved. | 7 | * Copyright (c) 2008 Jonathan Beck All Rights Reserved. |
| 8 | * | 8 | * |
| @@ -804,12 +804,21 @@ static char* text_parts_get_content(text_part_t *tp, int unesc_entities, size_t | |||
| 804 | return str; | 804 | return str; |
| 805 | } | 805 | } |
| 806 | 806 | ||
| 807 | static void node_from_xml(parse_ctx ctx, plist_t *plist, uint32_t depth) | 807 | static void node_from_xml(parse_ctx ctx, plist_t *plist) |
| 808 | { | 808 | { |
| 809 | char *tag = NULL; | 809 | char *tag = NULL; |
| 810 | char *keyname = NULL; | 810 | char *keyname = NULL; |
| 811 | plist_t subnode = NULL; | 811 | plist_t subnode = NULL; |
| 812 | const char *p = NULL; | 812 | const char *p = NULL; |
| 813 | plist_t parent = NULL; | ||
| 814 | int has_content = 0; | ||
| 815 | |||
| 816 | struct node_path_item { | ||
| 817 | const char *type; | ||
| 818 | void *prev; | ||
| 819 | }; | ||
| 820 | struct node_path_item* node_path = NULL; | ||
| 821 | |||
| 813 | while (ctx->pos < ctx->end && !ctx->err) { | 822 | while (ctx->pos < ctx->end && !ctx->err) { |
| 814 | parse_skip_ws(ctx); | 823 | parse_skip_ws(ctx); |
| 815 | if (ctx->pos >= ctx->end) { | 824 | if (ctx->pos >= ctx->end) { |
| @@ -929,28 +938,58 @@ static void node_from_xml(parse_ctx ctx, plist_t *plist, uint32_t depth) | |||
| 929 | if (!strcmp(tag, "plist")) { | 938 | if (!strcmp(tag, "plist")) { |
| 930 | free(tag); | 939 | free(tag); |
| 931 | tag = NULL; | 940 | tag = NULL; |
| 941 | has_content = 0; | ||
| 942 | |||
| 943 | if (!node_path && *plist) { | ||
| 944 | /* we don't allow another top-level <plist> */ | ||
| 945 | break; | ||
| 946 | } | ||
| 932 | if (is_empty) { | 947 | if (is_empty) { |
| 933 | PLIST_XML_ERR("Empty plist tag\n"); | 948 | PLIST_XML_ERR("Empty plist tag\n"); |
| 934 | break; | 949 | ctx->err++; |
| 950 | goto err_out; | ||
| 935 | } | 951 | } |
| 936 | if (!*plist) { | 952 | |
| 937 | /* only process first plist node found */ | 953 | struct node_path_item *path_item = malloc(sizeof(struct node_path_item)); |
| 938 | node_from_xml(ctx, plist, depth+1); | 954 | if (!path_item) { |
| 955 | PLIST_XML_ERR("out of memory when allocating node path item\n"); | ||
| 956 | ctx->err++; | ||
| 957 | goto err_out; | ||
| 939 | } | 958 | } |
| 959 | path_item->type = "plist"; | ||
| 960 | path_item->prev = node_path; | ||
| 961 | node_path = path_item; | ||
| 962 | |||
| 940 | continue; | 963 | continue; |
| 941 | } else if (depth == 1 && !strcmp(tag, "/plist")) { | 964 | } else if (!strcmp(tag, "/plist")) { |
| 942 | if (!*plist) { | 965 | if (!has_content) { |
| 943 | PLIST_XML_ERR("Empty plist tag\n"); | 966 | PLIST_XML_ERR("encountered empty plist tag\n"); |
| 967 | ctx->err++; | ||
| 968 | goto err_out; | ||
| 944 | } | 969 | } |
| 945 | goto err_out; | 970 | if (!node_path) { |
| 946 | } else if (depth == 1 && *plist) { | 971 | PLIST_XML_ERR("node path is empty while trying to match closing tag with opening tag\n"); |
| 947 | PLIST_XML_ERR("Unexpected tag <%s> found while </plist> is expected\n", tag); | 972 | ctx->err++; |
| 948 | ctx->err++; | 973 | goto err_out; |
| 949 | goto err_out; | 974 | } |
| 975 | if (strcmp(node_path->type, tag+1) != 0) { | ||
| 976 | PLIST_XML_ERR("mismatching closing tag <%s> found for opening tag <%s>\n", tag, node_path->type); | ||
| 977 | ctx->err++; | ||
| 978 | goto err_out; | ||
| 979 | } | ||
| 980 | struct node_path_item *path_item = node_path; | ||
| 981 | node_path = node_path->prev; | ||
| 982 | free(path_item); | ||
| 983 | |||
| 984 | free(tag); | ||
| 985 | tag = NULL; | ||
| 986 | |||
| 987 | continue; | ||
| 950 | } | 988 | } |
| 951 | 989 | ||
| 952 | plist_data_t data = plist_new_plist_data(); | 990 | plist_data_t data = plist_new_plist_data(); |
| 953 | subnode = plist_new_node(data); | 991 | subnode = plist_new_node(data); |
| 992 | has_content = 1; | ||
| 954 | 993 | ||
| 955 | if (!strcmp(tag, XPLIST_DICT)) { | 994 | if (!strcmp(tag, XPLIST_DICT)) { |
| 956 | data->type = PLIST_DICT; | 995 | data->type = PLIST_DICT; |
| @@ -1068,7 +1107,7 @@ static void node_from_xml(parse_ctx ctx, plist_t *plist, uint32_t depth) | |||
| 1068 | ctx->err++; | 1107 | ctx->err++; |
| 1069 | goto err_out; | 1108 | goto err_out; |
| 1070 | } | 1109 | } |
| 1071 | if (!strcmp(tag, "key") && !keyname && *plist && (plist_get_node_type(*plist) == PLIST_DICT)) { | 1110 | if (!strcmp(tag, "key") && !keyname && parent && (plist_get_node_type(parent) == PLIST_DICT)) { |
| 1072 | keyname = str; | 1111 | keyname = str; |
| 1073 | free(tag); | 1112 | free(tag); |
| 1074 | tag = NULL; | 1113 | tag = NULL; |
| @@ -1167,100 +1206,78 @@ static void node_from_xml(parse_ctx ctx, plist_t *plist, uint32_t depth) | |||
| 1167 | goto err_out; | 1206 | goto err_out; |
| 1168 | } | 1207 | } |
| 1169 | if (subnode && !closing_tag) { | 1208 | if (subnode && !closing_tag) { |
| 1170 | /* parse sub nodes for structured types */ | ||
| 1171 | if (data->type == PLIST_DICT || data->type == PLIST_ARRAY) { | ||
| 1172 | if (!is_empty) { | ||
| 1173 | /* only if not empty */ | ||
| 1174 | node_from_xml(ctx, &subnode, depth+1); | ||
| 1175 | if (ctx->err) { | ||
| 1176 | /* make sure to bail out if parsing failed */ | ||
| 1177 | goto err_out; | ||
| 1178 | } | ||
| 1179 | if ((data->type == PLIST_DICT) && (plist_dict_get_size(subnode) == 1)) { | ||
| 1180 | /* convert XML CF$UID dictionaries to PLIST_UID nodes */ | ||
| 1181 | plist_t uid = plist_dict_get_item(subnode, "CF$UID"); | ||
| 1182 | if (uid) { | ||
| 1183 | uint64_t val = 0; | ||
| 1184 | if (plist_get_node_type(uid) != PLIST_UINT) { | ||
| 1185 | ctx->err++; | ||
| 1186 | PLIST_XML_ERR("Invalid node type for CF$UID dict entry (must be PLIST_UINT)\n"); | ||
| 1187 | goto err_out; | ||
| 1188 | } | ||
| 1189 | plist_get_uint_val(uid, &val); | ||
| 1190 | plist_dict_remove_item(subnode, "CF$UID"); | ||
| 1191 | plist_data_t nodedata = plist_get_data((node_t*)subnode); | ||
| 1192 | free(nodedata->buff); | ||
| 1193 | nodedata->type = PLIST_UID; | ||
| 1194 | nodedata->length = sizeof(uint64_t); | ||
| 1195 | nodedata->intval = val; | ||
| 1196 | } | ||
| 1197 | } | ||
| 1198 | } | ||
| 1199 | } | ||
| 1200 | if (!*plist) { | 1209 | if (!*plist) { |
| 1201 | /* no parent? make this node the new parent node */ | 1210 | /* first node, make this node the parent node */ |
| 1202 | *plist = subnode; | 1211 | *plist = subnode; |
| 1203 | subnode = NULL; | 1212 | if (data->type != PLIST_DICT && data->type != PLIST_ARRAY) { |
| 1204 | } else { | 1213 | /* if the first node is not a structered node, we're done */ |
| 1205 | switch (plist_get_node_type(*plist)) { | 1214 | subnode = NULL; |
| 1215 | goto err_out; | ||
| 1216 | } | ||
| 1217 | parent = subnode; | ||
| 1218 | } else if (parent) { | ||
| 1219 | switch (plist_get_node_type(parent)) { | ||
| 1206 | case PLIST_DICT: | 1220 | case PLIST_DICT: |
| 1207 | if (!keyname) { | 1221 | if (!keyname) { |
| 1208 | PLIST_XML_ERR("missing key name while adding dict item\n"); | 1222 | PLIST_XML_ERR("missing key name while adding dict item\n"); |
| 1209 | ctx->err++; | 1223 | ctx->err++; |
| 1210 | break; | 1224 | break; |
| 1211 | } | 1225 | } |
| 1212 | plist_dict_set_item(*plist, keyname, subnode); | 1226 | plist_dict_set_item(parent, keyname, subnode); |
| 1213 | subnode = NULL; | ||
| 1214 | break; | 1227 | break; |
| 1215 | case PLIST_ARRAY: | 1228 | case PLIST_ARRAY: |
| 1216 | plist_array_append_item(*plist, subnode); | 1229 | plist_array_append_item(parent, subnode); |
| 1217 | subnode = NULL; | ||
| 1218 | break; | 1230 | break; |
| 1219 | default: | 1231 | default: |
| 1220 | /* should not happen */ | 1232 | /* should not happen */ |
| 1221 | PLIST_XML_ERR("while parsing XML plist: parent is not a structered node.\n"); | 1233 | PLIST_XML_ERR("parent is not a structered node\n"); |
| 1222 | ctx->err++; | 1234 | ctx->err++; |
| 1223 | break; | 1235 | break; |
| 1224 | } | 1236 | } |
| 1225 | } | 1237 | } |
| 1226 | } else if (closing_tag && *plist) { | 1238 | if (!is_empty && (data->type == PLIST_DICT || data->type == PLIST_ARRAY)) { |
| 1227 | switch (plist_get_node_type(*plist)) { | 1239 | struct node_path_item *path_item = malloc(sizeof(struct node_path_item)); |
| 1228 | case PLIST_DICT: | 1240 | if (!path_item) { |
| 1229 | if (keyname) { | 1241 | PLIST_XML_ERR("out of memory when allocating node path item\n"); |
| 1230 | PLIST_XML_ERR("missing value node in dict\n"); | ||
| 1231 | ctx->err++; | ||
| 1232 | } else if (strcmp(tag+1, XPLIST_DICT) != 0) { | ||
| 1233 | PLIST_XML_ERR("closing tag mismatch, expected: </%s> found: <%s>\n", XPLIST_DICT, tag); | ||
| 1234 | ctx->err++; | ||
| 1235 | } | ||
| 1236 | break; | ||
| 1237 | case PLIST_ARRAY: | ||
| 1238 | if (strcmp(tag+1, XPLIST_ARRAY) != 0) { | ||
| 1239 | PLIST_XML_ERR("closing tag mismatch, expected: </%s> found: <%s>\n", XPLIST_ARRAY, tag); | ||
| 1240 | ctx->err++; | 1242 | ctx->err++; |
| 1243 | goto err_out; | ||
| 1241 | } | 1244 | } |
| 1242 | break; | 1245 | path_item->type = (data->type == PLIST_DICT) ? XPLIST_DICT : XPLIST_ARRAY; |
| 1243 | default: | 1246 | path_item->prev = node_path; |
| 1244 | /* should not happen */ | 1247 | node_path = path_item; |
| 1245 | PLIST_XML_ERR("expected structered node but got type %d\n", plist_get_node_type(*plist)); | 1248 | |
| 1249 | parent = subnode; | ||
| 1250 | } | ||
| 1251 | subnode = NULL; | ||
| 1252 | } else if (closing_tag) { | ||
| 1253 | if (!node_path) { | ||
| 1254 | PLIST_XML_ERR("node path is empty while trying to match closing tag with opening tag\n"); | ||
| 1246 | ctx->err++; | 1255 | ctx->err++; |
| 1247 | break; | 1256 | goto err_out; |
| 1248 | } | 1257 | } |
| 1258 | if (strcmp(node_path->type, tag+1) != 0) { | ||
| 1259 | PLIST_XML_ERR("unexpected %s found (for opening %s)\n", tag, node_path->type); | ||
| 1260 | ctx->err++; | ||
| 1261 | goto err_out; | ||
| 1262 | } | ||
| 1263 | struct node_path_item *path_item = node_path; | ||
| 1264 | node_path = node_path->prev; | ||
| 1265 | free(path_item); | ||
| 1266 | |||
| 1267 | parent = ((node_t*)parent)->parent; | ||
| 1249 | } | 1268 | } |
| 1269 | |||
| 1250 | free(tag); | 1270 | free(tag); |
| 1251 | tag = NULL; | 1271 | tag = NULL; |
| 1252 | free(keyname); | 1272 | free(keyname); |
| 1253 | keyname = NULL; | 1273 | keyname = NULL; |
| 1254 | plist_free(subnode); | 1274 | plist_free(subnode); |
| 1255 | subnode = NULL; | 1275 | subnode = NULL; |
| 1256 | if (closing_tag) { | ||
| 1257 | break; | ||
| 1258 | } | ||
| 1259 | } | 1276 | } |
| 1260 | } | 1277 | } |
| 1261 | 1278 | ||
| 1262 | if (depth == 1) { | 1279 | if (node_path) { |
| 1263 | PLIST_XML_ERR("EOF while </plist> tag is expected\n"); | 1280 | PLIST_XML_ERR("EOF encountered while </%s> was expected\n", node_path->type); |
| 1264 | ctx->err++; | 1281 | ctx->err++; |
| 1265 | } | 1282 | } |
| 1266 | 1283 | ||
| @@ -1269,6 +1286,13 @@ err_out: | |||
| 1269 | free(keyname); | 1286 | free(keyname); |
| 1270 | plist_free(subnode); | 1287 | plist_free(subnode); |
| 1271 | 1288 | ||
| 1289 | /* clean up node_path if required */ | ||
| 1290 | while (node_path) { | ||
| 1291 | struct node_path_item *path_item = node_path; | ||
| 1292 | node_path = path_item->prev; | ||
| 1293 | free(path_item); | ||
| 1294 | } | ||
| 1295 | |||
| 1272 | if (ctx->err) { | 1296 | if (ctx->err) { |
| 1273 | plist_free(*plist); | 1297 | plist_free(*plist); |
| 1274 | *plist = NULL; | 1298 | *plist = NULL; |
| @@ -1284,5 +1308,5 @@ PLIST_API void plist_from_xml(const char *plist_xml, uint32_t length, plist_t * | |||
| 1284 | 1308 | ||
| 1285 | struct _parse_ctx ctx = { plist_xml, plist_xml + length, 0 }; | 1309 | struct _parse_ctx ctx = { plist_xml, plist_xml + length, 0 }; |
| 1286 | 1310 | ||
| 1287 | node_from_xml(&ctx, plist, 0); | 1311 | node_from_xml(&ctx, plist); |
| 1288 | } | 1312 | } |
