#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "json.h" //xml Schema is saved in here xmlSchemaValidCtxtPtr validCtxt = NULL; xmlRelaxNGValidCtxtPtr validctxtNG = NULL; //errors found in an xml file json_object *jarray = NULL; #define UNUSED(x) (void)(x) //removes new lines from a string char* strip(char *s) { char *r = s; char *p2 = s; while(*s != '\0') { if(*s != '\n') { *p2++ = *s++; } else { ++s; } } *p2 = '\0'; return r; } // callback function which handles parse/schema errors and fills a json object void handleStructedError(void* userData, xmlErrorPtr error) { UNUSED(userData); json_object *jsonReport = json_object_new_object(); json_object_object_add(jsonReport, "domain", json_object_new_int(error->domain)); json_object_object_add(jsonReport, "code", json_object_new_int(error->code)); json_object_object_add(jsonReport, "message", json_object_new_string(strip(error->message))); json_object_object_add(jsonReport, "level", json_object_new_int(error->level)); json_object_object_add(jsonReport, "line", json_object_new_int(error->line)); if (error->str1 != NULL){ json_object_object_add(jsonReport, "str1", json_object_new_string(strip(error->str1))); }else{ json_object_object_add(jsonReport, "str1", json_object_new_string("")); } if (error->str2 != NULL){ json_object_object_add(jsonReport, "str2", json_object_new_string(strip(error->str2))); }else{ json_object_object_add(jsonReport, "str2", json_object_new_string("")); } if (error->str3 != NULL){ json_object_object_add(jsonReport, "str3", json_object_new_string(strip(error->str3))); }else{ json_object_object_add(jsonReport, "str3", json_object_new_string("")); } json_object_object_add(jsonReport, "int1", json_object_new_int(error->int1)); json_object_object_add(jsonReport, "int2", json_object_new_int(error->int2)); json_object_array_add(jarray,jsonReport); } //sets a schema, returns true (1) on success and false (0) if an error occurred int set_schema(const char *xsdData) { xmlSchemaParserCtxtPtr parserCtxt = xmlSchemaNewMemParserCtxt(xsdData, strlen(xsdData)); if (parserCtxt == NULL){ fprintf(stderr, "error while parsing schma"); return 0; } xmlSchemaPtr schema = xmlSchemaParse(parserCtxt); if (schema == NULL){ fprintf(stderr, "error while parsing schma"); return 0; } if (validCtxt != NULL) { //free memory xmlSchemaFreeValidCtxt(validCtxt); } validCtxt = xmlSchemaNewValidCtxt(schema); if (validCtxt == NULL){ fprintf(stderr, "error while parsing schma"); return 0; } return 1; } //sets a schema, returns true (1) on success and false (0) if an error occurred //needs a filePATH int set_schema_file(const char *xsdPath) { xmlSchemaParserCtxtPtr parserCtxt = xmlSchemaNewParserCtxt(xsdPath); if (parserCtxt == NULL){ fprintf(stderr, "error while parsing schma"); return 0; } xmlSchemaPtr schema = xmlSchemaParse(parserCtxt); if (schema == NULL){ fprintf(stderr, "error while parsing schma"); return 0; } if (validCtxt != NULL) { //free memory xmlSchemaFreeValidCtxt(validCtxt); } validCtxt = xmlSchemaNewValidCtxt(schema); if (validCtxt == NULL){ fprintf(stderr, "error while parsing schma"); return 0; } return 1; } //validates an XML-File given as String //precondition: a schema is set with set_schema() const char* validate(const char *xmlData) { // schema validation errors will now have meaningfull line numbers and not constantly 0 xmlLineNumbersDefault(1); if (jarray != NULL){ //free memory json_object_put(jarray); } jarray = json_object_new_array(); //check if schema is set. if (validCtxt == NULL) { fprintf(stderr, "schema has to be set with 'set_schema' first\n"); return "schema has to be set with 'set_schema'"; } //set callback function xmlSetStructuredErrorFunc(NULL, handleStructedError); //oparse xmlDATA int xmlLength = strlen(xmlData); xmlDocPtr xml = xmlParseMemory(xmlData, xmlLength); //validate xml DATA xmlSchemaValidateDoc(validCtxt, xml); xmlFreeDoc(xml); //convert json object to strings const char* ret = json_object_to_json_string(jarray); return ret; } //sets an relaxNG schema, returns true (1) on success and false (0) if an error occurred int set_relaxNG_schema(const char *ngData){ xmlRelaxNGParserCtxtPtr parserCtxt = xmlRelaxNGNewMemParserCtxt(ngData, strlen(ngData)); if (parserCtxt == NULL){ fprintf(stderr, "error while parsing schma"); return 0; } xmlRelaxNGPtr schema = xmlRelaxNGParse(parserCtxt); if (schema == NULL){ fprintf(stderr, "error while parsing schma"); return 0; } if (validctxtNG != NULL) { //free memory xmlSchemaFreeValidCtxt(validCtxt); } validctxtNG = xmlRelaxNGNewValidCtxt(schema); return 1; } //sets an relaxNG schema from an given File, returns true (1) on success and false (0) if an error occurred //this is needed when the schema is split into multiple files int set_relaxNG_schema_file(const char *ngPath){ xmlRelaxNGParserCtxtPtr parserCtxt = xmlRelaxNGNewParserCtxt(ngPath); if (parserCtxt == NULL){ fprintf(stderr, "error while parsing schma"); return 0; } xmlRelaxNGPtr schema = xmlRelaxNGParse(parserCtxt); if (schema == NULL){ fprintf(stderr, "error while parsing schma"); return -1; } if (validctxtNG != NULL) { //free memory xmlSchemaFreeValidCtxt(validCtxt); } validctxtNG = xmlRelaxNGNewValidCtxt(schema); return 1; } //validates an XML-File given as String //precondition: a schema is set with set_schema() const char* validate_NG(const char *xmlData) { // schema validation errors will now have meaningfull line numbers and not constantly 0 xmlLineNumbersDefault(1); if (jarray != NULL){ //free memory json_object_put(jarray); } jarray = json_object_new_array(); //check if schema is set. if (validctxtNG == NULL) { fprintf(stderr, "schema has to be set with 'set_relaxNG_schema' first\n"); return "schema has to be set with 'set_relaxNG_schema'"; } //set callback function xmlSetStructuredErrorFunc(NULL, handleStructedError); //oparse xmlDATA int xmlLength = strlen(xmlData); xmlDocPtr xml = xmlParseMemory(xmlData, xmlLength); //validate xml DATA xmlRelaxNGValidateDoc(validctxtNG, xml); xmlFreeDoc(xml); //convert json object to strings const char* ret = json_object_to_json_string(jarray); return ret; } //get the size of an text file u_int32_t get_file_size(const char *file_name) { struct stat buf; if ( stat(file_name, &buf) != 0 ) return(0); return (unsigned int)buf.st_size; } //reads a text file char* readFile(const char* xmlPath) { int xmlLength = get_file_size(xmlPath); char *xmlSource = malloc(sizeof(char) * xmlLength); FILE *p = fopen(xmlPath, "r"); char c; unsigned int i = 0; while ((c = fgetc(p)) != EOF) { xmlSource[i++] = c; } fclose(p); xmlSource[xmlLength] = 0; return xmlSource; } //some simple tests int main (int argc, const char * argv[]) { const char* schema = "html5.xsd"; const char* multiFileSchemaNG = "html5-rng/xhtml.rng"; const char* multiFileSchemaNGOpenDocument = "OpenDocument-strict-schema-v1.1-errata01-complete.rng"; const char* test1 = "success.html"; const char* test2 = "fail.html"; const char* testOD1 = "success_example1.xml"; const char* testOD2 = "success_example2.xml"; const char* testOD3 = "fail_example1.xml"; const char* testOD4 = "fail_example2.xml"; fprintf(stderr, "============= reading schema: %s =============\n", schema); set_schema(readFile(schema)); if (!validCtxt) { fprintf(stderr, "Could not create XSD schema validation context.\n"); exit(1); } fprintf(stderr, "============= validate test1 (expected result: success): %s =============\n", test1); const char* result1 = validate(readFile(test1)); fprintf(stderr, "Validation:\n%s\n", result1); fprintf(stderr, "============= validate test2 (expected result: fail): %s =============\n", test2); const char* result2 = validate(readFile(test2)); fprintf(stderr, "Validation:\n%s\n", result2); fprintf(stderr, "============= reading RELAX NG multifile schema: %s =============\n", multiFileSchemaNG); set_relaxNG_schema_file(multiFileSchemaNG); fprintf(stderr, "============= validate test1 (expected result: success): %s =============\n", test1); const char* result3 = validate_NG(readFile(test1)); fprintf(stderr, "Validation NG1:\n%s\n", result3); fprintf(stderr, "============= validate test2 (expected result: fail): %s =============\n", test2); const char* result4 = validate_NG(readFile(test2)); fprintf(stderr, "Validation NG2:\n%s\n", result4); fprintf(stderr, "============= reading RELAX NG multifile schema: %s =============\n", multiFileSchemaNGOpenDocument); set_relaxNG_schema_file(multiFileSchemaNGOpenDocument); fprintf(stderr, "============= validate test1 (expected result: success): %s =============\n", testOD1); fprintf(stderr, "Validation OD:\n%s\n",validate_NG(readFile(testOD1))); fprintf(stderr, "============= validate test2 (expected result: success): %s =============\n", testOD2); fprintf(stderr, "Validation OD:\n%s\n",validate_NG(readFile(testOD2))); fprintf(stderr, "============= validate test3 (expected result: fail): %s =============\n", testOD3); fprintf(stderr, "Validation OD:\n%s\n",validate_NG(readFile(testOD3))); fprintf(stderr, "============= validate test4 (expected result: fail): %s =============\n", testOD4); fprintf(stderr, "Validation OD:\n%s\n",validate_NG(readFile(testOD4))); return 0; }