/* determines the size of the given AMF data */ size_t amf_data_size(const amf_data * data) { size_t s = 0; amf_node * node; if (data != NULL) { s += sizeof(byte); switch (data->type) { case AMF_TYPE_NUMBER: s += sizeof(number64_be); break; case AMF_TYPE_BOOLEAN: s += sizeof(uint8); break; case AMF_TYPE_STRING: s += sizeof(uint16) + (size_t)amf_string_get_size(data); break; case AMF_TYPE_OBJECT: node = amf_object_first(data); while (node != NULL) { s += sizeof(uint16) + (size_t)amf_string_get_size(amf_object_get_name(node)); s += (size_t)amf_data_size(amf_object_get_data(node)); node = amf_object_next(node); } s += sizeof(uint16) + sizeof(uint8); break; case AMF_TYPE_NULL: case AMF_TYPE_UNDEFINED: break; /*case AMF_TYPE_REFERENCE:*/ case AMF_TYPE_ASSOCIATIVE_ARRAY: s += sizeof(uint32); node = amf_associative_array_first(data); while (node != NULL) { s += sizeof(uint16) + (size_t)amf_string_get_size(amf_associative_array_get_name(node)); s += (size_t)amf_data_size(amf_associative_array_get_data(node)); node = amf_associative_array_next(node); } s += sizeof(uint16) + sizeof(uint8); break; case AMF_TYPE_ARRAY: s += sizeof(uint32); node = amf_array_first(data); while (node != NULL) { s += (size_t)amf_data_size(amf_array_get(node)); node = amf_array_next(node); } break; case AMF_TYPE_DATE: s += sizeof(number64) + sizeof(sint16); break; /*case AMF_TYPE_SIMPLEOBJECT:*/ case AMF_TYPE_XML: case AMF_TYPE_CLASS: case AMF_TYPE_END: break; /* end of composite object */ default: break; } } return s; }
/* write an object */ static size_t amf_object_write(const amf_data * data, amf_write_proc write_proc, void * user_data) { amf_node * node; size_t w = 0; uint16_be filler = swap_uint16(0); uint8 terminator = AMF_TYPE_END; node = amf_object_first(data); while (node != NULL) { w += amf_string_write(amf_object_get_name(node), write_proc, user_data); w += amf_data_write(amf_object_get_data(node), write_proc, user_data); node = amf_object_next(node); } /* empty string is the last element */ w += write_proc(&filler, sizeof(uint16_be), user_data); /* an object ends with 0x09 */ w += write_proc(&terminator, sizeof(uint8), user_data); return w; }
/* JSON metadata dumping */ static void json_amf_data_dump(const amf_data * data, json_emitter * je) { if (data != NULL) { amf_node * node; char str[128]; switch (data->type) { case AMF_TYPE_NUMBER: json_emit_number(je, data->number_data); break; case AMF_TYPE_BOOLEAN: json_emit_boolean(je, data->boolean_data); break; case AMF_TYPE_STRING: json_emit_string(je, (char *)amf_string_get_bytes(data), amf_string_get_size(data)); break; case AMF_TYPE_OBJECT: json_emit_object_start(je); node = amf_object_first(data); while (node != NULL) { json_emit_object_key(je, (char *)amf_string_get_bytes(amf_object_get_name(node)), amf_string_get_size(amf_object_get_name(node)) ); json_amf_data_dump(amf_object_get_data(node), je); node = amf_object_next(node); } json_emit_object_end(je); break; case AMF_TYPE_NULL: case AMF_TYPE_UNDEFINED: json_emit_null(je); break; case AMF_TYPE_ASSOCIATIVE_ARRAY: json_emit_object_start(je); node = amf_associative_array_first(data); while (node != NULL) { json_emit_object_key(je, (char *)amf_string_get_bytes(amf_associative_array_get_name(node)), amf_string_get_size(amf_associative_array_get_name(node)) ); json_amf_data_dump(amf_object_get_data(node), je); node = amf_associative_array_next(node); } json_emit_object_end(je); break; case AMF_TYPE_ARRAY: json_emit_array_start(je); node = amf_array_first(data); while (node != NULL) { json_amf_data_dump(amf_array_get(node), je); node = amf_array_next(node); } json_emit_array_end(je); break; case AMF_TYPE_DATE: amf_date_to_iso8601(data, str, sizeof(str)); json_emit_string(je, str, strlen(str)); break; case AMF_TYPE_XML: break; case AMF_TYPE_CLASS: break; default: break; } } }
/* dump AMF data into a stream as text */ void amf_data_dump(FILE * stream, const amf_data * data, int indent_level) { if (data != NULL) { amf_node * node; time_t time; struct tm * t; char datestr[128]; switch (data->type) { case AMF_TYPE_NUMBER: fprintf(stream, "%.12g", data->number_data); break; case AMF_TYPE_BOOLEAN: fprintf(stream, "%s", (data->boolean_data) ? "true" : "false"); break; case AMF_TYPE_STRING: fprintf(stream, "\'%.*s\'", data->string_data.size, data->string_data.mbstr); break; case AMF_TYPE_OBJECT: node = amf_object_first(data); fprintf(stream, "{\n"); while (node != NULL) { fprintf(stream, "%*s", (indent_level+1)*4, ""); amf_data_dump(stream, amf_object_get_name(node), indent_level+1); fprintf(stream, ": "); amf_data_dump(stream, amf_object_get_data(node), indent_level+1); node = amf_object_next(node); fprintf(stream, "\n"); } fprintf(stream, "%*s", indent_level*4 + 1, "}"); break; case AMF_TYPE_NULL: fprintf(stream, "null"); break; case AMF_TYPE_UNDEFINED: fprintf(stream, "undefined"); break; /*case AMF_TYPE_REFERENCE:*/ case AMF_TYPE_ASSOCIATIVE_ARRAY: node = amf_associative_array_first(data); fprintf(stream, "{\n"); while (node != NULL) { fprintf(stream, "%*s", (indent_level+1)*4, ""); amf_data_dump(stream, amf_associative_array_get_name(node), indent_level+1); fprintf(stream, " => "); amf_data_dump(stream, amf_associative_array_get_data(node), indent_level+1); node = amf_associative_array_next(node); fprintf(stream, "\n"); } fprintf(stream, "%*s", indent_level*4 + 1, "}"); break; case AMF_TYPE_ARRAY: node = amf_array_first(data); fprintf(stream, "[\n"); while (node != NULL) { fprintf(stream, "%*s", (indent_level+1)*4, ""); amf_data_dump(stream, node->data, indent_level+1); node = amf_array_next(node); fprintf(stream, "\n"); } fprintf(stream, "%*s", indent_level*4 + 1, "]"); break; case AMF_TYPE_DATE: time = amf_date_to_time_t(data); tzset(); t = localtime(&time); strftime(datestr, sizeof(datestr), "%a, %d %b %Y %H:%M:%S %z", t); fprintf(stream, "%s", datestr); break; /*case AMF_TYPE_SIMPLEOBJECT:*/ case AMF_TYPE_XML: break; case AMF_TYPE_CLASS: break; default: break; } } }
/* XML metadata dumping */ static void xml_amf_data_dump(const amf_data * data, int qualified, int indent_level) { if (data != NULL) { amf_node * node; time_t time; struct tm * t; char datestr[128]; int markers; char * ns; char ns_decl[50]; /* namespace to use whether we're using qualified mode */ ns = (qualified == 1) ? "amf:" : ""; /* if indent_level is zero, that means we're at the root of the xml document therefore we need to insert the namespace definition */ if (indent_level == 0) { sprintf(ns_decl, " xmlns%s=\"http://schemas.flvmeta.org/AMF0/1.0/\"", ns); } else { strcpy(ns_decl, ""); } /* print indentation spaces */ printf("%*s", indent_level * 2, ""); switch (data->type) { case AMF_TYPE_NUMBER: printf("<%snumber%s value=\"%.12g\"/>\n", ns, ns_decl, data->number_data); break; case AMF_TYPE_BOOLEAN: printf("<%sboolean%s value=\"%s\"/>\n", ns, ns_decl, (data->boolean_data) ? "true" : "false"); break; case AMF_TYPE_STRING: if (amf_string_get_size(data) > 0) { printf("<%sstring%s>", ns, ns_decl); /* check whether the string contains xml characters, if so, CDATA it */ markers = has_xml_markers((char*)amf_string_get_bytes(data), amf_string_get_size(data)); if (markers) { printf("<![CDATA["); } /* do not print more than the actual length of string */ printf("%.*s", (int)amf_string_get_size(data), amf_string_get_bytes(data)); if (markers) { printf("]]>"); } printf("</%sstring>\n", ns); } else { /* simplify empty xml element into a more compact form */ printf("<%sstring%s/>\n", ns, ns_decl); } break; case AMF_TYPE_OBJECT: if (amf_object_size(data) > 0) { printf("<%sobject%s>\n", ns, ns_decl); node = amf_object_first(data); while (node != NULL) { printf("%*s<%sentry name=\"%s\">\n", (indent_level + 1) * 2, "", ns, amf_string_get_bytes(amf_object_get_name(node))); xml_amf_data_dump(amf_object_get_data(node), qualified, indent_level + 2); node = amf_object_next(node); printf("%*s</%sentry>\n", (indent_level + 1) * 2, "", ns); } printf("%*s</%sobject>\n", indent_level * 2, "", ns); } else { /* simplify empty xml element into a more compact form */ printf("<%sobject%s/>\n", ns, ns_decl); } break; case AMF_TYPE_NULL: printf("<%snull%s/>\n", ns, ns_decl); break; case AMF_TYPE_UNDEFINED: printf("<%sundefined%s/>\n", ns, ns_decl); break; case AMF_TYPE_ASSOCIATIVE_ARRAY: if (amf_associative_array_size(data) > 0) { printf("<%sassociativeArray%s>\n", ns, ns_decl); node = amf_associative_array_first(data); while (node != NULL) { printf("%*s<%sentry name=\"%s\">\n", (indent_level + 1) * 2, "", ns, amf_string_get_bytes(amf_associative_array_get_name(node))); xml_amf_data_dump(amf_associative_array_get_data(node), qualified, indent_level + 2); node = amf_associative_array_next(node); printf("%*s</%sentry>\n", (indent_level + 1) * 2, "", ns); } printf("%*s</%sassociativeArray>\n", indent_level * 2, "", ns); } else { /* simplify empty xml element into a more compact form */ printf("<%sassociativeArray%s/>\n", ns, ns_decl); } break; case AMF_TYPE_ARRAY: if (amf_array_size(data) > 0) { printf("<%sarray%s>\n", ns, ns_decl); node = amf_array_first(data); while (node != NULL) { xml_amf_data_dump(amf_array_get(node), qualified, indent_level + 1); node = amf_array_next(node); } printf("%*s</%sarray>\n", indent_level * 2, "", ns); } else { /* simplify empty xml element into a more compact form */ printf("<%sarray%s/>\n", ns, ns_decl); } break; case AMF_TYPE_DATE: time = amf_date_to_time_t(data); tzset(); t = localtime(&time); strftime(datestr, sizeof(datestr), "%Y-%m-%dT%H:%M:%S", t); printf("<%sdate%s value=\"%s\"/>\n", ns, ns_decl, datestr); break; case AMF_TYPE_XML: break; case AMF_TYPE_CLASS: break; default: break; } } }
/* JSON metadata dumping */ static void amf_to_json(const amf_data * data, json_t ** object) { if (data != NULL) { json_t * value; amf_node * node; time_t time; struct tm * t; char str[128]; char * escaped_str; switch (data->type) { case AMF_TYPE_NUMBER: sprintf(str, "%.12g", data->number_data); *object = json_new_number(str); break; case AMF_TYPE_BOOLEAN: *object = (data->boolean_data) ? json_new_true() : json_new_false(); break; case AMF_TYPE_STRING: escaped_str = json_escape((char *)amf_string_get_bytes(data)); *object = json_new_string(escaped_str); free(escaped_str); break; case AMF_TYPE_OBJECT: *object = json_new_object(); node = amf_object_first(data); while (node != NULL) { amf_to_json(amf_object_get_data(node), &value); escaped_str = json_escape((char *)amf_string_get_bytes(amf_object_get_name(node))); json_insert_pair_into_object(*object, escaped_str, value); free(escaped_str); node = amf_object_next(node); } break; case AMF_TYPE_NULL: case AMF_TYPE_UNDEFINED: *object = json_new_null(); break; case AMF_TYPE_ASSOCIATIVE_ARRAY: *object = json_new_object(); node = amf_associative_array_first(data); while (node != NULL) { amf_to_json(amf_associative_array_get_data(node), &value); json_insert_pair_into_object(*object, (const char *)amf_string_get_bytes(amf_associative_array_get_name(node)), value); node = amf_associative_array_next(node); } break; case AMF_TYPE_ARRAY: *object = json_new_array(); node = amf_array_first(data); while (node != NULL) { amf_to_json(amf_array_get(node), &value); json_insert_child(*object, value); node = amf_array_next(node); } break; case AMF_TYPE_DATE: time = amf_date_to_time_t(data); tzset(); t = localtime(&time); strftime(str, sizeof(str), "%Y-%m-%dT%H:%M:%S", t); *object = json_new_string(str); break; case AMF_TYPE_XML: break; case AMF_TYPE_CLASS: break; default: break; } } }