xml2lpc.c 10.5 KB
Newer Older
Yann Diorcet's avatar
Yann Diorcet committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
/*
linphone
Copyright (C) 2012 Belledonne Communications SARL
Yann DIORCET (yann.diorcet@linphone.org)

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

#include "xml2lpc.h"
#include <string.h>
#include <libxml/xmlreader.h>


#define XML2LPC_BZ 2048

struct _xml2lpc_context {
	LpConfig *lpc;
	xml2lpc_function cbf;
	void *ctx;
32

Yann Diorcet's avatar
Yann Diorcet committed
33 34 35 36 37 38 39 40 41 42 43 44 45
	xmlDoc *doc;
	xmlDoc *xsd;
	char errorBuffer[XML2LPC_BZ];
	char warningBuffer[XML2LPC_BZ];
};


xml2lpc_context* xml2lpc_context_new(xml2lpc_function cbf, void *ctx) {
	xml2lpc_context *xmlCtx = (xml2lpc_context*)malloc(sizeof(xml2lpc_context));
	if(xmlCtx != NULL) {
		xmlCtx->lpc = NULL;
		xmlCtx->cbf = cbf;
		xmlCtx->ctx = ctx;
46

Yann Diorcet's avatar
Yann Diorcet committed
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
		xmlCtx->doc = NULL;
		xmlCtx->xsd = NULL;
		xmlCtx->errorBuffer[0]='\0';
		xmlCtx->warningBuffer[0]='\0';
	}
	return xmlCtx;
}

void xml2lpc_context_destroy(xml2lpc_context *ctx) {
	if(ctx->doc != NULL) {
		xmlFreeDoc(ctx->doc);
		ctx->doc = NULL;
	}
	if(ctx->xsd != NULL) {
		xmlFreeDoc(ctx->xsd);
		ctx->xsd = NULL;
	}
	free(ctx);
}

Yann Diorcet's avatar
Yann Diorcet committed
67
static void xml2lpc_context_clear_logs(xml2lpc_context *ctx) {
Yann Diorcet's avatar
Yann Diorcet committed
68 69 70 71
	ctx->errorBuffer[0]='\0';
	ctx->warningBuffer[0]='\0';
}

Yann Diorcet's avatar
Yann Diorcet committed
72
static void xml2lpc_log(xml2lpc_context *xmlCtx, int level, const char *fmt, ...) {
73 74
	va_list args;
	va_start(args, fmt);
Yann Diorcet's avatar
Yann Diorcet committed
75 76 77
	if(xmlCtx->cbf != NULL) {
		xmlCtx->cbf((xmlCtx)->ctx, level, fmt, args);
	}
78
	va_end(args);
Yann Diorcet's avatar
Yann Diorcet committed
79 80
}

81
static void xml2lpc_genericxml_error(void *ctx, const char *fmt, ...) {
Yann Diorcet's avatar
Yann Diorcet committed
82
	xml2lpc_context *xmlCtx = (xml2lpc_context *)ctx;
83
	size_t sl = strlen(xmlCtx->errorBuffer);
84 85
	va_list args;
	va_start(args, fmt);
Yann Diorcet's avatar
Yann Diorcet committed
86 87 88 89
	vsnprintf(xmlCtx->errorBuffer + sl, XML2LPC_BZ-sl, fmt, args);
	va_end(args);
}

90
static void xml2lpc_genericxml_warning(void *ctx, const char *fmt, ...) {
Yann Diorcet's avatar
Yann Diorcet committed
91
	xml2lpc_context *xmlCtx = (xml2lpc_context *)ctx;
92
	size_t sl = strlen(xmlCtx->warningBuffer);
93 94
	va_list args;
	va_start(args, fmt);
Yann Diorcet's avatar
Yann Diorcet committed
95 96 97 98
	vsnprintf(xmlCtx->warningBuffer + sl, XML2LPC_BZ-sl, fmt, args);
	va_end(args);
}

Yann Diorcet's avatar
Yann Diorcet committed
99
#if 0
Yann Diorcet's avatar
Yann Diorcet committed
100
static void dumpNodes(int level, xmlNode * a_node, xml2lpc_context *ctx) {
101
	xmlNode *cur_node = NULL;
Yann Diorcet's avatar
Yann Diorcet committed
102

103 104 105 106 107 108
	for (cur_node = a_node; cur_node; cur_node = cur_node->next) {
		if (cur_node->type == XML_ELEMENT_NODE) {
			xml2lpc_log(ctx, XML2LPC_DEBUG, "node level: %d type: Element, name: %s", level, cur_node->name);
		} else {
			xml2lpc_log(ctx, XML2LPC_DEBUG, "node level: %d type: %d, name: %s", level, cur_node->type, cur_node->name);
		}
Yann Diorcet's avatar
Yann Diorcet committed
109

110 111
		dumpNodes(level + 1, cur_node->children, ctx);
	}
Yann Diorcet's avatar
Yann Diorcet committed
112
}
Yann Diorcet's avatar
Yann Diorcet committed
113
#endif
Yann Diorcet's avatar
Yann Diorcet committed
114 115 116


static void dumpNode(xmlNode *node, xml2lpc_context *ctx) {
117
	xml2lpc_log(ctx, XML2LPC_DEBUG, "node type: %d, name: %s", node->type, node->name);
Yann Diorcet's avatar
Yann Diorcet committed
118 119 120
}

static void dumpAttr(xmlNode *node, xml2lpc_context *ctx) {
121
	xml2lpc_log(ctx, XML2LPC_DEBUG, "attr name: %s value:%s", node->name, node->children->content);
Yann Diorcet's avatar
Yann Diorcet committed
122 123 124
}

static void dumpContent(xmlNode *node, xml2lpc_context *ctx) {
125 126 127 128
	if (node->children)
		xml2lpc_log(ctx, XML2LPC_DEBUG, "content: %s", node->children->content);
	else
		xml2lpc_log(ctx, XML2LPC_DEBUG, "content: ");
Yann Diorcet's avatar
Yann Diorcet committed
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
}

static int processEntry(xmlElement *element, const char *sectionName, xml2lpc_context *ctx) {
	xmlNode *cur_attr = NULL;
	const char *name = NULL;
	const char *value = NULL;
	bool_t overwrite = FALSE;

	for (cur_attr = (xmlNode *)element->attributes; cur_attr; cur_attr = cur_attr->next) {
		dumpAttr(cur_attr, ctx);
		if(strcmp((const char*)cur_attr->name, "name") == 0) {
			name = (const char*)cur_attr->children->content;
		} else if(strcmp((const char*)cur_attr->name, "overwrite") == 0) {
			if(strcmp((const char*)cur_attr->children->content, "true") == 0) {
				overwrite = TRUE;
			}
		}
	}

	dumpContent((xmlNode *)element, ctx);
149 150 151 152
	if (element->children)
		value = (const char *)element->children->content;
	else
		value = "";
Yann Diorcet's avatar
Yann Diorcet committed
153 154 155 156

	if(name != NULL) {
		const char *str = lp_config_get_string(ctx->lpc, sectionName, name, NULL);
		if(str == NULL || overwrite) {
Yann Diorcet's avatar
Yann Diorcet committed
157
			xml2lpc_log(ctx, XML2LPC_MESSAGE, "Set %s|%s = %s", sectionName, name, value);
Yann Diorcet's avatar
Yann Diorcet committed
158 159 160 161 162 163 164
			lp_config_set_string(ctx->lpc, sectionName, name, value);
		} else {
			xml2lpc_log(ctx, XML2LPC_MESSAGE, "Don't touch %s|%s = %s",sectionName, name, str);
		}
	} else {
		xml2lpc_log(ctx, XML2LPC_WARNING, "ignored entry with no \"name\" attribute line:%d",xmlGetLineNo((xmlNode*)element));
	}
165
		return 0;
Yann Diorcet's avatar
Yann Diorcet committed
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
}

static int processSection(xmlElement *element, xml2lpc_context *ctx) {
	xmlNode *cur_node = NULL;
	xmlNode *cur_attr = NULL;
	const char *name = NULL;

	for (cur_attr = (xmlNode *)element->attributes; cur_attr; cur_attr = cur_attr->next) {
		dumpAttr(cur_attr, ctx);
		if(strcmp((const char*)cur_attr->name, "name") == 0) {
			name = (const char*)cur_attr->children->content;
		}
	}

	if(name != NULL) {
		for (cur_node = element->children; cur_node; cur_node = cur_node->next) {
			dumpNode(cur_node, ctx);
			if (cur_node->type == XML_ELEMENT_NODE) {
				if(strcmp((const char*)cur_node->name, "entry") == 0 ) {
					processEntry((xmlElement*)cur_node, name, ctx);
				}
			}
188 189 190 191

		}
		} else {
			xml2lpc_log(ctx, XML2LPC_WARNING, "ignored section with no \"name\" attribute, line:%d", xmlGetLineNo((xmlNode*)element));
Yann Diorcet's avatar
Yann Diorcet committed
192 193
		}

194
		return 0;
Yann Diorcet's avatar
Yann Diorcet committed
195 196 197 198 199 200 201
}

static int processConfig(xmlElement *element, xml2lpc_context *ctx) {
	xmlNode *cur_node = NULL;

	for (cur_node = element->children; cur_node; cur_node = cur_node->next) {
		dumpNode(cur_node, ctx);
202
		if (cur_node->type == XML_ELEMENT_NODE &&
Yann Diorcet's avatar
Yann Diorcet committed
203 204
			strcmp((const char*)cur_node->name, "section") == 0 ) {
			processSection((xmlElement*)cur_node, ctx);
205 206 207
			}

		}
Yann Diorcet's avatar
Yann Diorcet committed
208 209 210 211 212
	return 0;
}

static int processDoc(xmlNode *node, xml2lpc_context *ctx) {
	dumpNode(node, ctx);
213 214

	if (node->type == XML_ELEMENT_NODE &&
Yann Diorcet's avatar
Yann Diorcet committed
215 216 217 218 219 220 221 222
		strcmp((const char*)node->name, "config") == 0 ) {
		processConfig((xmlElement*)node, ctx);
	} else {
		xml2lpc_log(ctx, XML2LPC_WARNING, "root element is not \"config\", line:%d", xmlGetLineNo(node));
	}
	return 0;
}

223
static int internal_convert_xml2lpc(xml2lpc_context *ctx) {
224 225 226
	xmlNode *rootNode;
	int ret;

Yann Diorcet's avatar
Yann Diorcet committed
227
	xml2lpc_log(ctx, XML2LPC_DEBUG, "Parse started");
228
	rootNode = xmlDocGetRootElement(ctx->doc);
Yann Diorcet's avatar
Yann Diorcet committed
229
	//dumpNodes(0, rootNode, cbf, ctx);
230
	ret = processDoc(rootNode, ctx);
Yann Diorcet's avatar
Yann Diorcet committed
231 232 233 234 235 236
	xml2lpc_log(ctx, XML2LPC_DEBUG, "Parse ended ret:%d", ret);
	return ret;
}

int xml2lpc_validate(xml2lpc_context *xmlCtx) {
	xmlSchemaValidCtxtPtr validCtx;
237 238 239 240 241
	xmlSchemaParserCtxtPtr parserCtx;
	int ret;

	xml2lpc_context_clear_logs(xmlCtx);
	parserCtx = xmlSchemaNewDocParserCtxt(xmlCtx->xsd);
Yann Diorcet's avatar
Yann Diorcet committed
242 243
	validCtx = xmlSchemaNewValidCtxt(xmlSchemaParse(parserCtx));
	xmlSchemaSetValidErrors(validCtx, xml2lpc_genericxml_error, xml2lpc_genericxml_warning, xmlCtx);
244
	ret = xmlSchemaValidateDoc(validCtx, xmlCtx->doc);
245
	if(ret > 0) {
Yann Diorcet's avatar
Yann Diorcet committed
246 247 248 249
		if(strlen(xmlCtx->warningBuffer) > 0)
			xml2lpc_log(xmlCtx, XML2LPC_WARNING, "%s", xmlCtx->warningBuffer);
		if(strlen(xmlCtx->errorBuffer) > 0)
			xml2lpc_log(xmlCtx, XML2LPC_ERROR, "%s", xmlCtx->errorBuffer);
250
	} else if(ret < 0) {
Yann Diorcet's avatar
Yann Diorcet committed
251 252 253 254 255 256 257 258
		xml2lpc_log(xmlCtx, XML2LPC_ERROR, "Internal error");
	}
	xmlSchemaFreeValidCtxt(validCtx);
	return ret;
}

int xml2lpc_convert(xml2lpc_context *xmlCtx, LpConfig *lpc) {
	xml2lpc_context_clear_logs(xmlCtx);
Yann Diorcet's avatar
Yann Diorcet committed
259 260 261 262 263 264 265
	if(xmlCtx->doc == NULL) {
		xml2lpc_log(xmlCtx, XML2LPC_ERROR, "No doc set");
		return -1;
	}
	if(lpc == NULL) {
		xml2lpc_log(xmlCtx, XML2LPC_ERROR, "Invalid lpc");
	}
Yann Diorcet's avatar
Yann Diorcet committed
266
	xmlCtx->lpc = lpc;
267
	return internal_convert_xml2lpc(xmlCtx);
Yann Diorcet's avatar
Yann Diorcet committed
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363
}

int xml2lpc_set_xml_file(xml2lpc_context* xmlCtx, const char *filename) {
	xml2lpc_context_clear_logs(xmlCtx);
	xmlSetGenericErrorFunc(xmlCtx, xml2lpc_genericxml_error);
	if(xmlCtx->doc != NULL) {
		xmlFreeDoc(xmlCtx->doc);
		xmlCtx->doc = NULL;
	}
	xmlCtx->doc = xmlReadFile(filename, NULL, 0);
	if(xmlCtx->doc == NULL) {
		xml2lpc_log(xmlCtx, XML2LPC_ERROR, "Can't open/parse file \"%s\"", filename);
		xml2lpc_log(xmlCtx, XML2LPC_ERROR, "%s", xmlCtx->errorBuffer);
		return -1;
	}
	return 0;
}

int xml2lpc_set_xml_fd(xml2lpc_context* xmlCtx, int fd) {
	xml2lpc_context_clear_logs(xmlCtx);
	xmlSetGenericErrorFunc(xmlCtx, xml2lpc_genericxml_error);
	if(xmlCtx->doc != NULL) {
		xmlFreeDoc(xmlCtx->doc);
		xmlCtx->doc = NULL;
	}
	xmlCtx->doc = xmlReadFd(fd, 0, NULL, 0);
	if(xmlCtx->doc == NULL) {
		xml2lpc_log(xmlCtx, XML2LPC_ERROR, "Can't open/parse fd \"%d\"", fd);
		xml2lpc_log(xmlCtx, XML2LPC_ERROR, "%s", xmlCtx->errorBuffer);
		return -1;
	}
	return 0;
}

int xml2lpc_set_xml_string(xml2lpc_context* xmlCtx, const char *content) {
	xml2lpc_context_clear_logs(xmlCtx);
	xmlSetGenericErrorFunc(xmlCtx, xml2lpc_genericxml_error);
	if(xmlCtx->doc != NULL) {
		xmlFreeDoc(xmlCtx->doc);
		xmlCtx->doc = NULL;
	}
	xmlCtx->doc = xmlReadDoc((const unsigned char*)content, 0, NULL, 0);
	if(xmlCtx->doc == NULL) {
		xml2lpc_log(xmlCtx, XML2LPC_ERROR, "Can't parse string");
		xml2lpc_log(xmlCtx, XML2LPC_ERROR, "%s", xmlCtx->errorBuffer);
		return -1;
	}
	return 0;
}

int xml2lpc_set_xsd_file(xml2lpc_context* xmlCtx, const char *filename) {
	xml2lpc_context_clear_logs(xmlCtx);
	xmlSetGenericErrorFunc(xmlCtx, xml2lpc_genericxml_error);
	if(xmlCtx->xsd != NULL) {
		xmlFreeDoc(xmlCtx->xsd);
		xmlCtx->xsd = NULL;
	}
	xmlCtx->xsd = xmlReadFile(filename, NULL, 0);
	if(xmlCtx->xsd == NULL) {
		xml2lpc_log(xmlCtx, XML2LPC_ERROR, "Can't open/parse file \"%s\"", filename);
		xml2lpc_log(xmlCtx, XML2LPC_ERROR, "%s", xmlCtx->errorBuffer);
		return -1;
	}
	return 0;
}

int xml2lpc_set_xsd_fd(xml2lpc_context* xmlCtx, int fd) {
	xml2lpc_context_clear_logs(xmlCtx);
	xmlSetGenericErrorFunc(xmlCtx, xml2lpc_genericxml_error);
	if(xmlCtx->xsd != NULL) {
		xmlFreeDoc(xmlCtx->xsd);
		xmlCtx->xsd = NULL;
	}
	xmlCtx->xsd = xmlReadFd(fd, 0, NULL, 0);
	if(xmlCtx->xsd == NULL) {
		xml2lpc_log(xmlCtx, XML2LPC_ERROR, "Can't open/parse fd \"%d\"", fd);
		xml2lpc_log(xmlCtx, XML2LPC_ERROR, "%s", xmlCtx->errorBuffer);
		return -1;
	}
	return 0;
}

int xml2lpc_set_xsd_string(xml2lpc_context* xmlCtx, const char *content) {
	xml2lpc_context_clear_logs(xmlCtx);
	xmlSetGenericErrorFunc(xmlCtx, xml2lpc_genericxml_error);
	if(xmlCtx->xsd != NULL) {
		xmlFreeDoc(xmlCtx->xsd);
		xmlCtx->xsd = NULL;
	}
	xmlCtx->xsd = xmlReadDoc((const unsigned char*)content, 0, NULL, 0);
	if(xmlCtx->xsd == NULL) {
		xml2lpc_log(xmlCtx, XML2LPC_ERROR, "Can't parse string");
		xml2lpc_log(xmlCtx, XML2LPC_ERROR, "%s", xmlCtx->errorBuffer);
		return -1;
	}
	return 0;
Yann Diorcet's avatar
Yann Diorcet committed
364
}