lpc2xml.c 10.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
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
18
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19 20
*/

21
#include "lpc2xml.h"
22 23
#include <string.h>
#include <libxml/xmlsave.h>
24
#include <libxml/xmlversion.h>
25 26

#define LPC2XML_BZ 2048
27 28 29 30 31 32 33
#define ISO_ENCODING "ISO-8859-1"

static xmlChar* convert_iso_to_utf8(const char *in) {
	xmlChar *out = NULL;
	int ret, size, out_size, temp;
	xmlCharEncodingHandlerPtr handler;

34 35
	size = (int)strlen(in) + 1;
	out_size = size * 2 - 1;
36
	out = reinterpret_cast<xmlChar *>(ms_malloc((size_t)out_size));
37 38 39 40 41 42 43

	if (out) {
		handler = xmlFindCharEncodingHandler(ISO_ENCODING);
		if (!handler) {
			ms_free(out);
			return NULL;
		}
44

45 46 47 48 49 50
		temp = size-1;
		ret = handler->input(out, &out_size, (const xmlChar *)in, &temp);
		if (ret < 0 || temp - size + 1) {
			ms_free(out);
			return NULL;
		} else {
Benjamin REIS's avatar
Benjamin REIS committed
51
			out = reinterpret_cast<xmlChar *>(ms_realloc(out, (size_t)out_size + 1));
52 53 54 55
			out[out_size] = '\0';
		}
	}
	return out;
56
}
57 58 59 60 61

struct _lpc2xml_context {
	const LpConfig *lpc;
	lpc2xml_function cbf;
	void *ctx;
62

63 64 65 66 67 68 69 70 71 72 73 74
	xmlDoc *doc;
	char errorBuffer[LPC2XML_BZ];
	char warningBuffer[LPC2XML_BZ];
};


lpc2xml_context* lpc2xml_context_new(lpc2xml_function cbf, void *ctx) {
	lpc2xml_context *xmlCtx = (lpc2xml_context*)malloc(sizeof(lpc2xml_context));
	if(xmlCtx != NULL) {
		xmlCtx->lpc = NULL;
		xmlCtx->cbf = cbf;
		xmlCtx->ctx = ctx;
75

76 77 78 79 80 81 82 83 84 85 86 87 88 89
		xmlCtx->doc = NULL;
		xmlCtx->errorBuffer[0]='\0';
		xmlCtx->warningBuffer[0]='\0';
	}
	return xmlCtx;
}

void lpc2xml_context_destroy(lpc2xml_context *ctx) {
	if(ctx->doc != NULL) {
		xmlFreeDoc(ctx->doc);
		ctx->doc = NULL;
	}
	free(ctx);
}
Yann Diorcet's avatar
Yann Diorcet committed
90

91 92 93
static void lpc2xml_context_clear_logs(lpc2xml_context *ctx) {
	ctx->errorBuffer[0]='\0';
	ctx->warningBuffer[0]='\0';
Yann Diorcet's avatar
Yann Diorcet committed
94
}
95

96
static void lpc2xml_log(lpc2xml_context *xmlCtx, lpc2xml_log_level level, const char *fmt, ...) {
97 98
	va_list args;
	va_start(args, fmt);
99 100 101 102 103 104
	if(xmlCtx->cbf != NULL) {
		xmlCtx->cbf((xmlCtx)->ctx, level, fmt, args);
	}
 	va_end(args);
}

Yann Diorcet's avatar
Yann Diorcet committed
105 106
static void lpc2xml_genericxml_error(void *ctx, const char *fmt, ...) {
	lpc2xml_context *xmlCtx = (lpc2xml_context *)ctx;
107
	size_t sl = strlen(xmlCtx->errorBuffer);
108 109
	va_list args;
	va_start(args, fmt);
Yann Diorcet's avatar
Yann Diorcet committed
110 111 112 113
	vsnprintf(xmlCtx->errorBuffer + sl, LPC2XML_BZ-sl, fmt, args);
	va_end(args);
}

114
/*
Yann Diorcet's avatar
Yann Diorcet committed
115 116 117
static void lpc2xml_genericxml_warning(void *ctx, const char *fmt, ...) {
	lpc2xml_context *xmlCtx = (lpc2xml_context *)ctx;
	int sl = strlen(xmlCtx->warningBuffer);
118 119
	va_list args;
	va_start(args, fmt);
Yann Diorcet's avatar
Yann Diorcet committed
120 121 122
	vsnprintf(xmlCtx->warningBuffer + sl, LPC2XML_BZ-sl, fmt, args);
	va_end(args);
}
123
*/
Yann Diorcet's avatar
Yann Diorcet committed
124

