#include "sdp-xml.h" #include #include #include #include #include #define STRBUFSIZE 256 #define MAXINDENT 64 static void convert_raw_data_to_xml(sdp_data_t * data, int indentLevel, void *userData, void (*appendFunc) (void *, const char *)) { int i, hex; char buf[STRBUFSIZE]; char indent[MAXINDENT]; char indentPlusOne[MAXINDENT]; if (!data) return; if (indentLevel >= MAXINDENT) indentLevel = MAXINDENT - 2; for (i = 0; i < indentLevel; i++) { indent[i] = '\t'; indentPlusOne[i] = '\t'; } indent[i] = '\0'; indentPlusOne[i] = '\t'; indentPlusOne[i + 1] = '\0'; buf[STRBUFSIZE - 1] = '\0'; switch (data->dtd) { case SDP_DATA_NIL: appendFunc(userData, indent); appendFunc(userData, ""); appendFunc(userData, "\n"); break; case SDP_BOOL: appendFunc(userData, indent); appendFunc(userData, ""); appendFunc(userData, data->val.uint8 ? "True" : "False"); appendFunc(userData, "\n"); break; case SDP_UINT8: appendFunc(userData, indent); appendFunc(userData, ""); snprintf(buf, STRBUFSIZE - 1, "0x%02x", data->val.uint8); appendFunc(userData, buf); appendFunc(userData, "\n"); break; case SDP_UINT16: appendFunc(userData, indent); appendFunc(userData, ""); snprintf(buf, STRBUFSIZE - 1, "0x%04x", data->val.uint16); appendFunc(userData, buf); appendFunc(userData, "\n"); break; case SDP_UINT32: appendFunc(userData, indent); appendFunc(userData, ""); snprintf(buf, STRBUFSIZE - 1, "0x%08x", data->val.uint32); appendFunc(userData, buf); appendFunc(userData, "\n"); break; case SDP_UINT64: appendFunc(userData, indent); appendFunc(userData, ""); snprintf(buf, STRBUFSIZE - 1, "0x%016jx", data->val.uint64); appendFunc(userData, buf); appendFunc(userData, "\n"); break; case SDP_UINT128: appendFunc(userData, indent); appendFunc(userData, ""); for (i = 0; i < 16; i++) { sprintf(&buf[i * 2], "%02x", (unsigned char) data->val.uint128.data[i]); } appendFunc(userData, buf); appendFunc(userData, "\n"); break; case SDP_INT8: appendFunc(userData, indent); appendFunc(userData, ""); snprintf(buf, STRBUFSIZE - 1, "%d", data->val.int8); appendFunc(userData, buf); appendFunc(userData, "\n"); break; case SDP_INT16: appendFunc(userData, indent); appendFunc(userData, ""); snprintf(buf, STRBUFSIZE - 1, "%d", data->val.int16); appendFunc(userData, buf); appendFunc(userData, "\n"); break; case SDP_INT32: appendFunc(userData, indent); appendFunc(userData, ""); snprintf(buf, STRBUFSIZE - 1, "%d", data->val.int32); appendFunc(userData, buf); appendFunc(userData, "\n"); break; case SDP_INT64: appendFunc(userData, indent); appendFunc(userData, ""); snprintf(buf, STRBUFSIZE - 1, "%jd", data->val.int64); appendFunc(userData, buf); appendFunc(userData, "\n"); break; case SDP_INT128: appendFunc(userData, indent); appendFunc(userData, ""); for (i = 0; i < 16; i++) { sprintf(&buf[i * 2], "%02x", (unsigned char) data->val.int128.data[i]); } appendFunc(userData, buf); appendFunc(userData, "\n"); break; case SDP_UUID16: appendFunc(userData, indent); appendFunc(userData, ""); snprintf(buf, STRBUFSIZE - 1, "0x%04x", data->val.uuid.value.uuid16); appendFunc(userData, buf); appendFunc(userData, "\n"); break; case SDP_UUID32: appendFunc(userData, indent); appendFunc(userData, ""); snprintf(buf, STRBUFSIZE - 1, "0x%08x", data->val.uuid.value.uuid32); appendFunc(userData, buf); appendFunc(userData, "\n"); break; case SDP_UUID128: appendFunc(userData, indent); appendFunc(userData, ""); snprintf(buf, STRBUFSIZE - 1, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", (unsigned char) data->val.uuid.value. uuid128.data[0], (unsigned char) data->val.uuid.value. uuid128.data[1], (unsigned char) data->val.uuid.value. uuid128.data[2], (unsigned char) data->val.uuid.value. uuid128.data[3], (unsigned char) data->val.uuid.value. uuid128.data[4], (unsigned char) data->val.uuid.value. uuid128.data[5], (unsigned char) data->val.uuid.value. uuid128.data[6], (unsigned char) data->val.uuid.value. uuid128.data[7], (unsigned char) data->val.uuid.value. uuid128.data[8], (unsigned char) data->val.uuid.value. uuid128.data[9], (unsigned char) data->val.uuid.value. uuid128.data[10], (unsigned char) data->val.uuid.value. uuid128.data[11], (unsigned char) data->val.uuid.value. uuid128.data[12], (unsigned char) data->val.uuid.value. uuid128.data[13], (unsigned char) data->val.uuid.value. uuid128.data[14], (unsigned char) data->val.uuid.value. uuid128.data[15]); appendFunc(userData, "\n"); break; case SDP_TEXT_STR8: case SDP_TEXT_STR16: case SDP_TEXT_STR32: { hex = 0; for (i = 0; i < data->unitSize; i++) { if (i == (data->unitSize - 1) && data->val.str[i] == '\0') break; if (!isalnum(data->val.str[i]) && (data->val.str[i] != ' ')) { hex = 1; break; } } appendFunc(userData, indent); char *strBuf = 0; if (hex) { strBuf = (char *) malloc(sizeof(char) * (data->unitSize * 2 + 1)); appendFunc(userData, ""); for (i = 0; i < data->unitSize; i++) sprintf(&strBuf [i * sizeof (char) * 2], "%02x", (unsigned char) data->val.str[i]); strBuf[data->unitSize * 2] = '\0'; } else { strBuf = (char *) malloc(sizeof(char) * (data->unitSize + 1)); memcpy(strBuf, data->val.str, data->unitSize); strBuf[data->unitSize] = '\0'; appendFunc(userData, ""); } appendFunc(userData, strBuf); free(strBuf); appendFunc(userData, "\n"); break; } case SDP_URL_STR8: case SDP_URL_STR16: case SDP_URL_STR32: appendFunc(userData, indent); appendFunc(userData, ""); appendFunc(userData, data->val.str); appendFunc(userData, "\n"); break; case SDP_SEQ8: case SDP_SEQ16: case SDP_SEQ32: appendFunc(userData, indent); appendFunc(userData, "\n"); convert_raw_data_to_xml(data->val.dataseq, indentLevel + 1, userData, appendFunc); appendFunc(userData, indent); appendFunc(userData, "\n"); break; case SDP_ALT8: case SDP_ALT16: case SDP_ALT32: appendFunc(userData, indent); appendFunc(userData, "\n"); convert_raw_data_to_xml(data->val.dataseq, indentLevel + 1, userData, appendFunc); appendFunc(userData, indent); appendFunc(userData, "\n"); break; default: break; } convert_raw_data_to_xml(data->next, indentLevel, userData, appendFunc); } struct conversion_data { void *userData; void (*appendFunc) (void *userData, const char *); }; static void convert_raw_attr_to_xml_func(void *value, void *userData) { struct conversion_data *cd = (struct conversion_data *) userData; sdp_data_t *data = (sdp_data_t *) value; char buf[STRBUFSIZE]; buf[STRBUFSIZE - 1] = '\0'; snprintf(buf, STRBUFSIZE - 1, "\t\n", data->attrId); cd->appendFunc(cd->userData, buf); if (data) convert_raw_data_to_xml(data, 2, cd->userData, cd->appendFunc); else cd->appendFunc(cd->userData, "\t\tNULL\n"); cd->appendFunc(cd->userData, "\t\n"); } /* Will convert the sdp record to XML. The appendFunc and userData can be used to control where to output the record (e.g. file or a data buffer). The appendFunc will be called repeatedly with userData and the character buffer (containing parts of the generated XML) to append. */ void convert_sdp_record_to_xml(sdp_record_t * rec, void *userData, void (*appendFunc) (void *, const char *)) { struct conversion_data data; data.userData = userData; data.appendFunc = appendFunc; if (rec && rec->attrlist) { appendFunc(userData, "\n"); sdp_list_foreach(rec->attrlist, convert_raw_attr_to_xml_func, &data); appendFunc(userData, "\n"); } } #define DEFAULT_XML_DATA_SIZE 64 static sdp_xml_data_t *sdp_xml_data_alloc() { sdp_xml_data_t *elem; elem = (sdp_xml_data_t *) malloc(sizeof(sdp_xml_data_t)); /* Null terminate the text */ elem->size = DEFAULT_XML_DATA_SIZE; elem->text = (char *) malloc(sizeof(char) * DEFAULT_XML_DATA_SIZE); elem->text[0] = '\0'; elem->next = 0; elem->data = 0; elem->type = 0; return elem; } static void sdp_xml_data_free(sdp_xml_data_t * elem) { if (elem->data) sdp_data_free(elem->data); free(elem->text); free(elem); } static sdp_xml_data_t *sdp_xml_data_expand(sdp_xml_data_t * elem) { char *newbuf; newbuf = (char *) malloc(elem->size * 2); if (!newbuf) return NULL; memcpy(newbuf, elem->text, elem->size); elem->size *= 2; free(elem->text); elem->text = newbuf; return elem; } static void convert_xml_to_sdp_chardata(void *data, const XML_Char * s, int len) { sdp_xml_context *context = (sdp_xml_context *) data; int curlen; int i; curlen = strlen(context->stack_head->text); /* Ensure we're big enough */ while ((curlen + 1 + len) > context->stack_head->size) { fprintf(stderr, "Growing text\n"); sdp_xml_data_expand(context->stack_head); } memcpy(&context->stack_head->text[curlen], s, len); fprintf(stderr, "Curlen+len: %d\n", curlen + len); context->stack_head->text[curlen + len] = '\0'; } static void convert_xml_to_sdp_start(void *data, const char *el, const char **attr) { int i; sdp_xml_context *context = (sdp_xml_context *) data; if (!strcmp(el, "SdpRecord")) return; if (!strcmp(el, "Attribute")) { /* Get the ID */ for (i = 0; attr[i]; i += 1) { if (!strcmp(attr[i], "id")) { context->attrId = strtol(attr[i + 1], 0, 0); break; } } return; } /* Assume every other tag is an element of some sort */ if (context->stack_head) { sdp_xml_data_t *newElem = sdp_xml_data_alloc(); newElem->next = context->stack_head; context->stack_head = newElem; } else { context->stack_head = sdp_xml_data_alloc(); context->stack_head->next = 0; } if (!strcmp(el, "Sequence")) { context->stack_head->data = sdp_data_alloc(SDP_SEQ8, NULL); } else if (!strcmp(el, "Alternate")) { context->stack_head->data = sdp_data_alloc(SDP_ALT8, NULL); } else { if (!strcmp(el, "Text")) { /* Get the Type */ for (i = 0; attr[i]; i += 1) { if (!strcmp(attr[i], "type")) { if (!strcmp(attr[i + 1], "data")) context->stack_head->type = 1; break; } } } XML_SetCharacterDataHandler(context->parser, convert_xml_to_sdp_chardata); } } static void sdp_xml_parse_uuid16(sdp_xml_context * data) { uint16_t val; val = strtol(data->stack_head->text, 0, 16); data->stack_head->data = sdp_data_alloc(SDP_UUID16, &val); } static void sdp_xml_parse_uuid32(sdp_xml_context * data) { uint32_t val; val = strtoll(data->stack_head->text, 0, 16); data->stack_head->data = sdp_data_alloc(SDP_UUID32, &val); } static void sdp_xml_parse_uuid128(sdp_xml_context * data) { uint128_t val; int i; int j; char buf[3]; buf[2] = '\0'; for (j = 0, i = 0; i < strlen(data->stack_head->text); j++) { if (data->stack_head->text[i] == '-') { i++; continue; } buf[0] = data->stack_head->text[i]; buf[1] = data->stack_head->text[i + 1]; val.data[i] = strtoul(buf, 0, 16); i += 2; } data->stack_head->data = sdp_data_alloc(SDP_UUID128, &val); } static void sdp_xml_parse_int(sdp_xml_context * data, uint8_t dtd) { switch (dtd) { case SDP_BOOL: { uint8_t val = 0; if (!strcmp("True", data->stack_head->text)) { val = 1; } data->stack_head->data = sdp_data_alloc(dtd, &val); break; } case SDP_INT8: { int8_t val = strtoul(data->stack_head->text, 0, 10); data->stack_head->data = sdp_data_alloc(dtd, &val); break; } case SDP_UINT8: { uint8_t val = strtoul(data->stack_head->text, 0, 16); data->stack_head->data = sdp_data_alloc(dtd, &val); break; } case SDP_INT16: { int16_t val = strtoul(data->stack_head->text, 0, 10); data->stack_head->data = sdp_data_alloc(dtd, &val); break; } case SDP_UINT16: { uint16_t val = strtoul(data->stack_head->text, 0, 16); data->stack_head->data = sdp_data_alloc(dtd, &val); break; } case SDP_INT32: { int32_t val = strtoul(data->stack_head->text, 0, 10); data->stack_head->data = sdp_data_alloc(dtd, &val); break; } case SDP_UINT32: { uint32_t val = strtoul(data->stack_head->text, 0, 16); data->stack_head->data = sdp_data_alloc(dtd, &val); break; } case SDP_INT64: { int64_t val = strtoull(data->stack_head->text, 0, 10); data->stack_head->data = sdp_data_alloc(dtd, &val); break; } case SDP_UINT64: { uint64_t val = strtoull(data->stack_head->text, 0, 16); data->stack_head->data = sdp_data_alloc(dtd, &val); break; } case SDP_INT128: case SDP_UINT128: { uint128_t val; int i = 0; char buf[3]; buf[2] = '\0'; for (; i < 32; i += 2) { buf[0] = data->stack_head->text[i]; buf[1] = data->stack_head->text[i + 1]; val.data[i] = strtoul(buf, 0, 16); } data->stack_head->data = sdp_data_alloc(dtd, &val); break; } }; } static void sdp_xml_parse_url(sdp_xml_context * data) { uint8_t dtd = SDP_URL_STR8; if (strlen(data->stack_head->text) > UCHAR_MAX) { dtd = SDP_URL_STR16; } data->stack_head->data = sdp_data_alloc(dtd, data->stack_head->text); } static void sdp_xml_parse_text(sdp_xml_context * data) { uint8_t dtd = SDP_TEXT_STR8; char *text; char shouldFree = 0; if (data->stack_head->type == 0) { text = data->stack_head->text; } else { int len = strlen(data->stack_head->text); char buf[3]; int i; char *decoded = (char *) malloc(sizeof(char) * ((len >> 1) + 1)); fprintf(stderr, "Got a stack_head->text len of: %d(%s)\n", len, data->stack_head->text); fprintf(stderr, "len >> 1 = %d\n", len >> 1); buf[2] = '\0'; for (i = 0; i < len; i += 2) { buf[0] = data->stack_head->text[i]; buf[1] = data->stack_head->text[i + 1]; decoded[i >> 1] = strtoul(buf, 0, 16); } decoded[len >> 1] = '\0'; text = decoded; shouldFree = 1; } if (strlen(text) > UCHAR_MAX) { dtd = SDP_TEXT_STR16; } data->stack_head->data = sdp_data_alloc(dtd, text); fprintf(stderr, "Got text of length %d: -->%s<--\n", strlen(text), text); fprintf(stderr, "Unit size was: %d\n", data->stack_head->data->unitSize); if (shouldFree) free(text); } static void sdp_xml_parse_datatype(sdp_xml_context * context, const char *el) { if (!strcmp(el, "BOOL")) { sdp_xml_parse_int(context, SDP_BOOL); } else if (!strcmp(el, "UINT8")) { sdp_xml_parse_int(context, SDP_UINT8); } else if (!strcmp(el, "UINT16")) { sdp_xml_parse_int(context, SDP_UINT16); } else if (!strcmp(el, "UINT32")) { sdp_xml_parse_int(context, SDP_UINT32); } else if (!strcmp(el, "UINT64")) { sdp_xml_parse_int(context, SDP_UINT64); } else if (!strcmp(el, "UINT128")) { sdp_xml_parse_int(context, SDP_UINT128); } else if (!strcmp(el, "INT8")) { sdp_xml_parse_int(context, SDP_INT8); } else if (!strcmp(el, "INT16")) { sdp_xml_parse_int(context, SDP_INT16); } else if (!strcmp(el, "INT32")) { sdp_xml_parse_int(context, SDP_INT32); } else if (!strcmp(el, "INT64")) { sdp_xml_parse_int(context, SDP_INT64); } else if (!strcmp(el, "INT128")) { sdp_xml_parse_int(context, SDP_INT128); } else if (!strcmp(el, "UUID16")) { sdp_xml_parse_uuid16(context); } else if (!strcmp(el, "UUID32")) { sdp_xml_parse_uuid32(context); } else if (!strcmp(el, "UUID128")) { sdp_xml_parse_uuid128(context); } else if (!strcmp(el, "URL")) { sdp_xml_parse_url(context); } else if (!strcmp(el, "Text")) { sdp_xml_parse_text(context); } else { printf("Ending element: %s\n", el); printf("Gathered data: %s\n", context->stack_head->text); } } static void convert_xml_to_sdp_end(void *data, const char *el) { sdp_xml_context *context = (sdp_xml_context *) data; sdp_xml_data_t *elem; if (!strcmp(el, "SdpRecord")) return; XML_SetCharacterDataHandler(context->parser, 0); if (!strcmp(el, "Attribute")) { if (context->stack_head && context->stack_head->data) { int ret = sdp_attr_add(context->sdprec, context->attrId, context->stack_head->data); if (ret == -1) fprintf(stderr, "Trouble adding attribute\n"); context->stack_head->data = 0; sdp_xml_data_free(context->stack_head); context->stack_head = 0; } else { fprintf(stderr, "No Data for attribute: %d\n", context->attrId); } return; } else if (!strcmp(el, "Sequence")) { sdp_data_t *seq = context->stack_head->data->val.dataseq; fprintf(stderr, "seq unitSize is: %d\n", context->stack_head->data->unitSize); for (; seq; seq = seq->next) context->stack_head->data->unitSize += seq->unitSize; fprintf(stderr, "seq unitSize is now: %d\n", context->stack_head->data->unitSize); if (context->stack_head->data->unitSize > USHRT_MAX) { context->stack_head->data->unitSize += sizeof(uint32_t); context->stack_head->data->dtd = SDP_SEQ32; } else if (context->stack_head->data->unitSize > UCHAR_MAX) { context->stack_head->data->unitSize += sizeof(uint16_t); context->stack_head->data->dtd = SDP_SEQ16; } else { context->stack_head->data->unitSize += sizeof(uint8_t); } } else if (!strcmp(el, "Alternate")) { sdp_data_t *alt = context->stack_head->data->val.dataseq; fprintf(stderr, "seq unitSize is: %d\n", context->stack_head->data->unitSize); for (; alt; alt = alt->next) context->stack_head->data->unitSize += alt->unitSize; fprintf(stderr, "alt unitSize is now: %d\n", context->stack_head->data->unitSize); if (context->stack_head->data->unitSize > USHRT_MAX) { context->stack_head->data->unitSize += sizeof(uint32_t); context->stack_head->data->dtd = SDP_ALT32; } else if (context->stack_head->data->unitSize > UCHAR_MAX) { context->stack_head->data->unitSize += sizeof(uint16_t); context->stack_head->data->dtd = SDP_ALT16; } else { context->stack_head->data->unitSize += sizeof(uint8_t); } } else { sdp_xml_parse_datatype(context, el); } /* If we're not inside a seq or alt, then we're inside an attribute which will be taken care of later */ if (context->stack_head->next && context->stack_head->data && context->stack_head->next->data) { switch (context->stack_head->next->data->dtd) { case SDP_SEQ8: case SDP_SEQ16: case SDP_SEQ32: case SDP_ALT8: case SDP_ALT16: case SDP_ALT32: context->stack_head->next->data->val. dataseq = sdp_seq_append(context-> stack_head-> next->data-> val.dataseq, context->stack_head->data); context->stack_head->data = 0; break; } elem = context->stack_head; context->stack_head = context->stack_head->next; sdp_xml_data_free(elem); } } sdp_xml_context *sdp_xml_init_context() { sdp_xml_context *context = 0; context = (sdp_xml_context *) malloc(sizeof(sdp_xml_context)); if (!context) return NULL; context->parser = 0; context->sdprec = 0; context->stack_head = 0; context->parser = XML_ParserCreate(NULL); XML_SetElementHandler(context->parser, convert_xml_to_sdp_start, convert_xml_to_sdp_end); XML_SetUserData(context->parser, context); if (!context->parser) goto fail; context->sdprec = sdp_record_alloc(); if (!context->sdprec) goto fail; return context; fail: fprintf(stderr, "Failed to allocated context\n"); if (context->parser) free(context->parser); if (context->sdprec) sdp_record_free(context->sdprec); if (context) free(context); return NULL; } /* Should be called after sdp_xml_parse_chunk has been called with the final flag set Returns the resulting sdp_record_t. The returned value is will need to be freed by the caller by usig sdp_record_free. */ sdp_record_t *sdp_xml_get_record(sdp_xml_context * context) { sdp_record_t *rec = context->sdprec; context->sdprec = 0; return rec; } /* Frees all data except the sdp_record if the sdp_xml_get_record was called. Otherwise everything is freed. */ void sdp_xml_free_context(sdp_xml_context * context) { sdp_xml_data_t *elem; /* Free the stack */ while (context->stack_head) { elem = context->stack_head; context->stack_head = elem->next; sdp_xml_data_free(elem); } if (context->sdprec) sdp_record_free(context->sdprec); XML_ParserFree(context->parser); free(context); } /* parses an XML chunk. Returns -1 if a parse error occured, and 0 if successful pass final as 1 if the chunk is the final chunk */ int sdp_xml_parse_chunk(sdp_xml_context * context, const char *data, int size, int final) { if (!XML_Parse(context->parser, data, size, final)) { fprintf(stderr, "Parse error at line %d:\n%s\n", XML_GetCurrentLineNumber(context->parser), XML_ErrorString(XML_GetErrorCode(context->parser))); return -1; } return 0; }