125 126
static int processEntry(const char *section, const char *entry, xmlNode *node, lpc2xml_context *ctx) {
	const char *content = lp_config_get_string(ctx->lpc, section, entry, NULL);
127
	xmlChar *converted_content = NULL;
128 129
	if (content == NULL) {
		lpc2xml_log(ctx, LPC2XML_ERROR, "Issue when reading the lpc");
130 131
		return -1;
	}
Yann Diorcet's avatar
Yann Diorcet committed
132
	lpc2xml_log(ctx, LPC2XML_MESSAGE, "Set %s|%s = %s", section, entry, content);
133
	converted_content = convert_iso_to_utf8(content);
134

135 136 137 138 139 140 141 142 143 144
	if (converted_content) {
		// xmlNodeSetContent expects special characters to be escaped, xmlNodeAddContent doesn't (and escapes what needs to be)
		xmlNodeSetContent(node, (const xmlChar *) "");
		xmlNodeAddContent(node, (const xmlChar *) converted_content);
		ms_free(converted_content);
	} else {
		// xmlNodeSetContent expects special characters to be escaped, xmlNodeAddContent doesn't (and escapes what needs to be)
		xmlNodeSetContent(node, (const xmlChar *) "");
		xmlNodeAddContent(node, (const xmlChar *) content);
	}
145

146
	if (lp_config_get_overwrite_flag_for_entry(ctx->lpc, section, entry) || lp_config_get_overwrite_flag_for_section(ctx->lpc, section)) {
147 148
		xmlSetProp(node, (const xmlChar *)"overwrite", (const xmlChar *) "true");
	}
149 150 151 152 153 154 155 156 157 158 159 160
	return 0;
}

struct __processSectionCtx {
	int ret;
	const char *section;
	xmlNode *node;
	lpc2xml_context *ctx;
};

static void processSection_cb(const char *entry, struct __processSectionCtx *ctx) {
	if(ctx->ret == 0) {
161
		const char *comment = "#";
162 163
		xmlNode *node;
		xmlAttr *name_attr;
164 165 166 167 168
		if (strncmp(comment, entry, strlen(comment)) == 0) {
			lpc2xml_log(ctx->ctx, LPC2XML_WARNING, "Skipped commented entry %s", entry);
			ctx->ret = 0;
			return;
		}
169

170 171
		if (lp_config_get_skip_flag_for_entry(ctx->ctx->lpc, ctx->section, entry)) {
			lpc2xml_log(ctx->ctx, LPC2XML_WARNING, "Skipped entry %s", entry);
172
			ctx->ret = 0;
173 174
			return;
		}
175

176
		node = xmlNewChild(ctx->node, NULL, (const xmlChar *)"entry", NULL);
177 178 179 180 181
		if(node == NULL) {
			lpc2xml_log(ctx->ctx, LPC2XML_ERROR, "Can't create \"entry\" element");
			ctx->ret = -1;
			return;
		}
182
		name_attr = xmlSetProp(node, (const xmlChar *)"name", (const xmlChar *)entry);
183 184 185 186 187
		if(name_attr == NULL) {
			lpc2xml_log(ctx->ctx, LPC2XML_ERROR, "Can't create name attribute for \"entry\" element");
			ctx->ret = -1;
			return;
		}
188

189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
		ctx->ret = processEntry(ctx->section, entry, node, ctx->ctx);
	}
}

static int processSection(const char *section, xmlNode *node, lpc2xml_context *ctx) {
	struct __processSectionCtx pc_ctx = {0, section, node, ctx};
	lp_config_for_each_entry(ctx->lpc, section, (void (*)(const char *, void *))processSection_cb, (void*)&pc_ctx);
	return pc_ctx.ret;
}



struct __processConfigCtx {
	int ret;
	xmlNode *node;
	lpc2xml_context *ctx;
};

static void processConfig_cb(const char *section, struct __processConfigCtx *ctx) {
	if(ctx->ret == 0) {
209
		xmlNode *node;
210
		xmlAttr *name_attr;
211

212 213
		if (lp_config_get_skip_flag_for_section(ctx->ctx->lpc, section)) {
			lpc2xml_log(ctx->ctx, LPC2XML_WARNING, "Skipped section %s", section);
214
			ctx->ret = 0;
215 216
			return;
		}
217

218
		node = xmlNewChild(ctx->node, NULL, (const xmlChar *)"section", NULL);
219 220 221 222 223
		if(node == NULL) {
			lpc2xml_log(ctx->ctx, LPC2XML_ERROR, "Can't create \"section\" element");
			ctx->ret = -1;
			return;
		}
224
		name_attr = xmlSetProp(node, (const xmlChar *)"name", (const xmlChar *)section);
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
		if(name_attr == NULL) {
			lpc2xml_log(ctx->ctx, LPC2XML_ERROR, "Can't create name attribute for \"section\" element");
			ctx->ret = -1;
			return;
		}
		ctx->ret = processSection(section, node, ctx->ctx);
	}
}

static int processConfig(xmlNode *node, lpc2xml_context *ctx) {
	struct __processConfigCtx pc_ctx = {0, node, ctx};
	lp_config_for_each_section(ctx->lpc, (void (*)(const char *, void *))processConfig_cb, (void*)&pc_ctx);
	return pc_ctx.ret;
}

static int processDoc(xmlDoc *doc, lpc2xml_context *ctx) {
	int ret = 0;
242 243 244
	xmlNs *xsi_ns;
	xmlNs *lpc_ns;
	xmlAttr *schemaLocation;
245 246 247 248 249
	xmlNode *root_node = xmlNewNode(NULL, (const xmlChar *)"config");
	if(root_node == NULL) {
		lpc2xml_log(ctx, LPC2XML_ERROR, "Can't create \"config\" element");
		return -1;
	}
250
	lpc_ns = xmlNewNs(root_node, (const xmlChar *)"http://www.linphone.org/xsds/lpconfig.xsd", NULL);
251 252 253 254 255
	if(lpc_ns == NULL) {
		lpc2xml_log(ctx, LPC2XML_WARNING, "Can't create lpc namespace");
	} else {
		xmlSetNs(root_node, lpc_ns);
	}
256
	xsi_ns = xmlNewNs(root_node, (const xmlChar *)"http://www.w3.org/2001/XMLSchema-instance", (const xmlChar *)"xsi");
257 258 259
	if(lpc_ns == NULL) {
		lpc2xml_log(ctx, LPC2XML_WARNING, "Can't create xsi namespace");
	}
260
	schemaLocation = xmlNewNsProp(root_node, xsi_ns, (const xmlChar *)"schemaLocation", (const xmlChar *)"http://www.linphone.org/xsds/lpconfig.xsd lpconfig.xsd");
261 262 263 264 265 266 267 268 269 270
	if(schemaLocation == NULL) {
		lpc2xml_log(ctx, LPC2XML_WARNING, "Can't create schemaLocation");
	}
	ret = processConfig(root_node, ctx);
	xmlDocSetRootElement(doc, root_node);
	return ret;
}

static int internal_convert_lpc2xml(lpc2xml_context *ctx) {
	int ret = 0;
271
	xmlDoc *doc;
272 273 274 275 276
	lpc2xml_log(ctx, LPC2XML_DEBUG, "Generation started");
	if(ctx->doc != NULL) {
		xmlFreeDoc(ctx->doc);
		ctx->doc = NULL;
	}
277
	doc = xmlNewDoc((const xmlChar *)"1.0");
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
	ret  = processDoc(doc, ctx);
	if(ret == 0) {
		ctx->doc = doc;
	} else {
		xmlFreeDoc(doc);
	}
	lpc2xml_log(ctx, LPC2XML_DEBUG, "Generation ended ret:%d", ret);
	return ret;
}

int lpc2xml_set_lpc(lpc2xml_context* context, const LpConfig *lpc) {
	context->lpc = lpc;
	return 0;
}

int lpc2xml_convert_file(lpc2xml_context* context, const char *filename) {
Yann Diorcet's avatar
Yann Diorcet committed
294
	int ret = -1;
295
	xmlSaveCtxtPtr save_ctx;
Yann Diorcet's avatar
Yann Diorcet committed
296 297
	lpc2xml_context_clear_logs(context);
	xmlSetGenericErrorFunc(context, lpc2xml_genericxml_error);
298
	save_ctx = xmlSaveToFilename(filename, "UTF-8", XML_SAVE_FORMAT);
Yann Diorcet's avatar
Yann Diorcet committed
299 300 301
	if(save_ctx != NULL) {
		ret = internal_convert_lpc2xml(context);
		if(ret == 0) {
302
			ret = (int)xmlSaveDoc(save_ctx, context->doc);
Yann Diorcet's avatar
Yann Diorcet committed
303 304 305 306 307 308 309 310 311
			if(ret != 0) {
				lpc2xml_log(context, LPC2XML_ERROR, "Can't save document");
				lpc2xml_log(context, LPC2XML_ERROR, "%s", context->errorBuffer);
			}
		}
		xmlSaveClose(save_ctx);
	} else {
		lpc2xml_log(context, LPC2XML_ERROR, "Can't open file:%s", filename);
		lpc2xml_log(context, LPC2XML_ERROR, "%s", context->errorBuffer);
312 313 314 315 316
	}
	return ret;
}

int lpc2xml_convert_fd(lpc2xml_context* context, int fd) {
Yann Diorcet's avatar
Yann Diorcet committed
317
	int ret = -1;
318
	xmlSaveCtxtPtr save_ctx;
Yann Diorcet's avatar
Yann Diorcet committed
319 320
	lpc2xml_context_clear_logs(context);
	xmlSetGenericErrorFunc(context, lpc2xml_genericxml_error);
321
	save_ctx = xmlSaveToFd(fd, "UTF-8", XML_SAVE_FORMAT);
Yann Diorcet's avatar
Yann Diorcet committed
322 323 324
	if(save_ctx != NULL) {
		ret = internal_convert_lpc2xml(context);
		if(ret == 0) {
325
			ret = (int)xmlSaveDoc(save_ctx, context->doc);
Yann Diorcet's avatar
Yann Diorcet committed
326 327 328 329 330 331 332 333 334
			if(ret != 0) {
				lpc2xml_log(context, LPC2XML_ERROR, "Can't save document");
				lpc2xml_log(context, LPC2XML_ERROR, "%s", context->errorBuffer);
			}
		}
		xmlSaveClose(save_ctx);
	} else {
		lpc2xml_log(context, LPC2XML_ERROR, "Can't open fd:%d", fd);
		lpc2xml_log(context, LPC2XML_ERROR, "%s", context->errorBuffer);
335 336 337 338
	}
	return ret;
}

339
int lpc2xml_convert_string(lpc2xml_context* context, char **content) {
Yann Diorcet's avatar
Yann Diorcet committed
340
	int ret = -1;
341
	xmlBufferPtr buffer = xmlBufferCreate();
342
	xmlSaveCtxtPtr save_ctx;
Yann Diorcet's avatar
Yann Diorcet committed
343 344
	lpc2xml_context_clear_logs(context);
	xmlSetGenericErrorFunc(context, lpc2xml_genericxml_error);
345
	save_ctx = xmlSaveToBuffer(buffer, "UTF-8", XML_SAVE_FORMAT);
Yann Diorcet's avatar
Yann Diorcet committed
346 347 348
	if(save_ctx != NULL) {
		ret = internal_convert_lpc2xml(context);
		if(ret == 0) {
349
			ret = (int)xmlSaveDoc(save_ctx, context->doc);
Yann Diorcet's avatar
Yann Diorcet committed
350 351 352 353 354 355 356 357 358
			if(ret != 0) {
				lpc2xml_log(context, LPC2XML_ERROR, "Can't save document");
				lpc2xml_log(context, LPC2XML_ERROR, "%s", context->errorBuffer);
			}
		}
		xmlSaveClose(save_ctx);
	} else {
		lpc2xml_log(context, LPC2XML_ERROR, "Can't initialize internal buffer");
		lpc2xml_log(context, LPC2XML_ERROR, "%s", context->errorBuffer);
359 360
	}
	if(ret == 0) {
361 362 363 364 365
#if LIBXML_VERSION >= 20800
		*content = (char *)xmlBufferDetach(buffer);
#else
		*content = strdup((const char *)xmlBufferContent(buffer));
#endif
366 367 368 369
	}
	xmlBufferFree(buffer);
	return ret;
}